T-CREATOR

Flutter ウィジェット早見表:レイアウト・入力・ナビゲーションを 1 枚で把握

Flutter ウィジェット早見表:レイアウト・入力・ナビゲーションを 1 枚で把握

Flutter アプリケーション開発では、数多くのウィジェットを組み合わせて UI を構築していきます。しかし、初心者の方にとっては「どのウィジェットをどんな場面で使えばいいのか」を把握するのが難しいと感じることもあるでしょう。本記事では、レイアウト・入力・ナビゲーションに特化した主要ウィジェットを、1 枚の早見表にまとめてご紹介します。

それぞれのウィジェットの役割や使用例を理解することで、開発効率が大幅に向上し、より直感的に UI を設計できるようになるでしょう。

レイアウトウィジェット早見表

以下の表は、Flutter でよく使用されるレイアウトウィジェットをまとめたものです。

#ウィジェット名主な用途配置方向特徴
1Container単一の子要素を装飾・配置-パディング、マージン、背景色、境界線などを設定可能
2Column複数の子要素を縦方向に配置垂直方向のリスト表示に最適
3Row複数の子要素を横方向に配置水平方向のリスト表示に最適
4Stack子要素を重ねて配置重ね合わせ画像の上にテキストを配置するなどに使用
5Expanded親要素の余白を埋めるように拡張-Column/Row 内で比率に基づいた配置が可能
6Flexible柔軟にサイズ調整可能な配置-子要素のサイズを柔軟に変更
7Padding子要素に余白を追加-内側の余白を設定
8Center子要素を中央に配置-縦横中央揃えに使用
9Align子要素を任意の位置に配置-上下左右の任意の位置に配置可能
10SizedBox固定サイズのボックス-余白の作成やサイズ制限に使用
11Wrap子要素を折り返して配置自動折り返しタグやチップの表示に最適
12ListViewスクロール可能なリスト縦/横動的なリスト表示に使用
13GridViewグリッド状に配置グリッド画像ギャラリーなどに最適

入力ウィジェット早見表

ユーザーからの入力を受け付けるウィジェットの一覧です。

#ウィジェット名主な用途入力タイプ特徴
1TextFieldテキスト入力テキスト単一行・複数行のテキスト入力に対応
2TextFormFieldフォームでのテキスト入力テキストバリデーション機能を持つテキスト入力
3Checkboxチェックボックス真偽値オン/オフの二択選択
4Radioラジオボタン選択肢複数の選択肢から一つを選択
5Switchスイッチ真偽値トグル式のオン/オフ切り替え
6Sliderスライダー数値範囲内の数値を選択
7DropdownButtonドロップダウンメニュー選択肢リストから一つを選択
8DatePicker日付選択日付カレンダーから日付を選択
9TimePicker時刻選択時刻時刻を選択
10Formフォーム全体の管理-複数の入力フィールドをグループ化

ナビゲーションウィジェット早見表

画面遷移やナビゲーションに関連するウィジェットの一覧です。

#ウィジェット名主な用途遷移タイプ特徴
1Navigator画面遷移の管理プッシュ/ポップスタック構造で画面を管理
2MaterialPageRouteMaterial デザインの画面遷移プッシュアニメーション付き画面遷移
3BottomNavigationBar下部ナビゲーションバータブ切り替え3〜5 個のタブで画面切り替え
4TabBarタブバータブ切り替え上部タブでコンテンツ切り替え
5Drawerサイドメニュースライドメニュー左右からスライドするメニュー
6AppBarアプリバー-画面上部のヘッダー
7PageViewスワイプで画面切り替えスワイプ横スワイプでページ遷移
8CupertinoNavigationBariOS スタイルのナビゲーションバー-iOS 風のヘッダー
9CupertinoTabBariOS スタイルのタブバータブ切り替えiOS 風の下部タブバー

背景

Flutter は Google が開発したクロスプラットフォーム開発フレームワークで、単一のコードベースから iOS、Android、Web、デスクトップアプリケーションを構築できます。

Flutter の特徴は、すべてがウィジェットで構成されていることです。画面に表示されるあらゆる要素、レイアウト、装飾、アニメーションに至るまで、すべてがウィジェットとして実装されています。

