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

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

この Codelab では、基本的な Flutter モバイルアプリを拡張してインタラクティブな機能を追加します。また、ユーザーが移動できる 2 つ目のページ(ルートと呼ばれます)を作成します。最後に、アプリのテーマ(色)を変更します。この Codelab はパート 1(遅延読み込みされる無限リストを作成する)を拡張したものですが、パート 2 から始める場合の開始コードも用意されています。

パート 2 の学習内容

  • iOS、Android、ウェブで自然に見える Flutter アプリの作成方法
  • 開発サイクルを短縮するためのホットリロードの使用方法
  • ステートフル ウィジェットにインタラクティブな機能を追加する方法
  • 予備画面を作成して移動する方法
  • テーマを使用してアプリの外観を変更する方法

パート 2 で作成するもの

まずは、スタートアップ企業の名前候補の無限リストを生成する簡単なモバイルアプリを作成します。この Codelab を終了すると、エンドユーザーは名前の選択や選択解除を行い、最適な名前を保存できるようになります。アプリバーの右上にあるリストアイコンをタップすると、お気に入りの名前だけが表示される新しいページ(ルートと呼ばれます)に移動します。

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

7fcab088cd22cff7.gif

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

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

パート 1 を完了していない場合は、初めての Flutter アプリの作成(パート 1)Flutter 環境をセットアップするの手順に沿って、Flutter 開発用に環境をセットアップしてください。

この Codelab のパート 1 で作業していた場合は、すでに開始用アプリ startup_namer があるので、次の手順に進むことができます。

startup_namer がない場合でも、次の手順で取得できるので心配いりません。

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

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

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

b2f84ff91b0e1396.pnglib/main.dart からすべてのコードを削除し、このファイルのコードに置き換えます。このファイルには、スタートアップ名候補の遅延読み込みされた無限リストが表示されます。

b2f84ff91b0e1396.png英単語パッケージを追加して pubspec.yaml を更新します。

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^1.0.2
  english_words: ^4.0.0-0    // NEW

英単語パッケージは、スタートアップ名として使用される可能性のある、ランダムな単語のペアを生成します。

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

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

b2f84ff91b0e1396.pngアプリを実行します。

必要に応じて画面をスクロールすると、スタートアップ名の候補が表示され続けます。

このステップでは、各行にハートアイコンを追加します。次のステップでは、パートアイコンをタップ可能にし、お気に入りの名前を保存します。

b2f84ff91b0e1396.png_saved Set_RandomWordsState に追加します。この Set は、ユーザーがお気に入りに追加した単語のペアを保存します。SetList よりも好適です。Set は、適切に実装されると重複するエントリを許可しないためです。

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

b2f84ff91b0e1396.png単語のペアがすでにお気に入りに追加されていないことを確認するために、_buildRow 関数に alreadySaved チェックを追加します。

Widget _buildRow(WordPair pair) {
  final alreadySaved = _saved.contains(pair);  // NEW
  ...
}

_buildRow() では、ListTile オブジェクトにハート形のアイコンを追加して、お気に入り機能を有効にします。次のステップでは、ハートアイコンを操作する機能を追加します。

b2f84ff91b0e1396.png以下のように、テキストの後にアイコンを追加します。

Widget _buildRow(WordPair pair) {
  final alreadySaved = _saved.contains(pair);
  return ListTile(
    title: Text(
      pair.asPascalCase,
      style: _biggerFont,
    ),
    trailing: Icon(   // NEW from here...
      alreadySaved ? Icons.favorite : Icons.favorite_border,
      color: alreadySaved ? Colors.red : null,
    ),                // ... to here.
  );
}

b2f84ff91b0e1396.pngアプリをホットリロードします。

各行にハートが表示されていますが、まだ操作できません。

Android

iOS

トラブルシューティング

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

このステップでは、ハートアイコンをタップ可能にします。ユーザーがリスト内のエントリをタップしてお気に入りの状態を切り替えると、保存されているお気に入りのセットに対して単語のペアの追加または削除を行えます。

これを行うには、_buildRow 関数を変更します。単語がすでにお気に入りに追加されている場合は、その単語をもう一度タップするとお気に入りから削除できます。タイルがタップされると、関数は setState() を呼び出して、状態が変更されたことをフレームワークに通知します。

b2f84ff91b0e1396.png次に示すように、onTap_buildRow メソッドに追加します。

Widget _buildRow(WordPair pair) {
  final alreadySaved = _saved.contains(pair);
  return ListTile(
    title: Text(
      pair.asPascalCase,
      style: _biggerFont,
    ),
    trailing: Icon(
      alreadySaved ? Icons.favorite : Icons.favorite_border,
      color: alreadySaved ? Colors.red : null,
    ),
    onTap: () {      // NEW lines from here...
      setState(() {
        if (alreadySaved) {
          _saved.remove(pair);
        } else {
          _saved.add(pair);
        }
      });
    },               // ... to here.
  );
}

b2f84ff91b0e1396.pngアプリをホットリロードします。

任意のタイルをタップすると、エントリをお気に入りに追加したり、それを解除したりできます。タイルをタップすると、タップ位置から暗黙的なインク スプラッシュ アニメーションが生成されます。

Android

iOS

トラブルシューティング

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

