flutter 流(Stream)介绍&结合RxDart使用

在 Flutter 中,Stream 是一种用于处理异步数据序列的机制。它允许你以异步的方式接收和处理一系列的数据,类似于事件流。下面将详细介绍 Flutter 中 Stream 的使用,包括创建、监听、转换和控制等方面。

Flutter 流(Stream)介绍

1、创建 Stream

在 Flutter 中有多种方式可以创建 Stream,下面是几种常见的创建方式:

使用 StreamController

StreamController 是创建 Stream 的最常用方式,它允许你手动添加数据到流中。

csharp 复制代码
import 'dart:async';

void main() {
  // 创建一个 StreamController
  final streamController = StreamController<int>();

  // 获取 Stream
  final stream = streamController.stream;

  // 向流中添加数据
  streamController.sink.add(1);
  streamController.sink.add(2);
  streamController.sink.add(3);

  // 监听流
  stream.listen((data) {
    print('Received data: $data');
  });

  // 关闭 StreamController
  streamController.close();
}
使用 Stream.fromIterable

Stream.fromIterable 可以将一个可迭代对象(如 List)转换为 Stream

ini 复制代码
import 'dart:async';

void main() {
  final list = [1, 2, 3, 4, 5];
  final stream = Stream.fromIterable(list);

  stream.listen((data) {
    print('Received data: $data');
  });
}
使用 Stream.periodic

Stream.periodic 可以创建一个按指定时间间隔重复发送数据的 Stream

dart 复制代码
import 'dart:async';

void main() {
  final stream = Stream.periodic(Duration(seconds: 1), (count) => count);

  final subscription = stream.listen((data) {
    print('Received data: $data');
    if (data >= 5) {
      // 取消订阅
      subscription.cancel();
    }
  });
}

在上述代码中,创建了一个每隔 1 秒发送一次数据的 Stream,数据从 0 开始递增。当接收到的数据大于等于 5 时,取消订阅。

2、监听 Stream

使用 listen 方法可以监听 Stream 中的数据,listen 方法接受一个回调函数,当有新的数据到达时,该回调函数将被调用。

dart 复制代码
import 'dart:async';

void main() {
  final stream = Stream.fromIterable([1, 2, 3]);

  stream.listen(
    (data) {
      print('Received data: $data');
    },
    onError: (error) {
      print('Error: $error');
    },
    onDone: () {
      print('Stream is done');
    },
  );
}

在上述代码中,listen 方法接受三个可选参数:

  • onData:当有新的数据到达时调用。
  • onError:当流中出现错误时调用。
  • onDone:当流关闭时调用

3、 转换 Stream

可以使用 Stream 的各种转换方法对数据进行处理,例如 mapwhere 等。

使用 map 转换数据
ini 复制代码
import 'dart:async';

void main() {
  final stream = Stream.fromIterable([1, 2, 3]);

  final transformedStream = stream.map((data) => data * 2);

  transformedStream.listen((data) {
    print('Transformed data: $data');
  });
}
使用 where 过滤数据
ini 复制代码
import 'dart:async';

void main() {
  final stream = Stream.fromIterable([1, 2, 3, 4, 5]);

  final filteredStream = stream.where((data) => data % 2 == 0);

  filteredStream.listen((data) {
    print('Filtered data: $data');
  });
}

4、控制 Stream

可以使用 StreamSubscription 对象来控制 Stream 的监听,例如暂停、恢复和取消订阅。

ini 复制代码
import 'dart:async';

void main() {
  final stream = Stream.periodic(Duration(seconds: 1), (count) => count);

  final subscription = stream.listen((data) {
    print('Received data: $data');
    if (data == 2) {
      // 暂停订阅
      subscription.pause();
      Future.delayed(Duration(seconds: 3), () {
        // 恢复订阅
        subscription.resume();
      });
    }
    if (data >= 5) {
      // 取消订阅
      subscription.cancel();
    }
  });
}

结合RxDart封装

Stream可以简单的处理数据流,但遇到更复杂的需求时,发现原生Stream的操作符不够用。这个时候我们就可以借助于RxDart。RxDart可以提供更多的操作符的链式调用、错误处理、流的组合。

