Flutter数据流监听插件stream_watcher的使用

Flutter数据流监听插件stream_watcher的使用

stream_watcher

Pub basic-tests Donate

Provider出色的watch扩展到BuildContext的启发,此插件为Stream添加了扩展功能,允许监听流的值,并在更改时自动更新父级小部件。

使用方法

导入包,然后在Stream上调用.watch(context)

import 'package:stream_watcher/stream_watcher.dart';

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

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

class _ExampleState extends State<Example> {
  Stream<double> progressStream;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: CircularProgressIndicator(
        value: progressStream.watch(context) ?? 0,
      ),
    );
  }
}

限制

当使用.watch(...)方法时,你必须确保所监视的Stream不会在重建时被重新创建。例如,如果你的流获取器隐式调用了someStream.map(...),则会创建一个新的流,初始值为null,这会导致无限循环。

虽然我认为StreamBuilder也会遇到这个问题,但由于StreamBuilder会重新构建自身而不是父级小部件,因此这个问题发生的频率较低。

性能

显然,关于这一点最大的问题是性能。不幸的是,它的性能不如普通的StreamBuilder,但性能损失并不严重。这意味着,虽然你不应该在性能问题严重的情况下使用它,但在大多数情况下它是完全可以接受的。使用.watch(...)带来的编码简便性也是一个优点,使用.watch(...)是一种非常方便地访问流值的方式。

包含的示例应用程序包含三种访问流的配置:

  1. StreamBuilder
  2. 在自己的上下文中使用(使用构建器)的.watch(...)
  3. 在具有30个构建器的上下文中使用.watch(...)

最后一项配置被包括是因为我认为小部件的大小可能会影响性能。当然在这个测试案例中,差异是微不足道的。

在我的三星Galaxy S8上使用性能覆盖层(UI线程)测量计算时间,我们得到(大约 - 最大值尤其变化很大):

方法 平均 最大
1 4.8 毫秒 15 毫秒
2 6.0 毫秒 15 毫秒
3 6.2 毫秒 16 毫秒

注意事项

watch(...)函数的实现在一个点上捕获了一个FlutterError。这并不是一个具体的错误,但可能被认为是不良实践,因为错误应该被修复而不是被捕获。在这种情况下,这是我发现的唯一方法来发现元素是否仍然挂载。

StreamBuilder类似,.watch(...)最初返回一个null值。我最初包含了一个可选的initialValue参数,该参数在开始时设置值,但是这仅在BuildContext中只监视一次流时才有效,因此它被移除了。处理这个null值的最佳方式是使用空安全操作符someStream.watch(context) ?? backupValue

示例代码

import 'package:flutter/material.dart';
import 'data_bloc.dart';
import 'pages/stream_builder_page.dart';
import 'pages/single_widget_page.dart';
import 'pages/builder_widget_page.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Stream Watcher Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(),
    );
  }
}

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

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

class _HomePageState extends State<HomePage> {
  final dataBloc = DataBloc(30, Duration(milliseconds: 10));
  final titles = [
    'StreamBuilder',
    'Watch with individual widgets',
    'Watch with single widgets',
  ];
  int titleIndex = 0;

  @override
  void dispose() {
    dataBloc.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(titles[titleIndex]),
      ),
      body: PageView(
        onPageChanged: (value) => setState(() {
          titleIndex = value;
        }),
        children: [
          StreamBuilderPage(dataBloc: dataBloc),
          BuilderWidgetPage(dataBloc: dataBloc),
          SingleWidgetPage(dataBloc: dataBloc),
        ],
      ),
    );
  }
}

相关页面代码

// pages/stream_builder_page.dart
import 'package:flutter/material.dart';
import 'data_bloc.dart';

class StreamBuilderPage extends StatelessWidget {
  final DataBloc dataBloc;

  StreamBuilderPage({required this.dataBloc});

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<double>(
      stream: dataBloc.progressStream,
      initialData: 0.0,
      builder: (context, snapshot) {
        return Center(
          child: CircularProgressIndicator(value: snapshot.data),
        );
      },
    );
  }
}

