1. タイムループイベントループの仕組み
プログラムがスタックする理由は、UI インターフェイスを更新して画面を更新する時間がないためです。
ラグには主に 2 つのタイプがあります。
1. CPU が大量の計算でビジー状態になる
2. 待機、サーバーの応答を待機、ユーザーの入力を待機、ファイルが読み取られるのを待機...など。
マルチスレッド メカニズムでは、何かを待機する必要があるたびに、それを保護するために新しいスレッドが開かれ、UI の更新を担当するメイン スレッドがハングしたり、スタックしたりすることはありません。
しかし、Dart では、各スレッドは Isolate にカプセル化され、各 Isolate は分離されて分離され、互いにメモリを共有せず、メッセージを送信することで相互に通信します。
直接実行する | マイクロタスク | イベント |
Future.sync() |
スケジュールマイクロタスク() |
未来() |
Future.value() | Future.microtask() |
Future.遅延() |
_。それから() | _completed.then() |
二、FutureBuilder
渡された将来の値の変化を監視してインターフェイスを更新します (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();
}),
);
}
3. ストリームとStreamBuilder
future は 1 回だけ印刷されますが、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 でストリームをより便利に管理
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) {}); が報告されます。ブロードキャストになるには、最終的な StreamController streamController = StreamController.broadcast(); を使用する必要があります。
例:
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
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<Puzzle> createState() => _PuzzleState();
}
class _PuzzleState extends State<Puzzle> 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),
),
),
);
});
}
}