flutter:Future、Stream、RxDart

Future

在Flutter中,Future是Dart语言中的一个类,用于表示异步操作的结果。与Future相关的的重要关键字包括async和await。

  • async:这个关键字用于在方法或函数声明前添加,以指示该方法为异步方法。在异步方法中,执行顺序可以是非阻塞的,不会阻塞当前线程。
  • await:这个关键字用于在异步方法中等待并获取异步表达式的执行结果。它只能在async修饰的方法中使用。
dart 复制代码
class _MyHomePageState extends State<MyHomePage> {
  String string = '';
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          backgroundColor: Theme.of(context).colorScheme.inversePrimary,
          title: Text(widget.title),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () async{
              print("开始获取数据");
             await fetchData();
              print(string);
            },
            child: Text("获取数据"),
          ),
        ));
  }

  Future fetchData() async {
    await Future.delayed(const Duration(seconds: 2));
    string = '数据获取完成';
  }
}

Stream

在Flutter中,Stream(流)是一种用于处理异步事件序列的概念。它常见的应用包括:

  • 异步数据获取:Stream常用于从服务器或本地数据库等异步源获取数据。你可以使用Stream来监听数据源的变化,并在数据可用时进行响应。

  • 状态管理:Stream可以用作应用程序的状态管理工具。你可以将应用程序中的状态封装成一个Stream,通过监听该Stream来更新用户界面。例如,你可以在一个Stream中存储应用的登录状态,并在登录状态发生变化时通知界面进行相应的UI更新。

  • 事件总线:Stream可以用作事件总线,用于在应用程序的不同部分传递事件和数据。你可以创建一个全局的Stream,订阅者可以监听该Stream并接收事件。这样可以实现不同组件之间的解耦和通信。

  • 用户输入:当处理用户输入时,Stream也很有用。你可以使用Stream来监听用户在应用程序中的各种操作,例如点击按钮、滑动屏幕等。通过将用户输入转化为Stream事件,你可以将应用程序与用户交互关联起来。

  • 文件读写:Stream还可以用于处理文件读写操作。你可以通过Stream来读取和写入文件,以便异步处理大型文件或流式传输数据。

在Flutter中,使用Dart的Stream类来创建和管理Stream。你可以使用StreamController来控制Stream的创建、数据添加和Stream关闭操作。另外,Flutter还提供了许多Stream相关的操作符和方法,如mapwheretransform等,用于流的转换和处理。

异步数据获取

dart 复制代码
class _MyHomePageState extends State<MyHomePage> {
  // 创建控制器
  final StreamController _streamController = StreamController();

  @override
  void dispose() {
    super.dispose();
    _streamController.close();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          backgroundColor: Theme.of(context).colorScheme.inversePrimary,
          title: Text(widget.title),
        ),
        body: Column(
          children: [
            ElevatedButton(
                onPressed: () {
                  fetchData();
                },
                child: const Text("获取数据")),
            StreamBuilder(
                stream: _streamController.stream,
                builder: (BuildContext context, AsyncSnapshot snapshot) {
                  if (snapshot.hasData) {
                    return Text('异步数据:${snapshot.data}');
                  } else if (snapshot.hasError) {
                    return Text('发生错误:${snapshot.error}');
                  } else {
                    return const Text('加载中...');
                  }
                })
          ],
        ));
  }

  fetchData() async {
    await Future.delayed(const Duration(seconds: 1));
    _streamController.sink.add(1); // 发送第一个值
    await Future.delayed(const Duration(seconds: 2));
    _streamController.sink.add(2); // 发送第二个值
  }
}

使用Stream来处理异步和使用async/await来处理异步有几个区别,包括:

  1. 控制流:使用Stream时,可以通过监听数据流的事件来处理异步操作的结果。当新的数据到达时,可以执行相应的逻辑。而使用async/await时,代码会在等待异步操作完成后继续执行,顺序执行。

  2. 数据处理:使用Stream可以处理多个值或者一系列值的异步操作,例如数据流、事件流等。而使用async/await一次只能处理一个异步操作的结果。

  3. 使用场景:Stream适用于需要处理持续产生数据的异步操作,例如网络请求、传感器数据等。而async/await适用于一次性获取结果的异步操作,例如读取文件、等待用户输入等。

  4. 代码结构:使用Stream时,需要创建StreamController并手动管理数据的发送和订阅。而使用async/await时,可以直接在异步函数中使用关键字await来等待异步操作的结果,代码更加简洁。

