Flutter Widgets — その概要、ウィジェットツリーとbuild

著者: IT Sectr 公開日: 2026-02-21 読了時間: 8 分

Flutter Widgets — Google Flutterフレームワーク(2018年12月に安定版1.0がリリース)におけるユーザーインターフェースの基本的な構成要素です。Flutterでは、スクリーンやボタンからパディングや配置に至るまで、すべてがウィジェットです。ウィジェットはツリー(widget tree)を形成し、Flutterはそれを独自のSkia/Impellerエンジンでレンダリングし、プラットフォームのネイティブコンポーネントをバイパスします。Google I/O(2025)によると、FlutterはGoogle PlayとApp Storeで50万以上のアプリで使用されています。FlutterアーキテクチャはウィジェットをStatelessWidget(不変)とStatefulWidget(Stateオブジェクトによる可変状態)に分類します。

重要なポイント

  • Flutter Widgets — FlutterのすべてのUI要素:スクリーンやボタンからパディングや配置まで。フレームワークはプラットフォームのネイティブコンポーネントを使用しません。
  • StatelessWidget — 可変状態を持たないウィジェット。コンストラクターを介して渡されたデータを表示し、build後は変更されません。
  • StatefulWidget — 別のStateオブジェクトに格納された可変状態を持つウィジェットで、リビルド後も維持されます。
  • ウィジェットツリー(widget tree) — UIを記述するウィジェットの階層構造。FlutterはこれをElement TreeとRender Treeに変換してレンダリングします。
  • BuildContext — ツリー内のウィジェットの位置、テーマ、MediaQuery、InheritedWidgetへのアクセスを提供するビルドコンテキスト。

Flutter Widgetsとは?

Flutter Widgets — FlutterフレームワークにおけるすべてのUI要素で、不変(immutable)なDartクラスとして実装されています。ウィジェットはUI要素の構成です:サイズ、色、位置、イベントハンドラー。Android(XML + View)やiOS(Storyboard + UIView)とは異なり、Flutterはネイティブコンポーネントを使用せず、すべてがCanvas上のSkia(Android/iOS/Linux)またはImpeller(iOS/macOS)エンジンによって描画されます。これにより、プラットフォームによる差異なく、すべてのプラットフォームで一貫した外観が保証されます。Flutterには2つのウィジェットセットが付属しています:Material Widgets(Android/ウェブ向けGoogle Material Design 3)とCupertino Widgets(iOS/macOS向けApple HIG)。開発者は1つのアプリケーションで両方のセットを組み合わせることができます。各ウィジェットにはbuild()メソッドがあり、子ウィジェットまたはそれらの構成を返します — これがウィジェットツリーの構築方法です。

Flutterの3つのツリー

Flutterは3つのツリーで動作します:Widget Tree(開発者が作成する構成)、Element Tree(ツリー内のウィジェットの位置へのマッピング、フレームワークが管理)、Render Tree(レイアウトを計算しペイントを実行するオブジェクト)。WidgetはElementを作成し、ElementはRenderObjectを作成します。構成が変更された(リビルド)場合、Elementは再利用されます — これがFlutterの最適化メカニズムです:ウィジェットの型とkeyが一致する場合、Elementは再作成されず、新しい構成で更新されます。3つのツリーを理解することは、Flutterアプリケーションのパフォーマンス最適化の鍵です。

StatelessWidget:不変ウィジェット

StatelessWidget — 内部状態を保持しないウィジェットです。コンストラクターを介してパラメーターを受け取り、build後に変更する機能なしで表示します。StatelessWidgetは静的な要素に最適です:ヘッダー、アイコン、アバター、固定データのリスト。build()メソッドは作成時に1回、および親ウィジェットが変更されたときに呼び出されます。StatelessWidgetは状態を持たないため、個別のStateオブジェクトは不要です — build()はウィジェットクラスで直接定義されます。

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

class ProfileHeader extends StatelessWidget {
  final String name;
  final String avatarUrl;

  const ProfileHeader({super.key, required this.name, required this.avatarUrl});

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        CircleAvatar(
          radius: 24,
          backgroundImage: NetworkImage(avatarUrl),
        ),
        const SizedBox(width: 12),
        Text(
          name,
          style: const TextStyle(
            fontSize: 18,
            fontWeight: FontWeight.bold,
          ),
        ),
      ],
    );
  }
}

ProfileHeader — コンストラクターを介してnameとavatarUrlを受け取るStatelessWidgetです。CircleAvatarがアバターを表示し、Textが名前を表示します。ウィジェットは作成後にこれらの値を変更できません。constコンストラクター(const ProfileHeader)を使用すると、親のリビルド中にFlutterがウィジェットインスタンスを再利用できます — すべてのStatelessWidgetに推奨されるマイクロ最適化です。

StatefulWidget:状態を持つウィジェット

