Flutter BLoC Architecture - Role(s)

352

So I'm not a Flutter developer, but I might be able to offer some advice based on my general experience and some research I did for your question. And I'm assuming your concept of Bloc's align's somewhat with this.

I'd like to further 'split' my users into roles ... I'm not sure where exactly would I do this role check

  • I would make a Bloc (logic class) that represents a users authentication and authorization state, which for the sake of discussion we'll call UserAuthBloc.
  • As your other Bloc's go doing stuff they can refer to the UserAuthBloc, whenever they need to make a decision based on the users authentication status or role.
  • Using BlocProvider to implement dependency injection is generally wise, and you can also use it to provide the UserAuthBloc to widgets that need to make decisions based on the users authentication status and role membership.

Do I query the database in the file above, and do the checks there or, is there a way to sort of separate my logic further? Should I be using BloCs for roles as well?

  • The database querying for user authentication and authorization (roles) should be done exclusively through the UserAuthBloc.
  • The checks are done at the Bloc level, at least as much as possible. If you are doing checks in the UI, then try and limit them to reusable widgets so that there's less code to change if you need to makes changes.
  • Keeping authentication and authorization (roles) in the same Bloc makes sense to me in most cases, because the roles depend on the user being authenticated.
Share:
352
AAnon
Author by

AAnon

Updated on January 01, 2023

Comments

  • AAnon
    AAnon over 1 year

    This is a question mostly about the best practice, I'm new to flutter dev and to the BLoC architecture, to summarize I have an app that has an app_bloc which handles user authentication (firebase login), it's pretty simple, has two states Authenticated and Unauthenticated.

    My app renders pages depending on whether user is authenticated or not, either login page or home page.

    import 'package:authentication_repository/authentication_repository.dart';
    import 'package:flow_builder/flow_builder.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter_bloc/flutter_bloc.dart';
    import 'package:majstor/app/bloc/app_bloc.dart';
    import 'package:majstor/app/routes/routes.dart';
    import 'package:majstor/theme.dart';
    
    class App extends StatelessWidget {
      const App({
        Key? key,
        required AuthenticationRepository authenticationRepository,
      })  : _authenticationRepository = authenticationRepository,
            super(key: key);
    
      final AuthenticationRepository _authenticationRepository;
    
      @override
      Widget build(BuildContext context) {
        return RepositoryProvider.value(
          value: _authenticationRepository,
          child: BlocProvider(
            create: (_) => AppBloc(
              authenticationRepository: _authenticationRepository,
            ),
            child: const AppView(),
          ),
        );
      }
    }
    
    class AppView extends StatelessWidget {
      const AppView({Key? key}) : super(key: key);
    
    
    
      @override
      Widget build(BuildContext context) {
    /// try context.select here maybe?
        return MaterialApp(
          home: FlowBuilder<AppStatus>(
            state: context.select((AppBloc bloc) => bloc.state.status),
            onGeneratePages: onGenerateAppViewPages,
          ),
        );
      }
    }
    import 'package:flutter/widgets.dart';
    import 'package:majstor/app/app.dart';
    import 'package:majstor/home/home.dart';
    import 'package:majstor/login/login.dart';
    
    List<Page> onGenerateAppViewPages(AppStatus state, List<Page<dynamic>> pages) {
      switch (state) {
        case AppStatus.authenticated:
          return [HomePage.page()];
        case AppStatus.unauthenticated:
        default:
          return [LoginPage.page()];
      }
    }
    

    Now what I'm having a hard time wrapping my head around is this, in my home page case, I'd like to further 'split' my users into roles, there will be two roles stored on a Firebase collection with the user's corresponding id on the same document, and for each role a different page entirely should be built, I'm not sure where exactly would I do this role check, I could technically in the home page below, query the database by using the user id, and getting the role and then similarly as i did with the login and home, use a flowbuilder to generate different page depending on role? But is that a good practice? I kind of feel like I should be separating the database logic elsewhere, but as I'm new to this kind of development I don't know where I should be doing this to build a scalable app.

    import 'package:cloud_firestore/cloud_firestore.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter_bloc/flutter_bloc.dart';
    import 'package:majstor/app/app.dart';
    import 'package:majstor/home/home.dart';
    import 'package:majstor/utils/databaseservice.dart';
    
    class HomePage extends StatelessWidget {
      const HomePage({Key? key}) : super(key: key);
    
      static Page page() => const MaterialPage<void>(child: HomePage());
    
      @override
      Widget build(BuildContext context) {
        final textTheme = Theme.of(context).textTheme;
        final user = context.select((AppBloc bloc) => bloc.state.user);
        Database db = Database();
        db.readData(user.id);
    
        return Scaffold(
          appBar: AppBar(
            title: const Text('Home'),
            actions: <Widget>[
              IconButton(
                key: const Key('homePage_logout_iconButton'),
                icon: const Icon(Icons.exit_to_app),
                onPressed: () => context.read<AppBloc>().add(AppLogoutRequested()),
              )
            ],
          ),
          body: Align(
            alignment: const Alignment(0, -1 / 3),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                Avatar(photo: user.photo),
                const SizedBox(height: 4),
                Text(user.email ?? '', style: textTheme.headline6),
                const SizedBox(height: 4),
                Text(user.name ?? '', style: textTheme.headline5),
              ],
            ),
          ),
        );
      }
    }
    

    Do I query the database in the file above, and do the checks there or, is there a way to sort of separate my logic further? Should I be using BloCs for roles aswell? or should I be somehow integrating roles into my existing BloC? Hope I was clear enough with my explanation.

  • AAnon
    AAnon over 2 years
    Thanks man, that was a really detailed and thought out answer, I'll try to do this!
  • Adrian K
    Adrian K over 2 years
    Good luck! I really need to have a go at building it myself, too.