I'm working on flutter bloc to make login status for entire app
but I'm having some problems
The functions I wrote are working properly but when I try to access variables in main function by eg'(bloc.state.loginstate)' it is showing unupdated results.
also onChange is not showing any results on debug console too.
main Function
void main(){run(MyApp());}
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context){
return MultiBlocProvider(
providers: [
BlocProvider(create: (BuildContext context) => LoginDataBloc(), lazy: false,),
],
child: MaterialApp( ... )
);
}
}
class YoureInApp extends StatefulWidget {
@override
_YoureInAppState createState() => _YoureInAppState();
}
class _YoureInAppState extends State<YoureInApp> {
final bloc = LoginDataBloc();
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
_autologin();
});
}
_autologin () async {
context.read<LoginDataBloc>().add(ReadSignEvent()); //copyWith Done, ReadSignEvent Done message
Future.delayed(Duration(seconds: 2) ,(){
context.read<LoginDataBloc>().add(PrintSignEvent()); // loginState : true, {'accountName : ... '}
print(bloc.state.loginState); // loginState : false - Problem
if(bloc.state.loginState == false) Navigator.push(context, MaterialPageRoute(builder: (_) => LoginStartApp()));
});
}
State
class LoginDataState extends Equatable {
final String userInfo;
final bool loginState;
LoginDataState({this.userInfo = '', this.loginState = false});
LoginDataState copyWith({String? userInfo, bool? loginState}) {
return LoginDataState(
userInfo: userInfo?? this.userInfo,
loginState: loginState?? this.loginState,
);
}
@override
List<Object?> get props => [userInfo, loginState];
}
Event
abstract class LoginDataEvent extends Equatable{
}
class SignInEvent extends LoginDataEvent {
final String accountName;
final String phoneNumber;
final String passWord;
SignInEvent(this.accountName, this.phoneNumber, this.passWord);
@override
List<Object?> get props => [accountName, phoneNumber, passWord];
}
class SignOutEvent extends LoginDataEvent {
@override
List<Object?> get props => [];
}
class ReadSignEvent extends LoginDataEvent {
@override
List<Object?> get props => [];
}
class PrintSignEvent extends LoginDataEvent {
@override
List<Object?> get props => [];
}
Bloc
class LoginDataBloc extends Bloc<LoginDataEvent,LoginDataState>{
LoginDataBloc(): super(LoginDataState()){
const storage = FlutterSecureStorage();
on<SignInEvent> ((event, emit) async {
String userInfo = jsonEncode(Login(event.accountName, event.phoneNumber, event.passWord));
await storage.write(key: 'login', value: userInfo);
emit(state.copyWith(userInfo: userInfo));
emit(state.copyWith(loginState: true));
});
on<SignOutEvent>((event, emit) async {
await storage.delete(key: 'login');
emit(state.copyWith(userInfo: ''));
emit(state.copyWith(loginState: false));
});
on<ReadSignEvent>((event, emit) async {
var userInfo = await storage.read(key: 'login');
if(userInfo != null) {
emit(state.copyWith(loginState: true));
emit(state.copyWith(userInfo: userInfo));
print("copyWith Done"); // TEST FUNCTION
}
print('ReadSignEvent'); //TEST FUNCTION
});
on<PrintSignEvent>((event, emit) {
print(state.loginState); //TEST FUNCTION
print(state.userInfo); //TEST FUNCTION
},);
@override // NOT WORKING
void onChange(Change<LoginDataState> change){
super.onChange(change);
print(change);
}
}
}
I checked props copyWith Equatable but is not working
The issue is that you're using two different instances of LoginDataBloc
.
You create one instance using the BlocProvider
:
BlocProvider(create: (BuildContext context) => LoginDataBloc(), lazy: false)
// ^^^^^^^^^^^^^^ creates an instance
Then, inside _YourinAppState
, you create a different instance:
final bloc = LoginDataBloc(); // this is not the same one as above
Since every instance has its own state, you are modifying the first instance, and then checking the state of the second instance. This will clearly not reflect any operations from the first instance.
Solution: Remove final bloc = LoginDataBloc();
, and simply access your bloc's state with context.read<LoginDataBloc>().state
.
Why is onChange
not being called?
Answer: You're declaring the onChange
method override inside the constructor, instead of inside the class definition (see comments).
class LoginDataBloc extends Bloc<LoginDataEvent, LoginDataState> {
LoginDataBloc() : super(LoginDataState()) {
/* ... event handlers ... */
/// Oops, this is actually just a function declaration inside the
/// constructor, it's not overriding anything.
@override // NOT WORKING
void onChange(Change<LoginDataState> change) {
super.onChange(change);
print(change);
}
}
}
Solution: Move the onChange
definition inside the class definition (see comments).
class LoginDataBloc extends Bloc<LoginDataEvent, LoginDataState> {
LoginDataBloc() : super(LoginDataState()) {
/* ... event handlers ... */
}
/// Now this is overriding the Bloc's onChange method.
@override // WORKING
void onChange(Change<LoginDataState> change) {
super.onChange(change);
print(change);
}
}