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

Bloc pattern does not show newly added user to sqflite on screen

我正在使用 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 我试过但没有帮助。正如评论

中指出的那样,我还在 insertUser() 中调用了 fetchUser();

以下是完整示例link

https://github.com/pritsawa/sqflite_example

问题是您使用的是 PublishSubject

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

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

根据评论跟进,您似乎在这两个页面中没有 UsersBloc 的单个实例。 HomePage 和 UserDetails return 都是一个 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 所说,使用 return 先前值的主题之一。

@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(),
        );
      }
...