Ademir Villena Zevallos
Ademir Villena Zevallos

Reputation: 1561

Build the architecture for a Flutter App with flutter_bloc

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:

  1. 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?

  2. 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

Answers (1)

chunhunghan
chunhunghan

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(),
          ),
      }
    );
  }
}

and use it https://gitlab.kaleidos.net/piweek/betover/betover-mobile/blob/7b9368288743be602f37014f7a49ed4ef943afc1/lib/ui/widgets/map.dart

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

Related Questions