如何在上下文之间共享集团

Posted

技术标签:

【中文标题】如何在上下文之间共享集团【英文标题】:How to share the bloc between contexts 【发布时间】:2019-08-20 21:56:16 【问题描述】:

在使用showDialog() 导航到新上下文后,我正在尝试访问在我的应用程序根附近创建的 bloc 实例。但是,如果我尝试像往常一样通过从_thisBlocInstance = BlocProvider.of<ThisBlocType>(context) 之类的上下文中获取它来获取集团,我会收到一个错误,表明在此上下文中没有提供集团。

我认为这是因为showDialog() builder 方法为对话框中的小部件分配了一个新的上下文,这些小部件不知道我要查找的 Bloc,它在用户登录后立即实例化:

  @override
Widget build(BuildContext context) 
  _authBloc = BlocProvider.of<AuthBloc>(context);
  _accountBloc = AccountBloc(authBloc: _authBloc);

  return BlocProvider(
    bloc: _accountBloc,

 ....

角落里有一个按钮,显示一个对话框:

@override
Widget build(BuildContext context) 
  return Align(
    alignment: Alignment.bottomRight,
    child: Padding(
      padding: const EdgeInsets.all(18.0),
      child: FloatingActionButton(
        onPressed: () => showDialog(
          context: context,
          builder: (newContext) => EventDialog(),
        ).then(
          (val) => print(val)
        ),
        child: Icon(Icons.add),
      ),
    ),
  );

然后在 EventDialog 中,我尝试再次找到带有上下文的 bloc:

@override
void build(BuildContext context) 

  _accountBloc = BlocProvider.of<AccountBloc>(context);
  _userMenuItems = _accountBloc.usersInAccount
    .map((user) => DropdownMenuItem(
          child: Text(user.userName),
          value: user.userId,
        ))
    .toList();

这失败了,出现错误“getter bloc was called on null”,或者,没有该类型的 bloc 附加到此上下文。

在使用 showDialog() 或导航到新的上下文之后,是否有某种方法可以仅从上下文中访问块?

这是块提供者类:

import 'package:flutter/material.dart';

//This class is a generic bloc provider from https://www.didierboelens.com/2018/08/reactive-programming---streams---bloc/
//it allows easy access to the blocs by ancestor widgets and handles calling their dispose method

class BlocProvider<T extends BlocBase> extends StatefulWidget 
  BlocProvider(
    Key key,
    @required this.child,
    @required this.bloc,
  ): super(key: key);

  final T bloc;
  final Widget child;

  @override
  _BlocProviderState<T> createState() => _BlocProviderState<T>();

  static T of<T extends BlocBase>(BuildContext context)
    final type = _typeOf<BlocProvider<T>>();
    BlocProvider<T> provider = context.ancestorWidgetOfExactType(type);
    return provider.bloc;
  

  static Type _typeOf<T>() => T;


class _BlocProviderState<T> extends State<BlocProvider<BlocBase>>
  @override
  void dispose()
    widget.bloc.dispose();
    super.dispose();
  

  @override
  Widget build(BuildContext context)
    return widget.child;
  


abstract class BlocBase 
  void dispose();

【问题讨论】:

【参考方案1】:

我发现在新上下文中访问原始块的最佳方法是将对其的引用传递给管理新上下文逻辑的新块。为了保持代码模块化,每个块不应控制超过一页的逻辑或一件事(例如用户的登录状态)。因此,当我使用 showDialog() 创建一个新的屏幕/上下文时,我还应该有一个新的块来处理该屏幕中的逻辑。如果我需要对原始 bloc 的引用,我可以通过对话框小部件的构造函数将其传递给新 bloc 的构造函数,因此新 bloc/context 仍然可以访问原始 bloc 中的任何信息:

    child: FloatingActionButton(
      onPressed: () => showDialog(
        context: context,
        builder: (newContext) => NewEventDialog(
          accountBloc: BlocProvider.of<AccountBloc>(context),
        ),
      ).then((event) => eventsBloc.addEvent(event)),

...

class NewEventDialog extends StatelessWidget 
  final AccountBloc accountBloc;
  NewEventBloc _newEventBloc;

  NewEventDialog(this.accountBloc) : assert(accountBloc != null);

  @override
  Widget build(BuildContext context) 
    _newEventBloc = NewEventBloc(accountBloc: accountBloc);

    return BlocProvider(
      bloc: _newEventBloc,
...

【讨论】:

【参考方案2】:

最后一个答案还可以,但可以简化,就是将 Bloc 转移到其子小部件。

@override
Widget build(BuildContext context) 
  return Align(
    alignment: Alignment.bottomRight,
    child: Padding(
      padding: const EdgeInsets.all(18.0),
      child: FloatingActionButton(
        onPressed: () => showDialog(
          context: context,
          builder: (newContext) => EventDialog((
          accountBloc: BlocProvider.of<AccountBloc>(context),
        ),
        ).then(
          (val) => print(val)
        ),
        child: Icon(Icons.add),
      ),
    ),
  );



class NewEventDialog extends StatelessWidget 
  final AccountBloc accountBloc;

  NewEventDialog(this.accountBloc) : assert(accountBloc != null);

@override
void build(BuildContext context) 

  _accountBloc = accountBloc;
  _userMenuItems = _accountBloc.usersInAccount
    .map((user) => DropdownMenuItem(
          child: Text(user.userName),
          value: user.userId,
        ))
    .toList();

到目前为止,我发现通过页面路由访问小部件时会出现此问题。我们可以将 Bloc 小部件转移到小部件来避免这个问题。

【讨论】:

以上是关于如何在上下文之间共享集团的主要内容,如果未能解决你的问题,请参考以下文章

React Native Context,如何在多个嵌套文件和组件之间共享上下文

在 Linux 上的上下文之间共享 OpenGL 对象

在核心数据的上下文之间共享非持久对象?

在 Tomcat 中的上下文之间共享会话数据

在 OS X 上的 OpenGL 上下文之间共享数据(不同的版本/配置文件)

在 Django 中的视图之间共享上下文?