Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Flutter ——状态管理 | StreamBuild

Flutter ——状态管理 | StreamBuild

原创
作者头像
CatEatFish
修改于 2020-07-09 06:24:44
修改于 2020-07-09 06:24:44
3K0
举报
文章被收录于专栏:干活分享干活分享

1.什么是stream?

StreamBuild从字面意思来讲是数据流构建,是一种基于数据流的订阅管理。Stream可以接受任何类型的数据,值、事件、对象、集合、映射、错误、甚至是另一个Stream,通过StreamController中的sink作为入口,往Stream中插入数据,然后通过你的自定义监听StreamSubscription对象,接受数据变化的通知。如果你需要对输出数据进行处理,可以使用StreamTransformer,它可以对输出数据进行过滤、重组、修改、将数据注入其他流等等任何类型的数据操作。

2.stream都有哪些类型

Stream有两种类型:单订阅Stream和广播Stream。单订阅Stream只允许在该Stream的整个生命周期内使用单个监听器,即使第一个subscription被取消了,你也没法在这个流上监听到第二次事件;而广播Stream允许任意个数的subscription,你可以随时随地给它添加subscription,只要新的监听开始工作流,它就能收到新的事件。

2.1 单订阅类型实例
代码语言:txt
AI代码解释
复制
import 'dart:async';

void main() {
  // 初始化一个单订阅的Stream controller
  final StreamController ctrl = StreamController();
  
  // 初始化一个监听
  final StreamSubscription subscription = ctrl.stream.listen((data) => print('$data'));

  // 往Stream中添加数据
  ctrl.sink.add('my name');
  ctrl.sink.add(1234);
  ctrl.sink.add({'a': 'element A', 'b': 'element B'});
  ctrl.sink.add(123.45);
  
  // StreamController用完后需要释放
  ctrl.close();
}
2.2广播类stream
代码语言:txt
AI代码解释
复制
import 'dart:async';

void main() {
  // 初始化一个int类型的广播Stream controller
  final StreamController<int> ctrl = StreamController<int>.broadcast();
  
  // 初始化一个监听,同时通过transform对数据进行简单处理
  final StreamSubscription subscription = ctrl.stream
                          .where((value) => (value % 2 == 0))
                          .listen((value) => print('$value'));

  // 往Stream中添加数据
  for(int i=1; i<11; i++){
    ctrl.sink.add(i);
  }
  
  // StreamController用完后需要释放
  ctrl.close();
}

3.stream有哪些好处?

####3.1.随意操作数据流。

刚才在stream定义那里已经说过了,stream是基于数据流的,从skin管道入口到StreamController提供stream属性作为数据的出口之间,可以对数据做任何操作,包括过滤、重组、修改等等。

####3.2 当数据流变化时,可以刷新小部件。

Stream是一种订阅者模式,当数据发生变化时,通知订阅者发生改变,重新构建小部件,刷新UI

###4.如何使用streamBuild?

代码语言:txt
AI代码解释
复制
  StreamBuilder<T>(
    key: ...可选...
    stream: ...需要监听的stream...
    initialData: ...初始数据,尽量不要填null...
    builder: (BuildContext context, AsyncSnapshot<T> snapshot){
        if (snapshot.hasData){
            return ...基于snapshot.hasData返回的控件
        }
        return ...没有数据的时候返回的控件
    },
)

下面是一个模仿官方自带demo“计数器”的一个例子,使用了StreamBuilder,而不需要任何setState:

我在代码里注释了步骤(四步):

代码语言:txt
AI代码解释
复制
import 'dart:async';
import 'package:flutter/material.dart';

class CounterPage extends StatefulWidget {
  @override
  _CounterPageState createState() => _CounterPageState();
}

class _CounterPageState extends State<CounterPage> {
  int _counter = 0;
  //步骤1:初始化一个StreamController<任何数据> 简单的可以扔一个int,string,开发中经常扔一个网络请求的model进去,具体看你使用场景了。
  final StreamController<int> _streamController = StreamController<int>();

  @override
  void dispose(){
  //步骤2.关流,不管流会消耗资源,同时会引起内存泄漏
    _streamController.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Stream version of the Counter App')),
      body: Center(
      //步骤3.使用StreamBuilder构造器
        child: StreamBuilder<int>(  // 监听Stream,每次值改变的时候,更新Text中的内容
          stream: _streamController.stream,
          initialData: _counter,
          builder: (BuildContext context, AsyncSnapshot<int> snapshot){
            return Text('You hit me: ${snapshot.data} times');
          }
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.add),
        onPressed: (){
          // 每次点击按钮,更加_counter的值,同时通过Sink将它发送给Stream;
          // 每注入一个值,都会引起StreamBuilder的监听,StreamBuilder重建并刷新counter
          //步骤4.往StreamBuilder里添加流,数据变了,就用通知小部件
          _streamController.sink.add(++_counter);
        },
      ),
    );
  }
}

