Describe the bug
When I follow the login with firebase tutorial in the flutterlibrary documentation, and I add the login feature to my application, the result is always a Stack Overflow
error
To Reproduce
Steps to reproduce the behavior:
Expected behavior
Can run normally
Screenshots
Logs
โโโโโโโ Exception caught by widgets library โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The following StackOverflowError was thrown building _InheritedProviderScope<LoginBloc>(value: null):
Stack Overflow
The relevant error-causing widget was
BlocProvider<LoginBloc>
lib/โฆ/pages/login_page.dart:10
When the exception was thrown, this was the stack
#0 _HashMap.[] (dart:collection-patch/collection_patch.dart:97:3)
#2 HttpOverrides.current (dart:_http/overrides.dart:36:24)
#3 new HttpClient (dart:_http:1554:46)
#4 new IOClient
package:http/src/io_client.dart:23
#5 createClient
package:http/src/io_client.dart:16
...
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
My Code
login_bloc:
class LoginBloc extends Bloc<LoginEvent, LoginState> {
final AuthRepository repository = AuthRepository();
@override
LoginState get initialState => LoginState.initial();
@override
Stream<Transition<LoginEvent, LoginState>> transformEvents(
Stream<LoginEvent> events,
TransitionFunction<LoginEvent, LoginState> transitionFn,
) {
final nonDebounceStream = events.where((event) {
return (event is! LoginEmailChanged && event is! LoginPasswordChanged);
});
final debounceStream = events.where((event) {
return (event is LoginEmailChanged || event is LoginPasswordChanged);
}).debounceTime(Duration(milliseconds: 300));
return super.transformEvents(
nonDebounceStream.mergeWith([debounceStream]),
transitionFn,
);
}
@override
Stream<LoginState> mapEventToState(
LoginEvent event,
) async* {
if (event is LoginEmailChanged) {
yield* _mapLoginEmailChangedToState(event.email);
} else if (event is LoginPasswordChanged) {
yield* _mapLoginPasswordChangedToState(event.password);
} else if (event is GetSubmittedLogin) {
yield* _mapSubmittedLoginToState(
email: event.email,
password: event.password,
);
}
}
Stream<LoginState> _mapLoginEmailChangedToState(String email) async* {
yield state.update(
isEmailValid: Validators.isValidEmail(email),
);
}
Stream<LoginState> _mapLoginPasswordChangedToState(String password) async* {
yield state.update(
isPasswordValid: Validators.isValidSimplePassword(password),
);
}
Stream<LoginState> _mapSubmittedLoginToState({
String email,
String password,
}) async* {
yield LoginState.loading();
try {
final loginModel = LoginModel(email: email, password: password);
await repository.postLogin(loginModelToJson(loginModel));
yield LoginState.success();
} catch (_) {
yield LoginState.failure();
}
}
}
login_state:
@immutable
class LoginState {
final bool isEmailValid;
final bool isPasswordValid;
final bool isSubmitting;
final bool isSuccess;
final bool isFailure;
bool get isFormValid => isEmailValid && isPasswordValid;
LoginState({
@required this.isEmailValid,
@required this.isPasswordValid,
@required this.isSubmitting,
@required this.isSuccess,
@required this.isFailure,
});
factory LoginState.initial() {
return LoginState(
isEmailValid: true,
isPasswordValid: true,
isSubmitting: false,
isSuccess: false,
isFailure: false,
);
}
factory LoginState.loading() {
return LoginState(
isEmailValid: true,
isPasswordValid: true,
isSubmitting: true,
isSuccess: false,
isFailure: false,
);
}
factory LoginState.failure() {
return LoginState(
isEmailValid: true,
isPasswordValid: true,
isSubmitting: false,
isSuccess: false,
isFailure: true,
);
}
factory LoginState.success() {
return LoginState(
isEmailValid: true,
isPasswordValid: true,
isSubmitting: false,
isSuccess: true,
isFailure: false,
);
}
LoginState update({
bool isEmailValid,
bool isPasswordValid,
}) {
return copyWith(
isEmailValid: isEmailValid,
isPasswordValid: isPasswordValid,
isSubmitting: false,
isSuccess: false,
isFailure: false,
);
}
LoginState copyWith({
bool isEmailValid,
bool isPasswordValid,
bool isSubmitEnabled,
bool isSubmitting,
bool isSuccess,
bool isFailure,
}) {
return LoginState(
isEmailValid: isEmailValid ?? this.isEmailValid,
isPasswordValid: isPasswordValid ?? this.isPasswordValid,
isSubmitting: isSubmitting ?? this.isSubmitting,
isSuccess: isSuccess ?? this.isSuccess,
isFailure: isFailure ?? this.isFailure,
);
}
@override
String toString() {
return '''LoginState {
isEmailValid: $isEmailValid,
isPasswordValid: $isPasswordValid,
isSubmitting: $isSubmitting,
isSuccess: $isSuccess,
isFailure: $isFailure,
}''';
}
}
login_event:
abstract class LoginEvent extends Equatable {
const LoginEvent();
@override
List<Object> get props => [];
}
class LoginEmailChanged extends LoginEvent {
final String email;
const LoginEmailChanged({@required this.email});
@override
List<Object> get props => [email];
@override
String toString() => 'EmailChanged { email :$email }';
}
class LoginPasswordChanged extends LoginEvent {
final String password;
const LoginPasswordChanged({@required this.password});
@override
List<Object> get props => [password];
@override
String toString() => 'PasswordChanged { password: $password }';
}
class GetSubmittedLogin extends LoginEvent {
final String email;
final String password;
const GetSubmittedLogin({
@required this.email,
@required this.password,
});
@override
List<Object> get props => [email, password];
@override
String toString() {
return 'SubmittedLogin { email: $email, password: $password }';
}
}
login_page:
class LoginPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => LoginBloc(),
child: LoginTemp(),
);
}
}
class LoginTemp extends StatefulWidget {
@override
_LoginTempState createState() => _LoginTempState();
}
class _LoginTempState extends State<LoginTemp> {
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
LoginBloc _loginBloc;
bool get isPopulated =>
_emailController.text.isNotEmpty && _passwordController.text.isNotEmpty;
bool isLoginButtonEnabled(LoginState state) {
return state.isFormValid && isPopulated && !state.isSubmitting;
}
@override
void initState() {
super.initState();
_loginBloc = BlocProvider.of<LoginBloc>(context);
_emailController.addListener(_onEmailChanged);
_passwordController.addListener(_onPasswordChanged);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(""),
),
body: BlocListener<LoginBloc, LoginState>(
listener: (context, state) {
if (state.isFailure) {
Scaffold.of(context)
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [Text('Login Failure'), Icon(Icons.error)],
),
backgroundColor: Colors.red,
),
);
}
if (state.isSubmitting) {
Scaffold.of(context)
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Logging In...'),
CircularProgressIndicator(),
],
),
),
);
}
// if (state.isSuccess) {
// BlocProvider.of<AuthenticationBloc>(context)
// .add(AuthenticationLoggedIn());
// }
},
child: BlocBuilder<LoginBloc, LoginState>(
builder: (context, state) {
return Padding(
padding: EdgeInsets.all(20.0),
child: Form(
child: ListView(
children: <Widget>[
// Padding(
// padding: EdgeInsets.symmetric(vertical: 20),
// child: Image.asset('assets/flutter_logo.png', height: 200),
// ),
TextFormField(
controller: _emailController,
decoration: InputDecoration(
icon: Icon(Icons.email),
labelText: 'Email',
),
keyboardType: TextInputType.emailAddress,
autovalidate: true,
autocorrect: false,
validator: (_) {
return !state.isEmailValid ? 'Invalid Email' : null;
},
),
TextFormField(
controller: _passwordController,
decoration: InputDecoration(
icon: Icon(Icons.lock),
labelText: 'Password',
),
obscureText: true,
autovalidate: true,
autocorrect: false,
validator: (_) {
return !state.isPasswordValid ? 'Invalid Password' : null;
},
),
Padding(
padding: EdgeInsets.symmetric(vertical: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
RaisedButton(
child: Text('Login Bos'),
onPressed: isLoginButtonEnabled(state)
? _onFormSubmitted
: null,
),
],
),
),
],
),
),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: (){},
tooltip: 'Increment',
child: Icon(Icons.gps_fixed),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}
void _onEmailChanged() {
_loginBloc.add(
LoginEmailChanged(email: _emailController.text),
);
}
void _onPasswordChanged() {
_loginBloc.add(
LoginPasswordChanged(password: _passwordController.text),
);
}
void _onFormSubmitted() {
_loginBloc.add(
GetSubmittedLogin(
email: _emailController.text,
password: _passwordController.text,
),
);
}
}
Please help me @felangel
Hi @wisnuwiry ๐
Thanks for opening an issue!
Can you please provide a link to a sample app which illustrates the problem so that I can debug it locally? Thanks ๐
https://github.com/wisnuwiry/K9met9JKysdfk90 this is my sample repo project. Thanks
Please... @felangel
Hi @wisnuwiry ๐
Have you managed to find the issue ?
Hi @RollyPeres
Already, because I was a little less thorough, Thank you for your attention
Is this fixed ? I have a very similar exception using Flutter web (dev channel) and bloc (^6.0.5).
The following JSRangeError was thrown building Builder:
Invalid argument: Maximum call stack size exceeded
The relevant error-causing widget was:
_InheritedProviderScope<StatementRepository> file:///Users/.../tools/flutter/.pub-cache/hosted/pub.dartlang.org/provider-4.3.2+2/lib/src/inherited_provider.dart:159:12
When the exception was thrown, this was the stack:
packages/example/src/route/home/home_route.dart 35:25 set [_widget]
packages/example/src/route/home/home_route.dart 35:25 set [_widget]
packages/example/src/route/home/home_route.dart 35:25 set [_widget]
packages/example/src/route/home/home_route.dart 35:25 set [_widget]
packages/example/src/route/home/home_route.dart 35:25 set [_widget]
.
@GyuriMajercsik can you provide a repo sample?
@felangel , thanks for the quick response. Actually, it seems to be related to flutter dev branch or my code is broken somehow, but pretty hard to investigate. If I found that bloc is the reason I will come back with a smaller sample.
I found something similar: https://github.com/flutter/flutter/issues/66122
@GyuriMajercsik I have the same problem
I managed to reproduce it. The steps are as follows:
The application only works again after running a clean flutter.
Flutter Doctor:
Doctor summary (to see all details, run flutter doctor -v):
[โ] Flutter (Channel dev, 1.23.0-7.0.pre, on Mac OS X 10.15.4 19E287 x86_64, locale pt-BR)
[โ] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
[โ] Xcode - develop for iOS and macOS (Xcode 12.0.1)
[โ] Chrome - develop for the web
[โ] Android Studio (version 3.5)
[โ] VS Code (version 1.49.2)
[โ] Connected device (4 available)
โข No issues found!
@ReniDelonzek can you provide a reproduction sample? Also can you confirm that the issue is bloc specific or can you also reproduce it with a regular provider? Thanks! ๐
Yes, I extracted a minimal piece of the application enough to reproduce the problem that is available here
I left all my dependencies on the original project as this can be useful.
The problem only occurs on the web
I managed to reproduce it. The steps are as follows:
- Run an application that uses bloc on the web
- Run Hot Reload
The application only works again after running a clean flutter.
Flutter Doctor:
Doctor summary (to see all details, run flutter doctor -v): [โ] Flutter (Channel dev, 1.23.0-7.0.pre, on Mac OS X 10.15.4 19E287 x86_64, locale pt-BR) [โ] Android toolchain - develop for Android devices (Android SDK version 29.0.2) [โ] Xcode - develop for iOS and macOS (Xcode 12.0.1) [โ] Chrome - develop for the web [โ] Android Studio (version 3.5) [โ] VS Code (version 1.49.2) [โ] Connected device (4 available) โข No issues found!
I confirm that this happens for me as well.
Same problem. Think that this issue is not related to your package.
flutter clean
help for me.
Most helpful comment
Hi @RollyPeres
Already, because I was a little less thorough, Thank you for your attention