Reputation: 7656
I'm learning bloc and using equatable.
here is my code
login_state.dart
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
abstract class LoginState extends Equatable {
@override
List<Object> get props => [];
}
class LoginInitial extends LoginState {}
class LoginLoading extends LoginState {}
class LoginSuccess extends LoginState {
final Map token;
LoginSuccess({@required this.token});
}
class LoginFailure extends LoginState {
final Map error;
LoginFailure({@required this.error});
@override
List<Object> get props => null;
}
login_view
@override
Widget build(BuildContext context) {
final bool loading = _authStore.loading;
return BlocProvider(
create: (BuildContext context) => _loginBloc,
child: BlocListener<LoginBloc, LoginState>(
listener: (BuildContext context, state) {
if (state is LoginFailure) {
print("loginFailure is triggered with ${state.error}");
FlashAlert(context: context, message: state.error['message']).showFlash();
}
},
child: Scaffold(
resizeToAvoidBottomInset: true,
resizeToAvoidBottomPadding: true,
backgroundColor: Colors.white,
appBar: AppBar(
automaticallyImplyLeading: true,
),
body: Container(
padding: EdgeInsets.symmetric(horizontal: spacing(3)),
margin: EdgeInsets.only(top: spacing(2)),
child: widget.isLogin ? loginView() : registerView(),
),
floatingActionButton: FloatingActionButton(
backgroundColor: theme.primaryColor,
onPressed: () {
widget.isLogin ? handleLogin('') : handleRegister();
},
child: loading ? Progress() : Icon(EvaIcons.arrowIosForward),
),
),
));
}
I've tried putting [error] on get props.
@override
List<Object> get props => [error];
The problem is FlashAlert
is only run 1 times when error occur. How can I keep using equatable but keep triggering the FlashAlert
when error occur?.
THanks.
----- edit using blocbuilder ---
@override
Widget build(BuildContext context) {
final bool loading = _authStore.loading;
return BlocProvider(
create: (BuildContext context) => _loginBloc,
child: BlocBuilder<LoginBloc, LoginState>(builder: (BuildContext context, state) {
if (state is LoginFailure) {
print("loginFailure is triggered with ${state.error}");
FlashAlert(context: context, message: state.error['message']).showFlash();
}
return Scaffold(
resizeToAvoidBottomInset: true,
resizeToAvoidBottomPadding: true,
backgroundColor: Colors.white,
appBar: AppBar(
automaticallyImplyLeading: true,
),
body: Container(
padding: EdgeInsets.symmetric(horizontal: spacing(3)),
margin: EdgeInsets.only(top: spacing(2)),
child: widget.isLogin ? loginView() : registerView(),
),
floatingActionButton: FloatingActionButton(
backgroundColor: theme.primaryColor,
onPressed: () {
widget.isLogin ? handleLogin('') : handleRegister();
},
child: state is LoginLoading ? Progress() : Icon(EvaIcons.arrowIosForward),
),
);
}),
);
}
return error:
VERBOSE-2:ui_dart_state.cc(157)] Unhandled Exception: setState() or markNeedsBuild() called during build.
This Overlay widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was:
Overlay-[LabeledGlobalKey<OverlayState>#21a25]
Upvotes: 1
Views: 1884
Reputation: 27137
You are using BlocListener that's why you are facing this error.BlocListener will only execute once only.
Following lines are from official documentation.
BlocListener is a Flutter widget which takes a BlocWidgetListener and an optional Bloc and invokes the listener in response to state changes in the bloc. It should be used for functionality that needs to occur once per state change such as navigation, showing a SnackBar, showing a Dialog, etc...
You can use BlocBuilder and it will solve your issue.
Update:
This is happening because when build method call at that time your are trying to show FlashAlert, so you can avoid it by adding wait of 1 microsecond.
call following method instead on doing in bloc.
void callme() async {
await Future.delayed(Duration(microseconds: 1));
if (state is LoginFailure) {
print("loginFailure is triggered with ${state.error}");
FlashAlert(context: context, message: state.error['message']).showFlash();
}
}
Upvotes: 1