Flutterやってみたよ part5 （状態管理導入 ChangeNotifierProvider編 ）

# はじめに

[前回のRedux導入](https://qiita.com/190yamashita/items/49ea447a4eb839a43059)のアプリにChangeNotifierProviderを使った状態管理を導入します。
終わった後の感想ですが、これが本命になりそうな予感です！
完全に独学なので間違いありましたらご指定いただけたら幸いです🙇‍♂️

# ChangeNotifierProviderについて
以前はGoogleはBLoc推しだったと思うのですが、いつの間にかChangeNotifierProvider推しに変わっているみたいです。
例によって良記事がたくさんあるので詳細は割愛します🙇‍♂️

# ソース

では早速SingInScreenでソースをみていきます。
みてもらえればわかると思いますが、とってもシンプルでわかりやすいです。

説明はいいから早くソースが必要って方は[こちら](https://github.com/190yamayama/flutter_changeNotifier_provider_sample)
に全ソースがあります。

画面のソース

```dart

class SignInScreen extends StatefulWidget {
  SignInScreen({Key key}) : super(key: key);

  static final formKey = new GlobalKey<FormState>();

  @override
  _SignInScreenState createState() => _SignInScreenState();
}

class _SignInScreenState extends State<SignInScreen> {

  @override
  Widget build(BuildContext context) {
    // ChangeNotifierProviderで囲む
    // 複数の状態クラスが必要な場合はMultiProviderで囲む
    return ChangeNotifierProvider(
      create: (context) => SignInScreenViewModel(),
      child: SignInScreenPage(),
    );
  }

}

// 状態管理と分離しているのでStatelessWidgetでOK
class SignInScreenPage extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Sign In Screen"),
      ),
      body: new Center(
        child: new SingleChildScrollView(
          padding: const EdgeInsets.symmetric(horizontal: 16.0),
          child: new Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget> [
              // 省略・・画面のコンテンツ・・
            ],
          ),
        ),
      ),
    );
  }

  Widget emailText(BuildContext context) {
    return padded(child: new TextFormField(
      key: new Key("email"),
      decoration: new InputDecoration(labelText: "Email"),
      autocorrect: false,
      validator: (val) => val.isEmpty ? "emailを入力してください" : null,
      onSaved: (val) => {
        // readは状態監視を必要としないアクセスに使う
        context.read<SignInScreenViewModel>().setEmail(val)
      },
    ));
  }

  Widget hintText(BuildContext context) {
    return new Container(
      //height: 80.0,
      padding: const EdgeInsets.all(32.0),
      child: new Text(
          // watchやselectは状態監視を必要とするアクセスに使う
          // ここはエラーメッセージを表示するところなのでselectで変わったら随時テキストを反映させている
          context.select((SignInScreenViewModel viewModel) => viewModel.errorMessage()),
          key: new Key("hint"),
          style: new TextStyle(fontSize: 18.0, color: Colors.grey),
          textAlign: TextAlign.center
      ),
    );
  }

}
```

ViewModelのソース

```dart

// with句でmixinの実装を引き継ぐ、もしくはextendsで継承する。
class SignInScreenViewModel with ChangeNotifier {

  Authentication _authentication;
  AuthenticationRepository _repository = AuthenticationRepository();

  String _email = "";
  String _password = "";

  SignInScreenViewModel([AuthenticationRepository repository]) {
    _repository = repository ?? AuthenticationRepository();
    _email = "";
    _password = "";
  }

  void setEmail(String val) {
    _email = val;
  }

  void setPassword(String val) {
    _password = val;
  }

  String errorMessage() {
    return _authentication?.errorMessage ?? "";
  }

  void signIn(BuildContext context) {
    _repository.signIn(_email, _password)
        .then((value) {
          _authentication = value;
          // notifyListenersを使用することでwidgetに状態変更が通知される
          // これでselectやwatchで使ってるwidgetがあれば更新される
          notifyListeners();
          if (_authentication.authStatus == AuthStatus.signedIn) {
            // 成功したときの遷移処理とかをかく
          }
        });
  }

}
```
たったのこれだけで状態管理を分離できてしかもビジネスロジックも分離できました！

# 終わりに
導入してみてこれが一番わかりやすいし、ソースコードも短いので一番お気に入りです。
他にもまだ試していないのありますし、プロジェクトの要件にもよりますがChangeNotifierProviderで十分こと足りそうな気がします！
完成したサンプルコードは[こちら](https://github.com/190yamayama/flutter_changeNotifier_provider_sample)です。

ChangeNotifierProviderの作者の方がフック型のRiverpodってのも作っているみたいなので、今後はそっちの状況も気にしつつ完成に近づいたらまた導入してみようと思います。

次は元Google推奨のBLoC導入してみたいと思います。

# 参考
大変参考になりました！ありがとうございました🙇‍♂️
- [Provider のススメ](https://ja.unflf.com/tech/flutter/provider/)
- [[Flutter] package:provider の各プロバイダの詳細](https://qiita.com/kabochapo/items/a90d8438243c27e2f6d9)