初めての Flutter アプリの作成(パート 1)

Flutter は、1 つのコードベースからネイティブにコンパイルして、モバイル、ウェブ、デスクトップの美しいアプリケーションを作成できる Google の UI ツールキットです。Flutter は既存のコードと組み合わせることも可能です。世界中のデベロッパーの皆様にご利用いただける、無料のオープンソースです。

この Codelab では、シンプルな Flutter モバイルアプリを作成します。オブジェクト指向のコードと、変数、ループ、条件などの基本的なプログラミングの概念に精通している場合は、Codelab を完了できます。Dart、モバイル、ウェブ プログラミングの経験は不要です。

パート 1 の学習内容

  • iOS、Android、ウェブで自然に見える Flutter アプリの作成方法
  • Flutter アプリの基本構造
  • 機能を拡張するためのパッケージの検索と使用
  • 開発サイクル短縮のためのホットリロードの使用
  • ステートフル ウィジェットを実装する方法
  • 遅延読み込みされる無限リストを作成する方法

この Codelab のパート 2 では、インタラクティブな機能の追加やアプリテーマの変更、新しいページに移動する機能(Flutter でルートと呼ばれる機能)の追加を行います。

パート 1 で作成するもの

スタートアップ企業の名前候補を生成する簡単なアプリを実装します。ユーザーは名前の選択や選択解除を行い、最適な名前を保存できます。このコードでは、一度に 10 個の名前が遅延生成されます。ユーザーがスクロールすると、名前がさらに生成されます。ユーザーがスクロールできる距離に制限はありません。

次のアニメーション GIF は、完了時にアプリが動作する様子を示しています。

6556f8b61acd6a89.gif

この Codelab で学びたいことは次のどれですか?

このトピックは初めてなので、簡単に概要を知りたい。 このトピックについてはある程度知っているが、復習したい。 プロジェクトで使用するサンプルコードを確認したい。 特定の項目に関する説明を確認したい。

このラボを完了するには、Flutter SDKエディタの 2 つのソフトウェアが必要です。この Codelab では、Android Studio を使用することを前提としています。別のエディタを使用しても構いません。

この Codelab は、次のいずれかのデバイスを使って実行できます。

  • パソコンに接続され、デベロッパー モードに設定された物理デバイス(Android または iOS
  • iOS シミュレータ(Xcode ツールのインストールが必要)
  • Android Emulator(Android Studio でセットアップが必要)
  • ブラウザ(デバッグには Chrome が必要)

b2f84ff91b0e1396.pngテンプレート化された簡単な Flutter アプリを作成します。startup_namer という Flutter プロジェクトを作成し、null 安全に移行します。以下をご覧ください。

$ flutter create startup_namer
$ cd startup_namer
$ dart migrate --apply-changes

基本的に、Dart コードが含まれている lib/main.dart を編集していきます。

b2f84ff91b0e1396.png lib/main.dart の内容を置き換えます。lib/main.dart からすべてのコードを削除し、次のコードに置き換えます。画面の中央に「Hello World」と表示されます。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Welcome to Flutter'),
        ),
        body: const Center(
          child: const Text('Hello World'),
        ),
      ),
    );
  }
}

b2f84ff91b0e1396.pngアプリを実行します。お使いのデバイスに応じて、Android、iOS、ウェブの出力のいずれかが表示されます。

Android

iOS

