✨pop を検知する方法 - Ki-Kobayashi/flutter_wiki GitHub Wiki

🟩 画面遷移Action時を検知する方法3つ

  • 【popした画面で検知】画面遷移処理(push)を await して、後続に戻ってきたときの処理を記述
       👉 Widgetで検知できる(go_routerで設定した画面単位でない)
  • 【画面遷移の動作全般検知】RouteAwareを使用して検知
       👉 画面Widgetに定義しないと、上手く検知しない
          🚨(画面内に配置されたWidget内で定義しても無意味)
  • 【popする直前を検知】(A -> B -> A の場合はB)WillPopScope
       🚨 WillPopScope はAndroidのみしか動作しない(バックキー検知を主にするものだから)
          👉 Ios版WillPopScopeを使えるパッケージがあったはず・・・それ使う
          👉 GestureDetectorを使う

.

🟡 【popした画面で検知】画面遷移処理(push)を await して、後続に戻ってきたときの処理を記述

async /await を追加することで、戻ってくるまで処理を待てるようになる
(POPで帰ってきたときに処理を実行できる)

InkWell(
	child: Xxxxx,
	onTap: () async { 👈 async追加
	  await context.push( 👈 await追加
		RouterPath.home,
		extra: param,
	  );
	  // 💡戻ってきたときに実行したい事
	  callback?.call(ref);
	},
),

.

🟡 画面遷移の動作全般検知】RouteAwareを使用して検知

💡追加パッケージは無し

.

💎 Providerの追加(どこの画面からでもアクセスできるように)

@Riverpod(keepAlive: true)
RouteObserver routeObserver(RouteObserverRef ref) => RouteObserver();

.

💎 GorouterにObserverをセット

 GoRouter(
      initialLocation: '/',
      routes: $appRoutes,
      observers: [
        ref.watch(routeObserverProvider), :point_left:追加
      ],
    );

.

💎 RouteAwareを拡張して、遷移イベントをコールバックで経由するようにする

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

class RouteAwareEvent extends RouteAware {
  VoidCallback? onDidPopNext;
  VoidCallback? onDidPop;
  VoidCallback? onDidPushNext;
  VoidCallback? onDidPush;

  @override
  void didPopNext() => onDidPopNext?.call();
  @override
  void didPop() => onDidPop?.call();
  @override
  void didPushNext() => onDidPushNext?.call();
  @override
  void didPush() => onDidPush?.call();
}

// hooks の返り値
enum RouteAwareType {
  didPush,
  didPop,
  didPopNext,
  didPushNext,
}

RouteAwareType? useRouteAwareEvent(RouteObserver routeObserver) {
  return use(_RouteAwareHook(routeObserver));
}

class _RouteAwareHook extends Hook<RouteAwareType?> {
  const _RouteAwareHook(this.routeObserver) : super();
  final RouteObserver routeObserver;

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

class __RouteAwareState extends HookState<RouteAwareType?, _RouteAwareHook> {
  RouteAwareType? _state;
  final aware = RouteAwareEvent();

  @override
  void initHook() {
    super.initHook();
    aware.onDidPop = () => _state = RouteAwareType.didPop;
    aware.onDidPopNext = () => _state = RouteAwareType.didPopNext;
    aware.onDidPush = () => _state = RouteAwareType.didPush;
    aware.onDidPushNext = () => _state = RouteAwareType.didPushNext;
  }

  var firstBuild = true;
  @override
  RouteAwareType? build(BuildContext context) {
    // initHook で context を触るとエラーになるので、ここで初期化する
    // また、ここでは useEffect が使えないので、firstBuild を利用し初回のみ subscribe する
    if (firstBuild) {
      firstBuild = false;
      hook.routeObserver.subscribe(aware, ModalRoute.of(context)!);
    }
    return _state;
  }

  @override
  void dispose() {
    super.dispose();
    hook.routeObserver.unsubscribe(aware);
  }
}

.

💎 利用時

class HogePage extends HookConsumerWidget {
  HogePage({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {

    final routeAware = useRouteAwareEvent(ref.watch(routeObserverProvider));
    useEffect(() {
      if (routeAware == RouteAwareType.didPop) {
        print('did pop');
      }
      return null;
    }, [routeAware]);

    return Scaffold();
  }
}

.

🟡 【popする直前を検知】(A -> B -> A の場合はB)WillPopScope

以下は、GestureDetectorを使用したケース

◆build内


return GestureDetector(
  behavior: HitTestBehavior.opaque,  👈これとonTapの2つで、キーボードからフォーカスを外す処理(画面タップで)
  onTap: () => primaryFocus?.unfocus(),
  // 👇ここ追加
  onHorizontalDragEnd: (detail) {
     if (details.primaryVelocity! > 0) { 
        context.pop();
     }
  },


.

🟩

🟡

.

🟩

🟡

.

🟩

🟡

.

🟩

🟡

.

🟩

🟡

.

🟩

🟡

.

🟩

🟡

.

🟩

🟡

.