FutureBuilder是一个可以自动追踪Future的状态并在其状态改变的时候自动重绘的组件。
FutureBuilder追踪Future的状态
- 定义一个模拟请求数据的异步方法
- 数据加载的时候加载一个Indicator,数据加载完毕后显示数据
import 'dart:math';
import 'package:flutter/material.dart';
class FormPage extends StatefulWidget {
final Map arguments;
const FormPage({
super.key,
required this.arguments,
});
@override
State<FormPage> createState() => _FormPageState();
}
class _FormPageState extends State<FormPage> {
Future<String> loadDate() async {
await Future.delayed(const Duration(seconds: 3));
return '我是表单页面的数据';
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.arguments['title']),
),
body: Center(
child: FutureBuilder(
future: loadDate(),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
print(snapshot.connectionState);
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
return Text('Contents: ${snapshot.data}');
}
} else {
return const CircularProgressIndicator();
}
},
),
)
);
}
}
FutureBuilder initialData
import 'dart:math';
import 'package:flutter/material.dart';
class FormPage extends StatefulWidget {
final Map arguments;
const FormPage({
super.key,
required this.arguments,
});
@override
State<FormPage> createState() => _FormPageState();
}
class _FormPageState extends State<FormPage> {
Future<String> loadDate() async {
await Future.delayed(const Duration(seconds: 3));
return '我是表单页面的数据';
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.arguments['title']),
),
body: Center(
child: FutureBuilder(
future: loadDate(),
initialData: '我是初始化的数据',
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if(snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}else {
return Text('Contents: ${snapshot.data}');
}
},
),
)
);
}
}
Stream 、StreamBuilder
前面章节我们给大家介绍了Future,Future在未来只会获取一个值。Stream的字面意思是水流,Stream不像Future那样只会在未来获取一个值,它可以异步获取0个或者多个值。如果说Future是一个异步版本的int或者String,Stream则更像是异步版本的列表,List,List,列表里面可能会有0个或者多个元素。
final future=Future.delayed(const Duration(seconds: 1),()=>43);
final stream=Stream.periodic(const Duration(seconds: 1),(value)=>value);
@override
void initState() {
super.initState();
future.then((value) => print(value));
stream.listen((event) {
print("stream:$event");
});
}
import 'dart:math';
import 'package:flutter/material.dart';
class FormPage extends StatefulWidget {
final Map arguments;
const FormPage({
super.key,
required this.arguments,
});
@override
State<FormPage> createState() => _FormPageState();
}
class _FormPageState extends State<FormPage> {
Future<String> loadDate() async {
return '我是表单页面的数据';
}
Stream<int> loadStreamDate() {
return Stream.periodic(Duration(seconds: 1), (v) => v);
}
@override
void initState() {
// TODO: implement initState
super.initState();
// 初始化数据
loadStreamDate().listen((event) {
print(event);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.arguments['title']),
),
body: Center(),
);
}
}
StreamBuilder
在 Flutter 中,StreamBuilder 是一个将 Stream 流与 Widget结合到一起的组件,可实现组件的局部数据更新 , StreamBuilder 组件和FutureBuilder组件比较相似,不同点在于它是一个可以自动跟踪Stream(数据流或事件流)的状态,并在Stream有变化时自动重绘的组件。Stream不同于Future,可能会在生命周期内释放出任意数量的数据值(正常)或者错误信息(异常),通常被用于读取文件或者下载网络资源等操作,也有时候用于状态管理。
StreamBuilder主要功能:
- 实现局部刷新
- 读取流实现读取文件或者下载网络资源等操作
- 父子组件之间的数据广播
StreamBuilder 实现一个计数器
import 'dart:math';
import 'package:flutter/material.dart';
class FormPage extends StatefulWidget {
final Map arguments;
const FormPage({
super.key,
required this.arguments,
});
@override
State<FormPage> createState() => _FormPageState();
}
class _FormPageState extends State<FormPage> {
Future<String> loadDate() async {
return '我是表单页面的数据';
}
Stream<int> loadStreamDate() {
return Stream.periodic(Duration(seconds: 1), (v){
if(Random().nextBool()){
return v;
}
throw Exception('随机数异常');
});
}
@override
void initState() {
// TODO: implement initState
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.arguments['title']),
),
body: Center(
child: StreamBuilder(
stream: loadStreamDate(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if(snapshot.hasError){
return Text('Error: ${snapshot.error}');
}
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('没有Stream');
case ConnectionState.waiting:
return Text('等待数据...');
case ConnectionState.active:
return Text('active: ${snapshot.data}');
case ConnectionState.done:
return Text('Stream已关闭');
}
},
),
),
);
}
}
StreamBuilder 实时显当前的时间
import 'dart:math';
import 'package:flutter/material.dart';
class FormPage extends StatefulWidget {
final Map arguments;
const FormPage({
super.key,
required this.arguments,
});
@override
State<FormPage> createState() => _FormPageState();
}
class _FormPageState extends State<FormPage> {
Stream<String> counter() {
return Stream.periodic(const Duration(seconds: 1), (count){
return "${DateTime.now().hour}:${DateTime.now().minute}:${DateTime.now().second}";
});
}
@override
void initState() {
// TODO: implement initState
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.arguments['title']),
),
body: Center(
child: StreamBuilder(
stream: counter(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if(snapshot.hasError){
return Text('Error: ${snapshot.error}');
}
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('没有Stream');
case ConnectionState.waiting:
return Text('等待数据...');
case ConnectionState.active:
return Text('active: ${snapshot.data}');
case ConnectionState.done:
return Text('Stream已关闭');
}
},
),
),
);
}
}
StreamController
一般情况我们都是监听别人给我们的数据流,比如File("").openRead() 读取文件, 刚才给大家讲了Stream.periodic 可以创建数据流,如果我们想创建更精确的数据流的话也可以使用StreamController
注意: import 'dart:async'; // 需要导入异步包
StreamController.broadcast()
StreamController 默认只能有一个监听这,如果有多个监听者的话就需要用StreamController.broadcast()
Stream过滤
flutter 中async和async*
- async 返回Future
- async* 返回Stream
async不必多言,有了解的都知道这是异步调用。当一个函数被标记成async的时候,意味这个方法可能要从事耗时工作,比如说网络请求、处理图片等等。被async标记的方法会把返回值用Future包裹一下。
async
async*
StreamBuilder 打字小游戏
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
class FormPage extends StatefulWidget {
final Map arguments;
const FormPage({
super.key,
required this.arguments,
});
@override
State<FormPage> createState() => _FormPageState();
}
class _FormPageState extends State<FormPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.arguments['title']),
),
body: const TypeWritingPage(),
);
}
}
class TypeWritingPage extends StatefulWidget {
const TypeWritingPage({super.key});
@override
State<TypeWritingPage> createState() => _TypeWritingPageState();
}
class _TypeWritingPageState extends State<TypeWritingPage> {
StreamController<int> _inputController = StreamController.broadcast();
@override
Widget build(BuildContext context) {
return Stack(
children: [
...List.generate(3, (index) {
return Game(inputController: _inputController);
}),
// Positioned(
// child: StreamBuilder<int>(
// stream: _inputController.stream,
// builder: (context, snapshot) {
// if(snapshot.hasData){
// return Text('${snapshot.data}');
// }
// if(snapshot.hasError){
// return Text('${snapshot.error}');
// }
// return const Text('监听中...');
// },
// ),
// ),
KeyPad(inputController: _inputController),
],
);
}
}
class Game extends StatefulWidget {
final StreamController<int> inputController;
const Game({super.key, required this.inputController});
@override
State<Game> createState() => _GameState();
}
class _GameState extends State<Game> with SingleTickerProviderStateMixin {
late int a,b;
late double x;
late Color color;
late AnimationController _animationController;
void reset(){
setState(() {
a = Random().nextInt(5)+1;
b = Random().nextInt(5);
x = Random().nextDouble()*320;
color = Colors.primaries[Random().nextInt(Colors.primaries.length)][300]!;
});
}
@override
void initState() {
reset();
widget.inputController.stream.listen((event) {
print(event);
if(event == a+b){
_animationController.stop();
reset();
_animationController.reset();
_animationController.forward();
}
});
color = Colors.primaries[Random().nextInt(Colors.primaries.length)][300]!;
super.initState();
_animationController = AnimationController(
duration: Duration(milliseconds: Random().nextInt(5000)+5000), vsync: this)..forward();
_animationController.addStatusListener((status) {
if(status == AnimationStatus.completed){
reset();
_animationController.reset();
_animationController.forward();
}
});
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animationController,
builder: (BuildContext context, Widget? child) {
return Positioned(
top: Tween(begin: -50.0, end: 500.0).animate(_animationController).value,
left: x,
child: Container(
padding: const EdgeInsets.fromLTRB(8, 5, 8, 5),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(18),
),
child: Text(
'$a+$b=?',
style: const TextStyle(
fontSize: 24,
color: Colors.white,
),
)
)
);
},
);
}
}
// 键盘
class KeyPad extends StatelessWidget {
final StreamController<int> inputController;
const KeyPad({super.key, required this.inputController});
@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.bottomCenter,
child: Container(
margin: const EdgeInsets.only(top: 100),
child: GridView.count(
shrinkWrap: true,
crossAxisCount: 3,
childAspectRatio: 5 / 2,
children: List.generate(9, (index) {
return TextButton(
onPressed: () {
inputController.add(index + 1);
},
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.primaries[index][300]),
foregroundColor: MaterialStateProperty.all(Colors.white),
shape: MaterialStateProperty.all(
const RoundedRectangleBorder()),
),
child: Text(
'${index + 1}',
style: const TextStyle(
fontSize: 32,
color: Colors.white,
),
),
);
}),
),
),
);
}
}