cf1e10b838bf60ee.png 確認内容

  • このサンプルでは、マテリアル アプリを作成します。マテリアルは、モバイルとウェブで標準的に使われているビジュアル デザイン言語です。Flutter には豊富なマテリアル ウィジェットが用意されています。
  • メインメソッドでは矢印(=>)表記を使用します。1 行の関数またはメソッドでは、矢印表記を使用してください。
  • アプリは StatelessWidget を拡張することで、アプリ自体をウィジェットにします。Flutter では、配置、パディング、レイアウトなど、ほとんどすべてがウィジェットです。
  • マテリアル ライブラリの Scaffold ウィジェットでは、デフォルトのアプリバー、タイトル、本文のプロパティ(ホーム画面のウィジェット ツリーを保持します)が提供されます。ウィジェットのサブツリーは非常に複雑になる可能性があります。
  • ウィジェットの主な役割は、build メソッドを提供することです。このメソッドは、他の下位レベルのウィジェットに関してウィジェットを表示する方法を記述します。
  • このサンプルの本文は、Text 子ウィジェットを含む Center ウィジェットで構成されています。Center ウィジェットでは、ウィジェットのサブツリーが画面の中心に配置されます。

このステップでは、english_words という名前のオープンソース パッケージを使用します。このパッケージには、使用頻度の高い約数千の英単語と、いくつかのユーティリティ関数が含まれています。

english_words パッケージや、他の多くのオープンソース パッケージは、pub.dev にあります。

b2f84ff91b0e1396.pngpubspec ファイルは Flutter アプリのアセットを管理します。pubspec.yaml で、依存関係のリストに english_words: ^4.0.0english_words 4.0.0 以降)を追加します。

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^1.0.2
  english_words: ^4.0.0   # add this line

b2f84ff91b0e1396.pngAndroid Studio のエディタビューで pubspec を表示し、続いて [Packages get] をクリックします。これにより、パッケージがプロジェクトに取り込まれ、コンソールに次のように表示されます。

flutter packages get
Running "flutter packages get" in startup_namer...
Process finished with exit code 0

dart pub get を実行すると、pubspec.lock ファイルも自動生成されます。このファイルには、プロジェクトに取り込まれたすべてのパッケージとそのバージョン番号のリストが含まれます。

b2f84ff91b0e1396.pnglib/main.dart に新しいパッケージをインポートします。

import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';  // Add this line.

文字を入力すると、インポートするライブラリの候補が表示されます。その後、インポート文字列がグレーで表示され、インポートされたライブラリが現在使用されていないことを示します。

次に、「Hello World」の代わりに english_words パッケージを使ってテキストを生成します。

b2f84ff91b0e1396.png次のように変更します。

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final wordPair = WordPair.random(); // Add this line.
    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Welcome to Flutter'),
        ),
        body: Center(                       // Drop the const, and
          //child: Text('Hello World'),     // Replace this text...
          child: Text(wordPair.asPascalCase),  // With this text.
        ),
      ),
    );
  }
}

b2f84ff91b0e1396.pngアプリが実行されている場合は、ホットリロード ボタン(e11f6ccd1560a28b.png)で実行中のアプリを更新します。コマンドラインでは r を入力してホットリロードを行えます。ホットリロードをクリックするかプロジェクトを保存するたびに、実行中のアプリでランダムに選択された別の単語のペアが表示されます。これは、ペア設定された単語が build メソッド内で生成されるためです。このメソッドは、MaterialApp でレンダリングが必要になるたびに、または Flutter Inspector で Platform を切り替えるときに実行されます。

Android

iOS

トラブルシューティング

アプリが正しく実行されていない場合は、入力ミスがないか探してください。必要に応じて、次のリンクのコードを確認してから、続きに進んでください。

ステートレス ウィジェットは不変です。つまり、プロパティは変更できず、すべての値が最終的なものです。

ステートフル ウィジェットは、ウィジェットの存続期間中に変更される可能性のある状態を維持します。ステートフル ウィジェットを実装するには、少なくとも 2 つのクラスが必要です(State クラスと、そのインスタンスを作成する StatefulWidget)。StatefulWidget オブジェクトは、それ自体が不変で、スローして再生成できますが、State オブジェクトはウィジェットの存続期間全体にわたって存続します。

このステップでは、ステートフル ウィジェット RandomWords を追加します。これにより State クラス _RandomWordsState が作成されます。次に、既存の MyApp ステートレス ウィジェット内で、RandomWords を子として使用します。

