单击浮动操作按钮时不重新生成 BlocBuilder

BlocBuilder not rebuilding on floating action button click

提问人:Quick learner 提问时间:11/9/2023 最后编辑:Quick learner 更新时间:11/18/2023 访问量:147

问:

使用 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);
    }
  }
}
Android Flutter setstate bloc do-not-use-typo-in-tag

评论

0赞 Ozan Taskiran 11/9/2023
您是否与调试器一起检查过您的 BLoC 是否跳入这一行:emit(UserLoadedState(users));
0赞 Quick learner 11/11/2023
是的,它确实发出,但它不调用 Bloc Builder 状态

答:

0赞 Md. Yeasin Sheikh 11/13/2023 #1

第一个问题是,它正在使用新的存储库创建新的 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"));
  }
}

评论

0赞 Quick learner 11/16/2023
emit(UserLoadedState([...oldUser,...用户]));- 此行在编译时抛出错误
0赞 Md. Yeasin Sheikh 11/16/2023
你能分享一下错误消息吗?你可以用 Flutter clean 复制和粘贴该部分full snippet
1赞 Faiz Ahmad Dae 11/18/2023 #2
  1. 转换为 :这使您可以正确维护页码状态。此外,它还允许您始终如一地与 bloc 实例进行交互。HomePageStatefulWidget
  2. 使用 的单个实例 :不要在每次单击按钮时创建一个新实例,而是使用通过 提供给小部件树的相同实例。UserBlocUserBlocBlocProvider

如何重构代码:

更新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
      },
    );
  }
}
0赞 NIMA Shahahmadian 11/18/2023 #3

我没有经常使用bloc,但是当您修改列表时,大多数状态管理不会触发变量更改的侦听事件,这就是为什么您应该替换现有列表而不仅仅是修改它的原因。

我的意思是,如果你没有,那就不要了 新增功能List AA.add()newA.fromList(...A,[])

0赞 Nova 11/18/2023 #4

该问题可能与更新页码和在 回调中触发数据提取的方式有关。与其直接创建新实例,不如使用 提供的现有实例,并从那里调度 。FloatingActionButtononPressedUserBlocUserBlocBlocProviderLoadUserEvent

以下是 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];
}

最后,在 中,更新处理以使用提供的页码:UserBlocLoadUserEvent

on<LoadUserEvent>((event, emit) async {
  emit(UserLoadingState());
  try {
    final users = await _userRepository.getUsers(event.pageNo);
    emit(UserLoadedState(users));
  } catch (e) {
    emit(UserErrorState(e.toString()));
  }
});

这应确保您使用现有实例并在触发数据提取时传递正确的页码。此更改应触发 的重建并相应地更新 。UserBlocBlocBuilderListView