Flutter 09 Future 和 Stream

一、Future 和 Stream 是处理异步操作的两个重要概念:

Future:

Future 用于表示一个延迟操作的值或错误,即异步操作的结果。通过 Future,可以在异步操作完成后获取其结果。可以使用 async 和 await 关键字来处理 Future,使得异步操作更加直观和易于管理。

Stream:

Stream 则用于处理一系列异步数据(事件)的序列。它可以持续地产生数据,而不是单一的结果。Stream 可以用于处理事件、文件读写、状态管理等需求。使用 StreamController 可以很好的控制 Stream 的创建和数据添加。

二、Future的使用:

async 和 await。

async:用于在方法或函数声明前添加,表示该方法是一个异步方法。在异步方法中,执行顺序可以是非阻塞的,不会阻塞当前线程。

await:用于在异步方法中等待并获取异步表达式的执行结果,只能在被 async 修饰的方法中使用。

1、模拟一个异步请求数据的实现

通过async关键字声明一个异步方法,延迟2秒后返回模拟的结果String值。

Dart 复制代码
Future<String> getAsyncData() async {
    setState(() {
      asyncData = "开始请求数据";
    });
    await Future.delayed(const Duration(seconds: 2));
    return "返回:Future get async data.";
}

通过按钮点击触发模拟接口请求,获取到异步数据后更新到页面中。

Dart 复制代码
ElevatedButton(
  onPressed: () {
	getAsyncData().then((value) {
	  setState(() {
		asyncData = value;
	  });
	});
  },
  child: const Text("Future get async data.")),
Text(asyncData),

2、模拟两个异步请求并行,待全部接口请求完成后获取数据的实现

通过async关键字声明两个异步方法,分别延迟2秒和延迟3秒后,返回模拟的结果String值。

Dart 复制代码
Future<String> getAsyncData2() async {
    setState(() {
      asyncTowData = "getAsyncData2开始请求数据";
    });
    await Future.delayed(const Duration(seconds: 2));
    setState(() {
      asyncTowData = "getAsyncData2完成请求数据";
    });
    return "延迟2秒返回";
}

Future<int> getAsyncData3() async {
    setState(() {
      asyncTowData = "getAsyncData3开始请求数据";
    });
    await Future.delayed(const Duration(seconds: 3));
    setState(() {
      asyncTowData = "getAsyncData3完成请求数据";
    });
    return 3;
}

创建第三个异步方法,将上面的两个异步方法添加到数组里面,并行执行。

Dart 复制代码
// 同时执行两个异步方法并等待它们的结果
Future<List<dynamic>> getTowAsyncData() async {
    List<Future<dynamic>> futures = [getAsyncData2(), getAsyncData3()];
    List<dynamic> result = await Future.wait(futures);
    return result;
}

通过按钮点击触发模拟接口请求,获取到异步数据后更新到页面中。

Dart 复制代码
ElevatedButton(
  onPressed: () {
	getTowAsyncData().then((value) {
	  var data1 = value[0] as String;
	  var data2 = value[1] as int;
	  setState(() {
		asyncTowData = "$data1,延迟$data2秒返回";
	  });
	});
  },
  child: const Text("Future get tow async data.")),
Text(asyncTowData),

3、FutureBuilder 使用

通过FutureBuilder实现请求数据时展示加载组件,请求成功后显示数据。

Dart 复制代码
Widget _init() {
    return FutureBuilder(
      future: initAsyncData(),
      builder: (context, snapshot) {
        // 等待状态显示的widget
        if (snapshot.connectionState == ConnectionState.waiting) {
          return const Center(
            child: CircularProgressIndicator(),
          );
          //  错误时显示的widget
        } else if (snapshot.hasError) {
          return const Text('Error');
          // 加载完成后显示的数据
        } else {
          return _widget();
        }
      },
    );
}

4、FuturePage完整代码

Dart 复制代码
import 'package:flutter/material.dart';

///create by itz on 2024/8/16 14:55
///desc : 
class FuturePage extends StatefulWidget {
  const FuturePage({super.key});

  @override
  State<FuturePage> createState() => _FuturePageState();
}

class _FuturePageState extends State<FuturePage> {

  String asyncData = "";
  String asyncTowData = "";
  bool isInit = false;

  Future initAsyncData() async {
    await Future.delayed(const Duration(seconds: 2));
    isInit = true;
  }

  Future<String> getAsyncData() async {
    setState(() {
      asyncData = "开始请求数据";
    });
    await Future.delayed(const Duration(seconds: 2));
    return "返回:Future get async data.";
  }

