18
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Flutter大学Advent Calendar 2021

Day 21

【最速入門】Provider・Riverpod 新旧対照表

Posted at

##背景
はじめまして、yamoriです。

状態管理としてこれまでProviderを使用していましたが、とあるプロジェクトでRiverpodを使う機会がありました。
はじめはとっつきにくい印象があったのですが、実際に使ってみると非常にスムーズに移行できた(とはいえ、もっと深めれば違いを感じたりすることもありそう)ので、Providerとの違いについてまとめようと思います。

##対象者
これまでにProvider(主にChangeNotifierProvider)を利用した経験があって、これからRiverpodを使ってみたいという人向けの入門記事です。

##Providerでのカウントアプリ
まず、Flutterの初期アプリであるカウントアプリにProviderを導入してみましょう。
(適切なユースケースではないような気がしますが、分かりやすさ重視のためです。)
ファイル名

providerのパッケージは以下から導入してください。

コードは以下の通りです。

main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:riverpod_sample/provider_view_model.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home:  MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
   MyHomePage({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    //①ChangeNotifierProviderを使用している(ここの書き方少し面倒臭い)
    return ChangeNotifierProvider<CountViewModel>(
      //②初期化処理がある場合(たとえばinit()など)はCountViewModel..init()とする。
        create: (_) => CountViewModel(),
        child: Consumer<CountViewModel>(
          //③第二引数のmodelを使って、ViewModelの値や関数を参照する
      builder: (context, model, child) {
        return Scaffold(
          appBar: AppBar(
            title: Text('${model.title}'),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Text(
                  'You have pushed the button this many times:',
                ),
                Text(
                  '${model.counter}',
                  style: Theme.of(context).textTheme.headline4,
                ),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: model.incrementCounter,
            tooltip: 'Increment',
            child: const Icon(Icons.add),
          ), // This trailing comma makes auto-formatting nicer for build methods.
        );
      })
    );
  }
}

count_view_model.dart
import 'package:flutter/material.dart';

class CountViewModel extends ChangeNotifier {

  final String title = 'provider';

  int _counter = 0;
  get counter => _counter;

  void incrementCounter() {
    _counter++;
    notifyListeners();
  }
}

##Riverpodでのカウントアプリ
それでは、上記カウントアプリをRiverpodで実装してみましょう。

Riverpodのパッケージは以下から導入してください。

コードは以下の通りです。

main.dart
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:riverpod_sample/count_view_model.dart';

void main() {
  runApp(
    //①MyAppをProviderScopeで囲む
    const ProviderScope(
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

//②StateleeWidgetをHookConsumerWidgetに変更
class MyHomePage extends HookConsumerWidget {
  MyHomePage({Key? key}) : super(key: key);
  @override
  //③第2引数にWidgetRef refを追加
  Widget build(BuildContext context, WidgetRef ref) {
    //④countViewModelを参照することを定義、vmでViewModelを参照する
    final vm = ref.watch(countViewModel);
    return Scaffold(
      appBar: AppBar(
        title: Text('${vm.title}'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '${vm.counter}',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: vm.incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}
count_view_model.dart
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

//①Riverpodのみ設定
final countViewModel = ChangeNotifierProvider((_) => CountViewModel());

//CountViewModelの中身は一緒
class CountViewModel extends ChangeNotifier {
  final String title = 'provider';

  int _counter = 0;
  get counter => _counter;

  void incrementCounter() {
    _counter++;
    notifyListeners();
  }
}

ビルドすると、同じカウントアプリができていることがわかると思います。
riverpodのコードはこちらからご覧いただけます。

##おわりに
ViewModelの書き方はほとんど変わらず、Viewの書き方はriverpodの方が簡素だと思います。(初心者の方だと、ProviderのChangeNotifierProvoiderの以下の部分の書き方で(や{の数で苦しんだことがあるかもしれません。

 Widget build(BuildContext context) {
    //①ChangeNotifierProviderを使用している(ここの書き方少し面倒臭い)
    return ChangeNotifierProvider<CountViewModel>(
      //②初期化処理がある場合(たとえばinit()など)はCountViewModel..init()とする。
        create: (_) => CountViewModel(),
        child: Consumer<CountViewModel>(
          //③第二引数のmodelを使って、ViewModelの値や関数を参照する
      builder: (context, model, child) {
        return Scaffold(
          appBar: AppBar(

このあたり、Riverpodだと定数を定義するだけなので簡単かなと思います。
どちらを採用すべき等の議論はあると思いますが、まずは使い方を覚えることで、公式ドキュメントを読んでいて引っかかるところも増えると思うので、この記事を書きました。
素敵な年末をお過ごしください!

18
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?