Flutter 上級

このセクションでは、Flutter の上級概念を紹介し、開発スキルの向上に役立てます。


レンダリングの原理

Flutter は Skia(3.0 以降は Impeller に段階的に移行)レンダリングエンジンを使用して UI を直接描画し、ネイティブコンポーネントを使用しません。

Widget ツリーと Element ツリー

  • Widget ツリー: UI の設定情報を記述します(不変)
  • Element ツリー: Widget のインスタンス化オブジェクト(可変)
  • RenderObject ツリー: 実際のレンダリングを担当

レンダリングの原理を理解すると、UI パフォーマンスの最適化に役立ち、不要な再構築を避けられます。


InheritedWidget - データ渡し

InheritedWidget は、Widget ツリーに沿ってデータを下方向に渡すことを可能にし、子 Widget は祖先のデータを取得できます。

例:カスタム InheritedWidget

// データコンテナを定義
class MyData extends InheritedWidget {
  final int value;

  const MyData({
    super.key,
    required this.value,
    required super.child,
  });

  // 便利メソッド:最も近い MyData を取得
  static MyData of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MyData>()!;
  }

  // 子 Widget を再構築する必要があるかどうかを判断
  @override
  bool updateShouldNotify(MyData oldWidget) {
    return value != oldWidget.value;
  }
}

// 使用
class ParentWidget extends StatelessWidget {
  const ParentWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return MyData(
      value: 42,
      child: const ChildWidget(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    // 祖先の MyData を取得
    final myData = MyData.of(context);
    return Text('Value: ${myData.value}');
  }
}

RepaintBoundary - 部分再描画

RepaintBoundary を使用すると、再描画領域を制限し、パフォーマンスを向上できます。

例:RepaintBoundary の使用

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

  @override
  Widget build(BuildContext context) {
    return RepaintBoundary(
      child: Container(
        // この領域の描画は外部に影響しません
        color: Colors.red,
        child: const Text('独立領域'),
      ),
    );
  }
}

Keys の深い理解

Keys は Flutter が Widget を識別し、新しい Widget と既存の Widget を区別するのに役立ちます。

ValueKey vs ObjectKey vs UniqueKey

タイプ説明
ValueKey値をキーとして使用(ID、数値、文字列など)
ObjectKeyオブジェクト参照をキーとして使用
UniqueKey作成ごとに一意のキーを生成し、再構築を妨げる

CustomPainter - カスタム描画

CustomPainter を使用すると、完全にカスタマイズされた図形描画を実現できます。

例:CustomPainter の使用

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

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: MyCirclePainter(),
      size: const Size(200, 200),
    );
  }
}

class MyCirclePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    // 円を描画
    final paint = Paint()
      ..color = Colors.blue
      ..style = PaintingStyle.fill;

    final center = Offset(size.width / 2, size.height / 2);
    final radius = size.width / 2 - 10;

    canvas.drawCircle(center, radius, paint);

    // 枠線を描画
    final borderPaint = Paint()
      ..color = Colors.black
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2;

    canvas.drawCircle(center, radius, borderPaint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
}

Stream と Future

例:Stream の使い方

// Stream を作成
Stream<int> countStream(int max) async* {
  for (int i = 1; i <= max; i++) {
    await Future.delayed(const Duration(seconds: 1));
    yield i;  // 値を送信
  }
}

// StreamBuilder を使用
class StreamExample extends StatelessWidget {
  const StreamExample({super.key});

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<int>(
      stream: countStream(10),
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return Text('カウント: ${snapshot.data}');
        } else if (snapshot.hasError) {
          return Text('エラー: ${snapshot.error}');
        }
        return const CircularProgressIndicator();
      },
    );
  }
}

まとめ

この Flutter 入門チュートリアルでは、以下の内容をカバーしています:

  • Flutter のインストールと環境設定
  • Widget の基本(StatelessWidget と StatefulWidget)
  • レイアウトシステム(Row、Column、Stack)
  • ユーザー入力の処理
  • 状態管理(setState、Provider)
  • ネットワークリクエストとデータストレージ
  • ナビゲーションとルーティング
  • テストと公開

さらに深く学ぶには、以下に注目してください:

  • BLoC パターン
  • Riverpod 状態管理
  • GetX ルーティングと状態管理
  • Flutter パフォーマンス最適化
  • Flutter Web とデスクトップ開発
その他の拡張