Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Right way to handle navigation using BLoC

Tags:

flutter

dart

Hello guys I'm using BLoC for app I'm currently developing but there some cases which I'm clueless like when you do login you fire API call and wait for result naturally I would send loading state and show loader but after that finishes how to handle for example navigating to different screen. I've currently have something like this

typedef void LoginSuccessCallback();
    class LoginBloc(){
    LoginBloc(Api this.api,LoginSuccessCallback loginSuccesCallback){
      _login.switchMap((ev) => api.login(ev.payload.email,ev.payload.password)).listen((_) => loginSuccessCallback);
     }
    }

But I'm sure there is much cleaner way for handling this I've tried to search some samples which have something similar but couldn't find anything.

like image 325
harisk92 Avatar asked Jul 13 '18 22:07

harisk92


People also ask

How do you use bloc pattern in Flutter?

Adding Bloc to Flutter Click CTRL + S to save, and you have successfully added the above dependencies to your Flutter application! The bloc dependency is used because we also used cubit in the example. The flutter_bloc will provide you with the widgets necessary to make it easier to use the Bloc pattern in Flutter.

How do you use a cubit Flutter?

CubitListener is a Flutter widget which takes a CubitWidgetListener and an optional Cubit and invokes the listener in response to state changes in the cubit. It should be used for functionality that needs to occur once per state change such as navigation, showing a SnackBar , showing a Dialog , etc...


1 Answers

Edit: After a few months with this solution in place, I noticed that there are a few problems with it:

  1. Android hardware back button does not work
  2. The app resets when you toggle "inspect" mode.
  3. No transitions possible
  4. No guarantee that no forbidden route is displayed

So I no longer recommend using this approach!


For normal user-initiated navigation, you don't need the BLoC pattern at all. Just use the Navigator.

Login is a special case. Following the BLoC pattern, it would make sense to provide a isAuthenticated stream:

abstract class MyBloc {
  Stream<bool> get isAuthenticated;
}

Your app will probably have 2 different named route trees: One for logged in users, and one for anonymous users:

final Map<String, WidgetBuilder> anonymousRoutes = {
  '/': (context) => new LoginScreen(), // default for anon
  '/register': (context) => new RegisterScreen(),
};

final Map<String, WidgetBuilder> authenticatedRoutes = {
  '/': (context) => new HomeScreen(), // default for logged in
  '/savings': (context) => new SavingsScreen(),
  // ...
};

Usually the Navigator and its named routes are tightly coupled to the MaterialApp, but you can also define your own that is rebuilt when the isAuthenticated stream is updated:

class MyApp extends StatelessWidget {
  const MyApp({Key key, this.bloc}) : super(key: key);

  final MyBloc bloc;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      builder: (BuildContext context, Widget child) {
        return StreamBuilder<bool>(
          stream: bloc.isAuthenticated,
          builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
            if (!snapshot.hasData) {
              return Text('loading...');
            }

            bool isAuthenticated = snapshot.data;
            return _buildNavigator(isAuthenticated);
          },
        );
      },
    );
  }
}

Navigator _buildNavigator(bool isAuthenticated) {
  // different route tree and different default route depending on auth state
  final routes = isAuthenticated ? authenticatedRoutes : anonymousRoutes;

  return Navigator(
    key: new ValueKey(isAuthenticated),
    onGenerateRoute: (RouteSettings settings) {
      final name = settings.name;
      return new MaterialPageRoute(
        builder: routes[name],
        settings: settings,
      );
    },
    onUnknownRoute: (RouteSettings settings) {
      throw Exception('unknown route');
    },
  );
}

Sadly right now (2018-07-14) there are a 2 conflicting asserts in the Flutter code which you have to remove to make the code above work (you can just edit it with your IDE):

Line 93 and 96 in packages\flutter\lib\src\widgets\app.dart

//assert(navigatorObservers != null),
//assert(onGenerateRoute != null || navigatorObservers == const <NavigatorObserver>[]),
like image 99
boformer Avatar answered Oct 04 '22 12:10

boformer