美文网首页
Flutter状态管理:BLoC(Business Logic

Flutter状态管理:BLoC(Business Logic

作者: 青叶小小 | 来源:发表于2021-01-22 16:36 被阅读0次

    一、前言

    占位

    二、什么是BLoC?

    BLoC只是一个概念(Reactive Programming,响应式编程),它是基于“dart:async”中的Stream、StreamController来实现的。


    BLOC.png
    • 用StreamBuilder包裹有状态的部件,StreamBuilder将会监听一个流;
    • 这个流来自于BLoC;
    • 有状态小部件中的数据来自于监听的流;
    • 用户交互手势被检测到,产生了事件。例如按了一下按钮;
    • 调用bloc的功能来处理这个事件;
    • 在bloc中处理完毕后将会吧最新的数据add进流的sink中;
    • StreamBuilder监听到新的数据,产生一个新的snapshot,并重新调用build方法;
    • Widget被重新构建;

    BLoC能够允许我们完美的分离业务逻辑!再也不用考虑什么时候需要刷新屏幕了,一切交给StreamBuilder和BLoC! BLoC由来自Google的两位工程师 Paolo Soares和Cong Hui设计,并在2018年DartConf期间(2018年1月23日至24日)首次展示!


    BLoC.png

    三、BLoC创建方式

    创建方式有三种:

    • 局部模式(类似 setState);
    • 全局单例模式(单例模式);
    • Scoped模式;

    3.1、全局单例模式

    全局单例模式并不推荐,原因在于:持久占用Stream而不会释放(dispose)

    3.1.1、新建Model

    // CountBloc.dart
    import 'dart:async';
    
    class CountBloc {
      int _count = 0;
      StreamController<int> _controller;
    
      Stream<int> get count => _controller.stream;
    
      CountBloc() {
        _count = 0;
        _controller = StreamController.broadcast();
      }
    
      void increment() {
        _controller.sink.add(++_count);
      }
    
      void dispose() {
        _controller.close();
      }
    }
    
    CountBloc bloc = CountBloc();
    

    3.1.2、新建两个页面(BlocPage和BlocPageTwo)

    // BlocPage.dart
    import 'package:flutter/material.dart';
    import 'package:stateresearch/bloc/models/CountBloc.dart';
    import 'package:stateresearch/pages/BlocPageTwo.dart';
    
    class BlocPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return _body(context);
      }
    
      Widget _body(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("BlocPage"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text('You have pushed the button this many times:'),
                StreamBuilder(
                    stream: bloc.count,
                    initialData: 0,
                    builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
                      return Text("${snapshot.data}");
                    }),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {
                return BlocPageTwo();
              }));
            },
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
        );
      }
    }
    
    // BlocPageTwo.dart
    import 'package:flutter/material.dart';
    import 'package:stateresearch/bloc/models/CountBloc.dart';
    
    class BlocPageTwo extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return _body(context);
      }
    
      Widget _body(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("BlocPageTwo"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text('You have pushed the button this many times:'),
                StreamBuilder(
                    stream: bloc.count,
                    initialData: 0,
                    builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
                      return Text("${snapshot.data}");
                    }),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              bloc.increment();
            },
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
        );
      }
    }
    

    3.1.3、修改main文件

    import 'package:flutter/material.dart';
    import 'package:stateresearch/pages/BlocPage.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter状态管理',
          theme: ThemeData(
            primarySwatch: Colors.blue,
            visualDensity: VisualDensity.adaptivePlatformDensity,
          ),
          home: BlocPage(),
        );
      }
    }
    

    3.2、Scoped模式

    推荐这种模式!该方式类似Provider。

    3.2.1、新建Model

    import 'dart:async';
    
    class CountBloc {
      int _count = 0;
      StreamController<int> _controller;
    
      Stream<int> get count => _controller.stream;
    
      CountBloc() {
        _count = 0;
        _controller = StreamController.broadcast();
      }
    
      void increment() {
        _controller.sink.add(++_count);
      }
    
      void dispose() {
        _controller.close();
      }
    }
    

    3.2.2、新建两个页面(BlocPage和BlocPageTwo)

    import 'package:flutter/material.dart';
    import 'package:stateresearch/bloc/providers/CountProvider.dart';
    import 'package:stateresearch/pages/BlocPageTwo.dart';
    
    class BlocPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return _body(context);
      }
    
      Widget _body(BuildContext context) {
        final _bloc = CountProvider.of(context);
    
        return Scaffold(
          appBar: AppBar(
            title: Text("BlocPage"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text('You have pushed the button this many times:'),
                StreamBuilder(
                    stream: _bloc.count,
                    initialData: 0,
                    builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
                      return Text("${snapshot.data}");
                    }),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {
                return BlocPageTwo();
              }));
            },
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
        );
      }
    }
    
    import 'package:flutter/material.dart';
    import 'package:stateresearch/bloc/providers/CountProvider.dart';
    
    class BlocPageTwo extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return _body(context);
      }
    
      Widget _body(BuildContext context) {
        final _bloc = CountProvider.of(context);
    
        return Scaffold(
          appBar: AppBar(
            title: Text("BlocPageTwo"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text('You have pushed the button this many times:'),
                StreamBuilder(
                    stream: _bloc.count,
                    initialData: 0,
                    builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
                      return Text("${snapshot.data}");
                    }),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              _bloc.increment();
            },
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
        );
      }
    }
    

    3.2.3、修改main文件

    import 'package:flutter/material.dart';
    import 'package:stateresearch/bloc/providers/CountProvider.dart';
    import 'package:stateresearch/pages/BlocPage.dart';
    
    void main() {
      runApp(CountProvider(child: MyApp()));
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter状态管理',
          theme: ThemeData(
            primarySwatch: Colors.blue,
            visualDensity: VisualDensity.adaptivePlatformDensity,
          ),
          home: BlocPage(),
        );
      }
    }
    

    四、总结

    我们发现,BLoC 和 ScopedModel / Provider 在跨页面间共享数组,

    • 相同点:
      • 都需要在顶层包一层(即包裹 MaterialApp);
      • 包裹的这一层实际是继承于 InheritedWidget ;
    • 不同点在于Model:
      • ScopedModel / Provider 的 Model 是继承于 Listenable,且需要主动调用 notifyListeners;
      • BLoC 的 Model 是通过 StreamController / Stream / Sink / StreamBuilder 的方式来异步刷新;
      • 三者 Model 都可以类似 extends / mixins 方式,将多个 Model / Provider 全并起来做全局共享,
        然而,因为 BLoC 是基于 Stream方式,当观察的 Model 数量太多时,性能可能会急剧下降;

    下篇会讲到 Redux ,它是一个很优秀的全局共享解决方案,可以考虑 全局redux + 局部bloc(rxdart) 管理方案!

    相关文章

      网友评论

          本文标题:Flutter状态管理:BLoC(Business Logic

          本文链接:https://www.haomeiwen.com/subject/jzxezktx.html