こんにちは!本記事では、Provider()というウィジェットについて、サンプルコードを用いながら実践的に説明しています。
Provider()とは
Provider()はパッケージの一つです。
Provider()を使うと、リアルタイムで変数を変化させたり、状態管理をすることができます。TextFieldに入れたものを他の画面でリアルタイムで見せたい・・という時などに便利なウィジェットです。
基礎になる構文
Provider()を使うにあたって必要になる構文は、三つあります。
- ChangeNotifierProvider<変数を格納しているクラス名>(create: , child: )
 →- ChangeNotifierのクラス内で- notifyListeners()が宣言された時、変数を変更できるようにするウィジェット
- ChangeNotifier→extendsとしてclassに使うと、- notifyListeners()が使えるようになる
- notifyListeners()→これを使うと、- ChangeNotifierProvider内にあるウィジェットの変数を更新させる
- Provider.of<変数を格納しているクラス名>(context)→変数を呼び出す際に使用する
使い方としては下記のような感じです。変数を変化させるときにnotifyListeners()を含めることで、リアルタイムで他のクラスにある変数を変化させることができます。
ChangeNotifierProvider<K>(
 create: (context) => K() ,
 child: Page(),
)
class Page extends StatelessWidget{
@override
Widget build(BuildContext context){
return Text(Provider.of<K>(context).k);   //text
}
}
class K extends ChangeNotifier {
String k = 'text';
void changeK(String newText){
k = newText;
notifyListeners();
}
}説明だけだと難しいので、実際に実装してみましょう。
使い方チュートリアル
実際にProvider()を使ってアプリを実装してみましょう。
今回作成するのは左の動画のように、上からAppBar()、TextField()、Text()を設置したデザインです。
TextField()で入力された文字はK()というクラスに格納され、入力文字が変化したと同時にAppBar()、Text()にも反映されます。
ページのデザイン構成は以下の通りです。MyApp()の下にPage1()があり、Page1()の中にColumn()としてPage2, Page3が格納されています。

Provider()をインポートする
公式ページを参考に、パッケージをインポートしましょう。(flutter pub add providerです。)
パッケージのインポート方法がわからない場合は以下の記事を参考にしてください。

変数を格納するクラスKの作成
変数kを格納するクラスKを作成しましょう。ChangeNotifierと設定するのを忘れずに。
class K extends ChangeNotifier {
  String k = 'text';
}MyApp()での設定
下に、MyApp()付近のコードを示します。MaterialAppの中でChangeNotifierProvider<K>を作成します。ChangeNotifierProvider<K>はcreate:とchild:が必要なので、以下のように宣言しましょう。これで、childで宣言されているウィジェット内はリアルタイムで変数が変わるように、常に準備している状態になります。
void main() {
  runApp(const MyApp());
}
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: ChangeNotifierProvider<K>(
          create: (context) => K(),
          child:  Page1() ,
          ),
      );
  }
}Page1()の作成
Page1は以下のように作成します。わかりやすいようにピンクの背景色をつけました。
AppBarのtitleにクラスK内にある変数のkを使用しています。さらにそのbodyでは、TextField()を格納しているPage2()、Text()を格納しているPage3()を配置します。
class Page1 extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return  Scaffold(
      backgroundColor: Colors.pink.shade100,
      appBar: AppBar(
        title: Text(Provider.of<K>(context).k)
      ),
      body: Column(
          children: [Page2(), Page3()]),
    );
  }
}Page2()の作成
Page2では、TextFieldを配置します。TextField内の変数が変化したとき(onChanged)、kをその変数にした後、notifyListeners()を呼び出します。これを忘れるとKクラス内にあるk自体の値は変化しますが、AppBar()内やText()内にある変数に関しては、リアルタイムで変化しません。notifyListeners()が各変数に変数が変わったからそっちも変えてね〜って呼びかけているようなイメージです。
class Page2 extends StatelessWidget{
  @override
  Widget build(BuildContext context){
    return TextField(
      cursorColor: Colors.white,
      onChanged: (newText){
        Provider.of<K>(context, listen: false).k = newText;
        Provider.of<K>(context, listen: false).notifyListeners();
      },
    );
  }
}Page3()の作成
Page3()でもText()を配置しましょう。
class Page3 extends StatelessWidget{
  @override
  Widget build(BuildContext context){
    return Text(
        Provider.of<K>(context, listen: false).k,
    style: TextStyle(
      backgroundColor: Colors.yellow.shade200
    ),);
  }
}TextFieldに文字を打って、変数が変わることを確認しよう
ここまで実装できたら、実際に実行してTextField内で文字を打ってみましょう。AppBar()の文字とText()の文字がリアルタイムで変化すると思います。
*全てのコードは、この記事の最後に載せておきます。
エラーが出た場合の対処方法
“Could not find the correct Provider<> above this Widget.”というエラーが出た場合は、ChangeNotifierProvider<K>をさらに上位のクラスに置くようにしてください。

関連記事
Flutter全般の記事まとめ

今回作成したコード全文
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
  runApp(const MyApp());
}
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: ChangeNotifierProvider<K>(
          create: (context) => K(),
          child:  Page1() ,
          ),
      );
  }
}
class Page1 extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return  Scaffold(
      backgroundColor: Colors.pink.shade100,
      appBar: AppBar(
        title: Text(Provider.of<K>(context).k)
      ),
      body: Column(
          children: [Page2(), Page3()]),
    );
  }
}
class Page2 extends StatelessWidget{
  @override
  Widget build(BuildContext context){
    return TextField(
      cursorColor: Colors.white,
      onChanged: (newText){
        Provider.of<K>(context, listen: false).k = newText;
        Provider.of<K>(context, listen: false).notifyListeners();
        //print(Provider.of<K>(context, listen: false).k);
      },
    );
  }
}
class Page3 extends StatelessWidget{
  @override
  Widget build(BuildContext context){
    return Text(
        Provider.of<K>(context, listen: false).k,
    style: TextStyle(
      backgroundColor: Colors.yellow.shade200
    ),);
  }
}
class K extends ChangeNotifier {
  String k = 'text';
}
コメント