190 lines
4.8 KiB
Dart
190 lines
4.8 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
class AppNavigator {
|
|
AppNavigator._();
|
|
|
|
static final navigatorKey = GlobalKey<NavigatorState>();
|
|
static final Set<String> _pushingRoutes = <String>{};
|
|
|
|
static BuildContext? get context => navigatorKey.currentContext;
|
|
|
|
static Future<T?> push<T>(
|
|
Widget page, {
|
|
BuildContext? context,
|
|
Object? arguments,
|
|
String? name,
|
|
bool preventDuplicate = true,
|
|
}) async {
|
|
final routeName = name ?? page.runtimeType.toString();
|
|
if (preventDuplicate && RouteTracker.contains(routeName)) {
|
|
return null;
|
|
}
|
|
if (_pushingRoutes.contains(routeName)) {
|
|
return null;
|
|
}
|
|
|
|
_pushingRoutes.add(routeName);
|
|
final nav = Navigator.of(
|
|
context ?? AppNavigator.context!,
|
|
rootNavigator: true,
|
|
);
|
|
try {
|
|
return await nav.push<T>(
|
|
SlidePageRoute(
|
|
builder: (_) => page,
|
|
settings: RouteSettings(name: routeName, arguments: arguments),
|
|
),
|
|
);
|
|
} finally {
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
_pushingRoutes.remove(routeName);
|
|
});
|
|
}
|
|
}
|
|
|
|
static Future<T?> pushReplacement<T, TO>(
|
|
Widget page, {
|
|
BuildContext? context,
|
|
Object? arguments,
|
|
String? name,
|
|
}) {
|
|
return Navigator.of(
|
|
context ?? AppNavigator.context!,
|
|
rootNavigator: true,
|
|
).pushReplacement<T, TO>(
|
|
SlidePageRoute(
|
|
builder: (_) => page,
|
|
settings: RouteSettings(name: name, arguments: arguments),
|
|
),
|
|
);
|
|
}
|
|
|
|
static Future<T?> pushAndRemoveUntil<T>(
|
|
Widget page, {
|
|
BuildContext? context,
|
|
RoutePredicate? predicate,
|
|
Object? arguments,
|
|
String? name,
|
|
}) {
|
|
return Navigator.of(
|
|
context ?? AppNavigator.context!,
|
|
rootNavigator: true,
|
|
).pushAndRemoveUntil<T>(
|
|
SlidePageRoute(
|
|
builder: (_) => page,
|
|
settings: RouteSettings(name: name, arguments: arguments),
|
|
),
|
|
predicate ?? (_) => false,
|
|
);
|
|
}
|
|
|
|
static Future<T?> pushTransparent<T>(
|
|
Widget page, {
|
|
BuildContext? context,
|
|
Color barrierColor = Colors.black54,
|
|
Duration duration = const Duration(milliseconds: 200),
|
|
bool dismissible = true,
|
|
}) {
|
|
return Navigator.of(context ?? AppNavigator.context!).push<T>(
|
|
PageRouteBuilder<T>(
|
|
opaque: false,
|
|
barrierColor: barrierColor,
|
|
barrierDismissible: dismissible,
|
|
transitionDuration: duration,
|
|
reverseTransitionDuration: duration,
|
|
pageBuilder: (_, __, ___) => page,
|
|
transitionsBuilder: (_, animation, __, child) {
|
|
return FadeTransition(opacity: animation, child: child);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
static void pop<T extends Object?>({BuildContext? context, T? result}) {
|
|
Navigator.of(
|
|
context ?? AppNavigator.context!,
|
|
rootNavigator: true,
|
|
).pop<T>(result);
|
|
}
|
|
|
|
static void popTimes({BuildContext? context, int count = 1}) {
|
|
var popped = 0;
|
|
Navigator.of(context ?? AppNavigator.context!).popUntil((route) {
|
|
if (popped < count) {
|
|
popped++;
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
}
|
|
|
|
class SlidePageRoute<T> extends MaterialPageRoute<T> {
|
|
SlidePageRoute({required super.builder, super.settings});
|
|
|
|
@override
|
|
Widget buildTransitions(
|
|
BuildContext context,
|
|
Animation<double> animation,
|
|
Animation<double> secondaryAnimation,
|
|
Widget child,
|
|
) {
|
|
if (Platform.isAndroid) {
|
|
final tween = Tween(
|
|
begin: const Offset(1, 0),
|
|
end: Offset.zero,
|
|
).chain(CurveTween(curve: Curves.easeOutCubic));
|
|
return SlideTransition(position: animation.drive(tween), child: child);
|
|
}
|
|
|
|
return Theme.of(context).pageTransitionsTheme.buildTransitions<T>(
|
|
this,
|
|
context,
|
|
animation,
|
|
secondaryAnimation,
|
|
child,
|
|
);
|
|
}
|
|
}
|
|
|
|
class RouteTracker extends NavigatorObserver {
|
|
static final List<String?> pageStack = [];
|
|
|
|
bool _isPage(Route<dynamic> route) => route is PageRoute;
|
|
|
|
@override
|
|
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
|
|
if (_isPage(route)) {
|
|
pageStack.add(route.settings.name);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
|
|
if (_isPage(route) && pageStack.isNotEmpty) {
|
|
pageStack.removeLast();
|
|
}
|
|
}
|
|
|
|
@override
|
|
void didRemove(Route<dynamic> route, Route<dynamic>? previousRoute) {
|
|
if (_isPage(route)) {
|
|
pageStack.remove(route.settings.name);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void didReplace({Route<dynamic>? newRoute, Route<dynamic>? oldRoute}) {
|
|
if (oldRoute != null && _isPage(oldRoute) && pageStack.isNotEmpty) {
|
|
pageStack.removeLast();
|
|
}
|
|
if (newRoute != null && _isPage(newRoute)) {
|
|
pageStack.add(newRoute.settings.name);
|
|
}
|
|
}
|
|
|
|
static bool contains(String name) => pageStack.contains(name);
|
|
}
|