[译]flutter_bloc 最新版本 0.19.0 的最新功能

7,465 阅读5分钟

flutter_bloc 是实现了 BLoC 模式的第三方库,可以让你很方便的使用 BLoC 模式开发 Flutter 应用。

Hi,大家好!我兴奋的宣布 flutter_bloc 发布了最新的版本 v0.19.0,其中包含了社区要求的几个特性,最重要的是提升了开发体验和减少模板代码。

在介绍更新的特性之前,我想先感谢社区里那些反馈使用体验、创建 PR、和提 isuues 的人,如果没有你们的共享的话,我们很难实现这些新特性。

废话不多说,现在让我们看看更新了哪些新特性!

BlocProvider 的改进

在此之前,为了初始化和回收 bloc,一般都会使用 StatefulWidget 来创建 bloc,因为 StatefulWidget 相比 StatelessWidget 自带生命周期。但是这也导致了许多模板代码,而且也会带来一些问题,例如很容易忘记释放 bloc 或者在 build 方法里初始化 bloc。为了解决这些问题,社区里有人甚至创建了插件,如 flutter_bloc_extensions 。我很高兴的宣布,这些的反馈已经被采纳了,现在 BlockProvider 已经可以自动的释放 blocs。

BlockProvider 现在有两种使用方法。

第一种方法是使用默认的构造函数:

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      builder: (BuildContext context) => MyBloc(),
      child: MyChild(),
    );
  }
}

在此之前,你只能这么写:

class MyWidget extends StatefulWidget {
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  MyBloc _myBloc;
  
  @override
  void initState() {
    super.initState();
    _myBloc = MyBloc();
  }
  
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      bloc: _myBloc,
      child: MyChild(),
    );
  }
  
  @override
  void dispose() {
    _myBloc.dispose();
    super.dispose();
  }
}

你可以看到,在此之前,需要使用 StatefulWidget 来写 bloc,但现在只需要使用 StatelessWidget 就可以了,实现 bloc 的代码量大幅减少,而且会自动释放 bloc,所以不用担心 bloc 的释放问题。

第二种方法是使用 BlocProvidervalue 构造函数:

Navigator.of(context).push(
  MaterialPageRoute<MyPage>(
    builder: (context) {
      return BlocProvider.value(
        value: BlocProvider.of<MyBloc>(context),
        child: MyPage(),
      );
    },
  ),
);

在上面的例子中,把一个已经存在的 MyBloc 实例给 MyPage 使用,MyPage 是一个新页面,使用 Navigator 打开。在这个例子里,我们要为 MyPage 提供一个已经存在的 MyBloc 实例,并且不希望释放 MyBlocMyPage 页面关闭的时候,因为还有其他地方在使用 MyBloc。所以,我们可以使用 BlocProvidervalue 构造函数为 Widget 提供一个已经存在的 bloc 并且不会自动释放。可以点击 route_access_recipe 查看更多。

总结一下,如果你要创建一个新的 bloc 给子树使用,就可以使用 BlocProvider 的默认构造函数来创建 bloc 实例,会有自动释放 bloc 的功能。但是如果只是要给子树提供一个已经存在的 bloc 实例,这个时候就不需要自动释放 bloc 的功能,所以使用 BlocProvidervalue 构造函数。想要释放 bloc 的话,就在创建 bloc 的地方释放。

你现在就不需要使用 StatefulWidget 来管理你的 blocs 了。

MultiBlocProvide(之前是 BlocProviderTree)

MultiBlocProvide 的功能是把多个 BlocProvider 合并到一起。

MultiBlocProvide 因为消除了多个 BlocProvider 的嵌套,所以提高了代码的可读性。

在此之前,多个 BlocProvider 是这么写的:

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider<BlocA>(
      builder: (BuildContext context) => BlocA(),
      child: BlocProvider<BlocB>(
        builder: (BuildContext context) => BlocB(),
        child: BlocProvider<BlocC>(
          builder: (BuildContext context) => BlocC(),
          child: ChildA(),
        )
      ),
    );
  }
}

