dev
dart 120 lines 4 KB
Raw
1 import 'package:cake_wallet/router.dart';
2 import 'package:cw_core/utils/print_verbose.dart';
3 import 'package:flutter/material.dart';
4
5 /// allows for full navigation with pages and routes inside a single modal sheet.
6 /// call this in your modal sheet's builder, passing whatever the modal's first page should be to rootPage.
7 /// afterwards you can call pushNamed to push other pages, they'll be created inside the modal.
8 /// you can use the back button (android) or the swipe back gesture (both android and iphone) to go back.
9 /// calling Navigator.of(context).pop() will pop the page INSIDE the modal sheet.
10 /// if you want to pop the whole sheet, use Navigator.of(context, rootNavigator: true).pop().
11 class ModalNavigator extends StatefulWidget {
12 const ModalNavigator(
13 {super.key,
14 required this.rootPage,
15 required this.parentContext,
16 this.heightMode = ModalHeightModes.fullScreen});
17
18 final Widget rootPage;
19 final BuildContext parentContext;
20 final ModalHeightModes heightMode;
21
22 @override
23 State<ModalNavigator> createState() => _ModalNavigatorState();
24 }
25
26 enum ModalHeightModes {
27 /// just render as big a modal as possible
28 fullScreen,
29
30 /// after first frame, read height and lock to that height
31 autoLock,
32
33 /// let the content size itself. jumps around when pushing different-sized pages!
34 natural
35 }
36
37 class _ModalNavigatorState extends State<ModalNavigator> {
38 final GlobalKey _sheetKey = GlobalKey();
39 final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
40 double? _sheetHeight;
41
42 @override
43 void initState() {
44 super.initState();
45 WidgetsBinding.instance.addPostFrameCallback((_) {
46 final context = _sheetKey.currentContext;
47 if (context == null) return;
48 if (_sheetHeight != null) return;
49
50 final box = context.findRenderObject() as RenderBox;
51 setState(() {
52 _sheetHeight = box.size.height;
53 });
54 });
55 }
56
57 @override
58 Widget build(BuildContext context) {
59 late final double? height;
60 switch (widget.heightMode) {
61 case ModalHeightModes.fullScreen:
62 height = MediaQuery.of(context).size.height;
63 break;
64 case ModalHeightModes.autoLock:
65 height = _sheetHeight;
66 break;
67 case ModalHeightModes.natural:
68 height = null;
69 break;
70 }
71 return Container(
72 key: _sheetKey,
73 height: height,
74 child: Theme(
75 data: Theme.of(context).copyWith(
76 pageTransitionsTheme: PageTransitionsTheme(
77 builders: {
78 // requested by ui - iphone-style back anim on every platform
79 TargetPlatform.android: CupertinoPageTransitionsBuilder(),
80 TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
81 TargetPlatform.linux: CupertinoPageTransitionsBuilder(),
82 TargetPlatform.macOS: CupertinoPageTransitionsBuilder(),
83 TargetPlatform.windows: CupertinoPageTransitionsBuilder(),
84 },
85 ),
86 ),
87 child: PopScope(
88 canPop: false,
89 onPopInvokedWithResult: (didPop, result) async {
90 if (didPop) return;
91
92 final navigator = _navigatorKey.currentState;
93 if (navigator != null && await navigator.maybePop()) {
94 return;
95 }
96
97 Navigator.of(widget.parentContext).pop();
98 },
99 child: Navigator(
100 key: _navigatorKey,
101 onGenerateRoute: (settings) {
102 printV(settings.name);
103
104 if (settings.name == "/")
105 return handleRouteWithPlatformAwareness(
106 (context) => PopScope(
107 canPop: false,
108 onPopInvokedWithResult: (didPop, result) async {
109 Navigator.of(widget.parentContext).pop();
110 },
111 child: widget.rootPage),
112 fullscreenDialog: false);
113 else
114 return createRoute(settings);
115 }),
116 ),
117 ),
118 );
119 }
120 }