如何创建一个可以通过访问 MaterialApp 上下文来监听所有页面的 BlocListener?

Posted

技术标签:

【中文标题】如何创建一个可以通过访问 MaterialApp 上下文来监听所有页面的 BlocListener?【英文标题】:How to create a BlocListener that can listen to all pages in flutter with access to MaterialApp context? 【发布时间】:2020-08-01 08:47:37 【问题描述】:

我正在尝试创建一个BlocListener,它能够侦听整个应用程序中的所有页面/路由,就像您如何访问整个应用程序中的BlocProvider(如果它们已定义)一样像下面的代码一样在根级别

runApp(
  MultiProvider(
    providers: [
      ChangeNotifierProvider<IdentityTokenProvider>(
        create: (_) => IdentityTokenProvider(),
      ),
    ],
    child: MultiBlocProvider(
      providers: [
        BlocProvider<AuthBloc>(
          create: (_) => AuthBloc(),
        ),
      ],
      child: MaterialApp(
        debugShowCheckedModeBanner: AppConfig.DEBUGGABLE,
        theme: ThemeData(
            // fontFamily: CustomFontStyle.montserrat,
            ),
        home: AuthListener(
          child: Center(
            child: const MainApp(),
          ),
        ),
      ),
    ),
  ),
);

如您所见,我有提供者、集团和一名听众。我在其他页面中访问集团和提供者没有问题。我的问题是身份验证侦听器。一旦我移动到不同的页面(通过删除堆栈),我就无法访问AuthListener,因为它在MaterialApp 内。但是,在这种情况下,我需要将特定的侦听器 (AuthListener) 放在 MaterialApp 内,因为它包含使用页面导航的代码(如果在小部件树的外部/上方完成实现,则该代码不起作用MaterialApp),并使我们使用 MaterialApp 上下文来显示对话框。

我的页面路由实现删除了堆栈,这是失去对AuthListener的访问的另一个原因

Navigator.pushAndRemoveUntil(
        context,
        MaterialPageRoute(builder: (_) => route),
        (Route<dynamic> route) => false);

为什么在移动到其他页面时要删除路由/页面堆栈?

我专门在认证后使用这个。您真的不希望用户在登录后能够按返回按钮,并将用户重定向回登录页面,对吗?通常后退按钮应该在他们登录时隐藏/关闭应用程序。

我的AuthListener 实现

class AuthListener extends StatefulWidget 
  final Widget child;

  const AuthListener(Key key, @required this.child) : super(key: key);

  @override
  _AuthListenerState createState() => _AuthListenerState();


class _AuthListenerState extends State<AuthListener> 
  @override
  Widget build(BuildContext context) 
    return BlocListener<AuthBloc, AuthState>(
      listener: (context, state) 
        if (state is AuthAuthenticated) 
          PageRouterController.pushAndRemoveStack(context, const EcomPage());
         else if (state is AuthUnauthenticated) 
          PageRouterController.pushAndRemoveStack(context, const LoginPage());
        
      ,
      child: widget.child,
    );
  

有没有其他方法可以解决这个问题?

【问题讨论】:

您是否尝试使用 Builder 小部件包装侦听器? 使用构建器小部件的目的是什么?那不是一个小部件,您可以在其中添加一些代码然后返回一个小部件吗? 如果我理解您的问题是您无法收听 AuthBloc,因为它在树中与 AuthBloc 处于同一级别,对吗? 据我了解,我也无法访问 AuthBloc(一旦我完成身份验证并移至其他页面),因为侦听器位于 MaterialApp 中。据我了解,我只能访问根级别的块、提供程序、侦听器(在 MaterialApp 之上定义)。这意味着我可以在登录页面访问 AuthListener,但是当我移动到不同的页面时。我无法访问它。 我正在尝试实现这个顺便说一句,因为我计划创建一个功能,当用户通过 FCM(仅有效负载)收到静默通知时强制注销用户。所以我需要 AuthListener 位于 MaterialApp 中,因为我需要它的上下文能够通过 MaterialPageRoute 更改路由/页面,以及显示对话框。但是,问题出现在我无法在不同页面访问它的地方,因为它必须在根级别才能在任何地方访问。 【参考方案1】:

所以我最终定义了一个

static final GlobalKey<NavigatorState> navigatorKey = new GlobalKey();

并在我的 MaterialApp 中使用它

@override
  Widget build(BuildContext context) 
    return MaterialApp(
      debugShowCheckedModeBanner: App.DEBUGGABLE,
      theme: ThemeData(
          // fontFamily: CustomFontStyle.montserrat,
          ),
      navigatorKey: App.navigatorKey,
      home: Center(
        child: const LoginPage(),
      ),
    );
  

因此,每当我必须在 MaterialApp 之外的实现情况下进行导航(在我的情况下,通过在 MaterialApp 上方的根级别找到的 AuthListener),我可以通过导航

App.navigatorKey.currentState.pushAndRemoveUntil(
            MaterialPageRoute(builder: (_) => route),
            (Route<dynamic> route) => false);

这意味着我终于可以访问 MaterialApp 导航器和上下文,即使在 MaterialApp 之外的侦听器也允许我进行导航和显示对话框

【讨论】:

以上是关于如何创建一个可以通过访问 MaterialApp 上下文来监听所有页面的 BlocListener?的主要内容,如果未能解决你的问题,请参考以下文章

如何增加整个MaterialApp的字体大小?

我可以用 Provider 包装 MaterialApp 小部件吗?

如何使用 Flutter 在 MaterialApp 中创建卡片

如何在颤动中覆盖 MaterialApp 的主题

如何在 Flutter 中删除 MaterialApp 上的 Android 后退按钮?

还记得第一个看到的Flutter组件吗?