// pages/builder_widget_page.dart
import 'package:flutter/material.dart';
import 'data_bloc.dart';

class BuilderWidgetPage extends StatelessWidget {
  final DataBloc dataBloc;

  BuilderWidgetPage({required this.dataBloc});

  @override
  Widget build(BuildContext context) {
    return Center(
      child: StreamWatcher<double>(
        stream: dataBloc.progressStream,
        builder: (context, value) {
          return CircularProgressIndicator(value: value ?? 0);
        },
      ),
    );
  }
}

// pages/single_widget_page.dart
import 'package:flutter/material.dart';
import 'data_bloc.dart';

class SingleWidgetPage extends StatelessWidget {
  final DataBloc dataBloc;

  SingleWidgetPage({required this.dataBloc});

  @override
  Widget build(BuildContext context) {
    return Center(
      child: StreamWatcher<double>(
        stream: dataBloc.progressStream,
        builder: (context, value) {
          return CircularProgressIndicator(value: value ?? 0);
        },
      ),
    );
  }
}

数据块类

// data_bloc.dart
import 'dart:async';

class DataBloc {
  final int itemCount;
  final Duration duration;
  StreamController<double> _progressController;
  Timer _timer;

  DataBloc(this.itemCount, this.duration) {
    _progressController = StreamController<double>();
    _timer = Timer.periodic(duration, (timer) {
      double newValue = (_progressController.value + 1 / itemCount) % 1;
      _progressController.sink.add(newValue);
    });
  }

  Stream<double> get progressStream => _progressController.stream;

  void dispose() {
    _progressController.close();
    _timer.cancel();
  }
}

更多关于Flutter数据流监听插件stream_watcher的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter数据流监听插件stream_watcher的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


stream_watcher 是一个 Flutter 插件,用于监听和观察数据流(Stream)的状态和变化。它可以帮助你在 Flutter 应用中更方便地管理和调试数据流。以下是如何使用 stream_watcher 插件的基本步骤:

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 stream_watcher 依赖:

dependencies:
  flutter:
    sdk: flutter
  stream_watcher: ^1.0.0  # 请使用最新版本

然后运行 flutter pub get 来安装依赖。

2. 导入包

在你的 Dart 文件中导入 stream_watcher 包:

import 'package:stream_watcher/stream_watcher.dart';

3. 使用 StreamWatcher

StreamWatcher 可以用来包装任何 Stream,并监听其状态和事件。

基本用法

import 'package:flutter/material.dart';
import 'package:stream_watcher/stream_watcher.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: StreamWatcherExample(),
    );
  }
}

class StreamWatcherExample extends StatefulWidget {
  [@override](/user/override)
  _StreamWatcherExampleState createState() => _StreamWatcherExampleState();
}

class _StreamWatcherExampleState extends State<StreamWatcherExample> {
  Stream<int> _stream;
  StreamWatcher<int> _streamWatcher;

  [@override](/user/override)
  void initState() {
    super.initState();
    _stream = Stream.periodic(Duration(seconds: 1), (count) => count).take(10);
    _streamWatcher = StreamWatcher<int>(_stream);

    // 监听流事件
    _streamWatcher.listen((event) {
      print('Event: $event');
    });

    // 监听流的完成
    _streamWatcher.onDone(() {
      print('Stream completed');
    });

    // 监听流的错误
    _streamWatcher.onError((error, stackTrace) {
      print('Error: $error');
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Stream Watcher Example'),
      ),
      body: Center(
        child: StreamBuilder<int>(
          stream: _streamWatcher.stream,
          builder: (context, snapshot) {
            if (snapshot.hasData) {
              return Text('Count: ${snapshot.data}');
            } else if (snapshot.hasError) {
              return Text('Error: ${snapshot.error}');
            } else {
              return Text('Waiting for data...');
            }
          },
        ),
      ),
    );
  }
}
回到顶部