Flutterの新規プロジェクト作成時の初期コードを読み解いてみた

2024.02.07

こんにちは、ゲームソリューション部のsoraです。
今回は、Flutterの新規プロジェクト作成時の初期コードを読み解いてみたことについて書いていきます。
私は最近Flutterを触り始めたばかりなので、間違っている部分があれば優しくご指摘いただけると幸いです。

新規プロジェクト作成時のソースコード

新規プロジェクト作成時のコードを実行したときの画面が以下です。
新規プロジェクト作成時のソースコード(main.dart)は以下です。
この後、細かく見ていきます。

main.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.deepPurple),
                useMaterial3: true,
            ),
            home: const MyHomePage(title: 'Flutter Demo Home Page'),
        );
    }
}

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

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

class _MyHomePageState extends State<MyHomePage> {
    int _counter = 0;
    void _incrementCounter() {
        setState(() {
            _counter++;
        });
    }

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

コードの読み解き

import

import 'package:flutter/material.dart';

ここではFlutterのmaterialデザインを使うためのライブラリを含む、Flutterアプリ開発に必要なファイルをインポートしています。
Material Components widgets
iOS風のWidgetが使いたい場合は、import 'package:flutter/cupertino.dart'もあります。

main()

main()は最初に実行される関数です。

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

runAppでは、Flutterのプロジェクト上で最初に展開したいWidgetを引数として指定します。
Widgetとは、Flutterを構成する要素です。
FlutterはWidgetの組み合わせで構成されています。

Widgetには、文字を表示したり画面をデザインするものや、画面の中央に寄せるといったレイアウトの調整をしたり状態管理したりするものがあります。

MyAppクラス

次は、main()runAppで指定されているMyAppクラスです。

class MyApp extends StatelessWidget {
    const MyApp({super.key});

extendsで親クラスとしてStatelessWidgetを継承しています。
StatelessWidgetは、ユーザの操作に対して状態が変化せず、内部で状態を持たないWidgetです。
親Widgetの変更でのみ更新が入ります。

StatelessWidgetは、buildメソッドを持っていて、Widgetかテキストを返します。
扱う値はすべて不変であり、内部で値を変更することができません。

    @override
    Widget build(BuildContext context) {
        return MaterialApp(
            title: 'Flutter Demo',
            theme: ThemeData(
                colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
                useMaterial3: true,
            ),
            home: const MyHomePage(title: 'Flutter Demo Home Page'),
        );
    }

@overrideで、StatelessWidgetにあるbuildメソッドを上書きしています。

title: 'Flutter Demo'は、Webで表示したときのタブに表示されるページタイトルになります。(スマホアプリではおそらく関係なし)
themeプロパティで、アプリ全体のテーマを指定しています。
ThemeData.dark()とすると、ダークモードにすることができます。

MyHomePageクラス

次は、main()runAppで指定されているMyAppクラスで呼び出されているMyHomePageクラスです。

class MyHomePage extends StatefulWidget {

先ほどとは異なり、今回はStatefulWidgetを継承しています。
StatefulWidgetは、ユーザの操作や外部データによって状態が変化して、UIに更新が入るWidgetです。
状態が変化すると更新されます。

    const MyHomePage({super.key, required this.title});
    final String title;

上記では、コンストラクタ(クラスのインスタンスが生成されるときに自動実行される処理)を定義しています。

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

createState()は、ビルド後に呼ばれる必須のメソッドであり、Stateクラス(今回だと_MyHomePageState())を返します。
Flutter(Dart)では_から始めることにより、プライベートであると認識されます。

_MyHomePageStateクラス

class _MyHomePageState extends State<MyHomePage> {
    int _counter = 0;
    void _incrementCounter() {
        setState(() {
            _counter++;
        });
    }

Stateクラスを継承しています。
Stateクラスはその名の通り状態管理周りのメソッドを持っています。
setState()では、Widgetを再構築して変更を反映させています。
カウンターを設定して、_incrementCounter()が呼び出されると、_counterに1を加算して反映させます。

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

buildメソッドは、Widgetを表示させるのに使用されます。
再描画も、再度buildメソッドを呼び出すことで実行されます。

Scaffoldはアプリの基盤となるレイアウトを構成するWidgetです。
appBarがトップバーを構成するWidgetで、背景色やタイトルを表示している部分です。
bodyがコンテンツのメイン部分を構成するWidgetで、Centerでy軸の中央に、ColumnでWidgetを縦一列に整列させています。
Textで文字を表示させていて、$_counterのように、$を頭につけることで変数を代入しています。

最後のfloatingActionButtonで、カウンターのボタンを表示させていて、onPressedとしてボタンを押すことで、カウントを加算する_incrementCounterを呼び出すようになっています。

参考

【Flutter】なぜウィジェットの import 先に「material.dart」を選ぶのか
Flutterの基礎
StatefulWidgetとStatelessWidgetの違い
Flutter の初期コードを1から理解する

最後に

今回は、Flutterの新規プロジェクト作成時の初期コードを読み解いてみたことを記事にしました。
どなたかの参考になると幸いです。