(初学者向け)riverpodで作るContainerテスターアプリ①

image

riverpodの使い方、今一度確認しましょう!

こんにちは!mukaigaです。可茂IT塾でお世話になりはじめてはや4ヶ月が過ぎようとしています。
スゴすぎる先輩エンジニア達にはいつ追いつけるのでしょうか・・・。

さて今回は、riverpodのサンプルアプリのようなものを作ってきたのでご紹介します!
この記事で分からないことなどがあれば、ぜひ自分のTwitterのDMにてご質問下さい!
Mukaiga Twitter : https://twitter.com/MukaigawaraYuki

対象読者

  • riverpod調べてきたけどいまいち分からんわ!
  • StatefulWidgetとStatelessWidgetの違いくらい分かるよ!
  • とりあえず何となく教えて!
  • flutter楽しいな〜^^

今回作るもの

Containerを動的に操作できるアプリ
完成品

環境

ビルド

flutter-web

パッケージ

flutter_riverpod: ^1.0.3

どう作る?

まずはこうゆうアプリを作ろうとした時に、どう作るかをさーっと考えます。
変更した'width'などの値に対応して、Widgetを変化させていますよね。

「んー?アニメーションが必要ー?」
「StatefulWidgetであーだこーだするんでしょ!」

恐らくriverpodを使わないなら、StatefulWidgetでWidgetに状態を持たせるのが良いでしょう。
では一度、StatefulWidgetで作ってみます。

もし良かったら、一回自分で作ってみて、後からコードを見てもらうと良いかもしれません!

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ContainerTester',
      theme: ThemeData(primarySwatch: Colors.red),
      debugShowCheckedModeBanner: false,
      home: const MyWidget(),
    );
  }
}

/// ここからStatefulWidgetにしちゃうよ!
class MyWidget extends StatefulWidget {
  const MyWidget({Key? key}) : super(key: key);

  
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  /// widthの変数を宣言 widthはdouble型だよ!
  double _containerWidth = 100.0;
  /// リセット用の既定値
  final double _defaultWidth = 100.0;

  late String _labelText = _containerWidth.toString();
  final String _defaultText = "100.0";

  
  Widget build(BuildContext context) {
    /// 画面の大きさを取得する
    final Size _size = MediaQuery.of(context).size;
    return Scaffold(
      /// リセットボタン
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          /// 代入し直してsetStateで更新
          _containerWidth = _defaultWidth;
          _labelText = _defaultText;
          setState(() {});
        },
        child: const Icon(Icons.settings_backup_restore),
      ),
      body: Column(
        children: [
          SizedBox(
            /// 画面の大きさの半分
            height: _size.height * 0.5,
            child: Center(
              /// 動かす対象「今回の主役」!
              child: Container(
                width: _containerWidth,
                height: 100,
                color: Colors.red,
              ),
            ),
          ),
          const Divider(),
          Column(
            children: [
              Row(
                children: [
                  const SizedBox(
                    width: 100,
                    child: Center(
                      widthFactor: 100,
                      child: Text("width"),
                    ),
                  ),
                  SizedBox(
                    width: 150,
                    child: Center(child: Text(_labelText)),
                  ),
                  SizedBox(
                    width: 300,
                    child: Slider(
                        value: _containerWidth,
                        min: 1,
                        max: 400,
                        onChanged: (double value) {
                          _containerWidth = value;
                          _labelText = value.toString();

                          /// setStateでUIを更新!
                          setState(() {});
                        }),
                  ),
                ],
              ),
            ],
          ),
        ],
      ),
    );
  }
}

StatefulWidgetで実装した時の気になるところ

  • リセットボタンで、既定値を再代入して表現しているところ。いずれwidth以外のパラメータが増えていった時だるい
  • もしWidth以外の値、例えばHeightも操作できるように、とした時にmain.dartが肥大化していく。となると、見やすくするために、 操作部分のWidgetを他ページに分けたいが、その時に値の受け渡しがだるい
  • UI更新の時にSetStateを書くのがだるい

なんか段々こじつけみたいになって来たので、これくらいに・・・

ではriverpodを使えばどうなるか?

まずはmain.dartから!

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_web_uisite/UI/Parts/width_area.dart';

/// MyAppをProviderScopeで包む!
void main() => runApp(const ProviderScope(child: MyApp()));

/// Providerを作る!初期値で100.0を与えてるよ!
final widthProvider = StateProvider((ref) => 100.0);

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ContainerTester',
      theme: ThemeData(primarySwatch: Colors.red),
      debugShowCheckedModeBanner: false,
      home: const MyWidget(),
    );
  }
}