csharp 复制代码
class RxStream<T> {
  final BehaviorSubject<T> _subject = BehaviorSubject<T>();

  Stream<T> get stream => _subject.stream;

  // 添加数据
  void add(T value) => _subject.sink.add(value);

  // 链式操作符示例:防抖 + 过滤空值
  Stream<T> debounceAndFilter(Duration duration) {
    return stream
        .debounceTime(duration) // 防抖
        .where((value) => value != null); // 过滤空值
  }

  // 合并多个流(例如:搜索输入 + 筛选条件)
  static Stream<R> combineStreams<A, B, R>(
      Stream<A> streamA,
      Stream<B> streamB,
      R Function(A, B) combiner,
      ) {
    return Rx.combineLatest2(streamA, streamB, combiner);
  }

  // 关闭资源
  void dispose() => _subject.close();
}
 
场景 1:实时搜索防抖
arduino 复制代码
class SearchService {
  final RxStream<String> _searchStream = RxStream();
  Stream<String> get searchResults => _searchStream.debounceAndFilter(Duration(milliseconds: 300));
  void onSearchTextChanged(String text) {
    _searchStream.add(text);
  }
  void dispose() => _searchStream.dispose();
}

// 使用示例
final searchService = SearchService();
searchService.searchResults.listen((text) {
// 发起搜索请求(防抖后)
print('Searching for: $text');
});
场景 2:数据流合并
less 复制代码
class _MyHomePageState extends State<MyHomePage> {
  late EnhancedRxStream<String> searchStream;
  late EnhancedRxStream<String> filterStream;
  late Stream<String> combinedStream;

  @override
  void initState() {
    super.initState();
    searchStream = EnhancedRxStream<String>();
    filterStream = EnhancedRxStream<String>();

    // 使用 combineStreams 方法合并两个流
    combinedStream = EnhancedRxStream.combineStreams(
      searchStream.stream,
      filterStream.stream,
      (searchTerm, filterTerm) {
        return '搜索词: $searchTerm, 筛选条件: $filterTerm';
      },
    );
  }

  @override
  void dispose() {
    searchStream.dispose();
    filterStream.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Combine Streams Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.all(16.0),
              child: TextField(
                onChanged: (text) {
                  searchStream.add(text);
                },
                decoration: const InputDecoration(
                  hintText: '输入搜索词',
                ),
              ),
            ),
            Padding(
              padding: const EdgeInsets.all(16.0),
              child: TextField(
                onChanged: (text) {
                  filterStream.add(text);
                },
                decoration: const InputDecoration(
                  hintText: '输入筛选条件',
                ),
              ),
            ),
            const SizedBox(height: 20),
            StreamBuilder<String>(
              stream: combinedStream,
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  return Text(
                    snapshot.data!,
                    style: Theme.of(context).textTheme.headline6,
                  );
                }
                return const Text('暂无数据');
              },
            ),
          ],
        ),
      ),
    );
  }
}
相关推荐
python资深爱好者3 小时前
Android中的触摸事件是如何传递和处理的
android
飞猿_SIR4 小时前
Exoplayer2源码编译FFmpeg拓展模块实现音频软解码
android·ffmpeg·音视频
Ya-Jun5 小时前
Android Studio安装与配置详解
android·ide·android studio
m0_748246355 小时前
MySQL Workbench安装教程以及菜单汉化
android·数据库·mysql
峥嵘life7 小时前
Android 系统开发的指导文档
android
卡尔特斯7 小时前
Flutter 监听当前页面可见与隐藏状态
android·flutter·ios
KevinWang_7 小时前
Compose 练习 - 选择 Yes
android
初级代码游戏8 小时前
MAUI(C#)安卓开发起步
android·开发语言·c#·hyper-v·maui·haxm·aehd
qq_124987075311 小时前
Android+SpringBoot的老年人健康饮食小程序平台
android·spring boot·小程序·毕业设计
daily_233314 小时前
c++领域展开第十四幕——STL(String类的常用接口说明以及相关练习)超详细!!!!
android·开发语言·c++