  Future<String> getAsyncData2() async {
    setState(() {
      asyncTowData = "getAsyncData2开始请求数据";
    });
    await Future.delayed(const Duration(seconds: 2));
    setState(() {
      asyncTowData = "getAsyncData2完成请求数据";
    });
    return "延迟2秒返回";
  }

  Future<int> getAsyncData3() async {
    setState(() {
      asyncTowData = "getAsyncData3开始请求数据";
    });
    await Future.delayed(const Duration(seconds: 3));
    setState(() {
      asyncTowData = "getAsyncData3完成请求数据";
    });
    return 3;
  }

  // 同时执行两个异步方法并等待它们的结果
  Future<List<dynamic>> getTowAsyncData() async {
    List<Future<dynamic>> futures = [getAsyncData2(), getAsyncData3()];
    List<dynamic> result = await Future.wait(futures);
    return result;
  }

  @override
  Widget build(BuildContext context) {
    Widget w = isInit ? _widget() : _init();
    return Scaffold(
      appBar: AppBar(
        title: const Text("Future"),
      ),
      body: w,
    );
  }

  Widget _init() {
    return FutureBuilder(
      future: initAsyncData(),
      builder: (context, snapshot) {
        // 等待状态显示的widget
        if (snapshot.connectionState == ConnectionState.waiting) {
          return const Center(
            child: CircularProgressIndicator(),
          );
          //  错误时显示的widget
        } else if (snapshot.hasError) {
          return const Text('Error');
          // 加载完成后显示的数据
        } else {
          return _widget();
        }
      },
    );
  }

  Widget _widget() {
    return Container(
      margin: const EdgeInsets.all(16),
      child: ListView(
        children: [
          ElevatedButton(
              onPressed: () {
                getAsyncData().then((value) {
                  setState(() {
                    asyncData = value;
                  });
                });
              },
              child: const Text("Future get async data.")),
          Text(asyncData),
          const SizedBox(height: 10),
          ElevatedButton(
              onPressed: () {
                getTowAsyncData().then((value) {
                  var data1 = value[0] as String;
                  var data2 = value[1] as int;
                  setState(() {
                    asyncTowData = "$data1,延迟$data2秒返回";
                  });
                });
              },
              child: const Text("Future get tow async data.")),
          Text(asyncTowData),
        ],
      ),
    );
  }
}

三、Stream的使用

在Flutter中,Stream 是用于处理异步事件序列的概念,常见应用包括:

异步数据获取:Stream在异步数据获取方面非常有用。例如,在网络请求中,可以使用 Stream 来处理异步数据的传输和响应。通过监听 Stream,可以实时获取数据并更新应用程序的界面,实现动态数据展示的功能。

状态管理:Stream在状态管理中扮演着重要的角色。通过创建包含状态信息的 Stream,可以在应用程序中管理状态的变化。当状态发生变化时,通过 Stream 向订阅者(监听者)发送新的状态信息,从而触发相应的操作或界面更新。

事件总线:Stream可以作为事件总线,用于在应用程序中处理和传递事件。通过创建一个全局的 Stream,不同部分的应用程序可以监听并发送事件,实现模块之间的通信和交互。这种方式可以实现解耦和灵活的组件通信。