mermaidflowchart TB
  app["Flutter アプリ"] --> ui["UI レイヤー"]
  ui --> layout["レイアウト<br/>ウィジェット"]
  ui --> input["入力<br/>ウィジェット"]
  ui --> navi["ナビゲーション<br/>ウィジェット"]
  layout --> column["Column"]
  layout --> row["Row"]
  layout --> stack["Stack"]
  input --> textfield["TextField"]
  input --> checkbox["Checkbox"]
  input --> slider["Slider"]
  navi --> navigator["Navigator"]
  navi --> bottomnav["BottomNavigationBar"]
  navi --> drawer["Drawer"]

上記の図は、Flutter アプリの UI がどのようにウィジェットで構成されているかを示しています。レイアウト、入力、ナビゲーションの 3 つのカテゴリに分類され、それぞれが特定の役割を担っています。

Flutter におけるウィジェットの役割

Flutter では、ウィジェットツリーと呼ばれる階層構造で UI を構築します。親ウィジェットの中に子ウィジェットを配置し、それを繰り返すことで複雑な UI を実現します。

この仕組みにより、以下のようなメリットが得られます。

  • 再利用性の高さ: 一度作成したウィジェットを複数の場所で再利用できます
  • 保守性の向上: コンポーネント単位で管理できるため、変更が容易です
  • カスタマイズの柔軟性: 既存のウィジェットを組み合わせて、独自のウィジェットを作成できます

しかし、Flutter には数百種類ものウィジェットが用意されているため、初学者にとっては「どのウィジェットを選べば良いか」が大きな課題となるでしょう。

課題

Flutter 開発において、多くの開発者が直面する課題は以下の通りです。

ウィジェット選択の難しさ

Flutter には膨大な数のウィジェットが存在し、似たような機能を持つウィジェットも多数あります。例えば、テキスト入力には TextFieldTextFormField があり、それぞれ異なる用途に最適化されています。

初心者の方は、これらの違いを理解せずに使用してしまい、後から問題が発生することがあります。

レイアウト構築の複雑さ

レイアウトを構築する際、ColumnRowStackExpandedFlexible などのウィジェットを適切に組み合わせる必要があります。これらの使い分けができないと、期待通りのレイアウトを実現できません。

特に、ExpandedFlexible の違いや、mainAxisAlignmentcrossAxisAlignment の使い分けは、初学者にとって理解が難しい部分です。

ナビゲーション実装の迷い

画面遷移やナビゲーションの実装では、NavigatorBottomNavigationBarDrawerTabBar など、複数の選択肢があります。アプリの要件に応じて適切なナビゲーション方法を選択する必要がありますが、どれを選べば良いか迷うことが多いでしょう。

mermaidflowchart LR
  dev["開発者"] -->|悩み| choice{"ウィジェット<br/>選択"}
  choice -->|多すぎる| problem1["選択肢が<br/>多すぎる"]
  choice -->|違いが不明| problem2["類似ウィジェット<br/>の違いが不明"]
  choice -->|使い分け| problem3["レイアウト<br/>組み合わせが複雑"]
  problem1 --> result["開発効率<br/>低下"]
  problem2 --> result
  problem3 --> result

この図は、開発者がウィジェット選択で直面する主な課題を示しています。選択肢の多さ、類似ウィジェットの違いの不明瞭さ、レイアウト組み合わせの複雑さが、開発効率の低下につながります。

解決策

上記の課題を解決するためには、以下のアプローチが効果的です。

カテゴリ別にウィジェットを整理する

ウィジェットをレイアウト入力ナビゲーションの 3 つのカテゴリに分類することで、目的に応じて必要なウィジェットを素早く見つけることができます。

本記事の冒頭で示した早見表は、この分類に基づいて作成されています。開発時にこの表を参照することで、適切なウィジェットを迅速に選択できるでしょう。

主要ウィジェットの特徴を理解する

各ウィジェットの主な用途特徴を把握することが重要です。例えば、以下のような理解が必要です。

  • Column は縦方向に子要素を配置する
  • Row は横方向に子要素を配置する
  • Stack は要素を重ねて配置する
  • TextField は単純なテキスト入力に使用する
  • TextFormField はバリデーション機能が必要な場合に使用する

サンプルコードで実践的に学ぶ

理論だけでなく、実際にコードを書いて動作を確認することが最も効果的な学習方法です。次のセクションでは、各カテゴリの代表的なウィジェットを使用した具体例をご紹介します。