b2f84ff91b0e1396.pngステートフル ウィジェットのボイラープレート コードを作成します。

ボイラープレート コードは MyApp 以外のファイルの任意の場所に配置できますが、ソリューションではファイルの末尾に追加します。lib/main.dart で、すべてのコードの後にカーソルを置き、Return キーを数回押して新しい行を開始します。IDE で「stful」と入力します。エディタから、Stateful ウィジェットを作成するかどうかを尋ねられます。Enter キーを押して確定します。2 つのクラスのボイラープレート コードが挿入され、カーソルがステートレス ウィジェットの名前を入力する位置に移動します。

b2f84ff91b0e1396.pngウィジェットの名前として「RandomWords」と入力します。

下のコードに示すように、State クラスを作成すること以外、RandomWords ウィジェットはほとんど何もしません。

ステートフル ウィジェットの名前として RandomWords を入力すると、IDE は、付属する State クラスを自動的に更新して _RandomWordsState という名前を付けます。デフォルトでは、State クラスの名前の先頭にアンダースコアが付けられます。識別子の先頭にアンダースコアを付けると、Dart 言語で秘匿化されます。これは、State オブジェクトに推奨されるベスト プラクティスです。

また、IDE は、状態クラスを自動的に更新して State<RandomWords> を拡張します。これは、RandomWords 専用の汎用 State クラスを使用していることを示します。アプリのロジックのほとんどはここに含まれており、RandomWords ウィジェットの状態が維持されます。このクラスは、生成された単語のペアのリストを保存します。このリストは、ユーザーのスクロールに応じて無限に拡張されます。このラボのパート 2 では、ユーザーがハートアイコンを切り替えてリストに単語のペアを追加(または削除)することで、これらの単語のペアがお気に入りに追加される操作を見ていきます。

どちらのクラスも以下のように表示されます。

class RandomWords extends StatefulWidget {
  @override
  _RandomWordsState createState() => _RandomWordsState();
}

class _RandomWordsState extends State<RandomWords> {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

b2f84ff91b0e1396.png_RandomWordsStatebuild() メソッドを更新します。

return Container(); を、次の 2 行で置き換えます。

class _RandomWordsState extends State<RandomWords> {
  @override
  Widget build(BuildContext context) {
    final wordPair = WordPair.random();      // NEW
    return Text(wordPair.asPascalCase);      // NEW
  }
}

b2f84ff91b0e1396.png次の変更を加えて、MyApp から単語生成コードを削除します。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final wordPair = WordPair.random();  // DELETE

    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Welcome to Flutter'),
        ),
        body: Center(
          //child: Text(wordPair.asPascalCase), // REPLACE with...
          child: RandomWords(),                 // ...this line
        ),
      ),
    );
  }
}

b2f84ff91b0e1396.pngアプリをホットリロードします。このアプリはこれまでと同様に動作し、アプリのホットリロードや保存が行われるたびに単語のペアを表示します。

トラブルシューティング

アプリが正しく実行されていない場合は、次のリンクのコードを確認してから、続きに進んでください。

このステップでは、_RandomWordsState を展開して単語のペアのリストを生成し、表示します。ユーザーがスクロールすると、リスト(ListView ウィジェットに表示されます)が無限に拡張されます。ListViewbuilder ファクトリ コンストラクタを使用すると、必要に応じてリスト表示を遅延的にビルドできます。

b2f84ff91b0e1396.png_RandomWordsState クラスに状態変数をいくつか追加します。

単語のペアの候補を保存するための _suggestions リストを追加します。また、フォントサイズを大きくするための _biggerFont 変数を追加します。

class _RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];                 // NEW
  final _biggerFont = const TextStyle(fontSize: 18); // NEW
  ...
}

次に、_buildSuggestions() 関数を _RandomWordsState クラスに追加します。このメソッドは、候補の単語のペアを表示する ListView をビルドします。

