Блок-шаблон не отображает нового пользователя в sqflite на экране
Я использую sqflite
База данных для сохранения списка пользователей. У меня есть экран со списком пользователей, который показывает список пользователей, и у него есть потрясающая кнопка, при нажатии на кнопку потрясающего пользователя, пользователь перенаправляется на следующий экран, где он может добавить нового пользователя в базу данных. Новый пользователь правильно вставлен в базу данных, но когда пользователь нажимает кнопку "Назад" и возвращается на экран списка пользователей, вновь добавленный пользователь не отображается на экране. Я должен закрыть приложение и открыть его снова, тогда вновь добавленный пользователь виден на экране.
я использую 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();
}
}
Поскольку Реми отправил ответ, говоря, что я должен попробовать BehaviorSubject
а также ReplaySubject
что я пробовал, но это не помогает. Я также позвонил fetchUser();
внутри insertUser()
как указано в комментариях
Ниже приведена ссылка на полный пример
3 ответа
Следите за комментариями, кажется, у вас нет ни одного экземпляра вашего UsersBloc на этих двух страницах. И HomePage, и UserDetails возвращают BlocProvider, который создает экземпляр UsersBloc. Поскольку у вас есть два экземпляра блоков (которых у вас не должно быть), вы не обновляете потоки должным образом.
Решение состоит в том, чтобы удалить BlocProvider с этих двух страниц (HomePage и UserDetail) и обернуть в него виджет 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(),
);
}
}
Удалите BlocProvider и из UserDetail. Затем в UsersBloc вызовите fetchUser() внутри метода insertUser() после вставки пользователя, чтобы запросить базу данных и обновить поток пользователей.
Также, как сказал Реми Русселе, используйте один из предметов, которые возвращают предыдущие значения.
Проблема в том, что вы используете PublishSubject
,
Когда новый слушатель подписывается на PublishSubject
, он не получит ранее отправленное значение и получит только следующие события.
Самое простое решение - использовать BehaviorSubject
или ReplaySubject
вместо. Эти два будут напрямую вызывать своего слушателя с последними значениями.
@override
void initState() {
super.initState();
userBloc = BlocProvider.of<UserBloc>(context);
userBloc.fetchUser();
}
Проблема в том, что вы вызвали функцию userBloc.fetchUser() в initState страницы.
Блок-поток генерируется всякий раз, когда к нему добавляются новые данные, а функция userBloc.fetchUser() делает именно это, добавляет список пользователей, который вы выбираете из базы данных Sqflite.
Всякий раз, когда вы возвращаетесь на экран списка пользователей с экрана добавления пользователя, функция init НЕ вызывается. Он вызывается только при создании экрана списка пользователей, то есть всякий раз, когда вы помещаете его в стек навигации.
Обходной путь должен вызывать userBloc.fetchUser() всякий раз, когда данные снимка вашего StreamBuilder нулевые.
...
if (!snapshot.hasData) {
userBloc.fetchUser();
return Center(
child: CircularProgressIndicator(),
);
}
...