mermaidflowchart TB
  start["ウィジェット<br/>選択開始"] --> category{"カテゴリ<br/>選択"}
  category -->|画面構成| layout["レイアウト<br/>ウィジェット"]
  category -->|ユーザー入力| input["入力<br/>ウィジェット"]
  category -->|画面遷移| navi["ナビゲーション<br/>ウィジェット"]
  layout --> check1{"目的に<br/>合致?"}
  input --> check2{"目的に<br/>合致?"}
  navi --> check3{"目的に<br/>合致?"}
  check1 -->|Yes| implement["実装"]
  check2 -->|Yes| implement
  check3 -->|Yes| implement
  check1 -->|No| category
  check2 -->|No| category
  check3 -->|No| category
  implement --> done["完成"]

この図は、ウィジェット選択のフローを示しています。まずカテゴリを選択し、目的に合致するウィジェットを見つけるまで探索を続けます。

図で理解できる要点:

  • ウィジェット選択は 3 つのカテゴリから開始する
  • 目的に合致しない場合は再度カテゴリ選択に戻る
  • 適切なウィジェットが見つかれば実装フェーズへ進む

具体例

それでは、各カテゴリの代表的なウィジェットを使用した具体的なコード例を見ていきましょう。

レイアウトウィジェットの実装例

Column と Row の基本的な使い方

ColumnRow は最も基本的なレイアウトウィジェットです。以下のコードは、縦方向と横方向に要素を配置する例を示しています。

dartimport 'package:flutter/material.dart';

まず、Flutter の基本パッケージをインポートします。

dartclass LayoutExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('レイアウト例'),
      ),
      body: Column(
        children: [
          // 縦方向に要素を配置
          Container(
            color: Colors.blue,
            height: 100,
            child: Center(child: Text('ヘッダー')),
          ),
          // 横方向に要素を配置
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              Container(
                color: Colors.red,
                width: 100,
                height: 100,
                child: Center(child: Text('左')),
              ),
              Container(
                color: Colors.green,
                width: 100,
                height: 100,
                child: Center(child: Text('中央')),
              ),
              Container(
                color: Colors.orange,
                width: 100,
                height: 100,
                child: Center(child: Text('右')),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

この例では、Column で縦方向にヘッダーとコンテンツエリアを配置し、コンテンツエリア内で Row を使って 3 つのボックスを横並びにしています。

mainAxisAlignment プロパティに spaceEvenly を指定することで、各要素が均等に配置されます。

Stack を使った重ね合わせレイアウト

Stack を使用すると、要素を重ねて配置できます。画像の上にテキストを配置する場合などに便利です。

dartclass StackExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        // 背景画像
        Container(
          width: 300,
          height: 200,
          color: Colors.grey,
        ),
        // 重ねて表示するテキスト
        Positioned(
          bottom: 20,
          right: 20,
          child: Container(
            padding: EdgeInsets.all(8),
            color: Colors.black54,
            child: Text(
              '画像の説明',
              style: TextStyle(color: Colors.white),
            ),
          ),
        ),
      ],
    );
  }
}

Positioned ウィジェットを使用することで、Stack 内の要素の配置位置を細かく制御できます。この例では、右下にテキストを配置しています。

Expanded を使った柔軟なレイアウト

Expanded を使用すると、利用可能なスペースを自動的に埋めることができます。

dartclass ExpandedExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        // 固定幅のコンテナ
        Container(
          width: 100,
          color: Colors.red,
          child: Center(child: Text('固定')),
        ),
        // 残りのスペースを埋めるコンテナ
        Expanded(
          child: Container(
            color: Colors.blue,
            child: Center(child: Text('拡張')),
          ),
        ),
        // 固定幅のコンテナ
        Container(
          width: 100,
          color: Colors.green,
          child: Center(child: Text('固定')),
        ),
      ],
    );
  }
}

この例では、左右に固定幅のコンテナを配置し、中央の Expanded ウィジェットが残りのスペースを自動的に埋めます。画面サイズが変わっても、レイアウトが適切に調整されるでしょう。

入力ウィジェットの実装例

TextField によるテキスト入力

TextField は最も基本的なテキスト入力ウィジェットです。

dartclass TextFieldExample extends StatefulWidget {
  @override
  _TextFieldExampleState createState() => _TextFieldExampleState();
}

