I am using sqflite
database to save user list. I have user list screen, which shows list of user and it has a fab button, on click of fab button, user is redirected to next screen where he can add new user to database. The new user is properly inserted to the database but when user presses back button and go backs to user list screen, the newly added user is not visible on the screen. I have to close the app and reopen it,then the newly added user is visible on the screen.
I am using bloc
pattern and following is my code to show user list
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,
);
}
},
),
);
}
}
Following is my bloc code
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();
}
}
As Remi posted answer saying i should try BehaviorSubject
and ReplaySubject
which i tried but it does not help. I have also called fetchUser();
inside insertUser()
as pointed in comments
Following is the link of the full example
Follow up from the comments, it seems you don't have a single instance of your UsersBloc in those two pages. Both the HomePage and UserDetails return a BlocProvider which instantiate a UsersBloc instance. Because you have two blocs instances(which you shouldn't have) you don't update the streams properly.
The solution is to remove the BlocProvider from those two pages(HomePage and UserDetail) and wrap the MaterialApp widget in it to make the same instance available to both pages.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
bloc: UserBloc(),
child:MaterialApp(...
The HomePage will be:
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return UserList(),
);
}
}
Remove the BlocProvider from UserDetail as well. In the UsersBloc then call fetchUser() inside the insertUser() method after the user insertion, to requery the database and update the users stream.
Also as Rémi Rousselet said, use one of the subjects that return previous values.
The issue is that you're using a PublishSubject
.
When a new listener subscribes to a PublishSubject
, it does not receive the previously sent value and will only receive the next events.
The easiest solution is to use a BehaviorSubject
or a ReplaySubject
instead. These two will directly call their listener with the latest values.
@override
void initState() {
super.initState();
userBloc = BlocProvider.of<UserBloc>(context);
userBloc.fetchUser();
}
The problem is that you have called the userBloc.fetchUser() function in the initState of the page.
Bloc stream emits whenever a new data is added to it and the userBloc.fetchUser() function does exactly that, it adds the userList that you fetch from the Sqflite database.
Whenever you come back to the userlist screen from add user screen, init function is NOT called. It is only called when the userlist screen is created, that is, whenever you push it to the navigation stack.
The workaround is to call userBloc.fetchUser() whenever your StreamBuilder's snapshot data is null.
...
if (!snapshot.hasData) {
userBloc.fetchUser();
return Center(
child: CircularProgressIndicator(),
);
}
...
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.