【Flutter】わかんないよ！オブジェクト指向なんてわかんないよ！

# はじめに 
「わかんない、わかんないよ！**Flutter**ってなんなの！**Dart**なんて初めて聞いたし、**オブジェクト指向**って言われても難しくてわかんないよ！」

どうも、[堕罪ｵｻﾑ](https://twitter.com/Alt_Heidelberg)です。
最近FlutterとDartを学習していて、その一環でFlutterのチュートリアル(？)アプリをいじって遊んだりしてたのですが、これ、**ありえないほどむずくねえですか？**
なんかこう、知らない単語イッパイだァみたいな……。

そういうわけで理解を深めるべく、そのお遊びでできたコードを一から解説することにしました。

本当になにもわからない私が読んでもわかるように(今後Flutterを使用してアプリを作るときに見返せるように)細かく調べてみたので、よかったら目を通していただけると嬉しいです！

<details><summary>書いたコードはコチラをクリック！</summary>

```dart
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),
      ),
    );
  }
}

```
</details>

## 解説

### Flutterアプリケーションの初期化とルートウィジェットの設定
この部分のコードは、Flutterアプリケーションのエントリーポイントである main 関数から始まり、アプリケーションの初期設定を行う MyApp クラスについて説明しています。

```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'),
    );
  }
}
```

1. `void main() ~ runApp(const MyApp());` 　⇒ 一番最初に実行されるコード(多分必須)。
2. `class MyApp extends StatelessWidget ~ const MyApp({super.key})`　⇒`MyApp`という**子クラス**が`StatelessWidget`**クラス**という**親クラス**を**継承(extends)**している。　※`StatelessWidget`を継承しているWidgetは**定数コンストラクタ**を含むことができる。
3. `@override ~ Widget build(BuildContext context)…`　⇒`build`メソッドで`MyApp`がウィジェットを構築するため`@override`で**親クラス**（`StatelessWidget`）は書き換えずに**子クラス**の`MyApp`を**上書き**している。※`MaterialApp`はFlutterフレームワークで作成されたアプリケーションのルートとなるウィジェット。アプリのテーマやナビゲーション、ローカライズなどのアプリ全体の管理を行う役割のウィジェット。
4. `return MaterialApp()…`　⇒子クラスの`MyApp`の`build`メソッド内に`MaterialApp`を定義している。`home`は最初に起動させたいページ`(MyHomePage`以降)を設定している。


### MyHomePage ウィジェットの設定

この部分のコードは、MyHomePage ウィジェットが状態を持ち、その状態を管理するための State オブジェクトを生成するためのものです。

```dart
class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}
```

1. `class MyHomePage extends StatefulWidget ~ const MyHomePage…`　⇒ `StatefulWidget`という親クラスが`MyHomePage`を継承(extends)している。また、`MyHomePage`クラスのコンストラクタは、`super.key`と`required this.title`の2つのパラメーターを受け取っている。`required this.title`は、コンストラクタ呼び出し時に`title`パラメーターが必要であることを示していいる。　
2. `final String title;`　⇒`final`に**文字列**(`String`)の`title`を代入する。↑で指定されている`required this.title`が**コンストラクタ呼び出し時**に`title`パラメーターが必要なので`final`で関連付ける。
3. `State<MyHomePage> createState() => _MyHomePageState();`　⇒`createState`メソッドの実装。`State<MyHomePage>`は、`State`クラスの**ジェネリクス型引数**として`MyHomePage`クラスを指定している。この指定により`*S*tate`オブジェクトがどのウィジェットの状態を管理するかを示し、ここでは`MyHomePage`ウィジェットの状態を管理するための`State`オブジェクトを作成している。また`createState`で作成した`state`が変化したときに再描画される。


### ボタンクラスの設定

このセクションでは、3つのボタンクラス（ResetButton、ReduceButton、MemoDataGetButton）の設定について説明します。
```dart
class ResetButton extends ElevatedButton {
  const ResetButton({Key? key, required VoidCallback? callback})
      : super(
            key: key,
            onPressed: callback,
            child: const Icon(
              Icons.exposure_zero,
            ));
}
```
:::note warn
このコードではボタンクラスが3つ(ResetButton・ReduceButton・MemoDataGetButton)あり、どれも同様に作られている。よってこの3つの命名以外はテンプレートのようになっています。
:::