文件读写:Stream`也可以用于处理文件读写操作。例如,读取大文件时可以使用 Stream实现分块读取,提高读取效率。同样,可以通过 Stream 监听文件写入事件,实现实时监控文件变化等功能。

1、异步数据获取

1)创建StreamController 控制器,控制 Stream 的创建、数据添加 和 关闭等操作。

Dart 复制代码
final StreamController _streamController = StreamController();

2)创建异步方法,模拟异步数据获取

Dart 复制代码
getStreamData() async {
    for (int i = 0; i < 10; i++) {
      await Future.delayed(const Duration(seconds: 1));
      // 发送值
      _streamController.sink.add(i);
    }
    await Future.delayed(const Duration(seconds: 1));
    // 发送值
    _streamController.sink.add("done");
}

3)创建StreamBuilder用于接收流式数据

Dart 复制代码
StreamBuilder(
  stream: _streamController.stream,
  builder: (context, snapshot) {
	if (snapshot.hasData) {
	  return Text('异步数据:${snapshot.data}');
	} else if (snapshot.hasError) {
	  return Text('发生错误:${snapshot.error}');
	} else {
	  return const Text('加载中...');
	}
  })

4)通过按钮点击触发模拟接口请求

Dart 复制代码
ElevatedButton(
  onPressed: () {
	getStreamData();
  },
  child: const Text("stream get async data.")),

5)完整代码

Dart 复制代码
/*年轻人,只管向前看,不要管自暴自弃者的话*/

import 'dart:async';

import 'package:flutter/material.dart';

///create by itz on 2024/8/16 15:46
///desc :
class StreamPage extends StatefulWidget {
  const StreamPage({super.key});

  @override
  State<StreamPage> createState() => _StreamPageState();
}

class _StreamPageState extends State<StreamPage> {
  // 创建控制器
  final StreamController _streamController = StreamController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Stream"),
      ),
      body: _widget(),
    );
  }

  Widget _widget() {
    return Container(
      margin: const EdgeInsets.all(16),
      child: ListView(
        children: [
          ElevatedButton(
              onPressed: () {
                getStreamData();
              },
              child: const Text("stream get async data.")),
          StreamBuilder(
              stream: _streamController.stream,
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  return Text('异步数据:${snapshot.data}');
                } else if (snapshot.hasError) {
                  return Text('发生错误:${snapshot.error}');
                } else {
                  return const Text('加载中...');
                }
              })
        ],
      ),
    );
  }

  getStreamData() async {
    for (int i = 0; i < 10; i++) {
      await Future.delayed(const Duration(seconds: 1));
      // 发送值
      _streamController.sink.add(i);
    }
    await Future.delayed(const Duration(seconds: 1));
    // 发送值
    _streamController.sink.add("done");
  }
}

2、事件总线

1)创建EventBus单例类

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

///create by itz on 2024/8/16 15:54
///desc :
class EventBus {
  static final EventBus _instance = EventBus._internal();

  factory EventBus() => _instance;

  EventBus._internal();

}

2)使用 broadcast() 方法创建了一个可以实时广播事件的 StreamController

Dart 复制代码
final _controller = StreamController<dynamic>.broadcast();

3)创建 get stream,发送事件,关闭控制器等方法

Dart 复制代码
  Stream get stream => _controller.stream;

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

  void dispose() {
    _controller.close();
  }

4)使用EventBus,设置EventBus事件监听

Dart 复制代码
    EventBus().stream.listen((event) {
      setState(() {
        eventInfo = event;
      });
    });

5)发送EventBus事件

Dart 复制代码
sendEvent(String event) {
    EventBus().fire(event);
  }

6)通过按钮点击触发发送事件

Dart 复制代码
ElevatedButton(
	onPressed: () {
	  sendEvent("cat");
	},
	child: const Text("send Event.")),
Text(eventInfo),

7)完整EventBus代码

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

///create by itz on 2024/8/16 15:54
///desc :
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();
  }
}

8)完整EventBusPage代码

Dart 复制代码
import 'package:async_test/util/EventBus.dart';
import 'package:flutter/material.dart';

///create by itz on 2024/8/16 16:01
///desc :
class EventBusPage extends StatefulWidget {
  const EventBusPage({super.key});

  @override
  State<EventBusPage> createState() => _EventBusPageState();
}

class _EventBusPageState extends State<EventBusPage> {
  String eventInfo = "";

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    EventBus().stream.listen((event) {
      setState(() {
        eventInfo = event;
      });
    });
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    EventBus().dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("EventBus"),
      ),
      body: Container(
        margin: const EdgeInsets.all(16),
        child: ListView(
          children: [
            ElevatedButton(
                onPressed: () {
                  sendEvent("cat");
                },
                child: const Text("send Event.")),
            Text(eventInfo),
          ],
        ),
      ),
    );
  }

  sendEvent(String event) {
    EventBus().fire(event);
  }
}

Stream 适用于处理持续产生数据的异步操作,而 async/await 适用于一次性获取结果的异步操作。两者使用区别包括控制流、数据处理、使用场景和代码结构。

Provider 与 Stream 区别在于使用 Provider 时状态可以被存储起来,而 Stream 不会存储。选择使用哪种取决于需求和代码结构的复杂度。

相关推荐
0wioiw04 小时前
Flutter基础(FFI)
flutter
Georgewu9 天前
【HarmonyOS 5】鸿蒙跨平台开发方案详解(一)
flutter·harmonyos
爱吃鱼的锅包肉9 天前
Flutter开发中记录一个非常好用的图片缓存清理的插件
flutter
张风捷特烈10 天前
每日一题 Flutter#13 | build 回调的 BuildContext 是什么
android·flutter·面试
恋猫de小郭10 天前
Flutter 又双叒叕可以在 iOS 26 的真机上 hotload 运行了,来看看又是什么黑科技
android·前端·flutter
QC七哥10 天前
跨平台开发flutter初体验
android·flutter·安卓·桌面开发
小喷友11 天前
Flutter 从入门到精通(水)
前端·flutter·app
恋猫de小郭11 天前
Flutter 里的像素对齐问题,深入理解为什么界面有时候会出现诡异的细线?
android·前端·flutter
tbit11 天前
dart私有命名构造函数的作用与使用场景
flutter·dart