class _TextFieldExampleState extends State<TextFieldExample> {
  // テキスト入力の内容を管理するコントローラー
  final TextEditingController _controller = TextEditingController();

  @override
  void dispose() {
    // コントローラーの破棄
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        TextField(
          controller: _controller,
          decoration: InputDecoration(
            labelText: 'ユーザー名',
            hintText: '名前を入力してください',
            border: OutlineInputBorder(),
          ),
        ),
        SizedBox(height: 20),
        ElevatedButton(
          onPressed: () {
            // 入力内容を取得
            print('入力された値: ${_controller.text}');
          },
          child: Text('送信'),
        ),
      ],
    );
  }
}

TextEditingController を使用することで、入力内容の取得や変更が簡単に行えます。decoration プロパティでラベルやヒントテキスト、ボーダーなどの装飾を設定できます。

Form と TextFormField によるバリデーション

フォーム全体を管理し、バリデーションを行う場合は FormTextFormField を組み合わせます。

dartclass FormExample extends StatefulWidget {
  @override
  _FormExampleState createState() => _FormExampleState();
}

class _FormExampleState extends State<FormExample> {
  // フォームの状態を管理するキー
  final _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        children: [
          TextFormField(
            decoration: InputDecoration(labelText: 'メールアドレス'),
            // バリデーション処理
            validator: (value) {
              if (value == null || value.isEmpty) {
                return 'メールアドレスを入力してください';
              }
              if (!value.contains('@')) {
                return '有効なメールアドレスを入力してください';
              }
              return null;
            },
          ),
          SizedBox(height: 20),
          ElevatedButton(
            onPressed: () {
              // フォームのバリデーションを実行
              if (_formKey.currentState!.validate()) {
                print('バリデーション成功');
              }
            },
            child: Text('送信'),
          ),
        ],
      ),
    );
  }
}

validator プロパティで入力内容の検証ロジックを定義します。_formKey.currentState!.validate() を呼び出すことで、すべてのフィールドのバリデーションを一度に実行できます。

Checkbox、Radio、Switch の使用例

選択式の入力ウィジェットの使用例を見てみましょう。

dartclass SelectionExample extends StatefulWidget {
  @override
  _SelectionExampleState createState() => _SelectionExampleState();
}

class _SelectionExampleState extends State<SelectionExample> {
  // チェックボックスの状態
  bool _checkboxValue = false;
  // ラジオボタンの選択値
  int _radioValue = 1;
  // スイッチの状態
  bool _switchValue = false;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // チェックボックス
        CheckboxListTile(
          title: Text('利用規約に同意する'),
          value: _checkboxValue,
          onChanged: (bool? value) {
            setState(() {
              _checkboxValue = value ?? false;
            });
          },
        ),
        // ラジオボタン
        RadioListTile<int>(
          title: Text('オプション 1'),
          value: 1,
          groupValue: _radioValue,
          onChanged: (int? value) {
            setState(() {
              _radioValue = value ?? 1;
            });
          },
        ),
        RadioListTile<int>(
          title: Text('オプション 2'),
          value: 2,
          groupValue: _radioValue,
          onChanged: (int? value) {
            setState(() {
              _radioValue = value ?? 2;
            });
          },
        ),
        // スイッチ
        SwitchListTile(
          title: Text('通知を有効にする'),
          value: _switchValue,
          onChanged: (bool value) {
            setState(() {
              _switchValue = value;
            });
          },
        ),
      ],
    );
  }
}

CheckboxListTileRadioListTileSwitchListTile を使用することで、タイトル付きの選択式入力が簡単に実装できます。setState を呼び出すことで、UI が自動的に更新されます。

ナビゲーションウィジェットの実装例

Navigator を使用した基本的な画面遷移の例です。

dart// 最初の画面
class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('最初の画面')),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            // 次の画面へ遷移
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => SecondScreen()),
            );
          },
          child: Text('次の画面へ'),
        ),
      ),
    );
  }
}

Navigator.push メソッドで新しい画面をスタックにプッシュします。MaterialPageRoute は Material Design のアニメーション付き画面遷移を提供します。

dart// 2番目の画面
class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('2番目の画面')),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            // 前の画面へ戻る
            Navigator.pop(context);
          },
          child: Text('戻る'),
        ),
      ),
    );
  }
}