总的来说,Stream更适用于处理连续产生数据的异步操作,并且可以方便地对数据流进行处理和转换。而async/await更适用于一次性获取结果的异步操作,代码结构更加简单明了。具体使用哪种方式取决于你的需求和代码结构的复杂度。在某些情况下,两种方式也可以结合使用,例如使用async/await等待一个Future的结果,并将其转换为Stream进行后续处理。

状态管理

上面那个例子也可以看做状态管理,当某一个状态发生改变后,Stream会监听到,然后根据新的状态来更新视图。

那与Provider有什么区别呢?我觉得最重要的区别就是使用Provider时状态可以被存储起来,而Stream不会存储起来。基于此可以来确定需要使用哪一个。

事件总线

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

class EventBus {
  static final EventBus _instance = EventBus._internal();

  factory EventBus() => _instance;

  EventBus._internal();
  // 使用 broadcast() 方法创建了一个可以实时广播事件的 StreamController
  final _controller = StreamController<dynamic>.broadcast();
  
  Stream get stream => _controller.stream;

  void fire(dynamic event) {
    _controller.sink.add(event);
  }

  void dispose() {
    _controller.close();
  }
}
dart 复制代码
// 订阅事件
EventBus().stream.listen((event) {
  // 处理事件
  print('Received event: $event');
});

// 发送事件
EventBus().fire('Event data');

用户输入

没太明白,好像也没什么用

文件读取

dart 复制代码
class _MyHomePageState extends State<MyHomePage> {
  // 创建控制器
  final StreamController _streamController = StreamController<String>();

  @override
  void dispose() {
    super.dispose();
    _streamController.close();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          backgroundColor: Theme.of(context).colorScheme.inversePrimary,
          title: Text(widget.title),
        ),
        body: Column(
          children: [
            ElevatedButton(
                onPressed: () {
                  fetchData();
                },
                child: const Text("获取数据")),
            const SizedBox(
              height: 30,
            ),
            StreamBuilder(
                stream: _streamController.stream,
                builder: (BuildContext context, AsyncSnapshot snapshot) {
                  if (snapshot.hasData) {
                    return Text('异步数据:${snapshot.data}');
                  } else if (snapshot.hasError) {
                    return Text('发生错误:${snapshot.error}');
                  } else {
                    return const Text('加载中...');
                  }
                })
          ],
        ));
  }

  fetchData() async {
    // 文件是对文件系统上文件的引用,因此不能使用文件从资产中读取文件。您不能逐个文件访问资产文件
    File file = File('a.txt');

    Stream<String> fileStream = file
        .openRead()
        .transform(utf8.decoder) // 解码
        .transform(const LineSplitter()); // 将内容按行切分

    fileStream.listen((String line) {
      // 发送读取到的内容到Stream
      _streamController.add(line);
    }, onDone: () {
      // 文件读取完成,关闭流
      _streamController.close();
    }, onError: (error) {
      // 发送错误事件到Stream
      _streamController.addError(error);
    });
  }
}

大概就是这样,但是你无法读取到项目下的文件。

要么使用path_provider来获取路径;要么将文件变成静态文件,但是变成静态文件后要使用rootBundle.loadString进行读取

优点

  • 使用Stream来处理文件时,可以更方便的处理大量数据,不必一次性将整个文件加载到内存中,这对于处理大型文件或实时数据流非常有用。
  • 使用Stream时,文件读取过程中会触发各种事件,例如数据可用、读取完成或发生错误等。你可以通过监听这些事件来采取适当的行动,如更新UI或处理错误。

常用方法

这个可以根据需要自行百度具体用法

  • map: 将数据流中的每个事件转换为一个新的事件。例如,可以使用map方法将数据流中的每个数字加倍。

  • where: 根据给定的条件过滤数据流中的事件。例如,可以使用where方法过滤出数据流中的偶数。

  • expand: 将每个事件转换为多个事件,并将它们展平成一个数据流。例如,可以使用expand方法将每个字符串事件拆分为单个字符事件。

  • take: 仅从数据流中获取前n个事件。例如,可以使用take方法获取前5个事件。

  • skip: 跳过数据流中的前n个事件,然后开始接收后续的事件。例如,可以使用skip方法跳过前3个事件。

  • distinct: 过滤掉数据流中重复的事件。例如,可以使用distinct方法过滤掉重复的字符串事件。

  • merge: 将多个数据流合并为一个数据流。例如,可以使用merge方法将两个整数数据流合并为一个整数数据流。

  • zip: 将两个数据流中的事件一一配对,并将它们合并为一个新的事件。例如,可以使用zip方法将一个字符串数据流和一个整数数据流配对为一个新的数据流