ListView クラスでは、ビルダー プロパティ itemBuilder が提供されます。これは、Factory ビルダーであり、匿名関数として指定されたコールバック関数です。2 つのパラメータ(BuildContext と行イテレータ i)が関数に渡されます。イテレータは 0 から始まり、関数が呼び出されるたびに(候補の単語のペアごとに 1 回ずつ)増加します。このモデルでは、ユーザーのスクロールに応じて候補のリストを継続的に拡張できます。

b2f84ff91b0e1396.png_buildSuggestions 関数全体を追加します。

_RandomWordsState クラスに次の関数を追加して、必要に応じてコメントを削除します。

  Widget _buildSuggestions() {
    return ListView.builder(
      padding: const EdgeInsets.all(16),
      // The itemBuilder callback is called once per suggested
      // word pairing, and places each suggestion into a ListTile
      // row. For even rows, the function adds a ListTile row for
      // the word pairing. For odd rows, the function adds a
      // Divider widget to visually separate the entries. Note that
      // the divider may be difficult to see on smaller devices.
      itemBuilder: (BuildContext _context, int i) {
        // Add a one-pixel-high divider widget before each row
        // in the ListView.
        if (i.isOdd) {
          return Divider();
        }

        // The syntax "i ~/ 2" divides i by 2 and returns an
        // integer result.
        // For example: 1, 2, 3, 4, 5 becomes 0, 1, 1, 2, 2.
        // This calculates the actual number of word pairings
        // in the ListView,minus the divider widgets.
        final int index = i ~/ 2;
        // If you've reached the end of the available word
        // pairings...
        if (index >= _suggestions.length) {
          // ...then generate 10 more and add them to the
          // suggestions list.
          _suggestions.addAll(generateWordPairs().take(10));
        }
        return _buildRow(_suggestions[index]);
      }
    );
  }

_buildSuggestions 関数は、単語のペアごとに _buildRow を 1 回呼び出します。この関数は新しいペアを ListTile 内にそれぞれ表示します。これをパート 2 で使用すると各行の外観が向上します。

b2f84ff91b0e1396.png_buildRow 関数を _RandomWordsState に追加します。

  Widget _buildRow(WordPair pair) {
    return ListTile(
      title: Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
    );
  }

b2f84ff91b0e1396.png_RandomWordsState.build メソッドを更新します。

このメソッドを変更して、単語生成ライブラリを直接呼び出すのではなく、_buildSuggestions() を使用するようにします(Scaffold でマテリアル デザインの基本的なビジュアル レイアウトが実装されます)。

  @override
  Widget build(BuildContext context) {
    //final wordPair = WordPair.random(); // Delete these...
    //return Text(wordPair.asPascalCase); // ... two lines.

    return Scaffold (                     // Add from here...
      appBar: AppBar(
        title: Text('Startup Name Generator'),
      ),
      body: _buildSuggestions(),
    );                                      // ... to here.
  }

b2f84ff91b0e1396.pngMyAppbuild メソッドを更新します。タイトルの変更、AppBar の削除、ホーム プロパティの RandomWords ウィジェットへの変更を行います。

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Startup Name Generator',
      home: RandomWords(),
    );
  }

b2f84ff91b0e1396.pngアプリを再起動します。スクロールした距離に関係なく、単語のペアのリストが表示されます。

Android

iOS

トラブルシューティング

アプリが正しく実行されていない場合は、次のリンクのコードを確認してから、続きに進んでください。

これで完了です

この Codelab のパート 1 を完了しました。このアプリを拡張する場合は、パート 2 に進んでください。パート 2 では、アプリを次のように変更します。

  • インタラクティブな機能を追加します。
  • 新しいルートに移動する機能を追加します。
  • テーマの色を変更します。

パート 2 が完了すると、アプリの外観は次のようになります。

7fcab088cd22cff7.gif

その他の次の手順

Flutter SDK について詳しくは、次のリソースをご覧ください。

その他のリソースには次のものがあります。

また、Flutter コミュニティとつながることもできます。