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.
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.
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...
Edit: After a few months with this solution in place, I noticed that there are a few problems with it:
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>[]),
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With