###5.具体如何使用?

刚刚介绍了stream的如何使用,是不是感觉还是懵的状态,实例代码仅仅是实例,如何应用到项目中呢?我们的项目不仅仅是一个简单的计数器,接下来我将结合项目,简单讲述一下如何使用streamBuild。这是我司的一张UI。

UI.png
UI.png

要求点击“关注”变为“已关注”

如何去实现的?实现的方法有好多种。

1.这个item是StatefulWidget,点击“关注”,然后setstate(){}

2.使用其他的状态管理去实现。如 【 ScopedModel 】 【 Provide 】 【Bloc】

3.使用 StreamBuild 实现。

我选择使用StreamBuild去实现,稍后我会解释为何要用streambuild 去实现。

代码语言:txt
AI代码解释
复制
import 'dart:async';
import 'package:easy_alert/easy_alert.dart';
import 'package:flutter/material.dart';
import 'package:hongka_flutter/app/Manager/IO/hk_request.dart';
import 'package:hongka_flutter/app/Manager/api/ConfigApi.dart';
import 'package:hongka_flutter/app/Modules/basemodel/focuseItemModel.dart';
import 'package:hongka_flutter/app/Modules/home/info_organization.dart';
//我会省略部分代码,并且注释使用步骤
//步骤一:使用 StatefulWidget,为何要用StatefulWidget?待会解释
class FollowsItem extends StatefulWidget {
  FocuseItemModel focusItemModel;
  String focusType; //不为空就是关注界面,隐藏关注按钮
  int index;
  String studentId;

  FollowsItem(
      {Key key,
      this.focusItemModel,
      this.focusType,
      this.index,
      this.studentId});

  @override
  _FollowsItemState createState() => _FollowsItemState();
}

class _FollowsItemState extends State<FollowsItem> {
  FocuseItemModel focusItemModel;
  String focusType; //不为空就是关注界面,隐藏关注按钮
  String headerImage;
  double screenWidth, marginLeft = 15.0, marginRight = 15.0, marginAll = 30.0;
  int index;
  String studentId;
  //步骤二:声明StreamController
  StreamController<FocuseItemModel> _streamController;

  @override
  void initState() {
    super.initState();
    this.focusItemModel = this.widget.focusItemModel;
    this.focusType = this.widget.focusType;
    this.index = this.widget.index;
    this.studentId = this.widget.studentId;
    //步骤三实现 StreamController<FocuseItemModel>,FocuseItemModel是我的实体类
    _streamController = StreamController<FocuseItemModel>.broadcast();
    //步骤四将数据添加到 _streamController
    _streamController.sink.add(focusItemModel);
  }

  @override
  void dispose() {
  //步骤五:关流
    _streamController.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        _clickItem(context);
      },
      child: Container(
        .....省略无关UI代码
        child: Stack(
          children: <Widget>[
            .....省略无关UI代码
            Positioned(
              right: 0,
              top: 0,
              child: Offstage(
                  offstage: !(focusType == null),
                  child: GestureDetector(
                    onTap: () {
                    //已关注return
                      if (focusItemModel.focusStatus == 1) {
                        return;
                      }
                      //未关注进行网络请求
                      focusOrganizationRequest(focusItemModel.positionId);
                    },
                    //步骤五:使用StreamBuilder构建
                    child: StreamBuilder<FocuseItemModel>(
                        stream: _streamController.stream,//数据流
                        initialData: focusItemModel,//初始值
                        builder: (BuildContext context,
                            AsyncSnapshot<FocuseItemModel> snapshot) {
                          return Image.asset(
                            (snapshot?.data?.focusStatus ?? 0) == 1
                                ? 'images/view/recommend_flowered.png'
                                : 'images/view/recommend_flower.png',
                            width: 54,
                            height: 40,
                          );
                        }),
                  )),
            )
          ],
        ),
      ),
    );
  }

  ///关注红广号
  Future focusOrganizationRequest(String positionId) async {
    String url = 'xxxx';
    var data = {
      'studentId': studentId,
      'positionId': positionId,
    };
    var options = await HK_Request().getRequestOptions();
    var response = await HK_Request()
        .post(url, context, isShowLoading: true, data: data, options: options);
    print(response.toString());
    if (response['status'] == 200) {
    //步骤六:改变model值,并将model 扔到_streamController里,此时数据改变,通知小部件,重新构建
      focusItemModel.focusStatus = 1;
      _streamController.sink.add(focusItemModel);
      Alert.toast(context, '关注成功!',
          position: ToastPosition.center, duration: ToastDuration.short);
      print('关注红广号成功' + response.toString());
    
    } else {
      print('关注红广号错误');
      Alert.toast(context, response['msg'],
          position: ToastPosition.center, duration: ToastDuration.short);
    }
  }

