提问人:Quick learner 提问时间:11/9/2023 最后编辑:Quick learner 更新时间:11/18/2023 访问量:147
单击浮动操作按钮时不重新生成 BlocBuilder
BlocBuilder not rebuilding on floating action button click
问:
使用 Bloc 模式显示从 rest api 获取的数据列表。 现在我想在单击按钮时更新列表视图上的列表。 当我单击按钮时,它会获取数据,但不会更新列表视图 这是完整的代码。
main.dart的
import 'package:bloc_demo/homepage.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
主页.dart
class HomePage extends StatelessWidget {
String pageNo = "1";
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<UserBloc>(
create: (BuildContext context) => UserBloc(UserRepository(),pageNo),
),
],
child: Scaffold(
appBar: AppBar(title: const Text('The BloC App')),
body: blocBody(),
floatingActionButton: FloatingActionButton(
onPressed: () {
pageNo = "2";
UserBloc(
UserRepository(),pageNo,
).add(LoadUserEvent());
},
child: const Icon(Icons.navigation),
),
),
);
}
Widget blocBody() {
return BlocProvider(
create: (context) => UserBloc(
UserRepository(),pageNo,
)..add(LoadUserEvent()),
child: BlocBuilder<UserBloc, UserState>(
builder: (context, state) {
if (state is UserLoadingState) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (state is UserLoadedState) {
List<UserModel> userList = state.users;
return ListView.builder(
itemCount: userList.length,
itemBuilder: (_, index) {
return Padding(
padding:
const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
child: Card(
color: Theme.of(context).primaryColor,
child: ListTile(
title: Text(
'${userList[index].firstName} ${userList[index].lastName}',
style: const TextStyle(color: Colors.white),
),
subtitle: Text(
'${userList[index].email}',
style: const TextStyle(color: Colors.white),
),
leading: CircleAvatar(
backgroundImage: NetworkImage(
userList[index].avatar.toString()),
))),
);
});
}
if (state is UserErrorState) {
return const Center(
child: Text("Error"),
);
}
return Container();
},
),
);
}
}
用户集团
class UserBloc extends Bloc<UserEvent, UserState> {
final UserRepository _userRepository;
final String pageNo;
UserBloc(this._userRepository,this.pageNo) : super(UserLoadingState()) {
on<LoadUserEvent>((event, emit) async {
emit(UserLoadingState());
try {
final users = await _userRepository.getUsers(pageNo);
emit(UserLoadedState(users));
} catch (e) {
emit(UserErrorState(e.toString()));
}
});
}
}
用户事件
@immutable
abstract class UserEvent extends Equatable {
const UserEvent();
}
class LoadUserEvent extends UserEvent {
@override
List<Object?> get props => [];
}
用户状态
@immutable
abstract class UserState extends Equatable {}
//data loading state
class UserLoadingState extends UserState {
@override
List<Object?> get props => [];
}
class UserLoadedState extends UserState {
final List<UserModel> users;
UserLoadedState(this.users);
@override
List<Object?> get props => [users];
}
class UserErrorState extends UserState {
final String error;
UserErrorState(this.error);
@override
List<Object?> get props => [error];
}
用户模型
class UserModel {
int? id;
String? email;
String? firstName;
String? lastName;
String? avatar;
UserModel({this.id, this.email, this.firstName, this.lastName, this.avatar});
UserModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
email = json['email'];
firstName = json['first_name'];
lastName = json['last_name'];
avatar = json['avatar'];
}
}
用户存储库
class UserRepository {
String userUrl = 'https://reqres.in/api/users?page=';
Future<List<UserModel>> getUsers(String pageNo) async {
Response response = await get(Uri.parse(userUrl+pageNo));
if (response.statusCode == 200) {
final List result = jsonDecode(response.body)['data'];
return result.map((e) => UserModel.fromJson(e)).toList();
} else {
throw Exception(response.reasonPhrase);
}
}
}
答:
第一个问题是,它正在使用新的存储库创建新的 bloc 实例。如果您愿意,可以创建单例类。UserRepository
要使用访问当前 bloc 实例,您可以使用 .BlocProvider.of<UserBloc>(context);
final bloc = BlocProvider.of<UserBloc>(context);
bloc.add(LoadUserEvent());
您无法按照创建该集团的相同上下文访问该集团。您可以将 Builder 或 new widget 用于单独的上下文。
现在,当我们获取数据时,旧数据将被新数据替换,而不是扩展列表。因此,我正在修改集团,例如
class UserBloc extends Bloc<UserEvent, UserState> {
final UserRepository _userRepository;
final String pageNo; //I prefer using event for this.
UserBloc(this._userRepository, this.pageNo) : super(UserLoadingState()) {
on<LoadUserEvent>((event, emit) async {
// emit(UserLoadingState()); //not needed, else we need to lift up the old user list
debugPrint("pageNo $pageNo");
try {
final users = await _userRepository.getUsers(pageNo);
List<UserModel> oldUser = []; //get the old user list
if (state is UserLoadedState) {
oldUser = (state as UserLoadedState).users;
debugPrint("oldUser.length ${oldUser.length}");
}
emit(UserLoadedState([...oldUser, ...users]));
} catch (e) {
emit(UserErrorState(e.toString()));
}
});
}
}
完整片段
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:http/http.dart';
import 'dart:convert';
import 'package:equatable/equatable.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
String pageNo = "1";
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<UserBloc>(
create: (BuildContext context) => UserBloc(UserRepository(), pageNo)
..add(LoadUserEvent()), //initially load the user
),
],
child: Builder(
builder: (context) {
/// you cant directly access the bloc from here, so use builder or new widget
return Scaffold(
appBar: AppBar(title: const Text('The BloC App')),
body: blocBody(),
floatingActionButton: FloatingActionButton(
onPressed: () {
final bloc = BlocProvider.of<UserBloc>(context);
var pageNo = "2";
bloc.add(LoadUserEvent());
},
child: const Icon(Icons.navigation),
),
);
},
),
);
}
Widget blocBody() {
return BlocBuilder<UserBloc, UserState>(
builder: (context, state) {
if (state is UserLoadingState) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (state is UserLoadedState) {
List<UserModel> userList = state.users;
debugPrint("userList.length ${userList.length}");
return ListView.builder(
itemCount: userList.length,
itemBuilder: (_, index) {
return Padding(
padding:
const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
child: Card(
color: Theme.of(context).primaryColor,
child: ListTile(
title: Text(
'${userList[index].firstName} ${userList[index].lastName}',
style: const TextStyle(color: Colors.white),
),
subtitle: Text(
'${userList[index].email}',
style: const TextStyle(color: Colors.white),
),
leading: CircleAvatar(
// backgroundImage:
// NetworkImage(userList[index].avatar.toString()),
),
),
),
);
});
}
if (state is UserErrorState) {
return const Center(
child: Text("Error"),
);
}
return Container();
},
);
}
}
class UserBloc extends Bloc<UserEvent, UserState> {
final UserRepository _userRepository;
final String pageNo;
UserBloc(this._userRepository, this.pageNo) : super(UserLoadingState()) {
on<LoadUserEvent>((event, emit) async {
// emit(UserLoadingState()); //not needed, else we need to lift up the old user list
debugPrint("pageNo $pageNo");
try {
final users = await _userRepository.getUsers(pageNo);
List<UserModel> oldUser = []; //get the old user list
if (state is UserLoadedState) {
oldUser = (state as UserLoadedState).users;
debugPrint("oldUser.length ${oldUser.length}");
}
emit(UserLoadedState([...oldUser, ...users]));
} catch (e) {
emit(UserErrorState(e.toString()));
}
});
}
}
@immutable
abstract class UserEvent extends Equatable {
const UserEvent();
}
class LoadUserEvent extends UserEvent {
@override
List<Object?> get props => [];
}
@immutable
abstract class UserState extends Equatable {}
//data loading state
class UserLoadingState extends UserState {
@override
List<Object?> get props => [];
}
class UserLoadedState extends UserState {
final List<UserModel> users;
UserLoadedState(this.users);
@override
List<Object?> get props => [users];
}
class UserErrorState extends UserState {
final String error;
UserErrorState(this.error);
@override
List<Object?> get props => [error];
}
class UserModel { //also prefer extending Equatable
int? id;
String? email;
String? firstName;
String? lastName;
String? avatar;
UserModel({this.id, this.email, this.firstName, this.lastName, this.avatar});
UserModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
email = json['email'];
firstName = json['first_name'];
lastName = json['last_name'];
avatar = json['avatar'];
}
}
class UserRepository {
String userUrl = 'https://reqres.in/api/users?page=';
Future<List<UserModel>> getUsers(String pageNo) async {
// Response response = await get(Uri.parse(userUrl+pageNo));
//
// if (response.statusCode == 200) {
// final List result = jsonDecode(response.body)['data'];
// return result.map((e) => UserModel.fromJson(e)).toList();
// } else {
// throw Exception(response.reasonPhrase);
// }
return List.generate(
3,
(index) => UserModel(
id: index,
email: "email",
firstName: "firstName",
lastName: "lastName",
avatar: "avatar"));
}
}
评论
full snippet
- 转换为 :这使您可以正确维护页码状态。此外,它还允许您始终如一地与 bloc 实例进行交互。
HomePage
StatefulWidget
- 使用 的单个实例 :不要在每次单击按钮时创建一个新实例,而是使用通过 提供给小部件树的相同实例。
UserBloc
UserBloc
BlocProvider
如何重构代码:
更新HomePage
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String pageNo = "1";
late UserBloc userBloc;
@override
void initState() {
super.initState();
userBloc = UserBloc(UserRepository(), pageNo)..add(LoadUserEvent());
}
@override
Widget build(BuildContext context) {
return BlocProvider<UserBloc>(
create: (BuildContext context) => userBloc,
child: Scaffold(
appBar: AppBar(title: const Text('The BloC App')),
body: blocBody(),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
pageNo = "2"; // Update the page number
});
userBloc.add(LoadUserEvent()); // Use the existing bloc instance
},
child: const Icon(Icons.navigation),
),
),
);
}
Widget blocBody() {
return BlocBuilder<UserBloc, UserState>(
builder: (context, state) {
// Your existing BlocBuilder code
},
);
}
}
我没有经常使用bloc,但是当您修改列表时,大多数状态管理不会触发变量更改的侦听事件,这就是为什么您应该替换现有列表而不仅仅是修改它的原因。
我的意思是,如果你没有,那就不要了
新增功能List A
A.add()
newA.fromList(...A,[])
该问题可能与更新页码和在 回调中触发数据提取的方式有关。与其直接创建新实例,不如使用 提供的现有实例,并从那里调度 。FloatingActionButton
onPressed
UserBloc
UserBloc
BlocProvider
LoadUserEvent
以下是 onPressed 回调的更新版本:FloatingActionButton
floatingActionButton: FloatingActionButton(
onPressed: () {
// Access the existing UserBloc instance using BlocProvider
context.read<UserBloc>().add(LoadUserEvent(pageNo: "2"));
},
child: const Icon(Icons.navigation),
),
然后,您需要修改您的类以包含页码:LoadUserEvent
class LoadUserEvent extends UserEvent {
final String pageNo;
LoadUserEvent({required this.pageNo});
@override
List<Object?> get props => [pageNo];
}
最后,在 中,更新处理以使用提供的页码:UserBloc
LoadUserEvent
on<LoadUserEvent>((event, emit) async {
emit(UserLoadingState());
try {
final users = await _userRepository.getUsers(event.pageNo);
emit(UserLoadedState(users));
} catch (e) {
emit(UserErrorState(e.toString()));
}
});
这应确保您使用现有实例并在触发数据提取时传递正确的页码。此更改应触发 的重建并相应地更新 。UserBloc
BlocBuilder
ListView
评论