After leaving the page and re-navigate to the page BlocBuilder still keeps old state.
Here is my widget:
class CreateRoom extends StatefulWidget {
@override
_CreateRoomState createState() => _CreateRoomState();
}
class _CreateRoomState extends State<CreateRoom>
with SingleTickerProviderStateMixin {
bool pressAttention = true;
final _scrollController = ScrollController();
final _scrollThreshold = 200.0;
InviteBloc _inviteBloc;
final List<User> invitedUsers = [];
String query;
@override
void initState() {
super.initState();
_scrollController.addListener(_onScroll);
_inviteBloc = BlocProvider.of<InviteBloc>(context);
}
@override
Widget build(BuildContext context) {
return BlocBuilder<InviteBloc, InviteState>(
builder: (blocContext, state) {
List _users = [];
if (state is InviteSuccess) {
_users = state.props[0];
}
print('show state');
print(state);
return Scaffold(
backgroundColor: Colors.white,
body: GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(new FocusNode());
},
child: NestedScrollView(
controller: _scrollController,
headerSliverBuilder: (BuildContext context, bool boxIsScrolled) {
return <Widget>[
SliverAppBar(
floating: false,
pinned: true,
elevation: 0,
automaticallyImplyLeading: false,
centerTitle: true,
leading: IconButton(
icon: Icon(
Icons.arrow_back_ios,
size: 18,
color: CustomColor.black,
),
onPressed: () {
Navigator.pop(context);
}),
title: PageTitle(
title: 'New Group',
textStyle: TextStyle(
color: CustomColor.black,
fontFamily: 'CircularStd',
fontWeight: FontWeight.w700,
fontSize: 19.0,
)),
actions: <Widget>[
Row(
children: <Widget>[
invitedUsers.isNotEmpty
? GestureDetector(
child: Container(
child: Padding(
padding: const EdgeInsets.only(
left: 25.0, top: 10, bottom: 10),
child: Text(
'Next',
style: TextStyle(
color: CustomColor.primary,
fontFamily: 'CircularStd',
fontWeight: FontWeight.w600,
fontSize: 16.0,
),
),
),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
CreateRoomImageUpload(
users: invitedUsers)),
);
},
)
: Container(),
SizedBox(width: 15)
],
),
],
),
];
},
body: SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SearchInput(
placeHolderText: 'Search for people to invite',
onChange: (text) {
if (text.length > 0) {
setState(() => query = text);
_inviteBloc.add(
InviteFetched(query: query, isSearching: true));
}
}),
state is InviteSuccess
? ListView.builder(
physics: ClampingScrollPhysics(),
shrinkWrap: true,
scrollDirection: Axis.vertical,
padding: EdgeInsets.symmetric(vertical: 0),
itemCount: _users.length,
itemBuilder: (BuildContext context, int index) {
User _userDetails = _users[index];
return index >= _users.length
? BottomLoader()
: UserInviteWidget(
userDetails: _userDetails,
onInvite: (user) => _inviteUser(user));
},
)
: Container(),
],
),
),
),
),
);
});
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
void _onScroll() {
if (query != null && query.length > 0) {
final maxScroll = _scrollController.position.maxScrollExtent;
final currentScroll = _scrollController.position.pixels;
if (maxScroll - currentScroll <= _scrollThreshold) {
_inviteBloc.add(InviteFetched(isSearching: false, query: query));
}
}
}
void _inviteUser(user) {
setState(() {
user.isInvited ? invitedUsers.remove(user) : invitedUsers.add(user);
user.isInvited = !user.isInvited;
});
}
}
My user model:
class User {
int id;
String username;
String email;
String profileImage;
String gender;
String city;
bool roomInvite = false;
bool roomMember = false;
User(
{this.id,
this.username,
this.profileImage,
this.email,
this.gender,
this.city});
User.fromJson(Map<String, dynamic> parsedJson)
: id = parsedJson['id'] ?? '',
username = parsedJson['username'] ?? '',
profileImage = parsedJson['profileImage'] ?? '',
email = parsedJson['email'] ?? '',
gender = parsedJson['gender'] ?? '',
city = parsedJson['city'] ?? '';
Map<String, dynamic> toJson() {
return <String, dynamic>{
"id": id,
"username": username,
"profileImage": profileImage,
"email": email,
"gender": gender,
"city": city
};
}
set isInvited(bool invited) {
this.roomInvite = invited;
}
bool get isInvited {
return roomInvite;
}
set isRoomMember(bool roomMember) {
roomMember = roomMember;
}
get isRoomMember {
return roomMember;
}
@override
String toString() =>
'User { id: $id, username: $username, profileImage: $profileImage, ' +
'email: $email, gender: $gender, city: $city }';
}
My bloc is like:
import 'dart:async';
import 'package:bloc/bloc.dart';
import '../models/user.dart';
import '../services/rooms_repository.dart';
import 'package:meta/meta.dart';
import 'package:rxdart/rxdart.dart';
import 'bloc.dart';
class InviteBloc extends Bloc<InviteEvent, InviteState> {
final RoomsRepository roomsRepository;
InviteBloc({@required this.roomsRepository}) : super(InviteInitial());
@override
Stream<Transition<InviteEvent, InviteState>> transformEvents(
Stream<InviteEvent> events,
TransitionFunction<InviteEvent, InviteState> transitionFn,
) {
return super.transformEvents(
events.debounceTime(const Duration(milliseconds: 500)),
transitionFn,
);
}
@override
Stream<InviteState> mapEventToState(InviteEvent event) async* {
final currentState = state;
if (event is InviteFetched && !_hasReachedMax(currentState)) {
try {
if (currentState is InviteInitial || event.isSearching) {
yield* _getUsersOnInitialState(query: event.query);
}
if (currentState is InviteSuccess) {
yield* _getUsersOnSuccessState(currentState,
query: event.query, isSearching: event.isSearching);
}
} catch (e) {
print(e);
// yield InviteFailure();
}
}
}
Stream<InviteState> _getUsersOnInitialState(
{String query, bool isSearching}) async* {
try {
final initialPage = 1;
final result = await _fetchUsersAndGetPages(initialPage, query: query);
final users = result['users'];
yield InviteSuccess(
users: users,
currentPage: result['currentPage'],
lastPage: result['lastPage'],
hasReachedMax: false);
} catch (e) {
print(e);
// yield InviteFailure();
}
}
/// Takes the current state of the bloc, check if the nextPage (`currentState.currentPage + 1`) is beyond
/// the lastPage (`nextPage > currentState.lastPage`). if it is setState to hasReachedMax
/// else get the requested users and set in state
Stream<InviteState> _getUsersOnSuccessState(currentState,
{String query, bool isSearching}) async* {
try {
int nextPage = currentState.currentPage + 1;
if (isSearching == false) {
if (nextPage > currentState.lastPage) {
yield currentState.copyWith(hasReachedMax: true);
return;
}
}
if (isSearching) {
nextPage = 1;
}
final result = await _fetchUsersAndGetPages(nextPage, query: query);
if (result == null) {
return;
}
final users = result['users'];
yield InviteSuccess(
users: isSearching ? users : currentState.users + users,
currentPage: result['currentPage'],
lastPage: result['lastPage'],
hasReachedMax: false,
);
} catch (e) {
print(e);
// yield InviteFailure();
}
}
bool _hasReachedMax(InviteState state) =>
state is InviteSuccess && state.hasReachedMax;
Future<Map<String, dynamic>> _fetchUsersAndGetPages(nextPage,
{String query}) async {
try {
final response =
await roomsRepository.getRoomInvite(page: nextPage, query: query);
print('Getting response');
print(response);
if (response == null) {
return null;
}
final users = _users(response['results']);
final int currentPage = response['pagination']['current_page'];
final int lastPage = response['pagination']['last_page'];
return {'users': users, 'currentPage': currentPage, 'lastPage': lastPage};
} catch (e) {
print(e);
throw Exception('error fetching users');
}
}
List _users(users) {
return users.map((rawDetail) {
return User(
id: rawDetail['id'],
username: rawDetail['username'],
email: rawDetail['email'],
profileImage: rawDetail['profile_image'],
gender: rawDetail['gender'],
city: rawDetail['city']);
}).toList();
}
}
I'm using:
flutter_bloc: ^6.0.1
equatable: ^1.0.0
meta: ^1.1.6
rxdart: ^0.23.1
Hi @ConfidenceYobo 馃憢
Could you create a minimal reproduction of your issue in a repo and share it ?
@RollyPeres
https://github.com/ConfidenceYobo/bloc_demonstration is sample repo to demonstrate the issue.
Since your bloc is globally available it keeps state between your navigations, so it needs to be resetted with the help of an event, as you can see in this PR I've opened. Hope it helps 馃憤
Most helpful comment
Since your bloc is globally available it keeps state between your navigations, so it needs to be resetted with the help of an event, as you can see in this PR I've opened. Hope it helps 馃憤