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('暂无数据');
              },
            ),
          ],
        ),
      ),
    );
  }
}
相关推荐
stevenzqzq1 小时前
android启动初始化和注入理解3
android
LawrenceLan2 小时前
Flutter 零基础入门(九):构造函数、命名构造函数与 this 关键字
开发语言·flutter·dart
一豆羹3 小时前
macOS 环境下 ADB 无线调试连接失败、Protocol Fault 及端口占用的深度排查
flutter
行者963 小时前
OpenHarmony上Flutter粒子效果组件的深度适配与实践
flutter·交互·harmonyos·鸿蒙
城东米粉儿3 小时前
compose 状态提升 笔记
android
冰淇淋真好吃3 小时前
iOS实现 WKWebView 长截图的优雅方案
ios
粤M温同学3 小时前
Android 实现沉浸式状态栏
android
ljt27249606614 小时前
Compose笔记(六十八)--MutableStateFlow
android·笔记·android jetpack
stevenzqzq5 小时前
Android Studio 断点调试核心技巧总结
android·ide·android studio