1.はじめに
これまでに7つのAndroidStudio&Flutter&Dart記事を書いている。以下は内容、
-
【Flutter/Dart】環境構築方法の紹介
https://qiita.com/my_programming/items/ad1493e3fef2fb63e036 -
【Flutter/Dart】簡単なプロジェクトの設定と共有方法の紹介
https://qiita.com/my_programming/items/cec09574a3b6e39be277 -
【Flutter/Dart】エミュレータの言語設定をかえよう
https://qiita.com/my_programming/items/6c67fed22c624fd9494b -
【Flutter/Dart】Dartの文法について学ぼう(今回説明する内容)
-
【Flutter/Dart】タスク管理アプリを作ろう
https://qiita.com/my_programming/items/9ed2276f6e9076f43444 -
【Flutter/Dart】Google地図アプリを作ろう
https://qiita.com/my_programming/items/26b9ac6f0d2b3d1bd766 -
【Flutter/Dart】情報管理アプリ+Google地図アプリを作ろう
https://qiita.com/my_programming/items/5bd1bb7b789b642635b4
ここではFlutterのプログラミングで使うDartについて学ぶ。
2.Dartの文法について学ぼう
背景
DartとはもともとJavaScriptに対抗するために2011にGoogleによって作られた。2018にDart2が、現在はDart2.17がhttps://dart.dev/にて公開されている。
実行方法
DartはFlutterのインストール時に一緒にインストールされる。Dartを実行するために以下のサンプルプログラムHelloWorld.dartを作る。
void main(){
print('Hello, World');
}
HelloWorld.dartを実行するために以下のコードをターミナルで書く。
$ dart HelloWorld.dart
実行結果) Hello, World
ファイル中のコードの段組みを自動調節したい場合にはVSCODEを使うとよい。VSCODEをインストールしてから拡張機能でDartをインストールする。その後で新規ファイルで拡張子をDartとしたファイルを作成してから、Dartファイルのあるフォルダで上記のコマンドを実行しても同じ結果が得られる。
文法の基礎
Dartはオブジェクト指向プログラミング言語である。すべての数値・変数・関数がオブジェクトとして扱われ、すべてのオブジェクトはObjectクラスから継承している。Dartでは、行の終わりはコロン(;)が必要、関数などは中括弧({})で囲む、ダブルスラッシュ(//)はコメントになるなど、基本的なルールは他の言語に近いものがある。関数の書き方には=>
オペレータを使った省略形がある。HelloWorld.dartを省略形を使って記述すると以下のようになる。
void main() => print('Hello, World!');
関数の省略形は無名関数で使われることが多く頻繁にでてくる。次に変数について解説する。varを使うと変数の型はコンピュータによって能動的に型付けされるという特徴がある。ただし、代入で異なる型のデータを入力するとエラーになる。
void main(){
var moji = 'moji';
moji = 123; // ここでエラー
print(moji);
}
もし変数の型を固定したくない場合、dynamic型で明示的に宣言する必要がある。文字型から整数型に動的に変換されることがわかる。変数の型付は比較的厳密に行われるため動的な変換で想定外の値が入ると行った事故は起こりにくい。
void main(){
dynamic moji = 'moji';
moji = 123;
print(moji);
}
変数
組み込み型
標準で使用可能な変数の型には以下のものがある。変数の宣言時に明示的に型を指定することで意図しない値が入ることを防止できる。
- 文字: String
- 数字: number, int, double
- ブーリアン: boolean
- 列挙型: List(Array), Set, Map
前回使ったvarについて使用例を改めて紹介する。
var name = 'Voyajer I';
var year = 1977;
var antennaDiameter = 3.7;
var flybyObjects = ['Jupiter', 'Saturn', 'Uranus', 'Neptune'];
var image = {
'tags': ['saturn'],
'url': '//path/to/saturn.jpg',
};
varキーワードを使うと最初に入れる値の型によって変数の型が決まる。型を調べるにはruntimeTypeプロパティを参照するか、isキーワードでオブジェクトのクラスを確認する。実際に入力して結果を確認しよう。
dynamic moji = 'moji';
print(moji.runtimeType);
print(moji is String)
}
どの様な型の値でも変数に入れられるようにするにはdynamicキーワードを利用するが、必要な場所以外ではvarキーワドを使う様にしよう。理由は、Dartのランタイムが変数に値を代入する度にdynamicで型チェックを行っているとアプリの実行速度がその型チェックの作業だけ遅くなるからである。
変数の初期化
Dartではすべての変数はオブジェクトとして扱われているため初期化されていない変数はデフォルト値としてnull(ヌル)を返す(これはDartの環境によって異なる)。以下に変数の初期化をしない場合を示す。
// 以下はすべてnullが入る
var name;
int count;
String str;
もし初期化後に不要に変更させたくない定数を入れる場合にはvarの代わりにfinalキーワードもしくはconstキーワードを使う。finalキーワードを付けた場合、1度変数への代入を行うと、2回目以降の代入でエラーになる。関数からの戻り値などの実行時に決まる値をその後のコードで変更させたくない場合に使う。以下にfinalキーワードの初期化の例を示す。
final nameA = 'Bob';
final String nameB = 'Bobby';
constキーワードもfinalキーワードに似ているが、constキーワードはコンパイル時に確定した値を定数として使う。決まった係数やパラメータなどをコードに持たせたい時に使う。以下にconstキーワードの初期化の例を示す。
// コンパイル時に計算可能であれば下の宣言も可能
const bar = 1000000; // Unit of pressure
const double atm = 1.01325 * bar;
変数の有効範囲(スコープ)
Dartにおけるへんすの有効範囲(スコープ)は単純に波括弧({})で囲まれた範囲である。クラスや関数の中で宣言した場合、そのクラスや関数内で利用できループの中で宣言した場合にはループ内でのみ有効になる。以下に変数のスコープの例を示す。
bool topLevel = true;
void main() {
var insideMain = true;
void myFunction() {
var insideFunction = true;
void nestedFunction(){
var insideNestedFunction = true;
// ここでtopLevelにアクセス可能
}
}
// ここでinsideFunctionはアクセス不可
}
上のコードではtopLevelだけ型指定を行っている。Dartのデザインガイドではグローバル変数は変数の型を指定し、ローカル変数は型を指定しない原則とするようガイドされている。
関数
Dartはオブジェクト指向言語のため関数もオブジェクトとして扱われる。基本的な関数の書き方を以下で説明する。
関数の正しい書き方と省略した書き方
Dartでは関数を省略して書くことができる。=>
記号は{return 戻り値}
を省略した形である。そのため省略形の中にループを記述できない。以下に関数の基本型を示す。
bool isNoble(int atmicNumber){
return _nobleGases[atomicNumber] != null;
}
関数からの返り値はreturnキーワードで返す。上記の関数の省略形は以下の様になる。
bool isNoble(int atomicNubmer) => _nobleGases[atomicNumber] != null;
もし関数が何も値を返さない場合には関数名の前にvoidキワードを入れる。以下に返り値がない場合の例を示す。
void doSomething(int x, int y){
print(x * y);
}
関数のパラメータ
関数への値の渡し型は、パラメータの順番で指定する位置パラメータと、パラメータ名を指定する名前付きパラメータの2つの方法がある。まず一般的な位置パラメータから説明する。位置パラメータは、関数の呼び出し時に宣言した順番にパラメータを並べる方法である。以下に位置パラメータの例を示す。
// 関数の宣言
void enableFlags(bool boold, bool hidden){}
// 関数の呼び出し
enableFlags(true, false);
関数を名前付きパラメータで記述すると次の様になる。以下に名前付きパラメータの例を示す。
// 関数の宣言
void enableFlags(bool boold, bool hidden){}
// 関数の呼び出し
enableFlags(boold: true, hiddlen: false);
位置パラメータと名前付きパラメータのどちらを使ってもかまわないがパラメータ指定をオプションにするための記述が異なってくる。以下にオプション・パラメータを例を示す。
// オプション・パラメータの宣言
void enableFlags(bool boold, [bool hiddlen=false]){}
//
void enableFlags(bool boold, bool hidden=false);
位置パラメータでオプション・パラメータを指定する場合はパラメータを[ ]で囲む。この場合、呼び出し時に指定されなかったバア愛のデフォルト値を設定することができる。名前付きパラメータはキホはすべてオプション・パラメータとなるためすべてのパラメータでデフォルト値の設定が可能である。オプション・パラメータのデフォルト値が指定されなかった場合はnullがデフォルト値として指定される。
無名関数(ラムダ式)
Dartでは無名関数(ラムダ式)を使うことができる。通常の関数は名前を付けて宣言した後に関数呼び出しという形で処理を実施する。一方、無名関数は関数呼び出しを行う場所で関数の宣言を行い実行させることができる。無名関数を使うとループ処理や非同期処理で呼び出す関数をその場で記述できるだめコードの量の削減と可読性の工場を同時に達成できる。以下に無名関数の例を示す。
void main(){
var list = ['apples', 'bananas', 'oranges'];
list.forEach((item){
print('${list.index0f(item)}: $item');
});
}
コードを実行した結果は以下の様になる。list.forEachはlistの要素を順にパラメータとして渡すメソッドである。その後の(item){・・・}が無名関数になっている。”名前がない”以外には普通の関数と違いはない。
$ dart anonfunc.dart
0: apples
1: bananas
2: oranges
無名関数を省略形で書くことができる。以下に無名関数の省略型の実行例である。
list.forEach((list) => print('${list.indexOf}(item)}: $item'));
無名関数を使うとループのなかで一度しか行わない様な関数の宣言が不要になるため無駄なコードを削減できる。実際に処理を呼び出す場所に内容を記述できるため可読性も高くなる。
演算子
Dartで覚えておくべき演算子を紹介する。
算術演算子
表1 算術演算子
演算子 | 意味 |
---|---|
+ | 足し算 |
- | 引き算 |
* | 掛け算 |
/ | 割り算(double) |
~/ | 割り算(int) |
% | 余り(modulo) |
表2 インクリメント/デクリメント演算子
演算子 | 意味 |
---|---|
++var | 1を加算してから使用 |
var++ | 使用してから1を加算 |
--var | 1を減算して使用 |
var-- | 使用してから1を減算 |
代入演算子
表3 複合代入演算子
演算子 | 意味 |
---|---|
a += b | a = a + b |
a op= b | a = a op b (opは算術演算子) |
割り当て演算子と条件式
表4 割り当て演算子
演算子 | 意味 |
---|---|
a ?? = b | aがnullであればbを代入 |
a ?? b | aがnullであればbを実行 |
表5 条件式演算子
演算子 | 意味 |
---|---|
x ? a : b | xがtrueのときaを実行 xがfalseのときbを実行 |
比較演算子
表6 比較演算子
演算子 | 意味 |
---|---|
a == b | aとbが同値 |
a != b | aとbが異なる値 |
a > b | aがbより大きい |
a < b | aがbより小さい |
a >= b | aがb以上 |
a <= b | aがb以下 |
論理演算子
表7 論理演算子
演算子 | 意味 |
---|---|
! | NOT 否定 |
|| | OR 論理和 |
&& | AND 論理積 |
型テスト演算子
表8 型テスト演算子
演算子 | 意味 |
---|---|
is | 指定した型をもつ |
is! | 指定した型をもたない |
上記にくわえて、obj is T
という書き方もあり、条件を満たすとtrueが返ってくる。以下に例を示す。
if (person is Employee){
person.division ='div no.1';
}
条件分岐
Dartでの条件分岐には以下を利用できる。
if-else, else if
最も基本的な条件分岐であるif文の評価はboolean型でのみ行われる。if文をつなげるelse ifの間の空白を削除不可。
if (isRaining()){
you.bringRainCoat();
} else if (isShowing()){
you.wearJacket();
} else {
car.putTopDown();
}
for, for-in
繰り返しにはfor文を使う。
var message = StringBuffer('Dart is fun');
for(var i=0; i<5; i++){
message.write('!');
}
list、mapなどのiterableな変数の内容に対するfor文はfor-in文を使うことで無駄な変数を使わずに記述できる。
var colllection = [1, 2, 3];
for (var x in collection){
print(x);
}
forEachメソッドを使うことで簡略することができる。
candidates.forEach((candidate) => candidate.interview());
while, do-while
条件が成立している間るーぷを実行する場合にはwhileを使う。
while(!isDone()){
doSomething();
}
条件の評価の後に行う場合にはdo-whileを使う。
do{
print();
}while(!atEndObPage());
break, continue
for文、while文の中でループの途中でループをやめたい場合にはbreakを使う。
while(true){
if(shutDownRequested()) break;
processIncomingRequests();
}
ループのイテレーションの処理を終了し、次のイテレーションに移りたい場合にはcontinueを使う。
for (int i = 0; i < canddiates.length; i++){
var candidate = candidates[i];
if(candidate.yearsExperience < 5){
continue;
}
candidate.interview();
};
iterableなオブジェクトに対しては簡略化して書くことが可能。以下に例を示す。
candidates
.where((c) => c.yearsExperience >= 5)
.forEach((c) => c.interview());
switch-case
変数の内容によって複数の処理の分岐を行う場合にはswitch文を使う。caseの内容と==
演算子による評価が行われtrueになった場合に実行される。Dartではcase文の処理の終わりはbreakを使わないと次のcase文の後の処理まで実行される。この仕組みを使ったフォールスルーは便利。どのcase文にも当てはまらない場合にはdefault文以下の処理が実行される。
import 'dart:_http';
import 'dart:_js_helper';
var command = 'OPEN';
swtch(command){
case 'OPEN':
execOpen();
break;
case 'CLOSING':
case 'CLOSED':
execClosed();
break;
defalt:
execUnknown();
}
例外処理
Dartでは処理の最終に何らかの予期しない状態となった場合に例外をthrowし、try-catch節で例外処理できる。
例外の通知(throw)
ExcptionクラスかErrorクラスから派生したクラスをthrowできる。任意のオブジェクトをthrowすることもできる。
// 例外クラスを指定したthrow
throw FormatException('Expected at least 1 section');
// Stringオブジェクトをthrow
thtow 'Out of llamas';
例外処理の実行(try-on/catch-finally)
try節の中でthrowされた例外はon/catch節によって受け取り例外処理を行うことができる。on節では特定の例外クラスについての処理を行い、catch節ではその他の例外の処理を行う。最後に例外が発生してもしなくても必ず実行したい処理をfinally節に記述する。
try{
// なんらかの処理
} on FormatException(e){
print('Exception detail: $e');
} catch(e){
print('Exception detail: $e');
} finally {
// クリーンアップ処理など
cleanup();
}
クラス
Dartはオブジェクト指向言語でありすべてのオブジェクトは何らかのクラスを実体化したものである。以下ではDartにおけるクラスについて説明する。
インスタンス化とアクセス
クラスをオブジェクトとして使うためにはインスタンス化する。Dartではインスタン化に使うnewキーワード
はオプションになっている。インスタン化したオブジェクト内のメンバー(インスタンス内のメソッドやプロパティ)にアクセスするためには.
(ドット)を使う。以下に単純なクラスの例を示す。
class Point{
double x;
double y;
double z = 0;
}
var p1 = Point();
p1.x = 4;
p1.y = 1;
この例の変数p1にはPointクラスのインスタンス化されたオブジェクトが入る。
インスタンスの初期化
インスタンス化する際の初期化処理を行うコンストラクタはクラス名と同名のメソッドで宣言する。また、名前付きコンストラクタという通常のコンストラクタとは別のコンストラクタを宣言することもできる。thisというオブジェクトはインスタンス地震を指している。以下にコンストラクタの一例を示す。
class Point{
double x;
double y;
// 通常のコンストラクタ
Point(double x, double this.y){
this.x = x;
this.y = y;
}
// 名前付きコンストラクタ
Point.origin(){
x = 0;
y = 0;
}
}
上記とは異なる変数の初期化方法とは異なるイニシャライザーリストと呼ばれる仕組みがある。イニシャライザーリストを使うと親クラスや自身の初期化の前にインスタンス変数を初期化できる様になる。こレが分かりにくい場合には、インスタンス変数をfinalで宣言したときの代入と、親クラスのコンストラクタを呼ぶために使うことだけを覚えておくとよい。以下にイニシャライザーリストの例を示す。
class Point{
final num x;
final num y;
Point(x, y): // イニシャライザーリスト
x = x,
y = y { // イニシャライザーリストの終わり
print('print $x, $y');
}
}
またfinalで宣言した変数(=定数)に値を代入するコンストラクタは以下の例の様な書き方ができる。
class Point{
final num x;
final num y;
Point(this.x, this.y){ // コンストラクタ
print('print $x, $y');
}
}
コンストラクタについては親クラスからの継承がないことに注意。また初期化が必要な場合には親クラスのコンストラクタをイニシャライザーリストの中で明示的に呼び出す必要がある。
メソッド
メソッドの書き方は関数と同じである。一例を示す。
class Point{
final num x;
final num y;
Point(this.x, this.y);
double distanceTo(Point other){ // メソッド
var dx = x ? other.x;
var dy = y ? other.y;
return sqrt(dx * dx + dy * dy);
}
}
プロパティとgetter/setter
インスタンス内の変数へのアクセスは.
(ドット)を使って行われる。インスタンス外からアクセスさせたくない変数には変数の名前の戦闘に_
(アンダースコア)にして宣言する。これでインスタンス内に閉じたプライベートな変数(ローカル変数)として扱われる。以下にローカル変数の例を示す。
class Point{
final num x;
final num y;
double _area; // ローカル変数
Point(this.x, this.y){
_area = x * y;
}
}
プロパティにアクセスする際には暗黙的にgetter/setterと呼ばれるメソッドが呼ばれている。これを上書き(オーバーライド)することで変数へのアクセスをコントロールしたりプロパティにみせた関数を作ることができる。以下にgetter/setterの例を示す。
class Rectangle{
double left, top, width, height;
Rectangle(this.left, this.top, this.width, this.height);
double get right => left + width;
set right(double val) => left = val ? width;
double get bottom=> top + height;
set bottom(double val) => top = val ? height;
}
void main(){
var rect = Rectangle(3, 4, 20, 15);
print('right=$rect.right');
rect.right = 12;
print('ruight=$rect.left');
}
クラスの継承とミックスイン
クラスの継承や多重継承について説明する。以下にクラス継承の例を示す。
class Person {
final _name;
Person(this._name);
String greet(String who) => 'Hi $who. I am $_name.';
}
// Personクラスの継承
class Employee extends Person{
Employee(String name): super(name);
}
Dartにはinterfaceキーワードはないかわりにクラスを定義すると暗黙的にインターフェイスが定義される。implementsキーワードを使うことでインターフェイスを継承できる。以下に暗黙的インターフェイスの例をを示す。
class Person {
final _name;
Person(this._name);
String greet(String who) => 'Hi $who. I am $_name.';
}
// Personクラスのインターフェイス継承
class Impostor implements Person{
String greet(String who) => 'Hi $who. Do you know who I am?';
}
もしJavaのinterfaceと同じようにベースのクラスに実装をしないばあいにはabstractキーワードを使って中傷クラスを利用すれば同様のことができる。以下に抽象クラスを利用した例を示す。
// 抽象クラス
abstract Person {
final _name;
Person(this._name);
String greet(String who);
}
// Personクラスのインターフェイス継承
class Impostor implements Person{
String greet(String who) => 'Hi $who. Do you know who I am?';
}
また、他のクラスの機能のみを追加するミックスイン(Mixin)を利用することができる。継承は親子関係を持つが、ミックスインは親子関係を持たずにその機能のみをクラスに追加する様なイメージ。Dartではミックスインとして使えるクラスには以下の条件がある。
- コンストラクタを定義していない
- スーパークラスがObjectである
- superキーワードを使っていない
これらの条件を満たすクラスまたはmixinキーワードを使って定義されたクラスをミックスインとして利用できる。ミックスインを利用してクラスを宣言するにはwithキーワードを使う。以下にミックスインの例を示す。
class Person {
final _name;
Person(this._name);
String greet(String who) => 'Hi $who. I am $_name.';
}
mixin Pianist{
bool canPlayPiano = false;
void canPlay(){
if(canPlayPiano){
print('Playing Piano');
}
}
}
class Musician extends Person with Pianist{
Musician(String name){
_name = name;
canPlayPiano = true;
}
}
まず普通のクラスPersonとミックスインクラスPianistを定義する。この2つのクラスには親子関係はない。次にMusicianクラスを定義する際にPersonクラスを継承しPianistをミックスインとして指定する。結果としてMusicianクラスはPersonの子クラスでありPianistの能力を持つクラスになる。ミックスインの使用時はwithキーワードで複数のクラスを指定できる。ただしミックスインを適用したクラスをミックスインとして使うことはできない。
ライブラリ
Dartでは名前空間を指定してライブラリを使う。ライブラリを使うためにはimportという作業が必要になる。import時にはDartに標準でビルトインされているライブラリの接頭辞としてdart:
を用いる。importの一例を以下に示す。
import 'dart:ライブラリ名'
異なるライブラリで名前がかぶる場合には、asキーワード
を用いる。
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
Element element1 = Element();
lib2.Element element2 = lib2.Element();
アプリ内で他のファイルを参照する場合には相対パス指定で直接ファイルを指定する。以下はパスを指定したimportの例である。
import 'applib1.dart';
import 'lib/test.dart';
非同期サポート
Flutterアプリはモバイルデバイスで動作する性質上、画面入力や通信の非同期入出力が大量に発生する。これらの処理をすべて同期させてプログラミングするとデータを受信してから画面を構成sいて表示すると行った動きになり大変使いづらいアプリになる。また待ち時間で無駄にリソースを浪費する。そのため非同期処を実装することでリソースを最適化する。Dartでは非同期処理のためにFutureオブジェクト、async/await、Streamオブジェクトがある。
Futureオブジェクト
Dartでは非同期処理の結果をFutureやFutureで表現する。DartではFutureを返す関数は実行直後に空のFutureオブジェクト(Future)が返された状態で実行される。呼び出し元の関数は、そのまま次の命令に処理を進むか、awaitキーワードが指定されていればそこで処理を止めてFutureオブジェクトに結果が返されるの待つ。非同期に実行された関数が正常終了かエラーで終了すると結果を先程の空のFutureオブジェクトに格納する。Futureオブジェクトに結果が返されるとFutureAPIと呼ばれるコールバック関数を通じて後続の処理を実行する。以下はFutureオブジェクトを使った非同期処理の例である。
void main() {
Future.delayed(
const Duration(seconds: 3),
() => 100
).then((value){
print('The value is $value.');
});
print('Waiting for a value...');
}
実行結果)
Waiting for a value...
The value is 100.
上記のコードはFutureオブジェクトのdelayedメソッドを使い、呼び出された3秒後に無名関数を実行し100を返すだけの処理を行っている。実行するとFutureオブジェクトの次のprint文が実行された後にthenで登録されたコールバック関数が実行される。thenメソッドではFuture APIで非同期処理が正常終了した場合にコールバックする関数を登録する。このFuture APIを使った実装では非同期処理を順番に実行するコードが書きづらいという問題がある。Futureオブジェクトを順番に実行する一例を以下に示す。
void main() {
Future.delayed(
const Duration(seconds: 3),
() => 100,
).then((value){
print('The value is $value.');
Future.delayed(
const Duration(seconds: 3),
() => 200,
).then((value){
print('The value is $value.');
});
});
print('Waiting for a value...');
}
実行結果)
Waiting for a value...
The value is 100.
The value is 200.
2つめのFutureオブジェクトがthenによって呼び出される構造になっている。コードの字下げや括弧などを揃えにくくこのままでは扱いにくい。次に非同期処理で順番に呼び出すケースでもっと書きやすい方法を紹介する。
async/await
awaitキーワードをつかうと非同期処理を同期処理のように記述できる。Futureオブジェクトを返す関数をawaitキーワードをつけて呼ぶと関数から結果が返されるまで処理を待つ。このawaitキーワードを使う関数にはasyncキーワードをつけるルールになっている。asyncキーワードをつけた関数の戻り値の型は必ずFutureになる。async/awaitを使った関数の例を示す。
Future checkVersion() async{
var varsion = await lookUpVersion();
await startProgram(version);
}
以下に非同期処理を順番に実行する例を示す。
Future<void> main() async{
var value;
value = await Future.delayed(
const Duration(seconds: 3),
() => 100);
print('The value is $value.');
value = await Future.delayed(
const Duration(seconds: 3),
() => 200);
print('The value is $value.');
print('Waiting for a value...');
}
実行結果)
The value is 100.
The value is 200.
Waiting for a value...
またawaitで実行中の非同期処理におけるエラーを補足したい場合にはtry-catchを使う。
Future chechVersion() async{
try{
var version = await lookUpVersion();
await startProgram(version);
}catch(e){
// 非同期処理のエラー処理
print('Error: $e');
}
}
Stream処理
Streamは継続的に処理が発生するイベントに対する非同期処理を行う。Streamを使うと、Streamにデータを発行する側と
Streamからデータを呼び出したいと登録した側で非同期にメッセージの受け渡しができる。
import 'dart:async';
var controller = StreamController<String>();
void registerListner(){
controller.stream.listen((val) => print("recieved data: $val"));
}
main(){
registerListner();
controller.sink.add("Hello!");
controller.sink.add("Welcome!");
}
実行結果)
recieved data: Hello!
recieved data: Welcome!
controller.streamがStreamオブジェクトになっており、これにlistenメソッドでコールバック関数を登録。ここでは受け取ったデータを表示するだけの関数を登録している。データを送る側はcontroller.sinkがStreamSinkインターフェースをもっており、addメソッドでデータを登録する。これだけでlisternerに登録した関数にデータを送信できる。複数のlistenerにデータを送信したい場合にはStreamContollerのbroadcastコンストラクタを使う。var controller = StreamController()の部分をvar controller = StreamController().broadcast()と変更するだけでよい。
3.まとめ
Flutterのプログラミングで使うDartについて学んだ。