Flutter: 親から子ウィジェット、子から親ウィジェットへのアクセスの仕方

Flutter: 親から子ウィジェット、子から親ウィジェットへのアクセスの仕方

August 13, 2024

親から子のフィールドにアクセス #

GlobalKey を使う #

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: MyParentWidget(),
  ));
}

class MyParentWidget extends StatelessWidget {
  MyParentWidget({super.key});

  final myChildWidgetKey = GlobalKey<MyChildWidgetState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          Text('${myChildWidgetKey.currentState?.count ?? 0}'),
          IconButton(
            icon: const Icon(Icons.add),
            onPressed: () {
              myChildWidgetKey.currentState?.increment();
              // myChildWidgetKey.currentState?.increment(); を呼び出しても、MyParentWidget 自身は再ビルドされず、
              // myChildWidgetKey.currentState?.count の更新が画面に反映されない
              // MyParentWidget 自身を再ビルドするために markNeedsBuild(); を呼び出す
              (context as Element).markNeedsBuild();
            },
          ),
          MyChildWidget(
            key: myChildWidgetKey,
          ),
        ],
      ),
    );
  }
}

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

  @override
  State<MyChildWidget> createState() => MyChildWidgetState();
}

class MyChildWidgetState extends State<MyChildWidget> {
  int count = 0;

  void increment() {
    setState(() {
      count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return const SizedBox();
  }
}

子から親のフィールドにアクセス #

親からプロップスを受け取る #

import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(
    home: MyParentWidget(),
  ));
}

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

  @override
  State<MyParentWidget> createState() => _MyParentWidgetState();
}

class _MyParentWidgetState extends State<MyParentWidget> {
  int _count = 0;

  void _increment() {
    setState(() {
      _count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MyChildWidget(
      count: _count,
      increment: _increment,
    );
  }
}

class MyChildWidget extends StatelessWidget {
  const MyChildWidget({
    super.key,
    required this.count,
    required this.increment,
  });

  final int count;
  final VoidCallback increment;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          Text('$count'),
          IconButton(
            icon: const Icon(Icons.add),
            onPressed: increment,
          ),
        ],
      ),
    );
  }
}

InheritedWidget を使う #

import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(
    home: MyParentWidget(),
  ));
}

class MyInheritedWidget extends InheritedWidget {
  const MyInheritedWidget({
    super.key,
    required this.count,
    required this.increment,
    required super.child,
  });

  final int count;
  final VoidCallback increment;

  @override
  bool updateShouldNotify(MyInheritedWidget oldWidget) {
    return true;
  }

  static MyInheritedWidget of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>()!;
  }
}

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

  @override
  State<MyParentWidget> createState() => _MyParentWidgetState();
}

class _MyParentWidgetState extends State<MyParentWidget> {
  int _count = 0;

  void _increment() {
    setState(() {
      _count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MyInheritedWidget(
      count: _count,
      increment: _increment,
      child: const MyChildWidget(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    final counter = MyInheritedWidget.of(context);

    return Scaffold(
      body: Column(
        children: [
          Text('${counter.count}'),
          IconButton(
            icon: const Icon(Icons.add),
            onPressed: counter.increment,
          ),
        ],
      ),
    );
  }
}

findAncestorStateOfType で親にアクセスする #

import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(
    home: MyParentWidget(),
  ));
}

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

  @override
  State<MyParentWidget> createState() => MyParentWidgetState();
}

class MyParentWidgetState extends State<MyParentWidget> {
  int count = 0;

  void increment() {
    setState(() {
      count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // const をつけると count が変わっても再ビルドされないため const を外す
    // ignore: prefer_const_constructors
    return MyChildWidget();
  }
}

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

  @override
  Widget build(BuildContext context) {
    final state = context.findAncestorStateOfType<MyParentWidgetState>()!;

    return Scaffold(
      body: Column(
        children: [
          Text('${state.count}'),
          IconButton(
            icon: const Icon(Icons.add),
            onPressed: state.increment,
          ),
        ],
      ),
    );
  }
}

GlobalKey を使う #

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: MyParentWidget(),
  ));
}

final myParentWidgetKey = GlobalKey<MyParentWidgetState>();

class MyParentWidget extends StatefulWidget {
  MyParentWidget() : super(key: myParentWidgetKey);

  @override
  State<MyParentWidget> createState() => MyParentWidgetState();
}

class MyParentWidgetState extends State<MyParentWidget> {
  int count = 0;

  void increment() {
    setState(() {
      count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // const をつけると count が変わっても再ビルドされないため const を外す
    // ignore: prefer_const_constructors
    return MyChildWidget();
  }
}

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

  @override
  Widget build(BuildContext context) {
    final state = myParentWidgetKey.currentState!;

    return Scaffold(
      body: Column(
        children: [
          Text('${state.count}'),
          IconButton(
            icon: const Icon(Icons.add),
            onPressed: state.increment,
          ),
        ],
      ),
    );
  }
}

その他参考 #