Help us understand the problem. What is going on with this article?

Flutter未経験者に初期サンプルコードの説明をしてみた

More than 1 year has passed since last update.

Flutter未経験者の方から「実際どんなコードなん?」と質問があったので、説明したみた内容を投稿します。

以下はお馴染みのあのコード。

Flutterの新しいプロジェクトを作成すると、すでにプログラムが入っています。
そのまま実行するとボタンに反応してカウントアップするアプリが実行されます。
ある程度土台ができているので、それを使って説明します。

main.dart
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

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.display1,
            ),
            FloatingActionButton(
              onPressed: _incrementCounter,
              tooltip: 'Increment',
              child: Icon(Icons.add),
            )
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), 
    );
  }
}

import文

main.dart
import 'package:flutter/material.dart';

一番最初の行にimport文があります。
Googleのアプリっぽいデザインやそれに合ったツールが使えるようになります。

main()関数と MyApp

main.dart
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

main()関数があって、MyAppをスクリーンに表示するように書かれています。
MyAppはクラスで名前は何でもいいです。

MyAppStatelessWidget を継承しています。
ウィジェットは画面の一要素、それのステートレスなものです。
つまり 「変更が起きない要素」 のようなものだと思ってください。

その中でウィジェットのビルドが行われています。

引数に context が呼ばれています。
日本語に訳すと「状況」や「文脈」などと言いますが、この context の中にどこでどういう風に作成されたかの情報を含んでいます。

build 文で MaterialApp が返されています。
一番最初の行にimport文で指定した material.dart が早速使われています。GoogleのUIっぽいデザインを返すようにしています。

引数にタイトル、全体の色などのテーマ情報、初期表示のホームとなるクラスを指定します。
ここでは MyHomePage が指定されてます。

実際の MyHomePage の中を見てみます。

MyHomePage

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

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

StatefulWidget を継承しています。先ほど説明した Stateless とは違って、Stateful なウィジェット、つまり見た目の変化や持っている値の変化などが起きるウィジェットです。

コンストラクタ に keytitle が渡されています。
MyApp の stateless ウィジェットで title を渡していますが、key はデフォルトでユニークなものが渡されます。

そのあとに : super(key: key) と書かれています。
Redirecting constructors と呼ばれる手法で、別のコンストラクタの処理を追加で行なっています。親クラスにもキーを渡しているんですね。

そして、createState() という関数でステートを作成しています。
Statefulウィジェットなので、ステート部分の作成は必須です。

ここでは _MyHomePageState というステートを作成しています。

では実際にステートの中身を見てみます。

_MyHomePageState

_MyHomePageState クラスは MyHomePage の型を持ったステートを継承しています。
「これはMyHomePage内のある一つの状態を管理するクラスですよ」と宣言していると思ってください。

では何の状態を管理しているのかを説明していきます。

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

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }
  //〜〜〜〜 以下、あとで説明 〜〜〜〜
}

まず、変数定義の部分です。
_counter という int 型の変数と、文字列型を格納する配列の _data があります。

次に _incrementCounter() 関数があります。
void型なので値は返しません。setStateという関数があって、その中で _counter をインクリメントしています。

この setState で画面の再描画を行います。
_counter の値が変更されたものを再描画したいので、 setState の中に_counter を入れています。

では、_incrementCounter() 関数はどこで呼ばれるのでしょうか?
ボタンを押した時に呼びたいので、これからデザイン部分を作っていきます。

_MyHomePageState の build 部分

main.dart
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      //個々の内容はあとで説明
      appBar: xxxxxxxx,
      body: xxxxxxxx,
      floatingActionButton: xxxxxxxx,
    );
  }

ウィジェットのビルドを行っています。
この中でボタンの配置や変化する数字など、内容を作っていきます。

build メソッドはStatelessWidgetの中にもありました。
今回のbuildメソッドは、StatefulWidgetの中に定義するのではなく、Stateの中に定義します。
(Stateの中で定義する理由は色んな記事があるので、今は割愛します。)

returnScaffold を返しています。
Scaffold は「足場」という意味で、デザインする際に便利な機能が沢山入ったものだと思ってください。
その中で appBarbodyfloatingActionButton という要素が入っています。
それぞれ順番に説明していきます。

appBar 部分

main.dart
      appBar: AppBar(
        title: Text(widget.title),
      ),

appBar はアプリケーション上部に表示するバー部分です。属性である title の内容を表示します。
今回は文字列要素の Text があり、widget.title が指定されています。

この widget.titleStatefulWidget である MyHomePage に渡された title を持ってきています。
Stateの中にいても、それを呼び出したウィジェットの情報をこうやって取得することができます。

body 部分

main.dart
      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.display1,
            ),
            FloatingActionButton(
              onPressed: _incrementCounter,
              tooltip: 'Increment',
              child: Icon(Icons.add),
            )
          ],
        ),
      ),

body について説明します。内容の中心となるものを配置していきます。
Center で中央に配置する要素を作っています。コンテンツを中央寄せにしたいんですね。
その中に child という属性があります。Centerはただの入れ物の要素なので、子要素が必要な要素は、childchildren が必須です。
今回の場合でいうと、Columnが子要素で、それを中央寄せにしています。

Columnはリストや表のような枠組みを作っていくのに便利な要素です。
mainAxisAlignment がありますが、属性を変更することで、縦に子要素を並べていったり、横に並べていったりを設定できます。
これらはHTMLのタグのようなもので、種類が沢山あるので、その都度調べて取り込んでいく必要があります。

Columnも子要素が必要です。
並べる子要素は複数なので、child ではなく、複数形の children になります。
プログラムでは Text で文字を設定しています。

style について

main.dart
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),

Text には style が設定されています。文字の大きさや色を設定する箇所です。
しかしここでは、「blue」とか「red」とか「12px」のような表現がありません。

Theme.of(context) で親のテーマも持ってきています。context は「文脈」なので、親のテーマ情報も持っています。それの「普通のテキスト」に関するテーマである textThemedisplay1 タイプを取得しています。
もちろん display2display3 もあり、数字が上がる毎に文字が大きくなっていきます。

floatingActionButton 部分

main.dart
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),

最後にfloatingActionButton があります。画面の一番下に浮動しているボタンなので、body の外に定義します。onPressed にボタンが押された時の動作を記述しますが、ここでずっと説明を放置されてきた_incrementCounter() を設定します。

最後にtooltip や アイコン を設定して終了です。

gaiax
人と人をつなげる Empowering the people to connect.
http://www.gaiax.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away