model值改变,streamBuild 通知小部件,并刷新小部件。

问题1 为何选择使用streamBuild

1.方法一使用StatefulWidget,刷新时使用setstate(){},使用setstate(){}刷新,会将整个item 进行重新构建,整个item 仅仅只有“关注”需要改变,其它控件都刷新,会造成资源浪费。

2.方法二使用状态管理bloc,如果使用了bloc,streamBuild中的stream 就因该传bloc< AModel >的数据,如果我其它地方使用也使用了这个item,那么这个stream就应该传bloc< BMode >,此时streamBuild中的stream 类型就不匹配了,这个item 就无法复用了,所以我放弃使用bloc等状态管理

3.为何item 最外层使用StatefulWidget?不是使用streamBuild 就可以不用使用StatefulWidget了吗?

的确使用streamBuild,就可以不使用StatefulWidget。但是 不用StatefulWidget,如何关流? StatelessWidget 没有dispose()方法,不能关流,所以此时还需要使用StatefulWidget。

代码语言:txt
AI代码解释
复制
 @override
  void dispose() {
  //步骤五:关流
    _streamController.close();
    super.dispose();
  }

有群友提出,可以将“关注”的属性提取出来,单独一个bloc去管理,我觉得为了一个按钮的改变,去做很多操作,有点不值得了。当然有兴趣的可以去实现一下。

###问题2.怎样才能不使用StatefulWidget?

bloc+streamBuild,此时的stream是bloc里的,不需要在dispose()方法中去关流,这样就可以放弃使用StatefulWidget了。

6. bloc结合streamBuild 实现状态管理会在下一篇内容中讲解。

