はじめに
「わかんない、わかんないよ!Flutterってなんなの!Dartなんて初めて聞いたし、オブジェクト指向って言われても難しくてわかんないよ!」
どうも、堕罪オサムです。
最近FlutterとDartを学習していて、その一環でFlutterのチュートリアル(?)アプリをいじって遊んだりしてたのですが、これ、ありえないほどむずくねえですか?
なんかこう、知らない単語イッパイだァみたいな……。
そういうわけで理解を深めるべく、そのお遊びでできたコードを一から解説することにしました。
本当になにもわからない私が読んでもわかるように(今後Flutterを使用してアプリを作るときに見返せるように)細かく調べてみたので、よかったら目を通していただけると嬉しいです!
書いたコードはコチラをクリック!
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.amber),
useMaterial3: true,
),
home: const MyHomePage(title: 'testApp'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class ResetButton extends ElevatedButton {
const ResetButton({Key? key, required VoidCallback? callback})
: super(
key: key,
onPressed: callback,
child: const Icon(
Icons.delete,
));
}
class ReduceButton extends ElevatedButton {
const ReduceButton({Key? key, required VoidCallback? callback})
: super(
key: key,
onPressed: callback,
child: const Text(
' \u2212 ', // Unicode 文字列 U+2212 はマイナス記号を表す
style: TextStyle(fontSize: 25),
));
}
class MemoDataGetButton extends ElevatedButton {
const MemoDataGetButton({Key? key, required VoidCallback? callback})
: super(
key: key,
onPressed: callback,
child: const Icon(Icons.done),
);
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
String _memoText = ''; // テキストを保持するための変数
final List<String> _memoList = []; // メモのリスト
final TextEditingController _textEditingController = TextEditingController();
void _incrementCounter() {
setState(() {
_counter++;
});
}
void _resetCounter() {
setState(() {
_counter = 0;
});
}
void _reduceCounter() {
setState(() {
_counter--;
});
}
void _updateText(String newText) {
setState(() {
_memoText = newText;
});
}
void _handleButtonPress() {
// ボタンが押されたときに新しいメモデータを取得し、_updateTextを呼び出す
_memoList.add(_memoText); // メモをリストに追加
_updateText(''); // メモデータを初期化
// TextFormFieldのテキストを初期化
_textEditingController.clear();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'count',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
ResetButton(callback: _resetCounter),
ReduceButton(callback: _reduceCounter),
TextFormField(
controller: _textEditingController, // TextEditingControllerを指定
decoration: const InputDecoration(
labelText: 'memo',
floatingLabelBehavior: FloatingLabelBehavior.never,
),
keyboardType: TextInputType.multiline,
maxLines: null,
onChanged: (newMemo) {
_updateText(newMemo);
// メモデータを入力するたびに更新
},
),
MemoDataGetButton(callback: _handleButtonPress),
//メモデータを取得するボタン
SizedBox(
height: 200,
width: double.infinity, // メモデータ左寄せ
child: ListView(
children: _memoList
.map((memo) => ListTile(
leading: const Icon(Icons.fiber_manual_record),
title: Text('Item $memo'),
))
.toList()),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
解説
Flutterアプリケーションの初期化とルートウィジェットの設定
この部分のコードは、Flutterアプリケーションのエントリーポイントである main 関数から始まり、アプリケーションの初期設定を行う MyApp クラスについて説明しています。
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.amber),
useMaterial3: true,
),
home: const MyHomePage(title: 'testApp'),
);
}
}
-
void main() ~ runApp(const MyApp());
⇒ 一番最初に実行されるコード(多分必須)。 -
class MyApp extends StatelessWidget ~ const MyApp({super.key})
⇒MyApp
という子クラスがStatelessWidget
クラスという親クラスを継承(extends)している。 ※StatelessWidget
を継承しているWidgetは定数コンストラクタを含むことができる。 -
@override ~ Widget build(BuildContext context)…
⇒build
メソッドでMyApp
がウィジェットを構築するため@override
で親クラス(StatelessWidget
)は書き換えずに子クラスのMyApp
を上書きしている。※MaterialApp
はFlutterフレームワークで作成されたアプリケーションのルートとなるウィジェット。アプリのテーマやナビゲーション、ローカライズなどのアプリ全体の管理を行う役割のウィジェット。 -
return MaterialApp()…
⇒子クラスのMyApp
のbuild
メソッド内にMaterialApp
を定義している。home
は最初に起動させたいページ(MyHomePage
以降)を設定している。
MyHomePage ウィジェットの設定
この部分のコードは、MyHomePage ウィジェットが状態を持ち、その状態を管理するための State オブジェクトを生成するためのものです。
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
-
class MyHomePage extends StatefulWidget ~ const MyHomePage…
⇒StatefulWidget
という親クラスがMyHomePage
を継承(extends)している。また、MyHomePage
クラスのコンストラクタは、super.key
とrequired this.title
の2つのパラメーターを受け取っている。required this.title
は、コンストラクタ呼び出し時にtitle
パラメーターが必要であることを示していいる。 -
final String title;
⇒final
に文字列(String
)のtitle
を代入する。↑で指定されているrequired this.title
がコンストラクタ呼び出し時にtitle
パラメーターが必要なのでfinal
で関連付ける。 -
State<MyHomePage> createState() => _MyHomePageState();
⇒createState
メソッドの実装。State<MyHomePage>
は、State
クラスのジェネリクス型引数としてMyHomePage
クラスを指定している。この指定により*S*tate
オブジェクトがどのウィジェットの状態を管理するかを示し、ここではMyHomePage
ウィジェットの状態を管理するためのState
オブジェクトを作成している。またcreateState
で作成したstate
が変化したときに再描画される。
ボタンクラスの設定
このセクションでは、3つのボタンクラス(ResetButton、ReduceButton、MemoDataGetButton)の設定について説明します。
class ResetButton extends ElevatedButton {
const ResetButton({Key? key, required VoidCallback? callback})
: super(
key: key,
onPressed: callback,
child: const Icon(
Icons.exposure_zero,
));
}
このコードではボタンクラスが3つ(ResetButton・ReduceButton・MemoDataGetButton)あり、どれも同様に作られている。よってこの3つの命名以外はテンプレートのようになっています。
-
class ResetButton extends ElevatedButton
⇒ElevatedButton
を継承したボタンのクラスを作る。※ ElevatedButton:立体的な効果を持ったボタンを作るためのウィジェット -
const ResetButton({Key? key, required VoidCallback? callback})
⇒ResetButton
のコンストラクタ。VoidCallback
はonPressed
などのボタンのイベント処理のときに使うもの。また引数を受け取らず何も返さない関数の型。?
はnull
を許容することを示している。{}
の中に定義されているパラメーター(引数)は、コンストラクタ呼び出し時に渡すことができる。required
があるためcallback
の引数を指定しなければならない。 - super(…) ⇒親クラスの
ElevatedButton
のコンストラクタを呼び出している。key
・onPressed
・child
はコンストラクタ引数。onPressed
が発火するタイミング(ResetButton押下時)のアクションをcallback
として受け取っている(ちなみにそれぞれのボタンのcallback
時の引数はのちに定義していく)。child
ではボタンアイコンの指定をしている。
ウィジェット内部の状態
このセクションでは、_MyHomePageStateクラス内でウィジェット内部の状態を管理する方法について説明します。
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
String _memoText = ''; // テキストを保持するための変数
final List<String> _memoList = []; // メモのリスト
final TextEditingController _textEditingController = TextEditingController();
void _incrementCounter() {
setState(() {
_counter++;
});
}
void _incrementCounter() のほかに_resetCounter() ・_reduceCounter() ・_updateText(String newText) が同じようなコードになります。
-
class _MyHomePageState extends State<MyHomePage>
⇒State<MyHomePage>
を継承した_MyHomePageState
クラスを作る(State
クラスはMyHomePage
ウィジェットの内部状態を管理する)。 -
int _counter = 0;
⇒_counter
ウィジェット内で使用される整数値変数。初期値は0
。 -
String _memoText = '';
⇒テキストフィールドに入力されたメモのテキストを保持するための文字列変数。_updateText
メソッドで新しいテキストが更新されると、この変数も更新される。 -
final List<String> _memoList = [];
⇒メモのリストを保持するための文字列リストを作る。_handleButtonPress
メソッド内で新しいメモが_memoList
に追加される。 -
final TextEditingController _textEditingController = TextEditingController();
⇒_textEditingController
はテキストフィールドの入力を制御するためのTextEditingController
オブジェクトです。テキストフィールドにテキストを入力する際に使用されます。 -
void _incrementCounter() {}
⇒カウンターをインクリメントするための_incrementCounter()
メソッドを作る。setState
内で変数の値を変更することでウィジェットの再描画をハンドルすることが可能。_counter++;
は_counter
ウィジェット内で使用される整数値変数を増加させるという処理。
_resetCounter() ・_reduceCounter() ・_updateText(String newText) についての説明
void _resetCounter() {
setState(() {
_counter = 0;
});
}
void _reduceCounter() {
setState(() {
_counter--;
});
}
void _updateText(String newText) {
setState(() {
_memoText = newText;
});
}
-
void _resetCounter(){}
⇒setState
内で変数の値を変更することでウィジェットの再描画をハンドルすることが可能。_counter = 0;
は_counter
変数で使用される整数値変数をリセットするという処理。 -
void _reduceCounter() {}
⇒setState
内で変数の値を変更することでウィジェットの再描画をハンドルすることが可能。_counter--;
は_counter
変数で使用される整数値変数を減少させるという処理。 -
_updateText(String newText) {}
⇒void _updateText(String newText)
は、void _updateText
が新しいテキスト情報を受け取るための引数newText
を持っていることを示す。setState
内で変数の値を変更することでウィジェットの再描画をハンドルすることが可能。_memoText = newText;
では新しいテキストnewText
が_memoText
に代入され、メモのテキストが新しい値に更新される。
ボタンが押されたときの処理
このセクションでは、ボタンが押されたときに実行される _handleButtonPress メソッドについて説明します。
void _handleButtonPress() {
// ボタンが押されたときに新しいメモデータを取得し、_updateTextを呼び出す
_memoList.add(_memoText); // メモをリストに追加
_updateText(''); // メモデータを初期化
// TextFormFieldのテキストを初期化
_textEditingController.clear();
}
-
void _handleButtonPress(){}
⇒MemoDataGetButton
を押下した際のコールバック処理が書かれている。(のちにMemoDataGetButton(callback: _handleButtonPress)
が出てくる) -
_memoList.add(_memoText);
⇒新しいメモデータ_memoText
を_memoList
と呼ばれるリストに追加する。 -
_updateText('');
⇒_updateText
メソッドを呼び出し、メモのテキストデータ_memoText
を初期化(空文字列''
) になるようにする。 -
_textEditingController.clear();
⇒_textEditingController
を使用して、テキストフィールドのテキストをクリアまたは初期化する。テキストを入力後、ボタンを押下するとテキストボックスが初期化される。
ウィジェットの描画と Scaffold
このセクションでは、build メソッドに焦点を当てて説明します。
@override
Widget build(BuildContext context) {
return Scaffold(...)
-
@override Widget build(BuildContext context)
⇒buildメソッドはstatefulWidgetから継承されたMyHomePageウィジェットを描画する。BuildContext context
はbuildメソッドの引数でこれを使用してウィジェットがどのように配置され、スタイルが適用されるかを決定する。 -
return Scaffold()
⇒Scaffold
はほかのウィジェットを描画するためのウィジェット。それを返している。Scaffoldのプロパティは多く実装されている。(body・appBar・floatingActionButton…など)
参考サイト・記事・単語まとめ
クラス – プログラミング用語解説|Unity高校&ゲームスクールのG学院
インスタンス – プログラミング用語解説|Unity高校&ゲームスクールのG学院
継承 – プログラミング用語解説|Unity高校&ゲームスクールのG学院
Dart のコンストラクタを理解する - エキサイト TechBlog.
【Flutter】build() でやってはいけない 3 つのこと - Qiita
【Flutter初心者向け】MaterialAppとScaffoldとは??サンプルアプリをもとに分かりやすく解説 - 文系プログラマー「いお」が語る
【5分でわかる】Widgetとは? | Flutter入門|KIMURA LOG
FlutterのBuildContextとは何か - Qiita
【Flutter】UI構築のスタート地点Scaffoldを徹底解説【サンプルコードあり】
※コンストラクタ:Classのインスタンスを作成したとき(newしたとき)に呼び出される初期化処理。
※パラメーター:ソフトウエアを実行したりプログラム内で関数を呼び出したりする時に、一連の処理を指定するために与える情報(「処理結果に影響を与える外部から投入される変動要素」のこと プログラムの世界では「引数(仮引数)」と呼ばれる場合もあります。 引用:https://wa3.i-3-i.info/word1443.html)
※superクラス:親クラスの別名
※ジェネリクス型引数:主にクラスや関数を汎用的に設計するための仕組み ⇒ジェネリクスを使うことで、クラスや関数が様々な型に対して動作する柔軟性を持つことができます。例えば、State
クラスはState<MyHomePage>
と指定することで、MyHomePage
クラスだけでなく、他のウィジェットの状態も管理することができる。