/// StatefulWidgetをConsumerWidgetに
class MyWidget extends ConsumerWidget {
  const MyWidget({Key? key}) : super(key: key);

  
  /// 要注意!ConsumerWidgetのbuildのコンストラクタにはWidgetRef型が必要!
  Widget build(BuildContext context, WidgetRef ref) {
    
    /// ここで作ったwidthProviderを読んでるよ!
    double _width = ref.watch(widthProvider.state).state;
    
    final Size _size = MediaQuery.of(context).size;
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          /// ref.refreshでリソースをリセット!
          ref.refresh(widthProvider);
        },
        child: const Icon(Icons.settings_backup_restore),
      ),
      body: Column(
        children: [
          SizedBox(
            height: _size.height * 0.5,
            child: Center(
              /// 滑らかにするためにAnimatedContainerに!
              child: AnimatedContainer(
                duration: const Duration(microseconds: 100000),
                width: _width,
                height: 100,
                color: Colors.red,
              ),
            ),
          ),
          const Divider(),
          Column(
            children: const [
              /// 別ページに移したよ!
              WidthArea(),
            ],
          ),
        ],
      ),
    );
  }
}

特に大事なのは

double _width = ref.watch(widthProvider.state).state;

ここで、ref.watchで自分の作ったProvider(widthProvider)を参照しているところです!
.watchは「更新を検知」=「値が変えられた時」に、自動で値を入れ直してくれるということです!
つまり、この文の意味は「double _width = widthProviderの値が更新された時に自動で値を入れる」みたいな感じです!

勝手に更新してくれるので、StatefulWidgetでUIを更新するために書いてた、SetStateメソッドは書く必要がないのです!

あともう一つ。リセットボタンの処理にて。

ref.refresh(widthProvider);

ここで、widthProviderを「.refresh」していますよね。
これは、今widthProviderが管理している値をその名の通りリフレッシュ!してくれます。

つまり、最初にwidthProviderに入れた値、100.0に戻してくれるのです。
これを書くだけでリセット処理が書けてしましました。楽ちんだし、綺麗!

さて、あと少し!今度は切り離したWidthAreaを見ていくよ!

(main.dartではなく、width_area.dartに書いています)
まずはコード!

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_web_uisite/main.dart';

class WidthArea extends ConsumerWidget {
  const WidthArea({Key? key}) : super(key: key);

  
  Widget build(BuildContext context, WidgetRef ref) {
    final _width = ref.watch(widthProvider.state).state;
    return Row(
      children: [
        const SizedBox(
          width: 100,
          child: Center(
            child: Text("width"),
          ),
        ),
        SizedBox(
          width: 100,
          child: Center(child: Text(_width.roundToDouble().toString())),
        ),
        SizedBox(
          width: 500,
          child: Slider(
              value: _width,
              min: 1,
              max: 400,
              onChanged: (double value) {
                ref.read(widthProvider.state).state = value;
              }),
        ),
      ],
    );
  }
}

さて、まずはここ!

final _width = ref.watch(widthProvider.state).state;

普通なら値を子Widgetに渡したり、ごちゃごちゃしなければいけません。
しかし、riverpodを使えばこのように他のファイルからでも簡単に値の管理ができるようになります!

 onChanged: (double value) {
                ref.read(widthProvider.state).state = value;
              }),

次に大事なのはここでしょう!さっきまでは、値を読み取るだけでした。
ここでしたいのは、値を書き換えることです!

ここではref.readを使っていきましょう!上の通りに書くだけです・・・。
これだけでwidthProviderの値にvalue(Sliderからの値)が入ります。

最後に

今回はwidthしかいじれるようにしていませんが、heightやcolorなども変えられたら面白いですよね。
全てのプロパティ変えられたら、Containerをwebで簡単に確認できるツールができるのでは!? 使わないと思いますが

riverpodについてもっと詳しく!という方は是非
可茂IT塾 > ブログ > riverpodタグ から記事をご覧ください!

お知らせ

5月28日開催のアプリ開発講座の参加者募集中!!

5月28日開催のアプリ開発講座の参加者募集中!!

5月28日にアプリ開発講座を開催します!会場は岐阜県美濃加茂市のコワーキングスペース「こやぁね」です。興味のある方は是非ご参加ください!

Read More
可茂IT塾ではFlutterインターンを募集しています!

可茂IT塾ではFlutterインターンを募集しています!

可茂IT塾ではFlutterインターンを募集しています!可茂IT塾のエンジニアの判断で、一定以上のスキルをを習得した方には有給でのインターンも受け入れています。

Read More

お知らせ

5月28日開催のアプリ開発講座の参加者募集中!!

5月28日開催のアプリ開発講座の参加者募集中!!

5月28日にアプリ開発講座を開催します!会場は岐阜県美濃加茂市のコワーキングスペース「こやぁね」です。興味のある方は是非ご参加ください!

Read More
可茂IT塾ではFlutterインターンを募集しています!

可茂IT塾ではFlutterインターンを募集しています!

可茂IT塾ではFlutterインターンを募集しています!可茂IT塾のエンジニアの判断で、一定以上のスキルをを習得した方には有給でのインターンも受け入れています。

Read More