本人对于 streamBuild 理解的也不是很深刻,没有往太细节去讲解,只是结合自己的项目去讲解了开发中遇到的问题,希望大家提提意见,共同进步。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
FlutterDojo设计之道—状态管理之路(三)
通过Dart提供的Stream机制,Flutter可以很轻松的构建响应式的编程方式,同时也让跨页面、跨Widget的数据管理问题迎刃而解。
用户1907613
2020/09/08
1.6K0
FlutterDojo设计之道—状态管理之路(三)
[译]Flutter响应式编程:Streams和BLoC
本文主要介绍Streams,Bloc和Reactive Programming(响应式编程)的概念。 理论和实践范例。
JarvanMo
2018/09/17
4.2K0
Flutter完整开发实战详解(十一、全面深入理解Stream)
Stream 在 Flutter 是属于非常关键的概念,在 Flutter 中,状态管理除了 InheritedWidget 之外,无论 rxdart,Bloc 模式,flutter_redux ,fish_redux 都离不开 Stream 的封装,而事实上 Stream 并不是 Flutter 中特有的,而是 Dart 中自带的逻辑。
GSYTech
2019/05/10
4K0
Flutter完整开发实战详解(十一、全面深入理解Stream)
flutter中使用BloC模式
BloC【Business Logic Component】模式是paolo soares 和 cong hui 在2018年Google dartconf上提出的,具体的视频你可以参考YouTube.
老码小张
2018/09/21
17.5K6
Flutter状态管理(2)——单Stream和广播Stream
在Flutter状态管理(1)——InheritedWidget中介绍了状态管理以及如何使用InheritedWidget来实现全局状态的管理。这篇博客将介绍如何使用Stream来实现状态管理。
用户1108631
2019/09/24
2.4K0
Flutter | 事件循环,Future
在 Dart 中,没有多线程的概念,所谓的异步操作全部都是在一个线程里面执行的, 并且不会造成卡顿的原因就是事件循环(Event Loop),
345
2022/02/11
4.3K0
Flutter | 事件循环,Future
【源码篇】Flutter Bloc背后的思想,一篇纠结的文章
用过Bloc的靓仔们,肯定能感受到,Bloc框架对开发页面,做了很清晰划分,框架强行定了俩种开发模式
小呆呆666
2021/06/15
2.4K0
Flutter完整开发实战详解(十二、全面深入理解状态管理设计)
在所有 响应式编程 中,状态管理一直老生常谈的话题,而在 Flutter 中,目前主流的有 scope_model 、BloC 设计模式 、flutter_redux 、fish_redux 等四种设计,它们的 复杂度 和 上手难度 是逐步递增的,但同时 可拓展性 、解耦度 和 复用能力 也逐步提升。
GSYTech
2019/05/14
2.1K0
Dart 异步
一条执行线上,同时且只能执行一个任务(事件),其他任务都必须在后面排队等待被执行。也就是说,在一条执行线上,为了不阻碍代码的执行,每遇到的耗时任务都会被挂起放入任务队列,待执行结束后再按放入顺序依次执行队列上的任务,从而达到异步效果。
Hankkin
2020/01/13
1.6K0
Dart 异步
告别setState()! 优雅的UI与Model绑定 Flutter DataBus使用~
现在页面上有两个数字key1和key2需要展示,当点击上方的按钮时,我们对应修改key1或者key2的值。
老孟Flutter
2020/09/29
2.5K0
告别setState()! 优雅的UI与Model绑定 Flutter DataBus使用~
Flutter 的状态管理方案:setState、BLoC、ValueNotifier、Provider
登录页面的主要导航是通过一个小部件实现的,该小部件使用 Drawer 菜单在不同选项中进行选择。
玖柒的小窝
2021/11/28
4.6K0
Flutter 的状态管理方案:setState、BLoC、ValueNotifier、Provider
Flutter 移动端架构实践:Widget-Async-Bloc-Service
在过去的一年中,各种不同的状态管理技术被提出,但截至目前,Flutter的团队和相关社区还没有得出单一的 首选解决方案。
CCCruch
2019/06/19
16.1K0
Flutter 移动端架构实践:Widget-Async-Bloc-Service
在 Flutter 中探索 StreamBuilderimage
异步交互可能需要一个理想的机会来进行总结。偶尔,在周期结束之前可能会发出一些值。在 Dart 中,您可以创建一个返回 Stream 的容量,该容量可以在异步进程处于活动状态时发射一些值。假设您需要根据一个 Stream 的快照在 Flutter 中构造一个小部件,那么有一个名为 StreamBuilder 的小部件。
前端小tips
2021/11/27
2.5K0
在 Flutter 中探索 StreamBuilderimage
Flutter —— 状态管理 | Provide
有关Provide的题外话,Provide 是 ScopedModel 的进阶或者说是兄弟,为何这么说呢?因为这两个插件的内容重叠的太多,所以对于这两个插件存在争议。
CatEatFish
2020/07/09
1.5K0
Flutter BLoC 异步通信、BlocBuilder的基本使用、BlocProvider的初探
BloC 全称是 Business Logic Component(业务逻辑组件),主要作用就是将业务逻辑和UI组件分离开。
早起的年轻人
2020/10/27
3.4K0
Flutter BLoC 异步通信、BlocBuilder的基本使用、BlocProvider的初探
Flutter中的状态管理
Flutter作为出自Google的一个跨平台(iOS,Android)应用开发方案。布局方式上和React或者说React Native非常相似——组件(Widget)化。写起来非常的高效,却有着React Native所不具有的优势: 一套代码到处运行,原生渲染,原生调用,不需要像RN需要桥接。
小刀c
2022/08/16
1.2K0
Flutter中的状态管理
让Flutter 应用程序性能提高 10 倍的 10 个技巧
Flutter 应用程序以其精美的设计和流畅的功能而闻名,但性能问题会很快破坏用户体验。借助这 10 个优化性能的专家技巧,将您的应用提升到一个新的水平。
IT千锋教育
2023/06/05
9170
FutureBuilder与Stream
FutureBuilder 是一个基于 Future 最后一次结果进行构建的 Widget。
徐建国
2021/08/09
1K0
Flutter 黏贴卡动画效果
设计非常出色的动画会使UI感觉更直觉,应用程序具有光滑的外观和感觉,改善用户体验。Flutter的动画支持使实现各种动画类型变得容易。许多小部件,特别是“Material”小部件,都伴随着其设计规范中所描述的标准运动效果,但是与此同时,也可以自定义这些效果。
老孟Flutter
2021/04/22
2.2K0
Flutter 黏贴卡动画效果
【Flutter】348- 写给前端工程师的 Flutter 教程
| 导语 最爱折腾的就是前端工程师了,从 jQuery 折腾到 AngularJs,再折腾到 Vue、React。最爱跨屏的也是前端工程师,从 phonegap,折腾到 React Native,这不又折腾到了 Flutter。
pingan8787
2019/09/17
1.1K0
【Flutter】348- 写给前端工程师的 Flutter 教程
推荐阅读
相关推荐
FlutterDojo设计之道—状态管理之路(三)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文