Reputation: 1561
I have to develop a simple app with Flutter and I am trying for the first time the bloc pattern. I am using flutter_bloc
package but I have two main problems and got stuck:
All the screens in the app have blocs that depends on repos to make requests to a server, and for each request I need a token. I obtain the token at the authentication_bloc
(login), but I can't share that token to all the blocs unless I make them dependent on auth_bloc (the bloc who has the user model storing the token). For a simple app, maybe it works but if other bloc has more bloc dependencies besides the auth_bloc, it could become a mess injecting all the blocs as dependencies. What alternative could be a good solution?
For the navigation I have a bloc
for bottom navigation bar. But I want to all the screens on tabs can call the change_tab()
function at bottom bar bloc. And again is the same problem, I only can add events at bottom bar bloc if the other blocs have a dependency on bottom bar bloc. I dont want to add the auth bloc and the bottom bar bloc as dependencies on all the blocs that manages all the screens.
I appreciate advices, alternatives or documentation where I can learn to build a good architecture for apps a bit more complex than the examples showed here
Upvotes: 2
Views: 3270
Reputation: 54367
You can store your token in Repository
such as userRepository.persistToken(event.token)
and await userRepository.deleteToken();
Repository act like Repository Pattern. You do not need to know data come from cloud or local database
In example below, userRepository can be app level so all bloc can use it
full example code https://github.com/felangel/bloc/blob/master/examples/flutter_login/lib/authentication/authentication_bloc.dart
code snippet
void main() {
BlocSupervisor.delegate = SimpleBlocDelegate();
final userRepository = UserRepository();
runApp(
BlocProvider<AuthenticationBloc>(
create: (context) {
return AuthenticationBloc(userRepository: userRepository)
..add(AppStarted());
},
child: App(userRepository: userRepository),
),
);
}
...
import 'package:meta/meta.dart';
import 'package:bloc/bloc.dart';
import 'package:user_repository/user_repository.dart';
import 'package:flutter_login/authentication/authentication.dart';
class AuthenticationBloc
extends Bloc<AuthenticationEvent, AuthenticationState> {
final UserRepository userRepository;
AuthenticationBloc({@required this.userRepository})
: assert(userRepository != null);
@override
AuthenticationState get initialState => AuthenticationUninitialized();
@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);
yield AuthenticationAuthenticated();
}
if (event is LoggedOut) {
yield AuthenticationLoading();
await userRepository.deleteToken();
yield AuthenticationUnauthenticated();
}
}
}
You can use MultiBlocProvider
to Wrap yous blocs in app level
The following example is Map and Position
https://gitlab.kaleidos.net/piweek/betover/betover-mobile/blob/7b9368288743be602f37014f7a49ed4ef943afc1/lib/main.dart
void main() {
BlocSupervisor.delegate = SimpleBlocDelegate();
runApp(
MultiBlocProvider(
providers: [
BlocProvider<MapBloc>(
create: (context) => MapBloc(),
),
BlocProvider<PoisBloc>(
create: (context) => PoisBloc(
mapBloc: BlocProvider.of<MapBloc>(context),
),
),
],
child: App(),
)
);
}
class App extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Bet Over',
theme: appTheme,
initialRoute: '/',
routes: {
'/': (context) =>
MultiBlocProvider(
providers: [
BlocProvider<MapBloc>(
create: (context) => BlocProvider.of<MapBloc>(context),
),
BlocProvider<PoisBloc>(
create: (context) => BlocProvider.of<PoisBloc>(context),
),
],
child: MapPage(),
),
'/create-poi': (context) =>
MultiBlocProvider(
providers: [
BlocProvider<MapBloc>(
create: (context) => BlocProvider.of<MapBloc>(context),
),
BlocProvider<PoisBloc>(
create: (context) => BlocProvider.of<PoisBloc>(context),
),
],
child: CreatePoimPage(),
),
}
);
}
}
class _MapState extends State<Map> {
MapBloc _mapBloc;
PoisBloc _poisBloc;
@override
void initState() {
super.initState();
_mapBloc = BlocProvider.of<MapBloc>(context);
_poisBloc = BlocProvider.of<PoisBloc>(context);
...
List<Marker> _generateMarkers () {
if (!(_mapBloc.state is MapViewboxChanged && _poisBloc.state is PoisLoaded))
return [];
Upvotes: 2