Navigator.pop メソッドで現在の画面をスタックから取り除き、前の画面に戻ります。AppBar の戻るボタンも自動的に表示されます。

BottomNavigationBar の実装

下部ナビゲーションバーを使用したタブ切り替えの例です。

dartclass BottomNavExample extends StatefulWidget {
  @override
  _BottomNavExampleState createState() => _BottomNavExampleState();
}

class _BottomNavExampleState extends State<BottomNavExample> {
  // 現在選択されているタブのインデックス
  int _selectedIndex = 0;

  // 各タブで表示する画面のリスト
  final List<Widget> _pages = [
    Center(child: Text('ホーム画面')),
    Center(child: Text('検索画面')),
    Center(child: Text('設定画面')),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('タブナビゲーション')),
      body: _pages[_selectedIndex],
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _selectedIndex,
        onTap: (int index) {
          setState(() {
            _selectedIndex = index;
          });
        },
        items: const [
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'ホーム',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.search),
            label: '検索',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.settings),
            label: '設定',
          ),
        ],
      ),
    );
  }
}

BottomNavigationBarcurrentIndex プロパティで現在のタブを管理し、onTap コールバックでタブの切り替えを処理します。タブに応じて表示する画面を切り替えることができます。

Drawer によるサイドメニュー

サイドメニューを実装する例を見てみましょう。

dartclass DrawerExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Drawer 例'),
      ),
      // サイドメニューの実装
      drawer: Drawer(
        child: ListView(
          padding: EdgeInsets.zero,
          children: [
            // ヘッダー部分
            DrawerHeader(
              decoration: BoxDecoration(
                color: Colors.blue,
              ),
              child: Text(
                'メニュー',
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 24,
                ),
              ),
            ),
            // メニュー項目
            ListTile(
              leading: Icon(Icons.home),
              title: Text('ホーム'),
              onTap: () {
                Navigator.pop(context);
                // ホーム画面への遷移処理
              },
            ),
            ListTile(
              leading: Icon(Icons.settings),
              title: Text('設定'),
              onTap: () {
                Navigator.pop(context);
                // 設定画面への遷移処理
              },
            ),
            ListTile(
              leading: Icon(Icons.info),
              title: Text('情報'),
              onTap: () {
                Navigator.pop(context);
                // 情報画面への遷移処理
              },
            ),
          ],
        ),
      ),
      body: Center(
        child: Text('メイン画面'),
      ),
    );
  }
}

Scaffolddrawer プロパティに Drawer ウィジェットを設定することで、左からスライドするメニューが実装できます。AppBar のハンバーガーメニューアイコンも自動的に表示されるでしょう。

mermaidflowchart LR
  user["ユーザー"] -->|操作| nav["ナビゲーション<br/>選択"]
  nav -->|画面遷移| push["Navigator.push"]
  nav -->|タブ切り替え| bottom["BottomNavigationBar"]
  nav -->|メニュー表示| drawer["Drawer"]
  push --> screen["新しい画面"]
  bottom --> tab["タブ画面"]
  drawer --> menu["メニュー項目"]
  screen -->|Navigator.pop| nav
  tab -->|タップ| nav
  menu -->|タップ| nav

この図は、ナビゲーションの基本的なフローを示しています。ユーザーの操作に応じて、画面遷移、タブ切り替え、メニュー表示のいずれかが実行され、再びナビゲーション選択に戻ります。

図で理解できる要点:

  • ナビゲーションは大きく 3 つのパターンに分類される
  • 各ナビゲーション方法は独自の UI パターンを持つ
  • すべてのナビゲーションは循環的なフローで機能する

まとめ

本記事では、Flutter の主要ウィジェットをレイアウト入力ナビゲーションの 3 つのカテゴリに分けてご紹介しました。

早見表を活用することで、開発時に必要なウィジェットを素早く見つけることができるでしょう。また、各ウィジェットの具体的な使用例を通して、実装方法も理解できたのではないでしょうか。

Flutter 開発では、これらのウィジェットを組み合わせることで、複雑な UI を構築していきます。最初はシンプルなレイアウトから始めて、徐々に複雑な構成に挑戦していくことをおすすめします。

ウィジェットの選択に迷ったときは、本記事の早見表に戻ってきて、目的に応じた最適なウィジェットを見つけてください。継続的に学習を進めることで、より効率的で美しい UI を実装できるようになるでしょう。

関連リンク