1. `class ResetButton extends ElevatedButton`　⇒ `ElevatedButton`を継承したボタンのクラスを作る。※ **ElevatedButton**：立体的な効果を持ったボタンを作るための**ウィジェット**
2. `const ResetButton({Key? key, required VoidCallback? callback})`　⇒`ResetButton` のコンストラクタ。`VoidCallback`は`onPressed`などのボタンのイベント処理のときに使うもの。また引数を受け取らず何も返さない関数の型。`?`は`null`を**許容する**ことを示している。`{}`の中に定義されているパラメーター(引数)は、コンストラクタ呼び出し時に渡すことができる。`required`があるため`callback`の引数を指定しなければならない。
3. super(…)　⇒**親クラス**の`ElevatedButton`のコンストラクタを呼び出している。`key`・`onPressed`・`child`は**コンストラクタ引数**。`onPressed`が発火するタイミング(ResetButton押下時)のアクションを`callback`として受け取っている(ちなみにそれぞれのボタンの`callback`時の引数はのちに定義していく)。`child`ではボタンアイコンの指定をしている。


###  ウィジェット内部の状態
このセクションでは、_MyHomePageStateクラス内でウィジェット内部の状態を管理する方法について説明します。

```dart
class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  String _memoText = ''; // テキストを保持するための変数
  final List<String> _memoList = []; // メモのリスト
  final TextEditingController _textEditingController = TextEditingController();

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }
```

:::note warn
void _incrementCounter() のほかに_resetCounter() ・_reduceCounter() ・_updateText(String newText) が同じようなコードになります。
:::

1. `class _MyHomePageState extends State<MyHomePage>`　⇒`State<MyHomePage>`を継承した`_MyHomePageState`クラスを作る(`State` クラスは `MyHomePage` ウィジェットの内部状態を管理する)。
2. `int _counter = 0;`　⇒`_counter`ウィジェット内で使用される**整数値変数**。初期値は `0` 。
3.  `String _memoText = '';`　⇒テキストフィールドに入力されたメモのテキストを保持するための**文字列変数**。`_updateText` メソッドで新しいテキストが更新されると、この変数も更新される。
4.  `final List<String> _memoList = [];`　⇒メモのリストを保持するための**文字列リスト**を作る。`_handleButtonPress` メソッド内で新しいメモが `_memoList` に追加される。
5.  `final TextEditingController _textEditingController = TextEditingController();`　⇒`_textEditingController`はテキストフィールドの入力を制御するための `TextEditingController` **オブジェクト**です。テキストフィールドにテキストを入力する際に使用されます。
6. `void _incrementCounter() {}`　⇒カウンターをインクリメントするための`_incrementCounter()` メソッドを作る。`setState`内で変数の値を変更することでウィジェットの再描画をハンドルすることが可能。`_counter++;`は`_counter`ウィジェット内で使用される**整数値変数**を**増加**させるという処理。

**_resetCounter() ・_reduceCounter() ・_updateText(String newText) についての説明**

```dart
 void _resetCounter() {
    setState(() {
      _counter = 0;
    });
  }

  void _reduceCounter() {
    setState(() {
      _counter--;
    });
  }

  void _updateText(String newText) {
    setState(() {
      _memoText = newText;
    });
  }
```

1.  `void _resetCounter(){}`　⇒`setState`内で変数の値を変更することでウィジェットの再描画をハンドルすることが可能。 `_counter = 0;`は`_counter`変数で使用される**整数値変数**をリセットするという処理。
2. `void _reduceCounter() {}`　⇒ `setState`内で変数の値を変更することでウィジェットの再描画をハンドルすることが可能。`_counter--;`は`_counter`変数で使用される**整数値変数**を**減少**させるという処理。
3.  `_updateText(String newText) {}`　⇒`void _updateText(String newText)`は、`void _updateText`が新しいテキスト情報を受け取るための引数 `newText` を持っていることを示す。`setState`内で変数の値を変更することでウィジェットの再描画をハンドルすることが可能。 `_memoText = newText;`では新しいテキスト `newText` が `_memoText` に代入され、メモのテキストが新しい値に更新される。

### ボタンが押されたときの処理
このセクションでは、ボタンが押されたときに実行される _handleButtonPress メソッドについて説明します。

