6
6

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 3 years have passed since last update.

いくぜ!Flutter!状態管理をあれする ~基礎編

6
Posted at

状態管理って、なんか今ひとつわかんねー。いや、言ってることは理屈ではわかるんだけどね。実装に結びつかないというか、じゃあ俺のしたいことはどうしたらできるのよ、っていうのがよくわかっていない。
そこで、Riverpodで状態管理を学んでみるよ。馬鹿になって学べ。
基本的にRiverpodの公式に書いてあることから学んでいくよ。

1.環境はこんな感じ

Riverpodはdartのみ、flutterのみ、flutter+flutter_hooksの環境で使えるよ。
おいらはちょっと臆病者だから、flutter_hooks使って深みにはまるのが怖いから、flutterのみの環境で使ってみるよ。
ちなみにflutterのバージョンは3.0.5だ。

2.インストールする

flutterでパッケージをインストールするときは、pubspec.yamlにインストールするパッケージを書けばいいよ。簡単だね。

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  flutter_riverpod: ^2.1.3    ←これを追加

修正したpubspec.yamlを保存したら、flutter pub getコマンドを実行すると、パッケージが取り込まれるよ。VSCodeだと、保存したときに自動でコマンド実行されるみたいだけど、信用できなきゃ自力でコマンド投入だ。
これでRiverpodが使えるようになるよ。

3.Hello Worldを出してみる

Riverpodをインストール出来たら、みんな大好きHello Worldを出してみるよ。
公式にもHello Worldを出しているサンプルソースが出ているけど、mainしか使ってないのでなんかやった感がない。ここはひとつ、こちらもみんな大好きflutterのプロジェクト作成時にデフォルトで作られるカウンタアプリを改造してHello Worldを出してみるよ。
ちなみに先人がデフォルトのカウンタアプリをRiverpod仕様に改造した事例があったよ。とても参考になりました。あざす!
早速ソースをのっけるよ。

hello_world_riverpod.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

final helloProvider = StateProvider<String>((ref) => ' ');
String statement = '';

void main() {
  runApp(
    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: const MyHomePage(title: ' Demo Home Page'),
    );
  }
}

class MyHomePage extends ConsumerWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button say Hello World:',
            ),
            Text(
              ref.watch(helloProvider).toString(),
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          statement = '${statement}Hello World!';
          ref.watch(helloProvider.notifier).state = statement;
        },
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

実行して+ボタンを押すと、「Hello World!」の文字が表示されるよ。
押せば押しただけ狂ったように表示されるよ。
制限つけていないから、表示できなくなったところでエラーになるけどね。

コピペするときは、あらかじめpubspec.yamlにflutter_riverpodを追加しておいてね。

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  flutter_riverpod: ^2.1.3  ←これ追加

4.ソースの解説

元のソースから変更したところを説明していくよ。

hello_world_riverpod.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; ←追加

flutter_riverpodのimportを追加するよ。

hello_world_riverpod.dart
final helloProvider = StateProvider<String>((ref) => ' ');
String statement = '';

グローバルでProviderを追加するよ。Hello Worldを出すから、属性はStringにするよ。
ついでに文字を入れておくエリアを定義するよ。カウンタアプリの「_counter」にあたるやつだね。

hello_world_riverpod.dart
void main() {
  runApp(
    const ProviderScope(    ←これで囲むよ
      child: MyApp(),
    ),
  );
}

メインのところは、ProviderScpoeで囲めって書いてあるから、言われた通り囲ったよ。
VSCodeにconstつけろって怒られたからつけてるけど、正解か?

hello_world_riverpod.dart
class MyHomePage extends ConsumerWidget {  StatefulWidgetから変更
  const MyHomePage({Key? key, required this.title}) : super(key: key);

MyHomePageのWidgetを、StatefulWIdgetからConsumerWIdgetに変更する。
これでこの中でProviderが使えるようになるよ。
それから、この下にある「State createState() => _MyHomePageState(); 」以下_MyHomePageStateのclassをまるっと消してしまうよ。
デフォルトのカウントアプリでは_MyHomePageStateでカウントの処理を書いていたけれど、今回の変更でonPressに直接処理を書いているので、ここはいらなくなるよ。

hello_world_riverpod.dart
@override
  Widget build(BuildContext context, WidgetRef ref) {   WidgetRef refを追加
    return Scaffold(
      appBar: AppBar(
        title: Text(title),    ← ちょっと変えた
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button say Hello World:',    ←ちょっと変えた
            ),
            Text(
              ref.watch(helloProvider).toString(),    ← 表示する値をref.watchで取得
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          statement = '${statement}Hello World!';    ← クリックするたび「Hello World!」をセットするように変更
          ref.watch(helloProvider.notifier).state = statement;    ← 「Hello World!」が設定されたエリアをProviderにセット
        },
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }

いよいよ実際の処理の部分だ。
riverpodに直接関係する部分は、以下の修正をしているよ。
・Widget buildの引数にWidgetRef refを追加したよ。
 これでProviderの値をやり取りできるようになるよ。
・ref.watch(helloProvier)で取得した値を画面に表示するようにしたよ。
 デフォルトのカウンタアプリでは、カウントする処理を文字列補間でそのまま表示していたけれど、今回はtoStringでhelloProviderの値を表示したよ。
・「Hello World!」の文字を出すようにしたよ。
 カウントする代わりに、「Hello World!」を表示するようにしたよ。ただセットするだけだと面白くないから、ボタンを押したら後ろに「Hello World!」を追加するようにしたよ。

5.できた!

これでカウンタアプリにRiverpodを組み込むことができたよ。
ぶっちゃけ見た目が変わるわけでないから、達成感ないよね。
次は画面一式を状態管理してみるよ。てことはよく見るあれを使うってことか?
刮目して待て!次号!

6
6
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
6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?