StatefulWidget — 別のStateオブジェクトに格納された可変状態を持つウィジェットです。StatefulWidget自体はStatelessWidgetと同様に不変です。可変部分はStateに委譲されます:Stateは1回作成され、複数のウィジェットリビルド後も存続するオブジェクトです。状態が変更されると(setState())、FlutterはStateのbuild()を呼び出し、UIを更新します。StatefulWidgetはインタラクティブな要素に使用されます:カウンター、テキストフィールド、チェックボックス、アニメーション、タイマー。

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

class LikeButton extends StatefulWidget {
  const LikeButton({super.key});

  @override
  State<LikeButton> createState() => _LikeButtonState();
}

class _LikeButtonState extends State<LikeButton> {
  bool liked = false;
  int count = 42;

  void _toggleLike() {
    setState(() {
      liked = !liked;
      count += liked ? 1 : -1;
    });
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: _toggleLike,
      child: Text(liked ? '♥ $count' : '♡ $count'),
    );
  }
}

LikeButton — Stateクラス_LikeButtonStateを持つStatefulWidgetです。フィールドlikedとcountはStateに格納された状態で、リビルド時にリセットされません。setState()はFlutterにUIの再構築が必要であることを通知します。Flutterは親のリビルドごとにStateを再作成しません — 構成(keyまたはウィジェット型)が変更された場合のみです。IT Sectrでは、表示にはStatelessWidget、インタラクティブコンポーネントにはStatefulWidgetを使用し、変更の境界までStatefulWidgetを最小化する原則に従っています。

ウィジェットツリー:WidgetからRenderObjectへ

Widget Tree — アプリケーションUIを記述するウィジェットの階層構造です。FlutterはWidget TreeをElement Treeに変換します:各WidgetはElementを作成し、Elementはツリー内の位置とRenderObjectとの接続を追跡します。Render Treeはレイアウト(サイズと位置の決定)とペイント(レンダリング)を実行します。Elementは重要な最適化レイヤーです:リビルド中にウィジェット型とkeyが変更されていない場合、Elementは再利用され、RenderObjectを再作成せずに構成を更新します。これにより、Flutterの高いフレームレート(60/120 FPS)でのパフォーマンスが保証されます。

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Flutterウィジェット')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Icon(Icons.flutter_dash, size: 80),
            const SizedBox(height: 16),
            const Text(
              'こんにちは、Flutter!',
              style: TextStyle(fontSize: 24),
            ),
          ],
        ),
      ),
    );
  }
}

HomeScreenウィジェットツリー:Scaffold → AppBar + Center → Column → [Icon, SizedBox, Text]。ScaffoldはルートのMaterial Designレイアウトウィジェットです。Centerは子のColumnを中央揃えにします。Columnは子を中央揃え(mainAxisAlignment.center)で垂直に配置します。Flutterはレイアウトを上から下(制約は親から渡される)、サイズを下から上(sizedByParentまたは子からのサイジング)に計算します。

BuildContext:ビルドコンテキストとツリーへのアクセス

BuildContext — Element Tree内のウィジェットの位置の記述子です。各build()メソッドはBuildContextを受け取り、ウィジェットはそれを通じて以下にアクセスできます:MediaQuery(画面サイズ、向き、ピクセル密度、セーフエリア)、Theme(MaterialThemeDataまたはCupertinoThemeData)、Navigator(ナビゲーションスタック)、ScaffoldMessenger(SnackBar)、InheritedWidget(ツリーを下ってデータを渡す)。BuildContextはbuild()完了後に保存したり、同期的コンテキスト外で使用したりしてはいけません — これにより“Scaffold.of() called with a context that does not contain a Scaffold”などのエラーが発生します。contextを保存する代わりに、データアクセスにはBuilderウィジェットまたはInheritedWidgetパターンを使用してください。

Key:ツリー内のウィジェット識別

Key — 各ウィジェットのコンストラクターのオプションパラメーター(super.key)です。Keyは、リスト内のアイテムの順序や数が変更されたときに、Flutterがウィジェットを識別するのに役立ちます。Keyがない場合、Flutterはウィジェットを型と位置で比較します。Keyなしでリストの途中にアイテムを追加すると、Flutterは誤ったウィジェットにElementを再利用し、状態のバグ(StatefulWidget)やアニメーションの問題を引き起こします。リストにはValueKey(一意の識別子)、ObjectKey(データオブジェクトに基づく)、UniqueKey(リビルドごとに一意が保証される — 強制再作成用)を使用してください。

Flutterウィジェットカタログ:Material、Cupertino、Layout