RxDart

RxDart是基于Dart的响应式编程库,提供了对Stream的扩展和增强。一般情况下使用Dart内置的Stream是完全足够的,这里只简单了解一下,感兴趣的可以自行查看文档

如何选择

使用Stream时:

  • 简单的异步操作:如果你只需要处理简单的异步操作,例如监听网络请求结果、处理用户输入事件等,使用Stream就足够了。Stream提供了基本的异步编程机制,可以满足大多数的需求。
  • 较少的数据转换和处理:如果你不需要复杂的数据转换和处理操作,只需要监听数据流的变化,并进行一些简单的操作,如过滤、排序等,那么使用Stream就足够了。

使用RxDart时:

  • 复杂的数据处理:如果你需要进行复杂的数据处理和转换操作,如数据映射、过滤、组合、扁平化等,RxDart提供了丰富的操作符和功能,能够极大地简化代码和提高开发效率。
  • 响应式需求:如果你需要实现响应式编程的思想,即将数据流分成多个阶段进行处理,并对每个阶段的数据进行监听和反应,RxDart非常适合。它提供的Observable对象和操作符能够帮助你构建响应式的数据流处理链。
  • 错误处理:RxDart提供了更便捷的错误处理机制,通过onError()操作符可以方便地捕获和处理异常,使得错误处理更加灵活和高效。

官方文档
https://pub-web.flutter-io.cn/packages/rxdart

安装

dart 复制代码
flutter pub add rxdart

异步数据获取

dart 复制代码
class _MyHomePageState extends State<MyHomePage> {
  // 创建控制器
  final BehaviorSubject<int> _streamController = BehaviorSubject<int>();

  @override
  void dispose() {
    super.dispose();
    _streamController.close();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          backgroundColor: Theme.of(context).colorScheme.inversePrimary,
          title: Text(widget.title),
        ),
        body: Column(
          children: [
            ElevatedButton(
                onPressed: () {
                  fetchData();
                },
                child: const Text("获取数据")),
            StreamBuilder(
                stream: _streamController.stream,
                builder: (BuildContext context, AsyncSnapshot snapshot) {
                  if (snapshot.hasData) {
                    return Text('异步数据:${snapshot.data}');
                  } else if (snapshot.hasError) {
                    return Text('发生错误:${snapshot.error}');
                  } else {
                    return const Text('加载中...');
                  }
                })
          ],
        ));
  }

  fetchData() async {
    await Future.delayed(const Duration(seconds: 1));
    _streamController.add(1); // 发送第一个值
    await Future.delayed(const Duration(seconds: 2));
    _streamController.add(2); // 发送第二个值
  }
}

文件读取

使用File类来打开要读取的文件。

dart 复制代码
final file = File('data.txt');

使用Observable来创建一个可观察的流,并使用fromStream方法将文件的内容转换为流。例如:

dart 复制代码
final observable = Observable.fromStream(file.openRead());

使用rxdart提供的操作符对流进行处理。例如使用listen方法来订阅流,并在每次数据可用时执行相应的操作。

dart 复制代码
observable.listen((data) {
  // 在这里处理读取到的数据
  print(data);
}, onError: (error) {
  // 处理错误
  print(error);
}, onDone: () {
  // 处理完成事件
  print('读取完成');
});
相关推荐
江上清风山间明月1 天前
Flutter开发的应用页面非常多时如何高效管理路由
android·flutter·路由·页面管理·routes·ongenerateroute
Zsnoin能1 天前
flutter国际化、主题配置、视频播放器UI、扫码功能、水波纹问题
flutter
早起的年轻人1 天前
Flutter CupertinoNavigationBar iOS 风格导航栏的组件
flutter·ios
HappyAcmen1 天前
关于Flutter前端面试题及其答案解析
前端·flutter
coooliang2 天前
Flutter 中的单例模式
javascript·flutter·单例模式
coooliang2 天前
Flutter项目中设置安卓启动页
android·flutter
JIngles1232 天前
flutter将utf-8编码的字节序列转换为中英文字符串
java·javascript·flutter
B.-2 天前
在 Flutter 中实现文件读写
开发语言·学习·flutter·android studio·xcode
freflying11192 天前
使用jenkins构建Android+Flutter项目依赖自动升级带来兼容性问题及Jenkins构建速度慢问题解决
android·flutter·jenkins
机器瓦力2 天前
Flutter应用开发:对象存储管理图片
flutter