Bloc 模式不会在屏幕上显示新添加的用户到 sqflite

Posted

技术标签:

【中文标题】Bloc 模式不会在屏幕上显示新添加的用户到 sqflite【英文标题】:Bloc pattern does not show newly added user to sqflite on screen 【发布时间】:2019-06-19 03:53:37 【问题描述】:

我正在使用sqflite 数据库来保存用户列表。 我有用户列表屏幕,它显示用户列表,它有一个 fab 按钮, 点击 fab 按钮,用户被重定向到下一个屏幕,他可以在其中将新用户添加到数据库。 新用户已正确插入数据库 但是当用户按下后退按钮并返回用户列表屏幕时, 新添加的用户在屏幕上不可见。 我必须关闭应用程序并重新打开它,然后屏幕上才能看到新添加的用户。

我正在使用bloc 模式,下面是我显示用户列表的代码

class _UserListState extends State<UserList> 
  UserBloc userBloc;

  @override
  void initState() 
    super.initState();
    userBloc = BlocProvider.of<UserBloc>(context);
    userBloc.fetchUser();
  

  @override
  void dispose() 
    userBloc?.dispose();
    super.dispose();
  

  @override
  Widget build(BuildContext context) 
    return Scaffold(

      floatingActionButton: FloatingActionButton(
        onPressed: () 
          Navigator.of(context).pushNamed("/detail");
        ,
        child: Icon(Icons.add),
      ),
      body: StreamBuilder(
        stream: userBloc.users,
        builder: (context, AsyncSnapshot<List<User>> snapshot) 
          if (!snapshot.hasData) 
            return Center(
              child: CircularProgressIndicator(),
            );
          

          if (snapshot.data != null) 
            return ListView.builder(
              itemBuilder: (context, index) 
                return Dismissible(
                  key: Key(snapshot.data[index].id.toString()),
                  direction: DismissDirection.endToStart,
                  onDismissed: (direction) 
                    userBloc.deleteParticularUser(snapshot.data[index]);
                  ,
                  child: ListTile(
                    onTap: () 
                      Navigator.of(context).push(MaterialPageRoute(
                          builder: (context) => UserDetail(
                               user: snapshot.data[index],
                              )));
                    ,
                    title: Text(snapshot.data[index].name),
                    subtitle:
                        Text("Mobile Number $snapshot.data[index].userId"),
                    trailing:
                        Text("User Id $snapshot.data[index].mobileNumber"),
                  ),
                );
              ,
              itemCount: snapshot.data.length,
            );
          
        ,
      ),
    );
  

以下是我的集团代码

    class UserBloc implements BlocBase 
  final _users = BehaviorSubject<List<User>>();

  Observable<List<User>> get users => _users.stream;

  fetchUser() async 
    await userRepository.initializeDatabase();

    final users = await userRepository.getUserList();
    _users.sink.add(users);
  

  insertUser(String name,int id,int phoneNumber) async 
    userRepository.insertUser(User(id, name, phoneNumber));
    fetchUser();
  

  updateUser(User user) async 
    userRepository.updateUser(user);
  

  deleteParticularUser(User user) async 
    userRepository.deleteParticularUser(user);
  

  deleteAllUser() 
    return userRepository.deleteAllUsers();
  

  @override
  void dispose() 
    _users.close();
  

正如 Remi 发布的答案所说,我应该尝试 BehaviorSubjectReplaySubject,但它没有帮助。正如 cmets 中所指出的,我还在 insertUser() 中调用了 fetchUser();

以下是完整示例的链接

https://github.com/pritsawa/sqflite_example

【问题讨论】:

你能展示你的集团吗? @RémiRousselet 我已经在问题中添加了代码,请看一下 首先,正如 Remi 所说,您应该使用 BehaviorSubject。您遇到的主要问题是,在插入新用户后,您不会从数据库中更新用户流。因此,在 insertUser() 方法中,除了调用 userRepository.insertUser() 之外,您还应该调用 fetchUser() 来重新查询数据库以获取新的存储用户列表并使用新数据更新您的用户流。 @Luksprog 我尝试了 BehaviourSubject 和 ReplaySubject 并在 insertUser() 中添加了 fetchUser(),但它仍然不起作用。当我插入完成不同的屏幕时,在 insertUser 中调用 fetch user 有什么意义,然后我也尝试了你的方法,但它不起作用 我同意@Luksprog,这应该会有所帮助 【参考方案1】:

从 cmets 跟进,您似乎在这两个页面中没有一个 UsersBloc 实例。 HomePage 和 UserDetails 都返回一个 BlocProvider 实例化一个 UsersBloc 实例。因为你有两个 blocs 实例(你不应该有)你没有正确更新流。

解决方案是从这两个页面(HomePage 和 UserDetail)中删除 BlocProvider,并将 MaterialApp 小部件包装在其中,以使相同的实例可用于两个页面。

class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return BlocProvider(
      bloc: UserBloc(),
      child:MaterialApp(...

主页将是:

class HomePage extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return UserList(),
    );
  

也从 UserDetail 中删除 BlocProvider。 然后在UsersBloc中,在插入用户后调用insertUser()方法内的fetchUser(),重新查询数据库并更新用户流。

正如 Rémi Rousselet 所说,使用返回先前值的主题之一。

【讨论】:

这甚至适用于 PublishSubject。非常感谢您的时间。作为 bloc 的初学者,我不知道如果它是同一个 bloc,我们应该对所有页面只有一个 bloc 实例。【参考方案2】:

问题是您使用的是PublishSubject

当一个新的监听器订阅PublishSubject时,它不会收到之前发送的值,只会接收下一个事件。

最简单的解决方案是改用BehaviorSubjectReplaySubject。这两个将直接用最新的值调用他们的监听器。

【讨论】:

不起作用。我尝试用 BehaviorSubject 和 ReplaySubject 替换 PublishSubject。 看看我更新的问题。我试过你的方法,但没有帮助【参考方案3】:
@override
  void initState() 
    super.initState();
    userBloc = BlocProvider.of<UserBloc>(context);
    userBloc.fetchUser();
  

问题是您在页面的 initState 中调用了 userBloc.fetchUser() 函数。

每当向其中添加新数据时都会发出 Bloc 流,而 userBloc.fetchUser() 函数正是这样做的,它添加了您从 Sqflite 数据库中获取的 userList。

当您从添加用户屏幕返回到用户列表屏幕时,不会调用 init 函数。它仅在创建用户列表屏幕时调用,即每当您将其推送到导航堆栈时。

解决方法是在 StreamBuilder 的快照数据为空时调用 userBloc.fetchUser()。

...
 if (!snapshot.hasData) 
      userBloc.fetchUser();
        return Center(
          child: CircularProgressIndicator(),
        );
      
...

【讨论】:

以上是关于Bloc 模式不会在屏幕上显示新添加的用户到 sqflite的主要内容,如果未能解决你的问题,请参考以下文章

Flutter BLoC 模式 - 如何在流事件后导航到另一个屏幕?

Flutter 再次触发 bloc 事件

当网络连接恢复时,使用 Bloc /Cubit 颤振不会发生自动刷新?

基于流的颤振小吃店展示

使用 Flutter BLoC 模式跟踪多个状态

如何在颤动中使用 bloc 模式进行错误处理?