使用BLoC处理导航的正确方法 [英] Right way to handle navigation using BLoC

查看:92
本文介绍了使用BLoC处理导航的正确方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

大家好,我正在使用BLoC开发我正在开发的应用程序,但是有些情况我一无所知,例如当您登录时触发API调用并自然等待结果,我会发送加载状态并显示加载程序,但之后
完成如何处理(例如导航到其他屏幕)的操作。
我目前有这样的东西

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

但是我敢肯定有更清洁的方法处理这个问题,我尝试搜索一些具有相似但找不到任何东西的样本。

解决方案

编辑:在使用该解决方案几个月后,我注意到它存在一些问题:


  1. Android硬件后退按钮不起作用

  2. 在切换检查模式时,应用会重置。

  3. 无法进行过渡

  4. 不能保证没有禁止的路线被显示

所以我不再建议使用这种方法! / p>




对于普通的用户启动的导航,您根本不需要BLoC模式。只需使用 Navigator



登录是一种特殊情况。遵循BLoC模式,提供 isAuthenticated 流将很有意义:

 抽象类MyBloc {
Stream< bool>获得isAuthenticated;
}

您的应用可能会有2种不同的命名路由树:一种用于登录用户,以及一个匿名用户:

  final Map< String,WidgetBuilder> onymousRoutes = {
’/’:(上下文)=> new LoginScreen(),//匿名
‘/ register’的默认值:(context)=>新的RegisterScreen(),
};

final Map< String,WidgetBuilder> authenticatedRoutes = {
’/’:(上下文)=> new HomeScreen(),//登录的
‘/ savings’的默认值:(context)=> new SavingsScreen(),
// ...
};

通常 Navigator 及其命名路线为与 MaterialApp 紧密耦合,但是您还可以定义在 isAuthenticated 流更新时重新构建的自己的内容:

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

最终的MyBloc集团;

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

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

导航器_buildNavigator(bool isAuthenticated){
//不同的路由树和不同的默认路由,取决于身份验证状态
最终路由= isAuthenticated ? authenticatedRoutes:匿名路由;

返回导航器(
键:new ValueKey(isAuthenticated),
onGenerateRoute:(RouteSettings设置){
最终名称= settings.name;
返回新的MaterialPageRoute(
生成器:routes [name],
设置:settings,
);
},
onUnknownRoute:(RouteSettings设置){
抛出Exception('unknown route');
},
);
}






现在可悲(2018 -07-14)在Flutter代码中有两个相互矛盾的断言删除以使上面的代码起作用(您可以仅使用IDE对其进行编辑):



包中的第93行和第96行\flutter\ lib\src\widgets\app.dart

  // assert(navigatorObservers!= null),
//声明(onGenerateRoute!= null || navigatorObservers == const< NavigatorObserver> []),


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.

解决方案

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>[]),

这篇关于使用BLoC处理导航的正确方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