Flutter x Riverpod でTextFieldの入力値を送信する

久しぶりの投稿です。
最近、業務でRiverpodを使っている影響でちょっとだけRiverpodのことが分かってきました。
Flutter x Riverpod
Flutter x RiverpodでのTextFieldの入力中の制御について解説します。
まずはサンプルアプリの画面を用意します。
main.dart
import 'package:flutter/material.dart';
void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}
class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('サンプル'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            Row(
              children: [
                Text('メールアドレス'),
                SizedBox(
                  width: 20.0,
                ),
                Expanded(
                  child: TextFormField(
                    enabled: true,
                    style: TextStyle(color: Colors.black),
                    obscureText: false,
                    maxLines: 1,
                  ),
                ),
              ],
            ),
            ElevatedButton(
                onPressed: () {
                  print('送信');
                },
                child: Text('送信')
            )
          ],
        ),
      ),// This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}
ビルドすると次のような画面が表示されます。
テキスト入力とボタンが存在します。

ここからRiverpodをインストールします。
pubspec.yamlを開いてRiverpodをインストールします。
dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
  flutter_riverpod: ^0.14.0+3バージョンは意味なく最新版を指定しています。
今回は、ボタンをタップするとコンソールに入力したメールアドレスが表示される機能を実装します。
メールアドレス用のStateProviderの定義
main.dartに次のコードを定義します。
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
// emailのcontrollerをStateProviderで定義する
final emailControllerStateProvider = StateProvider.autoDispose((ref) {
  return TextEditingController(text: '');
});
void main() {
  runApp(MyApp());
}位置関係を明確にするためにimport文とmain関数の間に宣言しました。
今回はTextEditingControllerを使う形にします。(このために別の処理で泥沼にハマりました。)
RiverpodをWidget全体で使えるようにProviderScopeを定義する
Riverpodを使うための準備はまだあります。
Riverpodを適用させたいウィジェットにProviderScopeで囲う必要があります。
main.dart
void main() {
  runApp(
    ProviderScope(
      child: MyApp(),
    ),
  );
}StatelessWidgetをConsumerWidgetに書き換える
次に定義したStateProviderを監視するためにStatelessWidgetの部分をConsumerWidgetに書き換えます。これでwatch関数が使えるようになります。
class MyHomePage extends ConsumerWidgetメールアドレスの変更を監視するためにbuild関数を次のように変更します。
main.dart
@override
  Widget build(BuildContext context, ScopedReader watch) {
    // 中身は TextEditingController
    final emailControllerProvider = watch(emailControllerStateProvider);
    return Scaffold(
      appBar: AppBar(
        title: Text('サンプル'),
      ),そして、TextFormFieldのcontrollerにこれをセットすればProviderとControllerが連携されます。
main.dart
Expanded(
                  child: TextFormField(
                    controller: emailControllerProvider.state,
                    enabled: true,
                    style: TextStyle(color: Colors.black),
                    obscureText: false,
                    maxLines: 1,
                  ),
                ),あとは、送信ボタンをタップした時に定義したProviderをprintします。
main.dart
ElevatedButton(
                onPressed: () {
                  print(emailControllerProvider.state.text);
                },
                child: Text('送信')
            )これで例えば、メールアドレスのテキスト入力欄に「sample@gmail.com」を入力して送信ボタンをタップするとコンソールに「sample@gmail.com」が表示されます。
Syncing files to device iPhone 12...
flutter: sample@gmail.comこれでRiverpodの基礎的な使い方です。ここから先がどんどん難しくなります。
Riverpod使いだしてまだ日が浅いので、ちょっと変更するにしても色々苦戦します。
全体のソースコード
全体のソースコードはこちらになります。
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
// emailのcontrollerをStateProviderで定義する
final emailControllerStateProvider = StateProvider.autoDispose((ref) {
  return TextEditingController(text: '');
});
void main() {
  runApp(
    ProviderScope(
      child: MyApp(),
    ),
  );
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}
class MyHomePage extends ConsumerWidget {
  const MyHomePage({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context, ScopedReader watch) {
    // 中身は TextEditingController
    final emailControllerProvider = watch(emailControllerStateProvider);
    return Scaffold(
      appBar: AppBar(
        title: Text('サンプル'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            Row(
              children: [
                Text('メールアドレス'),
                SizedBox(
                  width: 20.0,
                ),
                Expanded(
                  child: TextFormField(
                    controller: emailControllerProvider.state,
                    enabled: true,
                    style: TextStyle(color: Colors.black),
                    obscureText: false,
                    maxLines: 1,
                  ),
                ),
              ],
            ),
            ElevatedButton(
                onPressed: () {
                  print(emailControllerProvider.state.text);
                },
                child: Text('送信')
            )
          ],
        ),
      ),// This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}