Flutter SDK(安定版3.3+、Dart 3+)には200以上の組み込みウィジェットがカテゴリに分かれて含まれています:Layout(Row、Column、Stack、Container、Expanded、Flexible、Wrap、GridView、ListView、CustomScrollView)、Material Design(Scaffold、AppBar、Card、Drawer、BottomNavigationBar、FloatingActionButton、TabBar、Chip、DataTable)、Cupertino(CupertinoPageScaffold、CupertinoNavigationBar、CupertinoButton、CupertinoTabBar)、Painting & Effects(Opacity、DecoratedBox、ClipRect、Transform、BackdropFilter、ShaderMask)、Async(FutureBuilder、StreamBuilder)、Accessibility(Semantics、MergeSemantics、ExcludeSemantics)。レイアウトウィジェットはあらゆるFlutter UIの基盤です:Rowは水平方向の並び、Columnは垂直方向、Stackは重なり合う要素、ContainerとBoxDecorationは装飾用です。

カテゴリ主要ウィジェット目的
LayoutRow, Column, Stack, Container, Expanded, Flexible子要素の配置とサイズ設定
MaterialScaffold, AppBar, Card, Drawer, FAB, TabBarMaterial Design 3 UIコンポーネント
CupertinoCupertinoPageScaffold, CupertinoNavigationBar, CupertinoButtoniOSスタイルのコンポーネント
PaintingOpacity, ClipRect, Transform, DecoratedBox, BackdropFilter視覚効果とクリッピング
AsyncFutureBuilder, StreamBuilderUI状態を伴う非同期データ読み込み
AccessibilitySemantics, MergeSemanticsスクリーンリーダー向けセマンティックマークアップ

よくある質問

StatelessWidgetとStatefulWidgetの違いは何ですか?

StatelessWidgetは内部状態を持ちません — すべてのデータはコンストラクターを介して渡され、変更されません。StatefulWidgetは、リビルド後も存続する個別のStateオブジェクトに可変状態を保存します。状態を変更するとsetState()が呼び出され、FlutterがUIを再構築します。不変の要素にはStatelessWidgetを、インタラクティブな要素にはStatefulWidgetを使用してください。

FlutterのBuildContextとは何ですか?

BuildContextは、build()メソッドに渡されるElement Tree内のウィジェットの位置の記述子です。contextを通じて、ウィジェットはMediaQuery、Theme、Navigator、ScaffoldMessenger、InheritedWidgetにアクセスします。BuildContextはbuild()完了後に保存してはいけません。データアクセスには、BuilderウィジェットまたはInheritedWidgetを使用してください。

Flutterを使うにはDartを知っている必要がありますか?

はい、FlutterはDartを唯一のプログラミング言語として使用します。Dartはnull safety、sound null safety、extension methods、pattern matchingを備えた静的型付け言語です。始めるには、Dartの基本(クラス、関数、async/await、コレクション)で十分です。DartはDart Nativeを介してネイティブコード(AOT)に、Dart2JSを介してJavaScript(ウェブ用)にコンパイルされます。

Flutterはモバイルプラットフォーム専用ですか?

いいえ、Flutterは6つのプラットフォームをサポートしています:iOS、Android、Web、macOS、Windows、Linux。コードの90%以上がすべてのプラットフォームで共有されています。プラットフォーム適応(カメラ、ファイルシステム)はプラグインまたはプラットフォームチャネルを介して実装されます。デスクトップではFlutter Desktopウィンドウシェルを、ウェブではCanvasKit(WebGL)またはHTMLレンダラーを使用します。

Flutterはどのようにして60 FPSのパフォーマンスを実現しますか?

Flutterは60/120 FPSを実現します:独自の2Dエンジン(Skia/Impeller)、ネイティブブリッジの不在(React Nativeとは異なる)、3つのツリー(Widget → Element → Render)による型/keyが変わらない場合のElement再利用、単一方向パス(constraints down、sizes up)のレイアウトアルゴリズム、および画面の個別領域の再描画を分離するRepaintBoundary

まとめ

  • Flutter Widgets — ScaffoldからSizedBoxまですべてのUI要素。FlutterはSkia/Impellerで自己レンダリングし、ネイティブコンポーネントを使用しません。
  • StatelessWidget — 状態を持たないウィジェット。build()は不変のコンストラクターパラメーターに基づいてUIを返します。
  • StatefulWidget — Stateに可変状態を持つウィジェット。setState()がUIのリビルドをトリガーします。
  • Flutterの3つのツリー:Widget(構成)→ Element(位置)→ Render(レイアウト+ペイント)。パフォーマンスのためElementは再利用されます。
  • BuildContext — ツリー内のウィジェットの位置。MediaQuery、Theme、Navigator、InheritedWidgetへのアクセス。
  • MaterialとCupertino — それぞれGoogle Material Design 3とApple HIGのための2つのウィジェットセット。
  • Key(ValueKey、ObjectKey、UniqueKey) — リスト内の順序や数の変更時のウィジェット識別。

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください