前回に引き続き、Flutterの勉強として電卓アプリ(計算機)を作ったので、その作り方を順を追って、全3回で説明していきます。
今回は「ボタンUIと描写更新」を実装していきます。
では始めていきましょう。
表示(_TextFiledState)
今から表示されている文字の更新を行うメソッドを作っていきます。
このメソッドはキーボタン(Button)から呼ぶ予定です。
メソッドの作成
void _UpdateText(String letter){
if(letter == '=' || letter == 'C')
_expression = '';
else
_expression += letter;
}
しかしこれではUI描写の更新は行われません。
UI描写の更新にはsetState()メソッドが必要です。
setState()
Flutterの基礎(Qiita)
任意で呼べるメソッド
Widgetツリーを再構成して、変更を反映させる
簡単な変数の代入だけでなく非同期処理でも使える
なので、先ほどのコードを少し変更します。
void _UpdateText(String letter){
setState(() {
if(letter == '=' || letter == 'C')
_expression = '';
else
_expression += letter;
});
}
ここまでで、main.dart全体は以下のようになりました。
// 省略
class TextField extends StatefulWidget {
_TextFiledState createState() => _TextFiledState();
}
class _TextFiledState extends State<TextField> {
String _expression = '1+1';
/////追加/////
void _UpdateText(String letter){
setState(() {
if(letter == '=' || letter == 'C')
_expression = '';
else
_expression += letter;
});
}
/////ここまで/////
@override
Widget build(BuildContext context) {
//省略
これでUIの描写更新を行うメソッド(_UpdateText())の作成はできました。
次にそのメソッドを呼び出すところを作っていきます。
Streamを作る
_UpdateText()はButtonから呼び出したいので、_UpdateTextをstaticに。。とやってみましたが、できませんでした。
どうやら setState() はstaticメンバにできないようです。
そこで、今回はStreamを使ってみることにしました。
(多分もっとよい方法があるはず。。)
Streamについては
動かして理解する!dartのStreamとrxdart!
を参考にしました。
Streamの存在をついさっき知ったばかりなので、私の理解は...
StreamってRxDartの元らしい、、Rx...?、UniRx(UnityC#)...?、ふーん、なるほど()
って感じです。はい。
ではコードを書いていきます。
まずimport文を書きます。
import 'dart:async';
続いて、Streamを生成します。
クラスからのアクセスが出来るように [static] かつ [ _ (アンダースコア) なし]にします。
static final controller = StreamController<string>();
Streamの内容の登録はinitState()で行わせます。
initState()
Flutterの基礎(Qiita)
最初に一度呼ばれる
Widgetツリーの初期化を実行
static final controller = StreamController<String>();
@override
void initState() {
controller.stream.listen((event) => _UpdateText(event));
}
TextField付近のコードの確認
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
// 〜省略〜
// 表示
class TextField extends StatefulWidget {
_TextFiledState createState() => _TextFiledState();
}
class _TextFiledState extends State<TextField> {
String _expression = '1+1';
void _UpdateText(String letter){
setState(() {
if(letter == '='|| letter == 'C')
_expression = '';
else
_expression += letter;
});
}
@override
Widget build(BuildContext context) {
return Expanded(
flex: 1,
child: Container(
child: Align(
alignment: Alignment.centerRight,
child: Text(
_expression,
style: TextStyle(
fontSize: 64.0,
),
),
),
)
);
}
static final controller = StreamController<String>();
@override
void initState() {
controller.stream.listen((event) => _UpdateText(event));
}
}
//==============================================================================
// キーボード
class Keyboard extends StatelessWidget {
// 〜省略〜
キーボード(Button)
Buttonクラスにボタンを押したときの反応を書いていきます。
そこに書いてある通り、OnPressed()を追加していきます。
パラメータの値にボタンを押したときの反応を書きます。
// 〜省略〜
child: FlatButton(
child: Center(
child: Text(
_key,
style: TextStyle(fontSize: 46.0),
),
),
onPressed: (){
_TextFiledState.controller.sink.add(_key);
},
// 〜省略〜
これで、ボタンを押したときの反応を設定できました。
(これでボタン入力が働くという訳ではありません。次の「動かす」で動くようになります。)
OnPressedを設定したことによって、グレーアウトしていたボタンが元の色になりました。
ちょっと色が濃くて煩いので、薄くしておきます。
// 〜省略〜
child: Text(
_key,
style: TextStyle(
fontSize: 46.0,
color: Colors.black54,
),
// 〜省略〜
Button付近のコードの確認
// 〜省略〜
)
)
);
}
}
// キーボタン
class Button extends StatelessWidget {
final _key;
Button(this._key);
@override
Widget build(BuildContext context) {
return Container(
child: FlatButton(
child: Center(
child: Text(
_key,
style: TextStyle(
fontSize: 46.0,
color: Colors.black54,
)
),
),
onPressed: (){
_TextFiledState.controller.sink.add(_key);
},
)
);
}
}
動かす
OnPressedで発行したStreamの購読はinitState()で行われます。
そして、initState()はアプリ起動時に呼ばれます。
つまり、HoReloadではinitState()呼ばれません。
再ビルドします。
いい感じに動いています。
まとめ
UIボタンを押すと画面が更新されるようになりました。
次回は電卓機能の中心、計算機能の実装を行います。
現時点のmain.dart
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
TextField(),
Keyboard(),
],
)
)
);
}
}
//==============================================================================
// 表示
class TextField extends StatefulWidget {
_TextFiledState createState() => _TextFiledState();
}
class _TextFiledState extends State<TextField> {
String _expression = '';
void _UpdateText(String letter){
setState(() {
if(letter == '=' || letter == 'C')
_expression = '';
else
_expression += letter;
});
}
@override
Widget build(BuildContext context) {
return Expanded(
flex: 1,
child: Container(
child: Align(
alignment: Alignment.centerRight,
child: Text(
_expression,
style: TextStyle(
fontSize: 64.0,
),
),
),
)
);
}
static final controller = StreamController<String>();
@override
void initState() {
controller.stream.listen((event) => _UpdateText(event));
}
}
//==============================================================================
// キーボード
class Keyboard extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Expanded(
flex: 2,
child: Center(
child: Container(
color: const Color(0xff87cefa),
child: GridView.count(
crossAxisCount: 4,
mainAxisSpacing: 3.0,
crossAxisSpacing: 3.0,
children: [
'7', '8', '9', '÷',
'4', '5', '6', '×',
'1', '2', '3', '-',
'C', '0', '=', '+',
].map((key) {
return GridTile(
child: Button(key),
);
}).toList(),
),
)
)
);
}
}
// キーボタン
class Button extends StatelessWidget {
final _key;
Button(this._key);
@override
Widget build(BuildContext context) {
return Container(
child: FlatButton(
child: Center(
child: Text(
_key,
style: TextStyle(
fontSize: 46.0,
color: Colors.black54,
),
),
),
onPressed: (){
_TextFiledState.controller.sink.add(_key);
},
)
);
}
}
0 件のコメント:
コメントを投稿