Flutter系列笔记 - 局部刷新之StreamBuilder

3,632 阅读1分钟

局部刷新

在Flutter中页面状态管理是非常重要的,更新页面状态也是我们在做项目时最频繁的操作,那么在Flutter中刷新页面状态直接调用setState方法是非常方便的,但是这样会直接更新build方法,刷新整个页面,但是在某些特定情况下,我们只想更新页面中的某一个widget并不需要刷新整个页面,这个时候就需要用到局部刷新的功能。

- StreamBuilder 基于Stream
  官方说明:
 [StreamBuilder], which is specialized for the case where only the most recent interaction    
 is needed for widget building.
     专门构建于小部件数据更新时交互时使用的情况。

示例用法:

class TestPage extends StatefulWidget {
  const TestPage({Key? key}) : super(key: key);

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

class _TestPageState extends State<TestPage> with TickerProviderStateMixin {
  late StreamController _controller; // 控制器
  int num = 0;

  @override
  void initState() {
    super.initState();
    _controller = StreamController.broadcast(); // 初始化
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("局部刷新"),
      ),
      body: Center(
          child: StreamBuilder(
              initialData: num, // 初始化需要刷新的数据
              stream: _controller.stream, // 控制器对应的stream
              builder: (context, n) {
                return Text("data=${n.data}");// n.data 对应initialData的数据类型
              })),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          _controller.sink.add(num++); // 更新数据
        },
        child: Icon(Icons.add),
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();
    // 记得销毁
    _controller.close();
  }
}

在我们所需要更新的组件外套上StreamBuilder就可以了,用法也很简单,但是这样的用法如果在每个页面都写一遍的话也是比较繁琐的,所以我们需要将这个功能进行封装下。

封装工具类:

import 'dart:async';

import 'package:flutter/material.dart';

import 'stream_build_util.dart';

/// 局部刷新状态
class StreamBuild<T> {
  late StreamController<T> _controller;

  T? t;

  final String key;

  StreamBuild(this.key) {
    _controller = StreamController.broadcast();
  }

  factory StreamBuild.instance(String key) {
    return StreamBuild<T>(key);
  }

  get outer => _controller.stream;

  get data => t;

  // 改变数据发送
  changeData(T t) {
    this.t = t;
    _controller.sink.add(t);
  }

  dis() {
    _controller.close();
  }

// 监听者
  Widget addObserver(Widget Function(T t) ob, {required T initialData}) {
    this.t = data ?? initialData;
    // var streamBuild = this as StreamBuild<T>;
    return StreamBuilderWidget<T>(
      streamBuild: this,
      builder: ob,
      initialData: initialData,
    );
  }
}

class StreamBuilderWidget<T> extends StatefulWidget {
  final StreamBuild<T> streamBuild;
  final Widget Function(T t) builder;
  final T? initialData;

  const StreamBuilderWidget(
      {Key? key,
      required this.streamBuild,
      required this.builder,
      required this.initialData})
      : super(key: key);

  @override
  _StreamBuilderWidgetState createState() => _StreamBuilderWidgetState<T>();
}

class _StreamBuilderWidgetState<T> extends State<StreamBuilderWidget> {
  @override
  Widget build(BuildContext context) {
    return StreamBuilder<T>(
        initialData: widget.initialData,
        stream: widget.streamBuild.outer,
        builder: (context, n) {
          return widget.builder(n.data as T);
        });
  }

  @override
  void dispose() {
    super.dispose();
    widget.streamBuild.dis();

    StreamBuildUtil.instance.onDisposeKey(widget.streamBuild.key); // 清除map数据
  }
}

封装工具类:

import 'dart:collection';

import 'stream_build.dart';

/// 局部刷新工具类
class StreamBuildUtil {
  static StreamBuildUtil? _instance;

  StreamBuildUtil._();

  factory StreamBuildUtil() {
    return instance;
  }

  static StreamBuildUtil get instance => _getInstance();

  static StreamBuildUtil _getInstance() {
    if (_instance == null) {
      _instance = StreamBuildUtil._();
    }
    return _instance ?? StreamBuildUtil._();
  }

  final HashMap<String, StreamBuild> dataBus = HashMap();

  /// key = 刷新局部标记
  StreamBuild getStream(String key) {
    if (!dataBus.containsKey(key)) {
      StreamBuild streamBuild = StreamBuild.instance(key);
      dataBus[key] = streamBuild;
    }
    if (dataBus[key] == null) {
      return StreamBuild.instance(key);
    } else {
      return dataBus[key]!;
    }
  }

  void onDisposeAll() {
    if (dataBus.length > 0) {
      dataBus.values.forEach((f) => f.dis());
      dataBus.clear();
    }
  }

  void onDisposeKey(String? key) {
    if (dataBus.length > 0) {
      dataBus.remove(key);
    }
  }
}

在回到我们最初的例子,用法就变成了这样:

class TestPage extends StatefulWidget {
  const TestPage({Key? key}) : super(key: key);

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

class _TestPageState extends State<TestPage> with TickerProviderStateMixin {
  int num = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("局部刷新"),
      ),
      body: Center(
          child:
              StreamBuildUtil.instance.getStream("streamKey").addObserver((t) {
        return Text("data=${t.data}");
      }, initialData: 0)),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 更新数据
          StreamBuildUtil.instance.getStream("streamKey").changeData(num++); 
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

可以看到,我们就只需要关注我们的业务代码就可以了。 ---End