✨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();
}
},
.
🟩
🟡
.
🟩
🟡
.
🟩
🟡
.
🟩
🟡
.
🟩
🟡
.
🟩
🟡
.
🟩
🟡
.
🟩
🟡
.