```dart
void _handleButtonPress() {
    // ボタンが押されたときに新しいメモデータを取得し、_updateTextを呼び出す
    _memoList.add(_memoText); // メモをリストに追加
    _updateText(''); // メモデータを初期化

    // TextFormFieldのテキストを初期化
    _textEditingController.clear();
  }
```

1. `void _handleButtonPress(){}`　⇒`MemoDataGetButton`を押下した際の**コールバック処理**が書かれている。(のちに`MemoDataGetButton(callback: _handleButtonPress)`が出てくる)
2. `_memoList.add(_memoText);`　⇒新しいメモデータ `_memoText` を `_memoList` と呼ばれるリストに追加する。
3. `_updateText('');`　⇒`_updateText` メソッドを呼び出し、メモの**テキストデータ** `_memoText` を初期化(空文字列 `''`) になるようにする。
4. `_textEditingController.clear();`　⇒`_textEditingController` を使用して、**テキストフィールドのテキスト**をクリアまたは初期化する。テキストを入力後、ボタンを押下するとテキストボックスが初期化される。

### ウィジェットの描画と Scaffold
このセクションでは、build メソッドに焦点を当てて説明します。
```dart
 @override
  Widget build(BuildContext context) {
    return Scaffold(...)
```

1. `@override Widget build(BuildContext context)`　⇒buildメソッドはstatefulWidgetから継承されたMyHomePageウィジェットを描画する。`BuildContext context` はbuildメソッドの引数でこれを使用してウィジェットがどのように配置され、スタイルが適用されるかを決定する。
2. `return Scaffold()`　⇒`Scaffold`はほかのウィジェットを描画するためのウィジェット。それを返している。Scaffoldのプロパティは多く実装されている。(body・appBar・floatingActionButton…など)




# 参考サイト・記事・単語まとめ

[クラス – プログラミング用語解説｜Unity高校＆ゲームスクールのG学院](https://gimo.jp/glossary/details/class.html#:~:text=クラスとはオブジェクト指向,することができます。)

[インスタンス – プログラミング用語解説｜Unity高校＆ゲームスクールのG学院](https://gimo.jp/glossary/details/instance.html)

[継承 – プログラミング用語解説｜Unity高校＆ゲームスクールのG学院](https://gimo.jp/glossary/details/inheritance.html)

[Dart のコンストラクタを理解する - エキサイト TechBlog.](https://tech.excite.co.jp/entry/2022/09/12/120000#定数コンストラクタ)

[【Flutter】build() でやってはいけない 3 つのこと - Qiita](https://qiita.com/chooyan_eng/items/976adeea8eed1b5ebad4)

[【Flutter初心者向け】MaterialAppとScaffoldとは？？サンプルアプリをもとに分かりやすく解説 - 文系プログラマー「いお」が語る](https://kirickp.com/entry/2023/07/02/163845#:~:text=MaterialAppはFlutterフレームワーク,行う役割のウィジェットです。)

[Dart のコンストラクタの基本 (できることまとめ)](https://zenn.dev/kaleidot725/articles/2021-11-13-dart-constructor)

[【5分でわかる】Widgetとは? | Flutter入門｜KIMURA LOG](https://kimuralog.com/?p=1903#:~:text=Widgetとは、Flutterにおける,ることが特徴です。)


[FlutterのBuildContextとは何か - Qiita](https://qiita.com/ko2ic/items/f7bf98b4a30049027470#buildメソッドのbuildcontext)

[【Flutter】UI構築のスタート地点Scaffoldを徹底解説【サンプルコードあり】](https://gakogako.com/flutter_scaffold/)

※**コンストラクタ**：Classの**インスタンスを作成**したとき（newしたとき）に呼び出される**初期化処理**。

※**パラメーター**：ソフトウエアを実行したりプログラム内で関数を呼び出したりする時に、一連の処理を指定するために与える情報(「処理結果に影響を与える外部から投入される変動要素」のこと　プログラムの世界では「引数（仮引数）」と呼ばれる場合もあります。 **引用**：https://wa3.i-3-i.info/word1443.html)

※**superクラス**：親クラスの別名

※**ジェネリクス型引数**：主にクラスや関数を汎用的に設計するための仕組み　⇒ジェネリクスを使うことで、クラスや関数が様々な型に対して動作する柔軟性を持つことができます。例えば、`State`クラスは`State<MyHomePage>`と指定することで、`MyHomePage`クラスだけでなく、他のウィジェットの状態も管理することができる。