有了 MultiBlocProvide,就可以这么写:

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: [
        BlocProvider<BlocA>(
          builder: (BuildContext context) => BlocA(),
        ),
        BlocProvider<BlocB>(
          builder: (BuildContext context) => BlocB(),
        ),
        BlocProvider<BlocC>(
          builder: (BuildContext context) => BlocC(),
        ),
      ],
      child: ChildA(),
    );
  }
}

在上面的例子里,我们为 ChildA 提供了 BlocA,BlocB,BlocC 三个 bloc。使用 MultiBlocProvider 可以无需额外的嵌套,可以让我们的代码更好读。

Repository Provider (之前是 ImmutableProvider)

Repository Provider 是新引进来的 provider,专门用于在 widget 树中处理 repositories, repositories 用于处理数据请求,类似于 Redux 中的 Middleware。想要知道 repository 是什么和怎么用的,可以点击这里查看。

从架构上看,repository 是一个或多个 data providers 的封装,并且负责与 bloc 通信。所以,在整个 Widget 树里能访问 repositories 是非常重要的,随着最近的更新,这个功能很容易就能实现。

我们可以使用 RepositoryProvider 默认的构造函数创建和提供 repository:

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RepositoryProvider(
      builder: (context) => MyRepository(),
      child: MyChild(),
    );
  }
}

class MyChild extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      builder: (context) => MyBloc(RepositoryProvider.of<MyRepository>(context)),
      child: MyOtherChild(),
    );
  }
}

在上面的例子中,我们为 MyChild 及其子树 创建了 MyRepository 的实例。然后在接下来的 Widget 树里创建的 MyBloc (MyBloc 依赖 MyRepository) 可以使用 RepositoryProvider.of 访问 MyRepository 的实例。你会发现 MyRepository 的 API 和 BlocProvider 非常像,这样的话就会更加容易使用。

MultiRepositoryProvider (过去是 ImmutableProviderTree)

MultiRepositoryProvider 的功能是把多个 RepositoryProvider 合并到一起。

MultiRepositoryProvider 因为消除了多个 RepositoryProvider 的嵌套,所以提高了代码的可读性。

在此之前,多个 RepositoryProvider 是这么写的:

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RepositoryProvider<RepositoryA>(
      builder: (context) => RepositoryA(),
      child: RepositoryProvider<RepositoryB>(
        builder: (context) => RepositoryB(),
        child: RepositoryProvider<RepositoryC>(
          builder: (context) => RepositoryC(),
          child: ChildA(),
        )
      )
    );
  }
}

有了 MultiRepositoryProvider,就可以这么写:

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiRepositoryProvider(
      providers: [
        RepositoryProvider<RepositoryA>(
          builder: (context) => RepositoryA(),
        ),
        RepositoryProvider<RepositoryB>(
          builder: (context) => RepositoryB(),
        ),
        RepositoryProvider<RepositoryC>(
          builder: (context) => RepositoryC(),
        ),
      ],
      child: ChildA(),
    );
  }
}

在上面的例子里,我们为 ChildA 提供了 RepositoryA,RepositoryB,RepositoryC 。使用 MultiRepositoryProvider 可以无需额外的嵌套,可以让我们的代码更好读。

集成 Provider

你可能注意到,新的 BlocProviderRepositoryProvider 的 API 和 Provider 很像。v0.19.0 的最大更新就是底层使用了 Provider,这样开发者就可以拥有一个具有一致 API 的依赖注入库。在这里特别感谢 Remi Rousselet 的反馈和耐心。

flutter_bloc 还会继续吸收接纳其他的 providers(因为 flutter_bloc 会接受社区里的其他反馈),但是我们非常激动在底层使用 Provider,这样的话我们就不需要使用之前 我们自己的 InheritedWidget 的封装,不需要自己来维护,而是直接享受到 Provider 未来更新带来的新特性。

你可以查看我们完整的 change_log 来查看更多的信息。