Flutter開発手順についてまとめたはずなのに喪失してしまった。

以前Flutterアプリを作成した際に開発手順を残してたはずなのに、

どこかにやってしまったので今度こそ残しておく。

誰が消してしまったんd((

<br>
# 前提
Flutterアプリの開発手順として「Android Studio」が出てくる事が多いが、今回は「PowerShell + VSCode」で説明していく。

また、デフォルトアプリである「ボタンを押すとカウントアップするだけ」のアプリを改変していく形で説明していく。
![Screenshot_20210209-214949.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142821/425eef6e-bb0c-bbb6-441c-8933be258d75.png)


<br>
# 0. 事前準備
以下から、Flutterをインストールする。
https://flutter.dev/docs/get-started/install

インストールしたら、以下のコマンドを実行。

```powershell:PowerShell
# 実行すると「Android Studioを入れろ」「FlutterとDartのプラグインが入ってねえぞ」と怒られたりするので、お医者さん(doctor)の指示に従う。

flutter doctor
```

<br>
# 1. プロジェクト作成


以下のコマンドで、Flutterプロジェクトを作成。

```powershell:PowerShell
# オプション「-i」でiOS／「-a」でAndroidのネイティブコードを指定できるが、特にこだわりはないので何も付けてない
# ＜例＞ flutter create -i swift  demo_app （default: Objective-C）
# ＜例＞ flutter create -a kotlin demo_app （default: Java）

flutter create demo_app
```


<br>
# 2. 必要なディレクトリを作成
手順1でプロジェクトができたので、今後使うであろうディレクトリを作っておく。

```powershell:PowerShell
# 作成したプロジェクトのフォルダに移動
cd .\demo_app\

# 「スプラッシュ画面」や「アプリアイコン」に使用する画像などを配置するフォルダ
New-Item .\assets -type directory

# Flutterは「BLoC Pattern」であり、以下に分かれる（ https://note.com/yamarkz/n/n7f9106e53179 ）
# - MVCでいう「M」：Resource
# - MVCでいう「V」：Widget
# - MVCでいう「C」：Bloc
New-Item .\lib\resources -type directory
New-Item .\lib\blocs     -type directory
New-Item .\lib\widgets   -type directory
```

<br>
# 3. とりあえずHello world.
手順1の時点で「Hello world.」相当はできている。
ただ、保守性や拡張性も考えて`./main.dart`から`./widgets/Hello.dart`を呼び出すようにする。

という事で、まずはファイル作成。

```powershell:PowerShell
New-Item .\lib\widgets\Hello.dart -type file
```

<br>
次に、`./main.dart`を修正。
やってる事は「デフォルトソースからのコメント削除」「`./widgets/Hello.dart`の呼び出し」。

```dart:./main.dart
import 'package:flutter/material.dart';
import 'package:demo_app/widgets/Hello.dart';  // ←←←← 追加（`./widgets/Hello.dart`の呼び出し）

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),  // ←←←← 追加（`./widgets/Hello.dart`の呼び出し）
    );
  }
}
```

次に、`./widgets/Hello.dart`を加筆。
やってる事は「`main.dart`からの転記＆コメント削除」だけ。

```dart:./widgets/Hello.dart
//
// デフォルトソースから転記しているだけなので、特にコメントなし
//

import 'package:flutter/material.dart';

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}
```


<br>
# 4. スプラッシュ画面の設定
スプラッシュ画面は、アプリ起動時に一時的に表示されるアイコンみたいなやつ。（参考: https://uxmilk.jp/76678 ）

設定としては簡単で、以下を実施するだけ。
https://pub.dev/packages/flutter_native_splash

まずは、`pubspec.yaml`に以下を追記。

```yaml:pubspec.yaml
...
dependencies:
  flutter_native_splash: ^0.1.9  # ←←←← 追加

flutter_native_splash:                  # ←←←← 追加
  image: "./assets/smartphone_app.png"  # いらすとやさんから拝借（ https://www.irasutoya.com/2015/06/blog-post_824.html ）
  color: "#ffffff"                      # 背景色。必須。設定してなかったら怒られた。
...
```

あとは、コマンドで以下を実行。（ https://pub.dev/packages/flutter_native_splash に書いてる )

```powershell:PowerShell
flutter pub get 
flutter pub run flutter_native_splash:create
```

<br>
# 5. アイコンの変更

言わずもがな、スマホのホーム画面に表示されるアイコンの事。（参考: https://qiita.com/wforskk/items/74d5d22e6ccc2100e5b7 ）

設定としては簡単で、以下を実施するだけ。
https://pub.dev/packages/flutter_launcher_icons

まずは、`pubspec.yaml`に以下を追記。

```yaml:pubspec.yaml
...
dependencies:
  flutter_launcher_icons: ^0.8.0  # ←←←← 追加

flutter_icons:                               # ←←←← 追加
  android: "launcher_icon"
  ios: true
  image_path: "./assets/smartphone_app.png"  # 面倒臭かったので、手順4と同じ画像を使用
...
```

あとは、コマンドで以下を実行。（ https://pub.dev/packages/flutter_launcher_icons に書いてる )

```powershell:PowerShell
flutter pub get
flutter pub run flutter_launcher_icons:main
```


<br>
# 6. StatefulとStateless
手順3を見てみると「 `StatefulWidget` 」というクラスを継承している。という事は「 `StatelessWidget` 」というクラスも存在しそうな感じがする。

その推測は正解であり、Flutterでは「Stateful」と「Stateless」の2つの概念が存在する。
その名の通り、
「Stateful」は、state(=状態)を保持している。（何かアクションしたら、何かの値が変動する）
「Stateless」は、state(=状態)を保持していない。（何をしても何も変動しない）

デフォルトアプリを見てみると、
『ボタン』を押したら『変動するテキスト』は「Stateful」で、
ボタンを押しても『変動しないテキスト』は「Stateless」と考えられる。
![Screenshot_20210209-21494_description.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142821/c37559bd-e762-b015-b070-a0051870bdaf.png)

という事で、StatefulとStatelessに分けてみる。

<br>
まずは`./widgets/Hello.dart`について、手順3から以下を変更する。（コメント箇所のみ確認する事）

```dart:./widgets/Hello.dart
import 'package:flutter/material.dart';
import 'package:rss_recommender/ui/CustomText.dart';  // ←←←← 追加

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            CustomText(),  // ←←←← 変更(`/./ui/CustomText.dart`を呼び出し)
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}
```

<br>
次に、以下のコマンドで `./widgets/CustomText.dart` を作成する。

```powershell:PowerShell
New-Item .\lib\widgets\CustomText.dart -type file
```

そして、`.\widgets\CustomText.dart`に以下を記載する。

```dart:.\widgets\CustomText.dart
import 'package:flutter/material.dart';

class CustomText extends StatelessWidget {
  CustomText();

  Widget build(BuildContext context) {
    return Text(
      'You have pushed the button this many timessssssssss:',  // ←←←← 変更箇所が分かりやすくするためにssssssssssとしている
    );
  }
}
```

# 7. ここまでで確認

1~6まで実施してきた事については以下となる。

- スプラッシュ追加
- アイコン変更
- Widgetsの分離

現時点でのディレクトリ構成としては以下の通り。（特に関係ない箇所については省略）

```powershell:PowerShell
tree
(中略)
├─.dart_tool
├─.idea
├─android
├─assets
│  └─smartphone_app.png
├─build
├─ios
├─lib
│  ├─blocs
│  ├─resources
│  ├─wedgets
│  │ ├─CustomText.dart
│  │ └─Hello.dart
│  └─main.dart
├─test
├─README.md
└─pubspec.yaml
```

これを動画で確認すると以下の通り。
アイコンが変わっていて、スプラッシュも変わっていて、Statelessなテキストが変わっている事が分かると思う。
（アプリ名が「rss_recommender」なのは、RSSレコメンダーを作りたかったからです。そのままですね。）

![ezgif-6-dd95616dcbe6.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142821/6cbeeaad-127a-8ce7-c356-d4bdc81a49d8.gif)

# 999. 今後の展開

まだ使っていない「blocs」「resources」を使っていきたい。


<br>

一旦、以上！