I've just started with Flutter recently. BLoC does indeed has a steep learning curve...
As it is clear from the title, the BlocBuilder logic gets correctly executed only once, when the app is started. Afterwards, however, UI doesn't get rebuilt on state changes. Although all the events get emitted, and states change.
Thanks in advance for any help!
Here's my 'main.dart':
import 'package:co_flutter/auth/authentication_bloc.dart';
import 'package:co_flutter/auth/authentication_event.dart';
import 'package:co_flutter/auth/authentication_state.dart';
import 'package:co_flutter/auth/login/login_bloc.dart';
import 'package:co_flutter/auth/signup/signup_page.dart';
import 'package:co_flutter/loading_indicator.dart';
import 'package:co_flutter/splash_page.dart';
import 'package:co_flutter/user_repository.dart';
import 'package:flutter/material.dart';
import 'package:bloc/bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'auth/login/login_page.dart';
class SimpleBlocObserver extends BlocObserver {
@override
void onCreate(BlocBase bloc) {
super.onCreate(bloc);
print('onCreate -- ${bloc.runtimeType}');
}
@override
void onEvent(Bloc bloc, Object? event) {
super.onEvent(bloc, event);
print('onEvent -- ${bloc.runtimeType}, $event');
}
@override
void onTransition(Bloc bloc, Transition transition) {
super.onTransition(bloc, transition);
print('onTransition -- ${bloc.runtimeType}, $transition');
}
@override
void onChange(BlocBase bloc, Change change) {
super.onChange(bloc, change);
print('onChange -- ${bloc.runtimeType}, $change');
}
@override
void one rror(BlocBase bloc, Object error, StackTrace stackTrace) {
print('onError -- ${bloc.runtimeType}, $error');
super.onError(bloc, error, stackTrace);
}
@override
void onClose(BlocBase bloc) {
super.onClose(bloc);
print('onClose -- ${bloc.runtimeType}');
}
}
void main() {
Bloc.observer = SimpleBlocObserver();
runApp(MyApp(
userRepository: UserRepository(),
));
}
class MyApp extends StatefulWidget {
final UserRepository userRepository;
MyApp({Key? key, required this.userRepository}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late AuthenticationBloc authenticationBloc;
UserRepository get userRepository => widget.userRepository;
@override
void initState() {
authenticationBloc = AuthenticationBloc(userRepository: userRepository);
authenticationBloc.add(AppStarted());
super.initState();
}
@override
void dispose() {
authenticationBloc.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<AuthenticationBloc>(
create: (BuildContext context) => authenticationBloc,
),
BlocProvider<LoginBloc>(
create: (BuildContext context) => LoginBloc(
userRepository: userRepository,
authenticationBloc: authenticationBloc),
),
],
child: MaterialApp(
title: 'My App',
theme: ThemeData(
primarySwatch: Colors.indigo,
),
home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (BuildContext context, AuthenticationState state) {
if (state is AuthenticationUninitialized) {
return SplashPage();
}
if (state is AuthenticationAuthenticated) {
return Dashboard(
title: 'Dashboard',
);
}
if (state is AuthenticationUnauthenticated) {
return LoginPage(
userRepository: userRepository,
);
}
if (state is AuthenticationLoading) {
return LoadingIndicator();
}
// else {
// return Text('Error');
// }
return BlocBuilder<LoginBloc, LoginState>(
builder: (context, state) {
if (state is LoginToSignup) {
return SignUpPage();
} else
return SizedBox.shrink();
},
);
},
),
),
);
}
}
class Dashboard extends StatelessWidget {
final String title;
const Dashboard({Key? key, required this.title}) : super(key: key);
@override
Widget build(BuildContext context) {
final AuthenticationBloc authenticationBloc =
BlocProvider.of<AuthenticationBloc>(context);
return Scaffold(
appBar: AppBar(
title: Text('Dashboard'),
),
body: Container(
child: Center(
child: ElevatedButton(
child: Text('logout'),
onPressed: () {
authenticationBloc.add(LoggedOut());
},
),
),
),
);
}
}
authentication_bloc
import 'dart:async';
import 'package:co_flutter/auth/authentication_event.dart';
import 'package:co_flutter/auth/authentication_state.dart';
import 'package:bloc/bloc.dart';
import '../user_repository.dart';
class AuthenticationBloc
extends Bloc<AuthenticationEvent, AuthenticationState> {
UserRepository userRepository;
AuthenticationBloc({required this.userRepository})
: super(AuthenticationUninitialized()) {
userRepository = UserRepository();
}
@override
Stream<AuthenticationState> mapEventToState(
AuthenticationEvent event,
) async* {
if (event is AppStarted) {
final bool hasToken = await userRepository.hasToken();
if (hasToken) {
yield AuthenticationAuthenticated();
} else {
yield AuthenticationUnauthenticated();
}
}
if (event is LoggedIn) {
yield AuthenticationLoading();
await userRepository.persistToken(event.token, event.userId);
yield AuthenticationAuthenticated();
}
if (event is LoggedOut) {
yield AuthenticationLoading();
await userRepository.deleteToken();
yield AuthenticationUnauthenticated();
}
}
}
authentication_event
import 'package:equatable/equatable.dart';
abstract class AuthenticationEvent extends Equatable {
@override
List<Object> get props => [];
}
class AppStarted extends AuthenticationEvent {
@override
String toString() => 'AppStarted';
}
class LoggedIn extends AuthenticationEvent {
final String token;
final String userId;
LoggedIn({required this.token, required this.userId});
@override
String toString() => 'LoggedIn { token: $token}';
}
class LoggedOut extends AuthenticationEvent {
@override
String toString() => 'LoggedOut';
}
authentication_state
import 'package:equatable/equatable.dart';
abstract class AuthenticationState extends Equatable {
const AuthenticationState();
}
class AuthenticationUninitialized extends AuthenticationState {
@override
List<Object> get props => [];
}
class AuthenticationLoading extends AuthenticationState {
@override
List<Object> get props => [];
}
class AuthenticationAuthenticated extends AuthenticationState {
@override
List<Object> get props => [];
}
class AuthenticationUnauthenticated extends AuthenticationState {
@override
List<Object> get props => [];
}
CodePudding user response:
The problem is here. You already created a bloc:
authenticationBloc = AuthenticationBloc(userRepository: userRepository);
And here you are trying to create it again.
To fix it replace this code:
BlocProvider<AuthenticationBloc>(
create: (BuildContext context) => authenticationBloc,
),
with this:
BlocProvider<AuthenticationBloc>.value(
value: authenticationBloc,
),
Pass bloc into bloc builder
BlocBuilder<AuthenticationBloc, AuthenticationState>(
bloc: authenticationBloc,
Don't forget to dispose bloc authenticationBloc after you used it in dispose function.