このステップでは、お気に入りを表示する新しいページ(Flutter ではルートと呼ばれます)を追加します。ホームルートと新しいルートの間を移動する方法を学習します。

Flutter では、Navigator がアプリのルートを含むスタックを管理します。Navigator のスタックにルートをプッシュすると、表示がこのルートに更新されます。Navigator のスタックからルートをポップすると、表示が前のルートに戻ります。

次に、_RandomWordsStatebuild メソッドの AppBar にリストアイコンを追加します。ユーザーがリストアイコンをクリックすると、保存したお気に入りを含む新しいルートが Navigator にプッシュされ、アイコンが表示されます。

b2f84ff91b0e1396.pngアイコンとそれに対応するアクションを build メソッドに追加します。

class _RandomWordsState extends State<RandomWords> {
  ...
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Startup Name Generator'),
        actions: [
          IconButton(icon: Icon(Icons.list), onPressed: _pushSaved),
        ],
      ),
      body: _buildSuggestions(),
    );
  }
  ...
}

b2f84ff91b0e1396.png_pushSaved() 関数を _RandomWordsState クラスに追加します。

  void _pushSaved() {
  }

b2f84ff91b0e1396.pngアプリをホットリロードします。アプリバーにリストアイコン a114478ae13b853.png が表示されます。_pushSaved 関数は空なので、このアイコンをタップしても何も起こりません。

次に、ルートを作成して Navigator のスタックにプッシュします。この操作により、新しいルートを表示するように画面が変わります。新しいページのコンテンツは、匿名関数の MaterialPageRoutebuilder プロパティに作成されます。

b2f84ff91b0e1396.pngNavigator.push を呼び出すと、ルートがナビゲータのスタックにプッシュされます。以下をご覧ください。IDE はコードが無効であることを示しますが、次のセクションでこれを解決します。

void _pushSaved() {
  Navigator.of(context).push(
  );
}

次に、MaterialPageRoute とそのビルダーを追加します。ここでは、ListTile 行を生成するコードを追加します。ListTiledivideTiles() メソッドは、各 ListTile の間に水平方向の間隔を追加します。divided 変数は、簡易関数である toList() によってリストに変換された最終行を保持します。

b2f84ff91b0e1396.png次のコード スニペットに示すように、コードを追加します。

  void _pushSaved() {
    Navigator.of(context).push(
      MaterialPageRoute<void>(
        // NEW lines from here...
        builder: (BuildContext context) {
          final tiles = _saved.map(
            (WordPair pair) {
              return ListTile(
                title: Text(
                  pair.asPascalCase,
                  style: _biggerFont,
                ),
              );
            },
          );
          final divided = ListTile.divideTiles(
            context: context,
            tiles: tiles,
          ).toList();

          return Scaffold(
            appBar: AppBar(
              title: Text('Saved Suggestions'),
            ),
            body: ListView(children: divided),
          );
        }, // ...to here.
      ),
    );
  }

builder プロパティは、SavedSuggestions という名前の新しいルートのアプリバーを含む Scaffold を返します。新しいルートの本文は、ListTiles 行を含む ListView で構成されています。各行は分割線で区切ります。

b2f84ff91b0e1396.pngアプリをホットリロードします。選択した候補をお気に入りに追加し、アプリバーのリストアイコンをタップします。お気に入りを含む新しいルートが表示されます。ナビゲータにより [戻る] ボタンがアプリバーに追加されます。Navigator.pop を明示的に実装する必要はありませんでした。ホームルートに戻るには、[戻る] ボタンをタップします。

iOS - メインルート

iOS - 保存した候補ルート

トラブルシューティング

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

このステップでは、アプリのテーマを変更します。テーマはアプリの外観を制御します。デフォルトのテーマ(物理デバイスまたはエミュレータによって異なります)を使用することも、ブランドを反映するようにテーマをカスタマイズすることもできます。

アプリのテーマは、ThemeData クラスを設定することで簡単に変更できます。アプリではデフォルトのテーマが使用されていますが、アプリのプライマリ カラーを白に変更します。

b2f84ff91b0e1396.pngMyApp クラスの色を変更します。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Startup Name Generator',
      theme: ThemeData(          // Add the 3 lines from here...
        primaryColor: Colors.white,
      ),                         // ... to here.
      home: RandomWords(),
    );
  }
}

b2f84ff91b0e1396.pngアプリをホットリロードします。背景全体がアプリバーとともに白色に変わります。

演習として、ThemeData を使用して UI の他の部分を変更します。マテリアル ライブラリの Colors クラスには、利用できるさまざまな色の定数が用意されています。ホットリロードにより、UI をすばやく簡単に試すことができます。

Android

iOS

トラブルシューティング

順調に進まなかった場合は、次のリンクのコードを使って、完成したアプリのコードを確認してください。

次の手順で、iOS と Android で動作するインタラクティブな Flutter アプリを作成しました。

  • Dart コードを作成する
  • 開発サイクルの短縮のためにホットリロードを使用する
  • ステートフル ウィジェットを実装し、アプリにインタラクティブな機能を追加する
  • ルートを作成し、ホームルートと新しいルートの間を移動するためのロジックを追加する
  • テーマを使ってアプリの UI の外観を変更する方法を学習する

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

その他のリソース

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