📱

Flutter hooks_reverpod Navigator2.0で画面遷移する

2022/09/16に公開

みなさんこんにちは

今日のお題

Flutter + hooks_reverpod + Navigator 2.0で画面遷移を行いたい。
とゆうのも、これからアプリを作るのであれば、なにかしらのボタンを押したら、コンフィグ画面なりに移動したり戻ったりができた方がいい。のではないかと考えて。

Navigator2.0の今んとこの理解

ちょっとややこしいのですが、1.0もあってマイナーチェンジなのかと思いや、結構大きく変わっているようです。
Navigator1.0:HTMLのa hrefのリンクのような感じ。ボタンを押したら、あらかじめ設定されているページに移動する。
Navigator2.0:スマホのプッシュ通知を、スマホ内で完結しているイメージ。アプリのボタンなりデーモンなりが、特定の変数を監視していて、監視している、特定の変数に変更があった場合に、変更に応じてあらかじめ設定してあるページに移動する。
※ページにいどうだけではなくて、色々出来る。画像を変更したり。

ソース

https://github.com/kurayasuyu/hooksriverpodsample3
とりあえず、Githubにアップロードしておきました。

main.dart

ここに、監視変数が分かった時の処理を書く。
そのうちソースを分離したいですね。

main.dart
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'entity/data.dart';
import 'view/myhome.dart';
import 'view/page2.dart';

//ここが一番最初に動く
void main() {
  runApp(
    const ProviderScope(
      child: MyApp(),
    ),
  );
}

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

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    //Widget build(BuildContext context) {
    int intPageID = ref.watch(kousinProvider);
    //return const MaterialApp(
    return MaterialApp(
      //home: MyHomePage(),
        home: Navigator(//Navigator2.0の登録
          pages: [
            const MaterialPage(//初期起動ページ設定
              key: ValueKey('ListPage'),
              child: MyHomePage(),
            ),

            if (intPageID == 1)
            const MaterialPage(
              key: ValueKey('ListPage2'),
              child: Page2Page(),
            ),

          ],
          onPopPage: (route, result) {//Navigator2.0これも必須
            if (!route.didPop(result)) {
              return false;
            }
            ref.read(kousinProvider.state).state = 0;
            return true;
          },
        ),
    );
  }
}

myhome.dart

ref.read(kousinProvider.notifier).update((state) => 1);
↑の行で共通変数の数値を変えて、main.dartのpages: に記載されている、配列の(この場合は、1※0スタートです。)場所の処理を行う。

myhome.dart
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

import '../entity/data.dart';

class MyHomePage extends HookConsumerWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);

    //今回追加
    final ButtonStyle style = ElevatedButton.styleFrom(textStyle: const TextStyle(fontSize: 20));

    return Scaffold(
      appBar: AppBar(
        title: const Text('Riverpod counter example'),
      ),

      body: Center(
        /* //元々あったソース
        child: Text(
          '$count',
          style: Theme.of(context).textTheme.headline4,
        ),
         */
        //今回追加
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            Text(
              '$count',
              style: Theme.of(context).textTheme.headline4,
            ),
            const SizedBox(height: 30),
            ElevatedButton(
              style: style,
              //onPressed: () {},//ボタン押した時に動く。今は何も入れていない。
              onPressed: () {
                ref.read(counterProvider.notifier).setIncrement(0);
              },
              child: const Text('Enabled'),
            ),

            ElevatedButton(
                onPressed: () {
                  print("### MyHomePage#Widget onPressed2 !");
                  ref.read(kousinProvider.notifier).update((state) => 1);//ここで共通変数更新→ページ切り替え
                  /*
                  //Navigator1.0
                  Navigator.of(context).push<void>(
                    MaterialPageRoute(
                      builder: (context) => const MySecondPage(),
                    ),
                  );
                  */
                },
                child: const Text(
                  "画面移動",
                  style: TextStyle(
                    color: Colors.white,
                  ),
                ),

              ),

          ],
        ),
      ),

      floatingActionButton: FloatingActionButton(
        onPressed: () => ref.read(counterProvider.notifier).increment(),
        child: const Icon(Icons.add),
      ),
    );
  }
}

page2.dart

ページ移動先が必要なので、新たに新設。

page2.dart
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

import '../entity/data.dart';

//このソースは、ページ遷移を確認するために新規に作成
class Page2Page extends HookConsumerWidget {
  const Page2Page({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Riverpod Navigator2 example'),
      ),
      body: Center(
        child: Column(
            children: <Widget>[
              ElevatedButton(
                  onPressed: () {
                    print("### NewWindowPage#Widget onPressed3 !");
                    ref.read(kousinProvider.notifier).update((state) => 0);//ここで共通変数更新→ページ切り替え
                  /*
                  //Navigator1.0
                  Navigator.of(context).push<void>(
                    MaterialPageRoute(
                      builder: (context) => const MySecondPage(),
                    ),
                  );
                  */
                  },
                  child: const Text(
                    "戻る",
                    style: TextStyle(
                      color: Colors.white,
                    ),
                  ),

                ),
            ]
        ),
      ),
    );
  }
}

data.dart

ページ切り替えようの共通変数の定義と、データの上書きができるように処理の追加。
実際に監視しているのは、main.dartの以下の部分。
int intPageID = ref.watch(kousinProvider);

data.dart
//hooks_riverpodを利用するための宣言
import 'package:hooks_riverpod/hooks_riverpod.dart';

//Providerの宣言
final counterProvider = StateNotifierProvider<Counter, int>((_) => Counter());

//Providerに紐づいたクラスと関数
class Counter extends StateNotifier<int> {
  Counter() : super(0);//stateを0で初期化

  //increment()関数が呼ばれたら、stateの数字を+1する。
  void increment() => state++;

  //カウンターの値が指定値に変更出来る関数を作る
  void setIncrement( int intData ){
    state = intData;
  }
}

//ページを切り替えるための共通変数//Navigator2.0の状態変数
final kousinProvider = StateProvider((ref) => 0);

class Kousin {

  static void update(int intPageID) {
    // kousinProviderを更新する
    StateProvider((ref) {
      ref.read(kousinProvider.notifier).state = intPageID;
    });
  }//static void update

}

ファイル構成


※関係ないですが、MACのこの切り抜き機能とってもいいですね。Windowsにもあるのかもしれないけど。

動作確認


無事に画面遷移できました。当然、TOP画面にも戻れますよ。

今日の課題

意外にNavigator2.0の仕組みに悩みました。ネットで色々当たったのですが、いまいち完コピで動くようなものが発見できず、というのが続きました。
なんというか、Navigato1.0で大体のケースは吸収できているので、わざわざNavigator2.0にする必要ないってのもあるんだと思います。というのが、情報が少ない理由なんですかね。
しかも面倒でも、大体のことは、Navigator1.0でもできるらしいですしね。

今後の予定

説明のために、旧コードとかを残しつつ、最低限必要な機能が揃ったので、具体的に何か作っていきたいなと。
あーでも、Google Mapとか見れると面白そうですね。

Discussion