【Dart】Streamで非同期的にデータを受け渡す
はじめに
Flutterで離れたスコープにあるウィジェットに対して値やイベントを渡したい場合、どのようにすれば良いのか気になったので調べてみました。
Streamとは
簡単に言うと、ストリームにデータを渡したら、ストリームの受け手がデータを検知して何らかの処理を開始する。というものでしょうか。今回はあまり深堀りせず使い方を説明していきたいと思います。
StreamController class - dart:async library - Dart API
実行環境
Dart programming language | Dart
Flutter on CodePen
簡単なサンプル
では少しコードを書いてみましょう。
import 'dart:async';
void main() {
// Streamを制御するコントローラを定義
var controller = StreamController<String>();
// ストリームの受け手(リスナー)
controller.stream.listen((data){
print('通知:' + data);
});
// ストリームにデータを流す
controller.sink.add('お知らせ1');
controller.sink.add('お知らせ2');
}
/* output
通知:おしらせ1
通知:おしらせ2
*/
シンプルに書いちゃいましたけど、ストリームに渡したデータをリスナー側が検知して処理をしているということがわかるかと思います。
Flutterでの実際の使用場面
ではFlutterで実際の使い方を想定したコードを書いていきましょう。
イメージとしては、兄弟関係にある2つのウィジェットの片方のStateをもう片方から変更する、みたいな使い方かなと思ってます。
以下、今回作成したコードの全体像となります。
import 'package:flutter/material.dart';
import 'dart:async';
void main() {
runApp(
MaterialApp(
home: MyWidget(),
),
);
}
class MyWidget extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Keys(), // 送り手
KeyDisplay(), // 受け手
],
),
);
}
}
// ストリームを送るウィジェット
class Keys extends StatelessWidget {
Widget build(BuildContext context) {
return Center(
child: Row(
children: <Widget>[
Key("A"),
Key("B"),
Key("C"),
],
),
);
}
}
class Key extends StatelessWidget {
final String _key;
// コンストラクタ
Key(this._key);
Widget build(BuildContext context) {
return Expanded(
flex: 3,
child: FlatButton(
color: Colors.lightBlue,
child: Text(_key),
onPressed: () {
// ストリームにデータを流す
_KeyDisplayState.controller.sink.add(_key);
},
),
);
}
}
// ストリームを受け取るステートフルウィジェット
class KeyDisplay extends StatefulWidget {
_KeyDisplayState createState() => new _KeyDisplayState();
}
// Stateクラス
class _KeyDisplayState extends State<KeyDisplay> {
// コントローラーを定義
static final controller = StreamController<String>();
var _title = "";
// 初期化メソッドをオーバーライドしてリスナーをセット
void initState() {
controller.stream.listen((key) {
setState(() {
_title = key;
});
});
}
Widget build(BuildContext context) {
return Center(
child: Text(
_title,
style: Theme.of(context).textTheme.display1,
),
);
}
}
完成した画面がこちらになります。
ボタンを押下すると、そのボタンのテキストと同じものが下に表示されます。
コードをパーツごとに見ていきましょう。
まずはステートにリスナーを定義します。ここではstaticで定義してコントローラーを参照させます。
// ストリームを受け取るステートフルウィジェット
class KeyDisplay extends StatefulWidget {
_KeyDisplayState createState() => new _KeyDisplayState();
}
// Stateクラス
class _KeyDisplayState extends State<KeyDisplay> {
// コントローラーを定義
static final controller = StreamController<String>();
var _title = "";
// 初期化時にリスナーをセット
void initState() {
controller.stream.listen((key) {
setState(() {
_title = key;
});
});
}
Widget build(BuildContext context) {
return Center(
child: Text(
_title,
style: Theme.of(context).textTheme.display1,
),
);
}
}
あとはボタン側のウィジェットでKeyDisplayのコントローラーを参照して、Streamにデータを流しましょう!
// ストリームを送るウィジェット
class Keys extends StatelessWidget {
Widget build(BuildContext context) {
return Center(
child: Row(
children: <Widget>[
Key("A"),
Key("B"),
Key("C"),
],
),
);
}
}
class Key extends StatelessWidget {
// コンストラクタ
final String _key;
Key(this._key);
Widget build(BuildContext context) {
return Expanded(
flex: 3,
child: FlatButton(
color: Colors.lightBlue,
child: Text(_key),
onPressed: () {
// ストリームにデータを流す
_KeyDisplayState.controller.sink.add(_key);
},
),
);
}
}
これでウィジェット間でのデータのやり取りが簡単にできるってわけですね!
他にもデータの受け渡しをする方法は色々ある...らしい
RxDartとかProviderとか色々あるらしいんですけど、まぁまずは少しづつわかるところから理解していこうと思います。気が向けばこの辺りについても記事を書いてみようかなぁ。
参考文献
動かして理解する!dartのStreamとrxdart! - Qiita
【Flutter】電卓・計算機アプリの作成 2-開発記録
Discussion