一、时间循环 Event Loop机制
程序之所以卡说白了就是没有时间更新UI界面刷新屏幕导致
常见的卡顿主要是两种:
1.很大的计算量CPU忙不过来
2.等待,等服务器的响应、等用户的输入、等文件的读取...等等
在多线程的机制里每当遇到需要等的东西就开启一个新的线程去守着,负责更新UI的主线程就不会挂起就不会感到卡顿。
但是在Dart中,每个线程都是被封装在Isolate中,每个Isolate是被孤立的隔离开的,彼此不共享内存,彼此间通过发消息来进行通讯
直接运行 | Microtask | Event |
Future.sync() |
scheduleMicrotask() |
Future() |
Future.value() | Future.microtask() |
Future.delayed() |
_.then() | _completed.then() |
二、FutureBuilder
通过监听传递的future值的变化进行刷新界面(而不需要使用setState)
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(''),
),
body: FutureBuilder(
initialData: "haha",
future:
Future.delayed(const Duration(seconds: 2), () => throw ('error')),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Icon(Icons.error);
}
if (snapshot.hasData) {
return Text("${snapshot.data}");
}
return const CircularProgressIndicator();
}),
);
}
三、Stream与StreamBuilder
future只会打印一次,而Stream会持续不断的执行打印
final future = Future.delayed(const Duration(seconds: 2), () => 40);
final stream = Stream.periodic(const Duration(seconds: 1), (_) => 42);
void test() {
future.then((value) => print('future completed: $value'));
stream.listen((event) {
print("stream: $event");
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(''),
),
body: StreamBuilder(
stream: stream,
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return const Text("none: 没有数据");
case ConnectionState.waiting:
return const Text("waiting: 等待数据流");
case ConnectionState.active:
if (snapshot.hasError) {
return Text("active: 错误${snapshot.error}");
} else {
return Text("active: 正常${snapshot.data}");
}
case ConnectionState.done:
return const Text("active: 数据流已经关闭");
}
}));
}
StreamController更加方便的管理stream
final StreamController streamController = StreamController();
@override
void dispose() {
streamController.close();
super.dispose();
}
@override
void initState() {
super.initState();
streamController.sink.add(72);
streamController.stream.listen((event) {});
}
final StreamController streamController = StreamController();
int count = 10;
DefaultTextStyle(
style: Theme.of(context).textTheme.headline4!,
child: Column(children: [
RaisedButton(
child: const Text('10'),
onPressed: () {
count += 10;
streamController.sink.add(count);
}),
StreamBuilder(
stream: streamController.stream,
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return const Text("none: 没有数据");
case ConnectionState.waiting:
return const Text("waiting: 等待数据流");
case ConnectionState.active:
if (snapshot.hasError) {
return Text("active: 错误${snapshot.error}");
} else {
return Text("active: 正常${snapshot.data}");
}
case ConnectionState.done:
return const Text("active: 数据流已经关闭");
}
})
]));
如果被多次监听会报错streamController.stream.listen((event) {});,确实需要可以使用final StreamController streamController = StreamController.broadcast();变成广播
实例:
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State createState() => _MyHomePageState();
}
class _MyHomePageState extends State {
final _controller = StreamController();
int count = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: StreamBuilder(
stream: _controller.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text("你输入了: ${snapshot.data.toString()}",
style: const TextStyle(
fontSize: 24, fontWeight: FontWeight.bold));
}
return const Text("等待...");
}),
),
body: Stack(children: [
Puzzle(inputStream: _controller.stream),
Align(
alignment: Alignment.bottomCenter,
child: KeyPad(controller: _controller))
]
),
);
}
}
class KeyPad extends StatelessWidget {
final StreamController controller;
const KeyPad({Key? key, required this.controller}) : super(key: key);
@override
Widget build(BuildContext context) {
return GridView.count(
shrinkWrap: true,
childAspectRatio: 2 / 1,
crossAxisCount: 3,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
children: List.generate(
9,
(index) => FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
color: Colors.primaries[index][200],
onPressed: () {
controller.add(index + 1);
},
child: Text(
"${index + 1}",
style: const TextStyle(fontSize: 24),
))));
}
}
class Puzzle extends StatefulWidget {
final Stream inputStream;
const Puzzle({Key? key, required this.inputStream}) : super(key: key);
@override
State createState() => _PuzzleState();
}
class _PuzzleState extends State with SingleTickerProviderStateMixin {
late int a, b;
late Color color;
late AnimationController _controller; //显示动画
reset() {
a = Random().nextInt(5) + 1;
b = Random().nextInt(5);
color = Colors.primaries[Random().nextInt(Colors.primaries.length)];
}
@override
void initState() {
reset();
_controller =
AnimationController(vsync: this, duration: const Duration(seconds: 10))
..forward();
_controller.addStatusListener((status) {
if (status == AnimationStatus.completed) {
reset();
_controller.forward(from: 0.0);
}
});
widget.inputStream.listen((input) {
if (input == a + b) {
_controller.forward(from: 0.0);
}
});
super.initState();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
builder: (context, _) {
return Positioned(
top: 400 * _controller.value,
left: 200 * _controller.value,
child: Container(
decoration: BoxDecoration(
color: color,
border: Border.all(color: Colors.black),
borderRadius: BorderRadius.circular(24)),
padding: const EdgeInsets.all(5),
child: Text(
"$a + $b",
style: const TextStyle(fontSize: 24),
),
),
);
});
}
}