Jonny Borges
Committed by GitHub

Merge pull request #2145 from jonataslaw/refactor-nav

Refactor nav
Showing 53 changed files with 2061 additions and 1295 deletions

Too many changes to show.

To preserve performance only 53 of 53+ files are displayed.

... ... @@ -14,11 +14,11 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp.router(
return GetMaterialApp(
debugShowCheckedModeBanner: false,
enableLog: true,
logWriterCallback: Logger.write,
// initialRoute: AppPages.INITIAL,
initialRoute: AppPages.INITIAL,
getPages: AppPages.routes,
locale: TranslationService.locale,
fallbackLocale: TranslationService.fallbackLocale,
... ...
... ... @@ -5,13 +5,11 @@ import '../data/home_repository.dart';
import '../domain/adapters/repository_adapter.dart';
import '../presentation/controllers/home_controller.dart';
class HomeBinding extends Binding {
class HomeBinding extends Bindings {
@override
List<Bind> dependencies() {
return [
Bind.lazyPut<IHomeProvider>(() => HomeProvider()),
Bind.lazyPut<IHomeRepository>(() => HomeRepository(provider: Get.find())),
Bind.lazyPut(() => HomeController(homeRepository: Get.find())),
];
void dependencies() {
Get.lazyPut<IHomeProvider>(() => HomeProvider());
Get.lazyPut<IHomeRepository>(() => HomeRepository(provider: Get.find()));
Get.lazyPut(() => HomeController(homeRepository: Get.find()));
}
}
... ...
... ... @@ -6,6 +6,7 @@ import 'package:get/get.dart';
import '../controllers/home_controller.dart';
class CountryView extends GetView<HomeController> {
const CountryView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
... ... @@ -32,10 +33,11 @@ class CountryView extends GetView<HomeController> {
itemBuilder: (context, index) {
final country = controller.state.countries[index];
return ListTile(
onTap: () {
onTap: () async {
//Get.rootDelegate.toNamed('/home/country');
Get.rootDelegate
.toNamed('/home/country/details?id=$index');
final data = await Get.toNamed(
'/home/country/details?id=$index');
print(data);
},
trailing: CircleAvatar(
backgroundImage: NetworkImage(
... ...
... ... @@ -2,12 +2,14 @@ import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/home_controller.dart';
class DetailsView extends GetView<HomeController> {
const DetailsView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final parameter = Get.rootDelegate.parameters;
final parameter = context.params; //Get.parameters;
final country = controller.getCountryById(parameter['id'] ?? '');
return Container(
decoration: BoxDecoration(
... ... @@ -76,6 +78,11 @@ class DetailsView extends GetView<HomeController> {
'${country.totalRecovered}',
style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold),
),
TextButton(
onPressed: () {
Get.back(result: 'djsoidjsoidj');
},
child: Text('back'))
],
)),
),
... ...
... ... @@ -4,6 +4,8 @@ import 'package:get/get.dart';
import '../controllers/home_controller.dart';
class HomeView extends GetView<HomeController> {
const HomeView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
... ... @@ -75,7 +77,8 @@ class HomeView extends GetView<HomeController> {
shape: StadiumBorder(),
),
onPressed: () async {
await Get.rootDelegate.toNamed('/home/country');
//await Navigation Get.rootDelegate.toNamed('/home/country');
Get.toNamed('/home/country');
},
child: Text(
'fetch_country'.tr,
... ...
... ... @@ -13,20 +13,21 @@ class AppPages {
static final routes = [
GetPage(
name: Routes.HOME,
page: () => HomeView(),
binding: HomeBinding(),
children: [
GetPage(
name: Routes.COUNTRY,
page: () => CountryView(),
children: [
GetPage(
name: Routes.DETAILS,
page: () => DetailsView(),
),
],
),
]),
name: Routes.HOME,
page: () => HomeView(),
bindings: [HomeBinding()],
children: [
GetPage(
name: Routes.COUNTRY,
page: () => CountryView(),
children: [
GetPage(
name: Routes.DETAILS,
page: () => DetailsView(),
),
],
),
],
),
];
}
... ...
... ... @@ -4,4 +4,8 @@ abstract class Routes {
static const HOME = '/home';
static const COUNTRY = '/country';
static const DETAILS = '/details';
static const DASHBOARD = '/dashboard';
static const PROFILE = '/profile';
static const PRODUCTS = '/products';
}
... ...
... ... @@ -5,29 +5,31 @@ import '../routes/app_pages.dart';
class EnsureAuthMiddleware extends GetMiddleware {
@override
Future<GetNavConfig?> redirectDelegate(GetNavConfig route) async {
Future<RouteDecoder?> redirect(RouteDecoder route) async {
// you can do whatever you want here
// but it's preferable to make this method fast
// await Future.delayed(Duration(milliseconds: 500));
if (!AuthService.to.isLoggedInValue) {
final newRoute = Routes.LOGIN_THEN(route.location!);
return GetNavConfig.fromRoute(newRoute);
final path = route.args.name as String;
final newRoute = Routes.LOGIN_THEN(path);
return RouteDecoder.fromRoute(newRoute);
}
return await super.redirectDelegate(route);
return await super.redirect(route);
}
}
class EnsureNotAuthedMiddleware extends GetMiddleware {
@override
Future<GetNavConfig?> redirectDelegate(GetNavConfig route) async {
Future<RouteDecoder?> redirect(RouteDecoder route) async {
if (AuthService.to.isLoggedInValue) {
//NEVER navigate to auth screen, when user is already authed
return null;
//OR redirect user to another screen
//return GetNavConfig.fromRoute(Routes.PROFILE);
//return RouteDecoder.fromRoute(Routes.PROFILE);
}
return await super.redirectDelegate(route);
return await super.redirect(route);
}
}
... ...
... ... @@ -10,19 +10,19 @@ class HomeView extends GetView<HomeController> {
return GetRouterOutlet.builder(
builder: (context, delegate, currentRoute) {
//This router outlet handles the appbar and the bottom navigation bar
final currentLocation = currentRoute?.location;
final currentLocation = context.location;
var currentIndex = 0;
if (currentLocation?.startsWith(Routes.PRODUCTS) == true) {
if (currentLocation.startsWith(Routes.PRODUCTS) == true) {
currentIndex = 2;
}
if (currentLocation?.startsWith(Routes.PROFILE) == true) {
if (currentLocation.startsWith(Routes.PROFILE) == true) {
currentIndex = 1;
}
return Scaffold(
body: GetRouterOutlet(
initialRoute: Routes.DASHBOARD,
// anchorRoute: Routes.HOME,
key: Get.nestedKey(Routes.HOME),
// key: Get.nestedKey(Routes.HOME),
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: currentIndex,
... ...
... ... @@ -31,9 +31,8 @@ class LoginView extends GetView<LoginController> {
),
onPressed: () {
AuthService.to.login();
final thenTo = Get.rootDelegate.currentConfiguration!
.currentPage!.parameters?['then'];
Get.rootDelegate.offNamed(thenTo ?? Routes.HOME);
final thenTo = context.params['then'];
Get.offNamed(thenTo ?? Routes.HOME);
},
),
],
... ...
... ... @@ -31,8 +31,7 @@ class ProductsView extends GetView<ProductsController> {
final item = controller.products[index];
return ListTile(
onTap: () {
Get.rootDelegate
.toNamed(Routes.PRODUCT_DETAILS(item.id));
Get.toNamed(Routes.PRODUCT_DETAILS(item.id));
},
title: Text(item.name),
subtitle: Text(item.id),
... ...
... ... @@ -39,7 +39,8 @@ class ProfileView extends GetView<ProfileController> {
Get.defaultDialog(
title: 'Test Dialog In Home Outlet !!',
barrierDismissible: true,
navigatorKey: Get.nestedKey(Routes.HOME),
id: Routes.HOME,
// navigatorKey: Get.nestedKey(Routes.HOME),
);
},
)
... ...
... ... @@ -21,7 +21,7 @@ class DrawerWidget extends StatelessWidget {
ListTile(
title: Text('Home'),
onTap: () {
Get.rootDelegate.toNamed(Routes.HOME);
Get.toNamed(Routes.HOME);
//to close the drawer
Navigator.of(context).pop();
... ... @@ -30,7 +30,7 @@ class DrawerWidget extends StatelessWidget {
ListTile(
title: Text('Settings'),
onTap: () {
Get.rootDelegate.toNamed(Routes.SETTINGS);
Get.toNamed(Routes.SETTINGS);
//to close the drawer
Navigator.of(context).pop();
... ... @@ -46,7 +46,7 @@ class DrawerWidget extends StatelessWidget {
),
onTap: () {
AuthService.to.logout();
Get.rootDelegate.toNamed(Routes.LOGIN);
Get.toNamed(Routes.LOGIN);
//to close the drawer
Navigator.of(context).pop();
... ... @@ -61,7 +61,7 @@ class DrawerWidget extends StatelessWidget {
),
),
onTap: () {
Get.rootDelegate.toNamed(Routes.LOGIN);
Get.toNamed(Routes.LOGIN);
//to close the drawer
Navigator.of(context).pop();
... ...
... ... @@ -10,11 +10,11 @@ class RootView extends GetView<RootController> {
Widget build(BuildContext context) {
return GetRouterOutlet.builder(
builder: (context, delegate, current) {
final title = current?.location;
final title = context.location;
return Scaffold(
drawer: DrawerWidget(),
appBar: AppBar(
title: Text(title ?? ''),
title: Text(title),
centerTitle: true,
),
body: GetRouterOutlet(
... ...
... ... @@ -29,7 +29,7 @@ class AppPages {
GetPage(
name: '/',
page: () => RootView(),
binding: RootBinding(),
bindings: [RootBinding()],
participatesInRootNavigator: true,
preventDuplicates: true,
children: [
... ... @@ -40,19 +40,23 @@ class AppPages {
],
name: _Paths.LOGIN,
page: () => LoginView(),
binding: LoginBinding(),
bindings: [LoginBinding()],
),
GetPage(
preventDuplicates: true,
name: _Paths.HOME,
page: () => HomeView(),
binding: HomeBinding(),
bindings: [
HomeBinding(),
],
title: null,
children: [
GetPage(
name: _Paths.DASHBOARD,
page: () => DashboardView(),
binding: DashboardBinding(),
bindings: [
DashboardBinding(),
],
),
GetPage(
middlewares: [
... ... @@ -63,19 +67,19 @@ class AppPages {
page: () => ProfileView(),
title: 'Profile',
transition: Transition.size,
binding: ProfileBinding(),
bindings: [ProfileBinding()],
),
GetPage(
name: _Paths.PRODUCTS,
page: () => ProductsView(),
title: 'Products',
transition: Transition.zoom,
binding: ProductsBinding(),
bindings: [ProductsBinding()],
children: [
GetPage(
name: _Paths.PRODUCT_DETAILS,
page: () => ProductDetailsView(),
binding: ProductDetailsBinding(),
bindings: [ProductDetailsBinding()],
middlewares: [
//only enter this route when authed
EnsureAuthMiddleware(),
... ... @@ -88,7 +92,9 @@ class AppPages {
GetPage(
name: _Paths.SETTINGS,
page: () => SettingsView(),
binding: SettingsBinding(),
bindings: [
SettingsBinding(),
],
),
],
),
... ...
... ... @@ -7,7 +7,6 @@ environment:
dependencies:
cupertino_icons: ^1.0.2
effective_dart: 1.3.1
# get: ^4.1.4
get:
path: ../
... ...
... ... @@ -7,7 +7,7 @@ export 'get_common/get_reset.dart';
export 'get_connect/connect.dart';
export 'get_core/get_core.dart';
export 'get_instance/get_instance.dart';
export 'get_navigation/get_navigation.dart';
export 'get_navigation/get_navigation.dart' hide FirstWhereOrNullExt;
export 'get_rx/get_rx.dart';
export 'get_state_manager/get_state_manager.dart';
export 'get_utils/get_utils.dart';
... ...
... ... @@ -2,11 +2,6 @@ library get_navigation;
export 'src/bottomsheet/bottomsheet.dart';
export 'src/extension_navigation.dart';
export 'src/nav2/get_information_parser.dart';
export 'src/nav2/get_nav_config.dart';
export 'src/nav2/get_navigator.dart';
export 'src/nav2/get_router_delegate.dart';
export 'src/nav2/router_outlet.dart';
export 'src/root/get_cupertino_app.dart';
export 'src/root/get_material_app.dart';
export 'src/root/internacionalization.dart';
... ... @@ -14,6 +9,7 @@ export 'src/root/root_controller.dart';
export 'src/routes/custom_transition.dart';
export 'src/routes/default_route.dart';
export 'src/routes/get_route.dart';
export 'src/routes/index.dart';
export 'src/routes/observers/route_observer.dart';
export 'src/routes/route_middleware.dart';
export 'src/routes/transitions_type.dart';
... ...
... ... @@ -5,16 +5,14 @@ import 'package:flutter/scheduler.dart';
import '../../get_core/get_core.dart';
import '../../get_instance/src/bindings_interface.dart';
import '../../get_state_manager/src/simple/get_state.dart';
import '../../get_utils/get_utils.dart';
import '../get_navigation.dart';
import 'dialog/dialog_route.dart';
import 'root/parse_route.dart';
/// It replaces the Flutter Navigator, but needs no context.
/// You can to use navigator.push(YourRoute()) rather
/// Navigator.push(context, YourRoute());
NavigatorState? get navigator => GetNavigation(Get).key.currentState;
NavigatorState? get navigator => GetNavigationExt(Get).key.currentState;
extension ExtensionBottomSheet on GetInterface {
Future<T?> bottomSheet<T>(
... ... @@ -78,6 +76,7 @@ extension ExtensionDialog on GetInterface {
Curve? transitionCurve,
String? name,
RouteSettings? routeSettings,
dynamic id,
}) {
assert(debugCheckHasMaterialLocalizations(context!));
... ... @@ -110,22 +109,24 @@ extension ExtensionDialog on GetInterface {
navigatorKey: navigatorKey,
routeSettings:
routeSettings ?? RouteSettings(arguments: arguments, name: name),
id: id,
);
}
/// Api from showGeneralDialog with no context
Future<T?> generalDialog<T>({
required RoutePageBuilder pageBuilder,
bool barrierDismissible = false,
String? barrierLabel,
Color barrierColor = const Color(0x80000000),
Duration transitionDuration = const Duration(milliseconds: 200),
RouteTransitionsBuilder? transitionBuilder,
GlobalKey<NavigatorState>? navigatorKey,
RouteSettings? routeSettings,
}) {
Future<T?> generalDialog<T>(
{required RoutePageBuilder pageBuilder,
bool barrierDismissible = false,
String? barrierLabel,
Color barrierColor = const Color(0x80000000),
Duration transitionDuration = const Duration(milliseconds: 200),
RouteTransitionsBuilder? transitionBuilder,
GlobalKey<NavigatorState>? navigatorKey,
RouteSettings? routeSettings,
dynamic id}) {
assert(!barrierDismissible || barrierLabel != null);
final nav = navigatorKey?.currentState ??
final key = navigatorKey ?? Get.nestedKey(id)?.navigatorKey;
final nav = key?.currentState ??
Navigator.of(overlayContext!,
rootNavigator:
true); //overlay context will always return the root navigator
... ... @@ -148,6 +149,7 @@ extension ExtensionDialog on GetInterface {
EdgeInsetsGeometry? titlePadding,
TextStyle? titleStyle,
Widget? content,
dynamic id,
EdgeInsetsGeometry? contentPadding,
VoidCallback? onConfirm,
VoidCallback? onCancel,
... ... @@ -269,6 +271,7 @@ extension ExtensionDialog on GetInterface {
: baseAlertDialog,
barrierDismissible: barrierDismissible,
navigatorKey: navigatorKey,
id: id,
);
}
}
... ... @@ -474,7 +477,7 @@ extension ExtensionSnackbar on GetInterface {
}
}
extension GetNavigation on GetInterface {
extension GetNavigationExt on GetInterface {
/// **Navigation.push()** shortcut.<br><br>
///
/// Pushes a new `page` to the stack
... ... @@ -499,66 +502,60 @@ extension GetNavigation on GetInterface {
///
/// By default, GetX will prevent you from push a route that you already in,
/// if you want to push anyway, set [preventDuplicates] to false
Future<T?>? to<T>(
dynamic page, {
bool? opaque,
Transition? transition,
Curve? curve,
Duration? duration,
int? id,
String? routeName,
bool fullscreenDialog = false,
dynamic arguments,
Binding? binding,
bool preventDuplicates = true,
bool? popGesture,
bool showCupertinoParallax = true,
double Function(BuildContext context)? gestureWidth,
}) {
// var routeName = "/${page.runtimeType}";
routeName ??= "/${page.runtimeType}";
routeName = _cleanRouteName(routeName);
if (preventDuplicates && routeName == currentRoute) {
return null;
}
return global(id).currentState?.push<T>(
GetPageRoute<T>(
opaque: opaque ?? true,
page: _resolvePage(page, 'to'),
routeName: routeName,
gestureWidth: gestureWidth,
showCupertinoParallax: showCupertinoParallax,
settings: RouteSettings(
name: routeName,
arguments: arguments,
),
popGesture: popGesture ?? defaultPopGesture,
transition: transition ?? defaultTransition,
curve: curve ?? defaultTransitionCurve,
fullscreenDialog: fullscreenDialog,
binding: binding,
transitionDuration: duration ?? defaultTransitionDuration,
),
);
Future<T?>? to<T>(Widget Function() page,
{bool? opaque,
Transition? transition,
Curve? curve,
Duration? duration,
int? id,
String? routeName,
bool fullscreenDialog = false,
dynamic arguments,
List<BindingsInterface>? bindings,
bool preventDuplicates = true,
bool? popGesture,
bool showCupertinoParallax = true,
double Function(BuildContext context)? gestureWidth,
bool rebuildStack = true,
PreventDuplicateHandlingMode preventDuplicateHandlingMode =
PreventDuplicateHandlingMode.ReorderRoutes}) {
searchDelegate(id).to(
page,
opaque: opaque,
transition: transition,
curve: curve,
duration: duration,
id: id,
routeName: routeName,
fullscreenDialog: fullscreenDialog,
arguments: arguments,
bindings: bindings,
preventDuplicates: preventDuplicates,
popGesture: popGesture,
showCupertinoParallax: showCupertinoParallax,
gestureWidth: gestureWidth,
rebuildStack: rebuildStack,
preventDuplicateHandlingMode: preventDuplicateHandlingMode,
);
}
GetPageBuilder _resolvePage(dynamic page, String method) {
if (page is GetPageBuilder) {
return page;
} else if (page is Widget) {
Get.log(
'''WARNING, consider using: "Get.$method(() => Page())" instead of "Get.$method(Page())".
Using a widget function instead of a widget fully guarantees that the widget and its controllers will be removed from memory when they are no longer used.
''');
return () => page;
} else if (page is String) {
throw '''Unexpected String,
use toNamed() instead''';
} else {
throw '''Unexpected format,
you can only use widgets and widget functions here''';
}
}
// GetPageBuilder _resolvePage(dynamic page, String method) {
// if (page is GetPageBuilder) {
// return page;
// } else if (page is Widget) {
// Get.log(
// '''WARNING, consider using: "Get.$method(() => Page())" instead of "Get.$method(Page())".
// Using a widget function instead of a widget fully guarantees that the widget and its controllers will be removed from memory when they are no longer used.
// ''');
// return () => page;
// } else if (page is String) {
// throw '''Unexpected String,
// use toNamed() instead''';
// } else {
// throw '''Unexpected format,
// you can only use widgets and widget functions here''';
// }
// }
/// **Navigation.pushNamed()** shortcut.<br><br>
///
... ... @@ -592,10 +589,13 @@ you can only use widgets and widget functions here''';
page = uri.toString();
}
return global(id).currentState?.pushNamed<T>(
page,
arguments: arguments,
);
return searchDelegate(id).toNamed(
page,
arguments: arguments,
id: id,
preventDuplicates: preventDuplicates,
parameters: parameters,
);
}
/// **Navigation.pushReplacementNamed()** shortcut.<br><br>
... ... @@ -618,21 +618,23 @@ you can only use widgets and widget functions here''';
String page, {
dynamic arguments,
int? id,
bool preventDuplicates = true,
Map<String, String>? parameters,
}) {
if (preventDuplicates && page == currentRoute) {
return null;
}
// if (preventDuplicates && page == currentRoute) {
// return null;
// }
if (parameters != null) {
final uri = Uri(path: page, queryParameters: parameters);
page = uri.toString();
}
return global(id).currentState?.pushReplacementNamed(
page,
arguments: arguments,
);
return searchDelegate(id).offNamed(
page,
arguments: arguments,
id: id,
// preventDuplicates: preventDuplicates,
parameters: parameters,
);
}
/// **Navigation.popUntil()** shortcut.<br><br>
... ... @@ -648,34 +650,10 @@ you can only use widgets and widget functions here''';
/// or also like this:
/// `Get.until((route) => !Get.isDialogOpen())`, to make sure the
/// dialog is closed
void until(RoutePredicate predicate, {int? id}) {
void until(bool Function(GetPage<dynamic>) predicate, {int? id}) {
// if (key.currentState.mounted) // add this if appear problems on future with route navigate
// when widget don't mounted
return global(id).currentState?.popUntil(predicate);
}
/// **Navigation.pushAndRemoveUntil()** shortcut.<br><br>
///
/// Push the given `page`, and then pop several pages in the stack until
/// [predicate] returns true
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
///
/// Obs: unlike other get methods, this one you need to send a function
/// that returns the widget to the page argument, like this:
/// Get.offUntil(GetPageRoute(page: () => HomePage()), predicate)
///
/// [predicate] can be used like this:
/// `Get.offUntil(page, (route) => (route as GetPageRoute).routeName == '/home')`
/// to pop routes in stack until home,
/// or also like this:
/// `Get.until((route) => !Get.isDialogOpen())`, to make sure the dialog
/// is closed
Future<T?>? offUntil<T>(Route<T> page, RoutePredicate predicate, {int? id}) {
// if (key.currentState.mounted) // add this if appear problems on future with route navigate
// when widget don't mounted
return global(id).currentState?.pushAndRemoveUntil<T>(page, predicate);
return searchDelegate(id).backUntil(predicate);
}
/// **Navigation.pushNamedAndRemoveUntil()** shortcut.<br><br>
... ... @@ -698,7 +676,7 @@ you can only use widgets and widget functions here''';
/// Note: Always put a slash on the route name ('/page1'), to avoid unexpected errors
Future<T?>? offNamedUntil<T>(
String page,
RoutePredicate predicate, {
bool Function(GetPage<dynamic>)? predicate, {
int? id,
dynamic arguments,
Map<String, String>? parameters,
... ... @@ -708,11 +686,13 @@ you can only use widgets and widget functions here''';
page = uri.toString();
}
return global(id).currentState?.pushNamedAndRemoveUntil<T>(
page,
predicate,
arguments: arguments,
);
return searchDelegate(id).offNamedUntil<T>(
page,
predicate: predicate,
id: id,
arguments: arguments,
parameters: parameters,
);
}
/// **Navigation.popAndPushNamed()** shortcut.<br><br>
... ... @@ -737,11 +717,11 @@ you can only use widgets and widget functions here''';
final uri = Uri(path: page, queryParameters: parameters);
page = uri.toString();
}
return global(id).currentState?.popAndPushNamed(
page,
arguments: arguments,
result: result,
);
return searchDelegate(id).backAndtoNamed(
page,
arguments: arguments,
result: result,
);
}
/// **Navigation.removeRoute()** shortcut.<br><br>
... ... @@ -750,8 +730,8 @@ you can only use widgets and widget functions here''';
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
void removeRoute(Route<dynamic> route, {int? id}) {
return global(id).currentState?.removeRoute(route);
void removeRoute(String name, {int? id}) {
return searchDelegate(id).removeRoute(name);
}
/// **Navigation.pushNamedAndRemoveUntil()** shortcut.<br><br>
... ... @@ -776,7 +756,7 @@ you can only use widgets and widget functions here''';
/// Note: Always put a slash on the route ('/page1'), to avoid unexpected errors
Future<T?>? offAllNamed<T>(
String newRouteName, {
RoutePredicate? predicate,
// bool Function(GetPage<dynamic>)? predicate,
dynamic arguments,
int? id,
Map<String, String>? parameters,
... ... @@ -786,11 +766,13 @@ you can only use widgets and widget functions here''';
newRouteName = uri.toString();
}
return global(id).currentState?.pushNamedAndRemoveUntil<T>(
newRouteName,
predicate ?? (_) => false,
arguments: arguments,
);
return searchDelegate(id).offAllNamed<T>(
newRouteName,
//predicate: predicate ?? (_) => false,
arguments: arguments,
id: id,
parameters: parameters,
);
}
/// Returns true if a Snackbar, Dialog or BottomSheet is currently OPEN
... ... @@ -819,6 +801,13 @@ you can only use widgets and widget functions here''';
bool canPop = true,
int? id,
}) {
//TODO: remove this when change own api to Dialog and BottomSheets
//to declarative way
if (isDialogOpen! || isBottomSheetOpen!) {
searchDelegate(id).navigatorKey.currentState?.pop();
return;
}
//TODO: This code brings compatibility of the new snackbar with GetX 4,
// remove this code in version 5
if (isSnackbarOpen && !closeOverlays) {
... ... @@ -832,16 +821,21 @@ you can only use widgets and widget functions here''';
if (isSnackbarOpen) {
closeAllSnackbars();
}
navigator?.popUntil((route) {
return (!isDialogOpen! && !isBottomSheetOpen!);
});
while ((isDialogOpen! && isBottomSheetOpen!)) {
searchDelegate(id).navigatorKey.currentState?.pop();
}
// navigator?.popUntil((route) {
// return;
// });
}
if (canPop) {
if (global(id).currentState?.canPop() == true) {
global(id).currentState?.pop<T>(result);
if (searchDelegate(id).canBack == true) {
searchDelegate(id).back<T>(result);
}
} else {
global(id).currentState?.pop<T>(result);
searchDelegate(id).back<T>(result);
}
}
... ... @@ -856,7 +850,7 @@ you can only use widgets and widget functions here''';
times = 1;
}
var count = 0;
var back = global(id).currentState?.popUntil((route) => count++ == times);
var back = searchDelegate(id).backUntil((route) => count++ == times);
return back;
}
... ... @@ -887,15 +881,15 @@ you can only use widgets and widget functions here''';
/// By default, GetX will prevent you from push a route that you already in,
/// if you want to push anyway, set [preventDuplicates] to false
Future<T?>? off<T>(
dynamic page, {
bool opaque = false,
Widget Function() page, {
bool? opaque,
Transition? transition,
Curve? curve,
bool? popGesture,
int? id,
String? routeName,
dynamic arguments,
Binding? binding,
List<BindingsInterface>? bindings,
bool fullscreenDialog = false,
bool preventDuplicates = true,
Duration? duration,
... ... @@ -906,21 +900,34 @@ you can only use widgets and widget functions here''';
if (preventDuplicates && routeName == currentRoute) {
return null;
}
return global(id).currentState?.pushReplacement(GetPageRoute(
opaque: opaque,
gestureWidth: gestureWidth,
page: _resolvePage(page, 'off'),
binding: binding,
settings: RouteSettings(
arguments: arguments,
name: routeName,
),
routeName: routeName,
fullscreenDialog: fullscreenDialog,
popGesture: popGesture ?? defaultPopGesture,
transition: transition ?? defaultTransition,
curve: curve ?? defaultTransitionCurve,
transitionDuration: duration ?? defaultTransitionDuration));
return searchDelegate(id).off(
page,
opaque: opaque ?? true,
transition: transition,
curve: curve,
popGesture: popGesture,
id: id,
routeName: routeName,
arguments: arguments,
bindings: bindings,
fullscreenDialog: fullscreenDialog,
preventDuplicates: preventDuplicates,
duration: duration,
gestureWidth: gestureWidth,
);
}
Future<T?> offUntil<T>(
Widget Function() page,
bool Function(GetPage) predicate, [
Object? arguments,
int? id,
]) {
return searchDelegate(id).offUntil(
page,
predicate,
arguments,
);
}
///
... ... @@ -954,14 +961,14 @@ you can only use widgets and widget functions here''';
/// By default, GetX will prevent you from push a route that you already in,
/// if you want to push anyway, set [preventDuplicates] to false
Future<T?>? offAll<T>(
dynamic page, {
RoutePredicate? predicate,
bool opaque = false,
Widget Function() page, {
bool Function(GetPage<dynamic>)? predicate,
bool? opaque,
bool? popGesture,
int? id,
String? routeName,
dynamic arguments,
Binding? binding,
List<BindingsInterface>? bindings,
bool fullscreenDialog = false,
Transition? transition,
Curve? curve,
... ... @@ -970,24 +977,21 @@ you can only use widgets and widget functions here''';
}) {
routeName ??= "/${page.runtimeType.toString()}";
routeName = _cleanRouteName(routeName);
return global(id).currentState?.pushAndRemoveUntil<T>(
GetPageRoute<T>(
opaque: opaque,
popGesture: popGesture ?? defaultPopGesture,
page: _resolvePage(page, 'offAll'),
binding: binding,
gestureWidth: gestureWidth,
settings: RouteSettings(
name: routeName,
arguments: arguments,
),
fullscreenDialog: fullscreenDialog,
routeName: routeName,
transition: transition ?? defaultTransition,
curve: curve ?? defaultTransitionCurve,
transitionDuration: duration ?? defaultTransitionDuration,
),
predicate ?? (route) => false);
return searchDelegate(id).offAll<T>(
page,
predicate: predicate,
opaque: opaque ?? true,
popGesture: popGesture,
id: id,
// routeName routeName,
arguments: arguments,
bindings: bindings,
fullscreenDialog: fullscreenDialog,
transition: transition,
curve: curve,
duration: duration,
gestureWidth: gestureWidth,
);
}
/// Takes a route [name] String generated by [to], [off], [offAll]
... ... @@ -1072,20 +1076,21 @@ you can only use widgets and widget functions here''';
return _getxController.addKey(newKey);
}
GlobalKey<NavigatorState>? nestedKey(dynamic key) {
GetDelegate? nestedKey(dynamic key) {
keys.putIfAbsent(
key,
() => GlobalKey<NavigatorState>(
debugLabel: 'Getx nested key: ${key.toString()}',
() => GetDelegate(
//debugLabel: 'Getx nested key: ${key.toString()}',
pages: [],
),
);
return keys[key];
}
GlobalKey<NavigatorState> global(int? k) {
GlobalKey<NavigatorState> _key;
GetDelegate searchDelegate(int? k) {
GetDelegate _key;
if (k == null) {
_key = key;
_key = Get.rootController.rootDelegate;
} else {
if (!keys.containsKey(k)) {
throw 'Route id ($k) not found';
... ... @@ -1093,21 +1098,22 @@ you can only use widgets and widget functions here''';
_key = keys[k]!;
}
if (_key.currentContext == null && !testMode) {
throw """You are trying to use contextless navigation without
a GetMaterialApp or Get.key.
If you are testing your app, you can use:
[Get.testMode = true], or if you are running your app on
a physical device or emulator, you must exchange your [MaterialApp]
for a [GetMaterialApp].
""";
}
// if (_key.listenersLength == 0 && !testMode) {
// throw """You are trying to use contextless navigation without
// a GetMaterialApp or Get.key.
// If you are testing your app, you can use:
// [Get.testMode = true], or if you are running your app on
// a physical device or emulator, you must exchange your [MaterialApp]
// for a [GetMaterialApp].
// """;
// }
return _key;
}
/// give current arguments
dynamic get arguments => routing.args;
//dynamic get arguments => routing.args;
dynamic get arguments => _getxController.rootDelegate.arguments();
/// give name from current route
String get currentRoute => routing.current;
... ... @@ -1225,7 +1231,7 @@ you can only use widgets and widget functions here''';
GlobalKey<NavigatorState> get key => _getxController.key;
Map<dynamic, GlobalKey<NavigatorState>> get keys => _getxController.keys;
Map<dynamic, GetDelegate> get keys => _getxController.keys;
GetMaterialController get rootController => _getxController;
... ... @@ -1250,7 +1256,8 @@ you can only use widgets and widget functions here''';
Routing get routing => _getxController.routing;
Map<String, String?> get parameters => _getxController.parameters;
Map<String, String?> get parameters =>
_getxController.rootDelegate.parameters;
set parameters(Map<String, String?> newParameters) =>
_getxController.parameters = newParameters;
... ... @@ -1280,10 +1287,15 @@ extension NavTwoExt on GetInterface {
static late final _routeTree = ParseRouteTree(routes: []);
ParseRouteTree get routeTree => _routeTree;
void addPage(GetPage getPage) {
routeTree.addRoute(getPage);
}
void removePage(GetPage getPage) {
routeTree.removeRoute(getPage);
}
/// Casts the stored router delegate to a desired type
TDelegate? delegate<TDelegate extends RouterDelegate<TPage>, TPage>() =>
routerDelegate as TDelegate?;
... ... @@ -1307,15 +1319,15 @@ extension NavTwoExt on GetInterface {
// static GetDelegate? _delegate;
GetDelegate get rootDelegate => createDelegate();
GetDelegate createDelegate({
GetPage<dynamic>? notFoundRoute,
List<GetPage> pages = const [],
List<NavigatorObserver>? navigatorObservers,
TransitionDelegate<dynamic>? transitionDelegate,
PopMode backButtonPopMode = PopMode.History,
PreventDuplicateHandlingMode preventDuplicateHandlingMode =
PreventDuplicateHandlingMode.ReorderRoutes,
GlobalKey<NavigatorState>? navigatorKey,
}) {
if (routerDelegate == null) {
return routerDelegate = GetDelegate(
... ... @@ -1324,6 +1336,8 @@ extension NavTwoExt on GetInterface {
transitionDelegate: transitionDelegate,
backButtonPopMode: backButtonPopMode,
preventDuplicateHandlingMode: preventDuplicateHandlingMode,
pages: pages,
navigatorKey: navigatorKey,
);
} else {
return routerDelegate as GetDelegate;
... ...
import 'package:flutter/widgets.dart';
import '../../../get.dart';
// class GetRouterState extends GetxController {
// GetRouterState({required this.currentTreeBranch});
// final List<GetPage> currentTreeBranch;
// GetPage? get currentPage => currentTreeBranch.last;
// static GetNavConfig? fromRoute(String route) {
// final res = Get.routeTree.matchRoute(route);
// if (res.treeBranch.isEmpty) return null;
// return GetNavConfig(
// currentTreeBranch: res.treeBranch,
// location: route,
// state: null,
// );
// }
// }
/// This config enables us to navigate directly to a sub-url
class GetNavConfig extends RouteInformation {
final List<GetPage> currentTreeBranch;
GetPage? get currentPage => currentTreeBranch.last;
GetNavConfig({
required this.currentTreeBranch,
required String? location,
required Object? state,
}) : super(
location: location,
state: state,
);
GetNavConfig copyWith({
List<GetPage>? currentTreeBranch,
required String? location,
required Object? state,
}) {
return GetNavConfig(
currentTreeBranch: currentTreeBranch ?? this.currentTreeBranch,
location: location ?? this.location,
state: state ?? this.state,
);
}
static GetNavConfig? fromRoute(String route) {
final res = Get.routeTree.matchRoute(route);
if (res.treeBranch.isEmpty) return null;
return GetNavConfig(
currentTreeBranch: res.treeBranch,
location: route,
state: null,
);
}
@override
String toString() => '''
======GetNavConfig=====\nlocation: $location\ncurrentTreeBranch: $currentTreeBranch\n======GetNavConfig=====''';
}
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import '../../../get.dart';
import '../../../get_state_manager/src/simple/list_notifier.dart';
/// Enables the user to customize the intended pop behavior
///
/// Goes to either the previous history entry or the previous page entry
///
/// e.g. if the user navigates to these pages
/// 1) /home
/// 2) /home/products/1234
///
/// when popping on [History] mode, it will emulate a browser back button.
///
/// so the new history stack will be:
/// 1) /home
///
/// when popping on [Page] mode, it will only remove the last part of the route
/// so the new history stack will be:
/// 1) /home
/// 2) /home/products
///
/// another pop will change the history stack to:
/// 1) /home
enum PopMode {
History,
Page,
}
/// Enables the user to customize the behavior when pushing multiple routes that
/// shouldn't be duplicates
enum PreventDuplicateHandlingMode {
/// Removes the history entries until it reaches the old route
PopUntilOriginalRoute,
/// Simply don't push the new route
DoNothing,
/// Recommended - Moves the old route entry to the front
///
/// With this mode, you guarantee there will be only one
/// route entry for each location
ReorderRoutes
}
class GetDelegate extends RouterDelegate<GetNavConfig>
with ListNotifierSingleMixin {
final List<GetNavConfig> history = <GetNavConfig>[];
final PopMode backButtonPopMode;
final PreventDuplicateHandlingMode preventDuplicateHandlingMode;
final GetPage notFoundRoute;
final List<NavigatorObserver>? navigatorObservers;
final TransitionDelegate<dynamic>? transitionDelegate;
final Iterable<GetPage> Function(GetNavConfig currentNavStack)?
pickPagesForRootNavigator;
GlobalKey<NavigatorState> get navigatorKey => Get.key;
GetDelegate({
GetPage? notFoundRoute,
this.navigatorObservers,
this.transitionDelegate,
this.backButtonPopMode = PopMode.History,
this.preventDuplicateHandlingMode =
PreventDuplicateHandlingMode.ReorderRoutes,
this.pickPagesForRootNavigator,
}) : notFoundRoute = notFoundRoute ??
GetPage(
name: '/404',
page: () => Scaffold(
body: Text('Route not found'),
),
) {
Get.log('GetDelegate is created !');
}
Future<GetNavConfig?> runMiddleware(GetNavConfig config) async {
final middlewares = config.currentTreeBranch.last.middlewares;
if (middlewares == null) {
return config;
}
var iterator = config;
for (var item in middlewares) {
var redirectRes = await item.redirectDelegate(iterator);
if (redirectRes == null) return null;
iterator = redirectRes;
}
return iterator;
}
Future<void> _unsafeHistoryAdd(GetNavConfig config) async {
final res = await runMiddleware(config);
if (res == null) return;
history.add(res);
}
Future<void> _unsafeHistoryRemove(GetNavConfig config) async {
var index = history.indexOf(config);
if (index >= 0) await _unsafeHistoryRemoveAt(index);
}
Future<GetNavConfig?> _unsafeHistoryRemoveAt(int index) async {
if (index == history.length - 1 && history.length > 1) {
//removing WILL update the current route
final toCheck = history[history.length - 2];
final resMiddleware = await runMiddleware(toCheck);
if (resMiddleware == null) return null;
history[history.length - 2] = resMiddleware;
}
return history.removeAt(index);
}
T arguments<T>() {
return currentConfiguration?.currentPage?.arguments as T;
}
Map<String, String> get parameters {
return currentConfiguration?.currentPage?.parameters ?? {};
}
/// Adds a new history entry and waits for the result
Future<void> pushHistory(
GetNavConfig config, {
bool rebuildStack = true,
}) async {
//this changes the currentConfiguration
await _pushHistory(config);
if (rebuildStack) {
refresh();
}
}
Future<void> _removeHistoryEntry(GetNavConfig entry) async {
await _unsafeHistoryRemove(entry);
}
Future<void> _pushHistory(GetNavConfig config) async {
if (config.currentPage!.preventDuplicates) {
final originalEntryIndex =
history.indexWhere((element) => element.location == config.location);
if (originalEntryIndex >= 0) {
switch (preventDuplicateHandlingMode) {
case PreventDuplicateHandlingMode.PopUntilOriginalRoute:
await backUntil(config.location!, popMode: PopMode.Page);
break;
case PreventDuplicateHandlingMode.ReorderRoutes:
await _unsafeHistoryRemoveAt(originalEntryIndex);
await _unsafeHistoryAdd(config);
break;
case PreventDuplicateHandlingMode.DoNothing:
default:
break;
}
return;
}
}
await _unsafeHistoryAdd(config);
}
Future<GetNavConfig?> _popHistory() async {
if (!_canPopHistory()) return null;
return await _doPopHistory();
}
Future<GetNavConfig?> _doPopHistory() async {
return await _unsafeHistoryRemoveAt(history.length - 1);
}
Future<GetNavConfig?> _popPage() async {
if (!_canPopPage()) return null;
return await _doPopPage();
}
Future<GetNavConfig?> _pop(PopMode mode) async {
switch (mode) {
case PopMode.History:
return await _popHistory();
case PopMode.Page:
return await _popPage();
default:
return null;
}
}
// returns the popped page
Future<GetNavConfig?> _doPopPage() async {
final currentBranch = currentConfiguration?.currentTreeBranch;
if (currentBranch != null && currentBranch.length > 1) {
//remove last part only
final remaining = currentBranch.take(currentBranch.length - 1);
final prevHistoryEntry =
history.length > 1 ? history[history.length - 2] : null;
//check if current route is the same as the previous route
if (prevHistoryEntry != null) {
//if so, pop the entire history entry
final newLocation = remaining.last.name;
final prevLocation = prevHistoryEntry.location;
if (newLocation == prevLocation) {
//pop the entire history entry
return await _popHistory();
}
}
//create a new route with the remaining tree branch
final res = await _popHistory();
await _pushHistory(
GetNavConfig(
currentTreeBranch: remaining.toList(),
location: remaining.last.name,
state: null, //TOOD: persist state??
),
);
return res;
} else {
//remove entire entry
return await _popHistory();
}
}
Future<GetNavConfig?> popHistory() async {
return await _popHistory();
}
bool _canPopHistory() {
return history.length > 1;
}
Future<bool> canPopHistory() {
return SynchronousFuture(_canPopHistory());
}
bool _canPopPage() {
final currentTreeBranch = currentConfiguration?.currentTreeBranch;
if (currentTreeBranch == null) return false;
return currentTreeBranch.length > 1 ? true : _canPopHistory();
}
Future<bool> canPopPage() {
return SynchronousFuture(_canPopPage());
}
bool _canPop(PopMode mode) {
switch (mode) {
case PopMode.History:
return _canPopHistory();
case PopMode.Page:
default:
return _canPopPage();
}
}
/// gets the visual pages from the current history entry
///
/// visual pages must have [GetPage.participatesInRootNavigator] set to true
Iterable<GetPage> getVisualPages(GetNavConfig currentHistory) {
final res = currentHistory.currentTreeBranch
.where((r) => r.participatesInRootNavigator != null);
if (res.length == 0) {
//default behavoir, all routes participate in root navigator
return history.map((e) => e.currentPage!);
} else {
//user specified at least one participatesInRootNavigator
return res
.where((element) => element.participatesInRootNavigator == true);
}
}
@override
Widget build(BuildContext context) {
final currentHistory = currentConfiguration;
final pages = currentHistory == null
? <GetPage>[]
: pickPagesForRootNavigator?.call(currentHistory) ??
getVisualPages(currentHistory);
if (pages.length == 0) return SizedBox.shrink();
return GetNavigator(
key: navigatorKey,
onPopPage: _onPopVisualRoute,
pages: pages.toList(),
observers: [
GetObserver(),
...?navigatorObservers,
],
transitionDelegate:
transitionDelegate ?? const DefaultTransitionDelegate<dynamic>(),
);
}
@override
Future<void> setNewRoutePath(GetNavConfig configuration) async {
await pushHistory(configuration);
}
@override
GetNavConfig? get currentConfiguration {
if (history.isEmpty) return null;
final route = history.last;
return route;
}
Future<void> toNamed(
String page, {
dynamic arguments,
Map<String, String>? parameters,
}) async {
if (parameters != null) {
final uri = Uri(path: page, queryParameters: parameters);
page = uri.toString();
}
final decoder = Get.routeTree.matchRoute(page, arguments: arguments);
decoder.replaceArguments(arguments);
if (decoder.route != null) {
await pushHistory(
GetNavConfig(
currentTreeBranch: decoder.treeBranch,
location: page,
state: null, //TODO: persist state?
),
);
} else {
await pushHistory(
GetNavConfig(
currentTreeBranch: [notFoundRoute],
location: notFoundRoute.name,
state: null, //TODO: persist state?
),
);
}
}
//pops the previous route (if there is one) and goes to new route
Future<void> offNamed(
String page, {
dynamic arguments,
Map<String, String>? parameters,
PopMode popMode = PopMode.History,
}) async {
await popRoute(popMode: popMode);
return toNamed(page, arguments: arguments, parameters: parameters);
}
/// Removes routes according to [PopMode]
/// until it reaches the specifc [fullRoute],
/// DOES NOT remove the [fullRoute]
Future<void> backUntil(
String fullRoute, {
PopMode popMode = PopMode.History,
}) async {
// remove history or page entries until you meet route
var iterator = currentConfiguration;
while (_canPop(popMode) &&
iterator != null &&
iterator.location != fullRoute) {
await _pop(popMode);
// replace iterator
iterator = currentConfiguration;
}
refresh();
}
Future<bool> handlePopupRoutes({
Object? result,
}) async {
Route? currentRoute;
navigatorKey.currentState!.popUntil((route) {
currentRoute = route;
return true;
});
if (currentRoute is PopupRoute) {
return await navigatorKey.currentState!.maybePop(result);
}
return false;
}
@override
Future<bool> popRoute({
Object? result,
PopMode? popMode,
}) async {
//Returning false will cause the entire app to be popped.
final wasPopup = await handlePopupRoutes(result: result);
if (wasPopup) return true;
final _popped = await _pop(popMode ?? backButtonPopMode);
refresh();
if (_popped != null) {
//emulate the old pop with result
return true;
}
return false;
}
bool _onPopVisualRoute(Route<dynamic> route, dynamic result) {
final didPop = route.didPop(result);
if (!didPop) {
return false;
}
final settings = route.settings;
if (settings is GetPage) {
final config = history.cast<GetNavConfig?>().firstWhere(
(element) => element?.currentPage == settings,
orElse: () => null,
);
if (config != null) {
_removeHistoryEntry(config);
}
}
refresh();
return true;
}
}
... ... @@ -62,7 +62,7 @@ class GetCupertinoApp extends StatelessWidget {
final BackButtonDispatcher? backButtonDispatcher;
final CupertinoThemeData? theme;
final bool useInheritedMediaQuery;
const GetCupertinoApp({
GetCupertinoApp({
Key? key,
this.theme,
this.navigatorKey,
... ... @@ -115,17 +115,28 @@ class GetCupertinoApp extends StatelessWidget {
this.highContrastDarkTheme,
this.actions,
}) : routeInformationProvider = null,
backButtonDispatcher = null,
routeInformationParser = null,
routerDelegate = null,
backButtonDispatcher = null,
super(key: key);
static String _cleanRouteName(String name) {
name = name.replaceAll('() => ', '');
/// uncommonent for URL styling.
// name = name.paramCase!;
if (!name.startsWith('/')) {
name = '/$name';
}
return Uri.tryParse(name)?.toString() ?? name;
}
GetCupertinoApp.router({
Key? key,
this.theme,
this.routeInformationProvider,
RouteInformationParser<Object>? routeInformationParser,
RouterDelegate<Object>? routerDelegate,
this.routeInformationParser,
this.routerDelegate,
this.backButtonDispatcher,
this.builder,
this.title = '',
... ... @@ -165,32 +176,27 @@ class GetCupertinoApp extends StatelessWidget {
this.transitionDuration,
this.defaultGlobalState,
this.getPages,
this.navigatorObservers,
this.unknownRoute,
}) : routerDelegate = routerDelegate ??= Get.createDelegate(
notFoundRoute: unknownRoute,
),
routeInformationParser =
routeInformationParser ??= Get.createInformationParser(
initialRoute: getPages?.first.name ?? '/',
),
navigatorObservers = null,
navigatorKey = null,
}) : navigatorKey = null,
onGenerateRoute = null,
home = null,
onGenerateInitialRoutes = null,
onUnknownRoute = null,
routes = null,
initialRoute = null,
super(key: key) {
Get.routerDelegate = routerDelegate;
Get.routeInformationParser = routeInformationParser;
}
super(key: key);
@override
Widget build(BuildContext context) => GetBuilder<GetMaterialController>(
init: Get.rootController,
dispose: (d) {
onDispose?.call();
Get.clearRouteTree();
Get.clearTranslations();
Get.resetRootNavigator();
Get.routerDelegate = null;
Get.routeInformationParser = null;
},
initState: (i) {
Get.engine!.addPostFrameCallback((timeStamp) {
... ... @@ -211,6 +217,13 @@ class GetCupertinoApp extends StatelessWidget {
initialBinding?.dependencies();
if (getPages != null) {
Get.addPages(getPages!);
} else {
Get.addPage(
GetPage(
name: _cleanRouteName("/${home.runtimeType}"),
page: () => home!,
),
);
}
Get.smartManagement = smartManagement;
... ... @@ -226,72 +239,50 @@ class GetCupertinoApp extends StatelessWidget {
transitionDuration ?? Get.defaultTransitionDuration,
);
},
builder: (_) => routerDelegate != null
? CupertinoApp.router(
routerDelegate: routerDelegate!,
routeInformationParser: routeInformationParser!,
backButtonDispatcher: backButtonDispatcher,
routeInformationProvider: routeInformationProvider,
key: _.unikey,
theme: theme,
builder: defaultBuilder,
title: title,
onGenerateTitle: onGenerateTitle,
color: color,
locale: Get.locale ?? locale,
localizationsDelegates: localizationsDelegates,
localeListResolutionCallback: localeListResolutionCallback,
localeResolutionCallback: localeResolutionCallback,
supportedLocales: supportedLocales,
showPerformanceOverlay: showPerformanceOverlay,
checkerboardRasterCacheImages: checkerboardRasterCacheImages,
checkerboardOffscreenLayers: checkerboardOffscreenLayers,
showSemanticsDebugger: showSemanticsDebugger,
debugShowCheckedModeBanner: debugShowCheckedModeBanner,
shortcuts: shortcuts,
useInheritedMediaQuery: useInheritedMediaQuery,
)
: CupertinoApp(
key: _.unikey,
theme: theme,
navigatorKey: (navigatorKey == null
? Get.key
: Get.addKey(navigatorKey!)),
home: home,
routes: routes ?? const <String, WidgetBuilder>{},
initialRoute: initialRoute,
onGenerateRoute:
(getPages != null ? generator : onGenerateRoute),
onGenerateInitialRoutes: (getPages == null || home != null)
? onGenerateInitialRoutes
: initialRoutesGenerate,
onUnknownRoute: onUnknownRoute,
navigatorObservers: (navigatorObservers == null
? <NavigatorObserver>[
GetObserver(routingCallback, Get.routing)
]
: <NavigatorObserver>[
GetObserver(routingCallback, Get.routing)
]
..addAll(navigatorObservers!)),
builder: defaultBuilder,
title: title,
onGenerateTitle: onGenerateTitle,
color: color,
locale: Get.locale ?? locale,
localizationsDelegates: localizationsDelegates,
localeListResolutionCallback: localeListResolutionCallback,
localeResolutionCallback: localeResolutionCallback,
supportedLocales: supportedLocales,
showPerformanceOverlay: showPerformanceOverlay,
checkerboardRasterCacheImages: checkerboardRasterCacheImages,
checkerboardOffscreenLayers: checkerboardOffscreenLayers,
showSemanticsDebugger: showSemanticsDebugger,
debugShowCheckedModeBanner: debugShowCheckedModeBanner,
shortcuts: shortcuts,
useInheritedMediaQuery: useInheritedMediaQuery,
// actions: actions,
),
builder: (_) {
final routerDelegate = Get.createDelegate(
pages: getPages ?? [],
notFoundRoute: unknownRoute,
navigatorKey: navigatorKey,
navigatorObservers: (navigatorObservers == null
? <NavigatorObserver>[
GetObserver(routingCallback, Get.routing)
]
: <NavigatorObserver>[
GetObserver(routingCallback, Get.routing)
]
..addAll(navigatorObservers!)));
final routeInformationParser = Get.createInformationParser(
initialRoute: initialRoute ??
getPages?.first.name ??
_cleanRouteName("/${home.runtimeType}"),
);
return CupertinoApp.router(
routerDelegate: routerDelegate,
routeInformationParser: routeInformationParser,
backButtonDispatcher: backButtonDispatcher,
routeInformationProvider: routeInformationProvider,
key: _.unikey,
theme: theme,
builder: defaultBuilder,
title: title,
onGenerateTitle: onGenerateTitle,
color: color,
locale: Get.locale ?? locale,
localizationsDelegates: localizationsDelegates,
localeListResolutionCallback: localeListResolutionCallback,
localeResolutionCallback: localeResolutionCallback,
supportedLocales: supportedLocales,
showPerformanceOverlay: showPerformanceOverlay,
checkerboardRasterCacheImages: checkerboardRasterCacheImages,
checkerboardOffscreenLayers: checkerboardOffscreenLayers,
showSemanticsDebugger: showSemanticsDebugger,
debugShowCheckedModeBanner: debugShowCheckedModeBanner,
shortcuts: shortcuts,
useInheritedMediaQuery: useInheritedMediaQuery,
);
},
);
Widget defaultBuilder(BuildContext context, Widget? child) {
... ...
... ... @@ -66,7 +66,7 @@ class GetMaterialApp extends StatelessWidget {
final RouterDelegate<Object>? routerDelegate;
final BackButtonDispatcher? backButtonDispatcher;
final bool useInheritedMediaQuery;
const GetMaterialApp({
GetMaterialApp({
Key? key,
this.navigatorKey,
this.scaffoldMessengerKey,
... ... @@ -124,17 +124,28 @@ class GetMaterialApp extends StatelessWidget {
this.highContrastDarkTheme,
this.actions,
}) : routeInformationProvider = null,
backButtonDispatcher = null,
routeInformationParser = null,
routerDelegate = null,
backButtonDispatcher = null,
super(key: key);
static String _cleanRouteName(String name) {
name = name.replaceAll('() => ', '');
/// uncommonent for URL styling.
// name = name.paramCase!;
if (!name.startsWith('/')) {
name = '/$name';
}
return Uri.tryParse(name)?.toString() ?? name;
}
GetMaterialApp.router({
Key? key,
this.routeInformationProvider,
this.scaffoldMessengerKey,
RouteInformationParser<Object>? routeInformationParser,
RouterDelegate<Object>? routerDelegate,
this.routeInformationParser,
this.routerDelegate,
this.backButtonDispatcher,
this.builder,
this.title = '',
... ... @@ -181,148 +192,114 @@ class GetMaterialApp extends StatelessWidget {
this.getPages,
this.navigatorObservers,
this.unknownRoute,
}) : routerDelegate = routerDelegate ??= Get.createDelegate(
notFoundRoute: unknownRoute,
),
routeInformationParser =
routeInformationParser ??= Get.createInformationParser(
initialRoute: getPages?.first.name ?? '/',
),
//navigatorObservers = null,
navigatorKey = null,
}) : navigatorKey = null,
onGenerateRoute = null,
home = null,
onGenerateInitialRoutes = null,
onUnknownRoute = null,
routes = null,
initialRoute = null,
super(key: key) {
Get.routerDelegate = routerDelegate;
Get.routeInformationParser = routeInformationParser;
}
super(key: key);
@override
Widget build(BuildContext context) => GetBuilder<GetMaterialController>(
init: Get.rootController,
dispose: (d) {
onDispose?.call();
},
initState: (i) {
Get.engine!.addPostFrameCallback((timeStamp) {
onReady?.call();
});
if (locale != null) Get.locale = locale;
init: Get.rootController,
dispose: (d) {
onDispose?.call();
Get.clearRouteTree();
Get.clearTranslations();
Get.resetRootNavigator();
Get.routerDelegate = null;
Get.routeInformationParser = null;
},
initState: (i) {
// Get.routerDelegate = routerDelegate;
// Get.routeInformationParser = routeInformationParser;
Get.engine!.addPostFrameCallback((timeStamp) {
onReady?.call();
});
if (locale != null) Get.locale = locale;
if (fallbackLocale != null) Get.fallbackLocale = fallbackLocale;
if (fallbackLocale != null) Get.fallbackLocale = fallbackLocale;
if (translations != null) {
Get.addTranslations(translations!.keys);
} else if (translationsKeys != null) {
Get.addTranslations(translationsKeys!);
}
if (translations != null) {
Get.addTranslations(translations!.keys);
} else if (translationsKeys != null) {
Get.addTranslations(translationsKeys!);
}
Get.customTransition = customTransition;
Get.customTransition = customTransition;
initialBinding?.dependencies();
if (getPages != null) {
Get.addPages(getPages!);
}
initialBinding?.dependencies();
if (getPages != null) {
Get.addPages(getPages!);
} else {
Get.addPage(
GetPage(
name: _cleanRouteName("/${home.runtimeType}"),
page: () => home!,
),
);
}
//Get.setDefaultDelegate(routerDelegate);
Get.smartManagement = smartManagement;
onInit?.call();
//Get.setDefaultDelegate(routerDelegate);
Get.smartManagement = smartManagement;
onInit?.call();
Get.config(
enableLog: enableLog ?? Get.isLogEnable,
logWriterCallback: logWriterCallback,
defaultTransition: defaultTransition ?? Get.defaultTransition,
defaultOpaqueRoute: opaqueRoute ?? Get.isOpaqueRouteDefault,
defaultPopGesture: popGesture ?? Get.isPopGestureEnable,
defaultDurationTransition:
transitionDuration ?? Get.defaultTransitionDuration,
);
},
builder: (_) => routerDelegate != null
? MaterialApp.router(
routerDelegate: routerDelegate!,
routeInformationParser: routeInformationParser!,
backButtonDispatcher: backButtonDispatcher,
routeInformationProvider: routeInformationProvider,
key: _.unikey,
builder: defaultBuilder,
title: title,
onGenerateTitle: onGenerateTitle,
color: color,
theme: _.theme ?? theme ?? ThemeData.fallback(),
darkTheme:
_.darkTheme ?? darkTheme ?? theme ?? ThemeData.fallback(),
themeMode: _.themeMode ?? themeMode,
locale: Get.locale ?? locale,
scaffoldMessengerKey:
scaffoldMessengerKey ?? _.scaffoldMessengerKey,
localizationsDelegates: localizationsDelegates,
localeListResolutionCallback: localeListResolutionCallback,
localeResolutionCallback: localeResolutionCallback,
supportedLocales: supportedLocales,
debugShowMaterialGrid: debugShowMaterialGrid,
showPerformanceOverlay: showPerformanceOverlay,
checkerboardRasterCacheImages: checkerboardRasterCacheImages,
checkerboardOffscreenLayers: checkerboardOffscreenLayers,
showSemanticsDebugger: showSemanticsDebugger,
debugShowCheckedModeBanner: debugShowCheckedModeBanner,
shortcuts: shortcuts,
scrollBehavior: scrollBehavior,
useInheritedMediaQuery: useInheritedMediaQuery,
)
: MaterialApp(
key: _.unikey,
navigatorKey: (navigatorKey == null
? Get.key
: Get.addKey(navigatorKey!)),
scaffoldMessengerKey:
scaffoldMessengerKey ?? _.scaffoldMessengerKey,
home: home,
routes: routes ?? const <String, WidgetBuilder>{},
initialRoute: initialRoute,
onGenerateRoute:
(getPages != null ? generator : onGenerateRoute),
onGenerateInitialRoutes: (getPages == null || home != null)
? onGenerateInitialRoutes
: initialRoutesGenerate,
onUnknownRoute: onUnknownRoute,
navigatorObservers: (navigatorObservers == null
? <NavigatorObserver>[
GetObserver(routingCallback, Get.routing)
]
: <NavigatorObserver>[
GetObserver(routingCallback, Get.routing)
]
..addAll(navigatorObservers!)),
builder: defaultBuilder,
title: title,
onGenerateTitle: onGenerateTitle,
color: color,
theme: _.theme ?? theme ?? ThemeData.fallback(),
darkTheme:
_.darkTheme ?? darkTheme ?? theme ?? ThemeData.fallback(),
themeMode: _.themeMode ?? themeMode,
locale: Get.locale ?? locale,
localizationsDelegates: localizationsDelegates,
localeListResolutionCallback: localeListResolutionCallback,
localeResolutionCallback: localeResolutionCallback,
supportedLocales: supportedLocales,
debugShowMaterialGrid: debugShowMaterialGrid,
showPerformanceOverlay: showPerformanceOverlay,
checkerboardRasterCacheImages: checkerboardRasterCacheImages,
checkerboardOffscreenLayers: checkerboardOffscreenLayers,
showSemanticsDebugger: showSemanticsDebugger,
debugShowCheckedModeBanner: debugShowCheckedModeBanner,
shortcuts: shortcuts,
scrollBehavior: scrollBehavior,
useInheritedMediaQuery: useInheritedMediaQuery,
// actions: actions,
),
);
Get.config(
enableLog: enableLog ?? Get.isLogEnable,
logWriterCallback: logWriterCallback,
defaultTransition: defaultTransition ?? Get.defaultTransition,
defaultOpaqueRoute: opaqueRoute ?? Get.isOpaqueRouteDefault,
defaultPopGesture: popGesture ?? Get.isPopGestureEnable,
defaultDurationTransition:
transitionDuration ?? Get.defaultTransitionDuration,
);
},
builder: (_) {
final routerDelegate = Get.createDelegate(
pages: getPages ?? [],
notFoundRoute: unknownRoute,
navigatorKey: navigatorKey,
navigatorObservers: (navigatorObservers == null
? <NavigatorObserver>[GetObserver(routingCallback, Get.routing)]
: <NavigatorObserver>[GetObserver(routingCallback, Get.routing)]
..addAll(navigatorObservers!)));
final routeInformationParser = Get.createInformationParser(
initialRoute: initialRoute ??
getPages?.first.name ??
_cleanRouteName("/${home.runtimeType}"),
);
return MaterialApp.router(
routerDelegate: routerDelegate,
routeInformationParser: routeInformationParser,
backButtonDispatcher: backButtonDispatcher,
routeInformationProvider: routeInformationProvider,
key: _.unikey,
builder: defaultBuilder,
title: title,
onGenerateTitle: onGenerateTitle,
color: color,
theme: _.theme ?? theme ?? ThemeData.fallback(),
darkTheme: _.darkTheme ?? darkTheme ?? theme ?? ThemeData.fallback(),
themeMode: _.themeMode ?? themeMode,
locale: Get.locale ?? locale,
scaffoldMessengerKey: scaffoldMessengerKey ?? _.scaffoldMessengerKey,
localizationsDelegates: localizationsDelegates,
localeListResolutionCallback: localeListResolutionCallback,
localeResolutionCallback: localeResolutionCallback,
supportedLocales: supportedLocales,
debugShowMaterialGrid: debugShowMaterialGrid,
showPerformanceOverlay: showPerformanceOverlay,
checkerboardRasterCacheImages: checkerboardRasterCacheImages,
checkerboardOffscreenLayers: checkerboardOffscreenLayers,
showSemanticsDebugger: showSemanticsDebugger,
debugShowCheckedModeBanner: debugShowCheckedModeBanner,
shortcuts: shortcuts,
scrollBehavior: scrollBehavior,
useInheritedMediaQuery: useInheritedMediaQuery,
);
});
Widget defaultBuilder(BuildContext context, Widget? child) {
return Directionality(
... ... @@ -340,12 +317,12 @@ class GetMaterialApp extends StatelessWidget {
return PageRedirect(settings: settings, unknownRoute: unknownRoute).page();
}
List<Route<dynamic>> initialRoutesGenerate(String name) {
return [
PageRedirect(
settings: RouteSettings(name: name),
unknownRoute: unknownRoute,
).page()
];
}
// List<Route<dynamic>> initialRoutesGenerate(String name) {
// return [
// PageRedirect(
// settings: RouteSettings(name: name),
// unknownRoute: unknownRoute,
// ).page()
// ];
// }
}
... ...
... ... @@ -2,7 +2,9 @@ import 'package:flutter/material.dart';
import '../../../get.dart';
class GetMaterialController extends SuperController {
class GetMaterialController extends FullLifeCycleController {
static GetMaterialController get to => Get.find();
bool testMode = false;
Key? unikey;
ThemeData? theme;
... ... @@ -28,14 +30,14 @@ class GetMaterialController extends SuperController {
CustomTransition? customTransition;
var _key = GlobalKey<NavigatorState>(debugLabel: 'Key Created by default');
Map<dynamic, GetDelegate> keys = {};
Map<dynamic, GlobalKey<NavigatorState>> keys = {};
GlobalKey<NavigatorState> get key => rootDelegate.navigatorKey;
GlobalKey<NavigatorState> get key => _key;
GetDelegate get rootDelegate => Get.createDelegate();
GlobalKey<NavigatorState>? addKey(GlobalKey<NavigatorState> newKey) {
_key = newKey;
rootDelegate.navigatorKey = newKey;
return key;
}
... ... @@ -49,18 +51,6 @@ class GetMaterialController extends SuperController {
});
}
@override
void onDetached() {}
@override
void onInactive() {}
@override
void onPaused() {}
@override
void onResumed() {}
void restartApp() {
unikey = UniqueKey();
update();
... ...
... ... @@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import '../../../get.dart';
import '../router_report.dart';
import 'get_transition_mixin.dart';
@optionalTypeArgs
mixin RouteReportMixin<T extends StatefulWidget> on State<T> {
... ... @@ -33,10 +32,8 @@ mixin PageRouteReportMixin<T> on Route<T> {
}
}
class GetPageRoute<T> extends PageRoute<T> //MaterialPageRoute<T>
with
GetPageRouteTransitionMixin<T>,
PageRouteReportMixin {
class GetPageRoute<T> extends PageRoute<T>
with GetPageRouteTransitionMixin<T>, PageRouteReportMixin {
/// Creates a page route for use in an iOS designed app.
///
/// The [builder], [maintainState], and [fullscreenDialog] arguments must not
... ... @@ -54,7 +51,7 @@ class GetPageRoute<T> extends PageRoute<T> //MaterialPageRoute<T>
this.customTransition,
this.barrierDismissible = false,
this.barrierColor,
this.binding,
this.bindings,
this.binds,
this.routeName,
this.page,
... ... @@ -76,7 +73,7 @@ class GetPageRoute<T> extends PageRoute<T> //MaterialPageRoute<T>
final String? routeName;
//final String reference;
final CustomTransition? customTransition;
final BindingsInterface? binding;
final List<BindingsInterface>? bindings;
final Map<String, String>? parameter;
final List<Bind>? binds;
... ... @@ -121,11 +118,11 @@ class GetPageRoute<T> extends PageRoute<T> //MaterialPageRoute<T>
];
final localbindings = [
if (binding != null) ...<BindingsInterface>[binding!],
if (bindings != null) ...bindings!,
];
final bindingsToBind = middlewareRunner
.runOnBindingsStart(binding != null ? localbindings : localbinds);
.runOnBindingsStart(bindings != null ? localbindings : localbinds);
/// Retrocompatibility workaround, remove this when Bindings api
/// have been removed
... ...
... ... @@ -2,17 +2,18 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import '../../../get.dart';
import 'parse_route.dart';
class GetInformationParser extends RouteInformationParser<GetNavConfig> {
class GetInformationParser extends RouteInformationParser<RouteDecoder> {
final String initialRoute;
GetInformationParser({
this.initialRoute = '/',
required this.initialRoute,
}) {
Get.log('GetInformationParser is created !');
}
@override
SynchronousFuture<GetNavConfig> parseRouteInformation(
SynchronousFuture<RouteDecoder> parseRouteInformation(
RouteInformation routeInformation,
) {
var location = routeInformation.location;
... ... @@ -26,22 +27,16 @@ class GetInformationParser extends RouteInformationParser<GetNavConfig> {
Get.log('GetInformationParser: route location: $location');
final matchResult = Get.routeTree.matchRoute(location ?? initialRoute);
final routeName = location ?? initialRoute;
return SynchronousFuture(
GetNavConfig(
currentTreeBranch: matchResult.treeBranch,
location: location,
state: routeInformation.state,
),
);
return SynchronousFuture(RouteDecoder.fromRoute(routeName));
}
@override
RouteInformation restoreRouteInformation(GetNavConfig config) {
RouteInformation restoreRouteInformation(RouteDecoder config) {
return RouteInformation(
location: config.location,
state: config.state,
location: config.pageSettings?.name,
state: null,
);
}
}
... ...
import 'package:flutter/widgets.dart';
import '../../../get_instance/src/bindings_interface.dart';
import '../routes/get_route.dart';
import '../routes/transitions_type.dart';
mixin IGetNavigation {
Future<T?> to<T>(
Widget Function() page, {
bool? opaque,
Transition? transition,
Curve? curve,
Duration? duration,
int? id,
String? routeName,
bool fullscreenDialog = false,
dynamic arguments,
List<BindingsInterface>? bindings,
bool preventDuplicates = true,
bool? popGesture,
bool showCupertinoParallax = true,
double Function(BuildContext context)? gestureWidth,
});
Future<T?> off<T>(
Widget Function() page, {
bool? opaque,
Transition? transition,
Curve? curve,
Duration? duration,
int? id,
String? routeName,
bool fullscreenDialog = false,
dynamic arguments,
List<BindingsInterface>? bindings,
bool preventDuplicates = true,
bool? popGesture,
bool showCupertinoParallax = true,
double Function(BuildContext context)? gestureWidth,
});
Future<T?>? offAll<T>(
Widget Function() page, {
bool Function(GetPage route)? predicate,
bool opaque = true,
bool? popGesture,
int? id,
String? routeName,
dynamic arguments,
List<BindingsInterface>? bindings,
bool fullscreenDialog = false,
Transition? transition,
Curve? curve,
Duration? duration,
bool showCupertinoParallax = true,
double Function(BuildContext context)? gestureWidth,
});
Future<T?> toNamed<T>(
String page, {
dynamic arguments,
int? id,
bool preventDuplicates = true,
Map<String, String>? parameters,
});
Future<T?> offNamed<T>(
String page, {
dynamic arguments,
int? id,
Map<String, String>? parameters,
});
Future<T?>? offAllNamed<T>(
String newRouteName, {
// bool Function(GetPage route)? predicate,
dynamic arguments,
int? id,
Map<String, String>? parameters,
});
Future<T?>? offNamedUntil<T>(
String page, {
bool Function(GetPage route)? predicate,
dynamic arguments,
int? id,
Map<String, String>? parameters,
});
Future<T?> toNamedAndOffUntil<T>(
String page,
bool Function(GetPage) predicate, [
Object? data,
]);
Future<T?> offUntil<T>(
Widget Function() page,
bool Function(GetPage) predicate, [
Object? arguments,
]);
void back<T>([T? result]);
Future<R?> backAndtoNamed<T, R>(String page, {T? result, Object? arguments});
void backUntil(bool Function(GetPage) predicate);
void goToUnknownPage([bool clearPages = true]);
}
... ...
import 'package:flutter/widgets.dart';
import '../routes/default_route.dart';
import '../routes/get_route.dart';
... ... @@ -11,6 +12,7 @@ class GetNavigator extends Navigator {
bool reportsRouteUpdateToEngine = false,
TransitionDelegate? transitionDelegate,
String? initialRoute,
String? restorationScopeId,
}) : super(
//keys should be optional
key: key,
... ... @@ -35,6 +37,7 @@ class GetNavigator extends Navigator {
}
},
reportsRouteUpdateToEngine: reportsRouteUpdateToEngine,
restorationScopeId: restorationScopeId,
pages: pages,
observers: [
// GetObserver(),
... ... @@ -52,6 +55,7 @@ class GetNavigator extends Navigator {
bool reportsRouteUpdateToEngine = false,
TransitionDelegate? transitionDelegate,
String? initialRoute,
String? restorationScopeId,
}) : super(
//keys should be optional
key: key,
... ... @@ -65,6 +69,7 @@ class GetNavigator extends Navigator {
return true;
},
reportsRouteUpdateToEngine: reportsRouteUpdateToEngine,
restorationScopeId: restorationScopeId,
pages: pages,
observers: [
// GetObserver(),
... ...
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import '../../../get_core/src/get_main.dart';
import '../../../get_instance/src/bindings_interface.dart';
import '../../../get_state_manager/src/simple/get_state.dart';
import '../../get_navigation.dart';
... ... @@ -18,12 +19,14 @@ class GetPage<T> extends Page<T> {
final bool maintainState;
final bool opaque;
final double Function(BuildContext context)? gestureWidth;
final BindingsInterface? binding;
//final BindingsInterface? binding;
final List<BindingsInterface>? bindings;
final List<Bind> binds;
final CustomTransition? customTransition;
final Duration? transitionDuration;
final bool fullscreenDialog;
final bool preventDuplicates;
final Completer<T?>? completer;
// @override
// final LocalKey? key;
... ... @@ -42,6 +45,8 @@ class GetPage<T> extends Page<T> {
final GetPage? unknownRoute;
final bool showCupertinoParallax;
final PreventDuplicateHandlingMode preventDuplicateHandlingMode;
GetPage({
required this.name,
required this.page,
... ... @@ -56,7 +61,7 @@ class GetPage<T> extends Page<T> {
this.opaque = true,
this.transitionDuration,
this.popGesture,
this.binding,
this.bindings = const [],
this.binds = const [],
this.transition,
this.customTransition,
... ... @@ -67,13 +72,16 @@ class GetPage<T> extends Page<T> {
this.arguments,
this.showCupertinoParallax = true,
this.preventDuplicates = true,
this.preventDuplicateHandlingMode =
PreventDuplicateHandlingMode.ReorderRoutes,
this.completer,
}) : path = _nameToRegex(name),
assert(name.startsWith('/'),
'It is necessary to start route name [$name] with a slash: /$name'),
super(
key: ValueKey(name),
name: name,
arguments: Get.arguments,
// arguments: Get.arguments,
);
// settings = RouteSettings(name: name, arguments: Get.arguments);
... ... @@ -88,13 +96,14 @@ class GetPage<T> extends Page<T> {
Alignment? alignment,
bool? maintainState,
bool? opaque,
BindingsInterface? binding,
List<BindingsInterface>? bindings,
// BindingsInterface? binding,
List<Bind>? binds,
CustomTransition? customTransition,
Duration? transitionDuration,
bool? fullscreenDialog,
RouteSettings? settings,
List<GetPage>? children,
List<GetPage<T>>? children,
GetPage? unknownRoute,
List<GetMiddleware>? middlewares,
bool? preventDuplicates,
... ... @@ -102,6 +111,7 @@ class GetPage<T> extends Page<T> {
bool? participatesInRootNavigator,
Object? arguments,
bool? showCupertinoParallax,
Completer<T?>? completer,
}) {
return GetPage(
participatesInRootNavigator:
... ... @@ -117,7 +127,7 @@ class GetPage<T> extends Page<T> {
alignment: alignment ?? this.alignment,
maintainState: maintainState ?? this.maintainState,
opaque: opaque ?? this.opaque,
binding: binding ?? this.binding,
bindings: bindings ?? this.bindings,
binds: binds ?? this.binds,
customTransition: customTransition ?? this.customTransition,
transitionDuration: transitionDuration ?? this.transitionDuration,
... ... @@ -129,6 +139,7 @@ class GetPage<T> extends Page<T> {
arguments: arguments ?? this.arguments,
showCupertinoParallax:
showCupertinoParallax ?? this.showCupertinoParallax,
completer: completer ?? this.completer,
);
}
... ... @@ -164,6 +175,17 @@ class GetPage<T> extends Page<T> {
return PathDecoded(RegExp('^$stringPath\$'), keys);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is GetPage<T> && other.key == key;
}
@override
int get hashCode {
return key.hashCode;
}
}
@immutable
... ...
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import '../../../get_instance/src/bindings_interface.dart';
import '../../../get_state_manager/src/simple/list_notifier.dart';
import '../../../get_utils/src/platform/platform.dart';
import '../../../route_manager.dart';
/// Enables the user to customize the intended pop behavior
///
/// Goes to either the previous _activePages entry or the previous page entry
///
/// e.g. if the user navigates to these pages
/// 1) /home
/// 2) /home/products/1234
///
/// when popping on [History] mode, it will emulate a browser back button.
///
/// so the new _activePages stack will be:
/// 1) /home
///
/// when popping on [Page] mode, it will only remove the last part of the route
/// so the new _activePages stack will be:
/// 1) /home
/// 2) /home/products
///
/// another pop will change the _activePages stack to:
/// 1) /home
enum PopMode {
History,
Page,
}
/// Enables the user to customize the behavior when pushing multiple routes that
/// shouldn't be duplicates
enum PreventDuplicateHandlingMode {
/// Removes the _activePages entries until it reaches the old route
PopUntilOriginalRoute,
/// Simply don't push the new route
DoNothing,
/// Recommended - Moves the old route entry to the front
///
/// With this mode, you guarantee there will be only one
/// route entry for each location
ReorderRoutes,
Recreate,
}
class GetDelegate extends RouterDelegate<RouteDecoder>
with
ListNotifierSingleMixin,
PopNavigatorRouterDelegateMixin<RouteDecoder>,
IGetNavigation {
final List<RouteDecoder> _activePages = <RouteDecoder>[];
final PopMode backButtonPopMode;
final PreventDuplicateHandlingMode preventDuplicateHandlingMode;
final GetPage notFoundRoute;
final List<NavigatorObserver>? navigatorObservers;
final TransitionDelegate<dynamic>? transitionDelegate;
final Iterable<GetPage> Function(RouteDecoder currentNavStack)?
pickPagesForRootNavigator;
// GlobalKey<NavigatorState> get navigatorKey => Get.key;
@override
GlobalKey<NavigatorState> navigatorKey;
final String? restorationScopeId;
GetDelegate({
GetPage? notFoundRoute,
this.navigatorObservers,
this.transitionDelegate,
this.backButtonPopMode = PopMode.History,
this.preventDuplicateHandlingMode =
PreventDuplicateHandlingMode.ReorderRoutes,
this.pickPagesForRootNavigator,
this.restorationScopeId,
bool showHashOnUrl = false,
GlobalKey<NavigatorState>? navigatorKey,
required List<GetPage> pages,
}) : navigatorKey = navigatorKey ?? GlobalKey<NavigatorState>(),
notFoundRoute = notFoundRoute ??= GetPage(
name: '/404',
page: () => Scaffold(
body: Center(child: Text('Route not found')),
),
) {
if (!showHashOnUrl && GetPlatform.isWeb) setUrlStrategy();
Get.addPages(pages);
Get.addPage(notFoundRoute);
Get.log('GetDelegate is created !');
}
Future<RouteDecoder?> runMiddleware(RouteDecoder config) async {
final middlewares = config.currentTreeBranch.last.middlewares;
if (middlewares == null) {
return config;
}
var iterator = config;
for (var item in middlewares) {
var redirectRes = await item.redirect(iterator);
if (redirectRes == null) return null;
iterator = redirectRes;
}
return iterator;
}
Future<void> _unsafeHistoryAdd(RouteDecoder config) async {
final res = await runMiddleware(config);
if (res == null) return;
_activePages.add(res);
}
Future<T?> _unsafeHistoryRemove<T>(RouteDecoder config, T result) async {
var index = _activePages.indexOf(config);
if (index >= 0) return _unsafeHistoryRemoveAt(index, result);
}
Future<T?> _unsafeHistoryRemoveAt<T>(int index, T result) async {
if (index == _activePages.length - 1 && _activePages.length > 1) {
//removing WILL update the current route
final toCheck = _activePages[_activePages.length - 2];
final resMiddleware = await runMiddleware(toCheck);
if (resMiddleware == null) return null;
_activePages[_activePages.length - 2] = resMiddleware;
}
final completer = _activePages.removeAt(index).route?.completer;
if (completer?.isCompleted == false) completer!.complete(result);
return completer?.future as T?;
}
T arguments<T>() {
return currentConfiguration?.pageSettings?.arguments as T;
}
Map<String, String> get parameters {
return currentConfiguration?.pageSettings?.params ?? {};
}
PageSettings? get pageSettings {
return currentConfiguration?.pageSettings;
}
Future<T?> _removeHistoryEntry<T>(RouteDecoder entry, T result) async {
return _unsafeHistoryRemove<T>(entry, result);
}
Future<void> _pushHistory(RouteDecoder config) async {
if (config.route!.preventDuplicates) {
final originalEntryIndex = _activePages.indexWhere(
(element) => element.pageSettings?.name == config.pageSettings?.name);
if (originalEntryIndex >= 0) {
switch (preventDuplicateHandlingMode) {
case PreventDuplicateHandlingMode.PopUntilOriginalRoute:
popModeUntil(config.pageSettings!.name, popMode: PopMode.Page);
break;
case PreventDuplicateHandlingMode.ReorderRoutes:
await _unsafeHistoryRemoveAt(originalEntryIndex, null);
await _unsafeHistoryAdd(config);
break;
case PreventDuplicateHandlingMode.DoNothing:
default:
break;
}
return;
}
}
await _unsafeHistoryAdd(config);
}
Future<T?> _popHistory<T>(T result) async {
if (!_canPopHistory()) return null;
return await _doPopHistory(result);
}
Future<T?> _doPopHistory<T>(T result) async {
return _unsafeHistoryRemoveAt<T>(_activePages.length - 1, result);
}
Future<T?> _popPage<T>(T result) async {
if (!_canPopPage()) return null;
return await _doPopPage(result);
}
// returns the popped page
Future<T?> _doPopPage<T>(T result) async {
final currentBranch = currentConfiguration?.currentTreeBranch;
if (currentBranch != null && currentBranch.length > 1) {
//remove last part only
final remaining = currentBranch.take(currentBranch.length - 1);
final prevHistoryEntry = _activePages.length > 1
? _activePages[_activePages.length - 2]
: null;
//check if current route is the same as the previous route
if (prevHistoryEntry != null) {
//if so, pop the entire _activePages entry
final newLocation = remaining.last.name;
final prevLocation = prevHistoryEntry.pageSettings?.name;
if (newLocation == prevLocation) {
//pop the entire _activePages entry
return await _popHistory(result);
}
}
//create a new route with the remaining tree branch
final res = await _popHistory<T>(result);
await _pushHistory(
RouteDecoder(
remaining.toList(),
null,
//TOOD: persist state??
),
);
return res;
} else {
//remove entire entry
return await _popHistory(result);
}
}
Future<T?> _pop<T>(PopMode mode, T result) async {
switch (mode) {
case PopMode.History:
return await _popHistory<T>(result);
case PopMode.Page:
return await _popPage<T>(result);
default:
return null;
}
}
Future<T?> popHistory<T>(T result) async {
return await _popHistory<T>(result);
}
bool _canPopHistory() {
return _activePages.length > 1;
}
Future<bool> canPopHistory() {
return SynchronousFuture(_canPopHistory());
}
bool _canPopPage() {
final currentTreeBranch = currentConfiguration?.currentTreeBranch;
if (currentTreeBranch == null) return false;
return currentTreeBranch.length > 1 ? true : _canPopHistory();
}
Future<bool> canPopPage() {
return SynchronousFuture(_canPopPage());
}
bool _canPop(mode) {
switch (mode) {
case PopMode.History:
return _canPopHistory();
case PopMode.Page:
default:
return _canPopPage();
}
}
/// gets the visual pages from the current _activePages entry
///
/// visual pages must have [GetPage.participatesInRootNavigator] set to true
Iterable<GetPage> getVisualPages(RouteDecoder? currentHistory) {
final res = currentHistory!.currentTreeBranch
.where((r) => r.participatesInRootNavigator != null);
if (res.length == 0) {
//default behavoir, all routes participate in root navigator
return _activePages.map((e) => e.route!);
} else {
//user specified at least one participatesInRootNavigator
return res
.where((element) => element.participatesInRootNavigator == true);
}
}
@override
Widget build(BuildContext context) {
final currentHistory = currentConfiguration;
final pages = currentHistory == null
? <GetPage>[]
: pickPagesForRootNavigator?.call(currentHistory) ??
getVisualPages(currentHistory);
if (pages.length == 0) return SizedBox.shrink();
return GetNavigator(
key: navigatorKey,
onPopPage: _onPopVisualRoute,
pages: pages.toList(),
observers: navigatorObservers,
transitionDelegate:
transitionDelegate ?? const DefaultTransitionDelegate<dynamic>(),
);
}
@override
void goToUnknownPage([bool clearPages = false]) {
if (clearPages) _activePages.clear();
final pageSettings = _buildPageSettings(notFoundRoute.name);
final routeDecoder = _getRouteDecoder(pageSettings);
_push(routeDecoder!);
}
@protected
void _popWithResult<T>([T? result]) {
final completer = _activePages.removeLast().route?.completer;
if (completer?.isCompleted == false) completer!.complete(result);
}
@override
Future<T?> toNamed<T>(
String page, {
dynamic arguments,
int? id,
bool preventDuplicates = true,
Map<String, String>? parameters,
}) async {
final args = _buildPageSettings(page, arguments);
final route = _getRouteDecoder<T>(args);
if (route != null) {
return _push<T>(route);
} else {
goToUnknownPage();
}
}
@override
Future<T?> to<T>(Widget Function() page,
{bool? opaque,
Transition? transition,
Curve? curve,
Duration? duration,
int? id,
String? routeName,
bool fullscreenDialog = false,
dynamic arguments,
List<BindingsInterface>? bindings,
bool preventDuplicates = true,
bool? popGesture,
bool showCupertinoParallax = true,
double Function(BuildContext context)? gestureWidth,
bool rebuildStack = true,
PreventDuplicateHandlingMode preventDuplicateHandlingMode =
PreventDuplicateHandlingMode.ReorderRoutes}) async {
routeName = _cleanRouteName("/${page.runtimeType}");
if (preventDuplicateHandlingMode == PreventDuplicateHandlingMode.Recreate) {
routeName = routeName + page.hashCode.toString();
}
final getPage = GetPage<T>(
name: routeName,
opaque: opaque ?? true,
page: page,
gestureWidth: gestureWidth,
showCupertinoParallax: showCupertinoParallax,
popGesture: popGesture ?? Get.defaultPopGesture,
transition: transition ?? Get.defaultTransition,
curve: curve ?? Get.defaultTransitionCurve,
fullscreenDialog: fullscreenDialog,
bindings: bindings,
transitionDuration: duration ?? Get.defaultTransitionDuration,
preventDuplicateHandlingMode: preventDuplicateHandlingMode,
);
Get.addPage(getPage);
final args = _buildPageSettings(routeName, arguments);
final route = _getRouteDecoder<T>(args);
final result = await _push<T>(
route!,
rebuildStack: rebuildStack,
preventDuplicateHandlingMode: preventDuplicateHandlingMode,
);
Get.removePage(getPage);
return result;
}
@override
Future<T?> off<T>(
Widget Function() page, {
bool? opaque,
Transition? transition,
Curve? curve,
Duration? duration,
int? id,
String? routeName,
bool fullscreenDialog = false,
dynamic arguments,
List<BindingsInterface>? bindings,
bool preventDuplicates = true,
bool? popGesture,
bool showCupertinoParallax = true,
double Function(BuildContext context)? gestureWidth,
}) async {
routeName = _cleanRouteName("/${page.runtimeType}");
final route = GetPage<T>(
name: routeName,
opaque: opaque ?? true,
page: page,
gestureWidth: gestureWidth,
showCupertinoParallax: showCupertinoParallax,
popGesture: popGesture ?? Get.defaultPopGesture,
transition: transition ?? Get.defaultTransition,
curve: curve ?? Get.defaultTransitionCurve,
fullscreenDialog: fullscreenDialog,
bindings: bindings,
transitionDuration: duration ?? Get.defaultTransitionDuration,
);
final args = _buildPageSettings(routeName, arguments);
return _replace(args, route);
}
@override
Future<T?>? offAll<T>(
Widget Function() page, {
bool Function(GetPage route)? predicate,
bool opaque = true,
bool? popGesture,
int? id,
String? routeName,
dynamic arguments,
List<BindingsInterface>? bindings,
bool fullscreenDialog = false,
Transition? transition,
Curve? curve,
Duration? duration,
bool showCupertinoParallax = true,
double Function(BuildContext context)? gestureWidth,
}) async {
routeName = _cleanRouteName("/${page.runtimeType}");
final route = GetPage<T>(
name: routeName,
opaque: opaque,
page: page,
gestureWidth: gestureWidth,
showCupertinoParallax: showCupertinoParallax,
popGesture: popGesture ?? Get.defaultPopGesture,
transition: transition ?? Get.defaultTransition,
curve: curve ?? Get.defaultTransitionCurve,
fullscreenDialog: fullscreenDialog,
bindings: bindings,
transitionDuration: duration ?? Get.defaultTransitionDuration,
);
final args = _buildPageSettings(routeName, arguments);
final newPredicate = predicate ?? (route) => false;
while (_activePages.length > 1 && !newPredicate(_activePages.last.route!)) {
_popWithResult();
}
return _replace(args, route);
}
@override
Future<T?>? offAllNamed<T>(
String page, {
// bool Function(GetPage route)? predicate,
dynamic arguments,
int? id,
Map<String, String>? parameters,
}) async {
final args = _buildPageSettings(page, arguments);
final route = _getRouteDecoder<T>(args);
if (route == null) return null;
while (_activePages.length > 1) {
_activePages.removeLast();
}
return _replaceNamed(route);
}
@override
Future<T?>? offNamedUntil<T>(
String page, {
bool Function(GetPage route)? predicate,
dynamic arguments,
int? id,
Map<String, String>? parameters,
}) async {
final args = _buildPageSettings(page, arguments);
final route = _getRouteDecoder<T>(args);
if (route == null) return null;
final newPredicate = predicate ?? (route) => false;
while (_activePages.length > 1 && newPredicate(_activePages.last.route!)) {
_activePages.removeLast();
}
return _replaceNamed(route);
}
@override
Future<T?> offNamed<T>(
String page, {
dynamic arguments,
int? id,
Map<String, String>? parameters,
}) async {
final args = _buildPageSettings(page, arguments);
final route = _getRouteDecoder<T>(args);
if (route == null) return null;
_popWithResult();
return _push<T>(route);
}
@override
Future<T?> toNamedAndOffUntil<T>(
String page,
bool Function(GetPage) predicate, [
Object? data,
]) async {
final arguments = _buildPageSettings(page, data);
final route = _getRouteDecoder<T>(arguments);
if (route == null) return null;
while (_activePages.isNotEmpty && !predicate(_activePages.last.route!)) {
_popWithResult();
}
return _push<T>(route);
}
@override
Future<T?> offUntil<T>(
Widget Function() page,
bool Function(GetPage) predicate, [
Object? arguments,
]) async {
while (_activePages.isNotEmpty && !predicate(_activePages.last.route!)) {
_popWithResult();
}
return to<T>(page, arguments: arguments);
}
@override
void removeRoute<T>(String name) {
_activePages.remove(RouteDecoder.fromRoute(name));
}
@override
void back<T>([T? result]) {
_checkIfCanBack();
_popWithResult<T>(result);
refresh();
}
bool get canBack {
return _activePages.length > 1;
}
void _checkIfCanBack() {
assert(() {
if (!canBack) {
final last = _activePages.last;
final name = last.route?.name;
throw 'The page $name cannot be popped';
}
return true;
}());
}
@override
Future<R?> backAndtoNamed<T, R>(String page,
{T? result, Object? arguments}) async {
final args = _buildPageSettings(page, arguments);
final route = _getRouteDecoder<R>(args);
if (route == null) return null;
_popWithResult<T>(result);
return _push<R>(route);
}
/// Removes routes according to [PopMode]
/// until it reaches the specifc [fullRoute],
/// DOES NOT remove the [fullRoute]
@override
Future<void> popModeUntil(
String fullRoute, {
PopMode popMode = PopMode.History,
}) async {
// remove history or page entries until you meet route
var iterator = currentConfiguration;
while (_canPop(popMode) &&
iterator != null &&
iterator.pageSettings?.name != fullRoute) {
await _pop(popMode, null);
// replace iterator
iterator = currentConfiguration;
}
refresh();
}
@override
void backUntil(bool Function(GetPage) predicate) {
while (_activePages.length <= 1 && !predicate(_activePages.last.route!)) {
_popWithResult();
}
refresh();
}
Future<T?> _replace<T>(PageSettings arguments, GetPage<T> page) async {
final index = _activePages.length > 1 ? _activePages.length - 1 : 0;
Get.addPage(page);
final route = _getRouteDecoder(arguments);
final activePage = _configureRouterDecoder<T>(route!, arguments);
_activePages[index] = activePage;
refresh();
final result = await activePage.route?.completer?.future as Future<T?>?;
Get.removePage(page);
return result;
}
Future<T?> _replaceNamed<T>(RouteDecoder activePage) async {
final index = _activePages.length > 1 ? _activePages.length - 1 : 0;
// final activePage = _configureRouterDecoder<T>(page, arguments);
_activePages[index] = activePage;
refresh();
final result = await activePage.route?.completer?.future as Future<T?>?;
return result;
}
/// Takes a route [name] String generated by [to], [off], [offAll]
/// (and similar context navigation methods), cleans the extra chars and
/// accommodates the format.
/// TODO: check for a more "appealing" URL naming convention.
/// `() => MyHomeScreenView` becomes `/my-home-screen-view`.
String _cleanRouteName(String name) {
name = name.replaceAll('() => ', '');
/// uncommonent for URL styling.
// name = name.paramCase!;
if (!name.startsWith('/')) {
name = '/$name';
}
return Uri.tryParse(name)?.toString() ?? name;
}
PageSettings _buildPageSettings(String page, [Object? data]) {
var uri = Uri.parse(page);
return PageSettings(uri, data);
}
@protected
RouteDecoder? _getRouteDecoder<T>(PageSettings arguments) {
var page = arguments.uri.path;
final parameters = arguments.params;
if (parameters.isNotEmpty) {
final uri = Uri(path: page, queryParameters: parameters);
page = uri.toString();
}
final decoder = Get.routeTree.matchRoute(page, arguments: arguments);
final route = decoder.route;
if (route == null) return null;
return _configureRouterDecoder(decoder, arguments);
}
@protected
RouteDecoder _configureRouterDecoder<T>(
RouteDecoder decoder, PageSettings arguments) {
final parameters =
arguments.params.isEmpty ? arguments.query : arguments.params;
if (arguments.params.isEmpty) {
arguments.params.addAll(arguments.query);
}
if (decoder.parameters.isEmpty) {
decoder.parameters.addAll(parameters);
}
decoder.route = decoder.route?.copy(
completer: _activePages.isEmpty ? null : Completer(),
arguments: arguments,
parameters: parameters,
);
return decoder;
}
Future<T?> _push<T>(RouteDecoder activePage,
{bool rebuildStack = true,
PreventDuplicateHandlingMode preventDuplicateHandlingMode =
PreventDuplicateHandlingMode.ReorderRoutes}) async {
final onStackPage = _activePages.firstWhereOrNull(
(element) => element.route?.key == activePage.route?.key);
/// There are no duplicate routes in the stack
if (onStackPage == null) {
final res = await runMiddleware(activePage);
_activePages.add(res!);
} else {
/// There are duplicate routes, reorder
switch (preventDuplicateHandlingMode) {
case PreventDuplicateHandlingMode.DoNothing:
break;
case PreventDuplicateHandlingMode.ReorderRoutes:
_activePages.remove(onStackPage);
final res = await runMiddleware(onStackPage);
_activePages.add(res!);
break;
case PreventDuplicateHandlingMode.PopUntilOriginalRoute:
while (_activePages.last == onStackPage) {
_popWithResult();
}
break;
case PreventDuplicateHandlingMode.Recreate:
_activePages.remove(onStackPage);
final res = await runMiddleware(activePage);
_activePages.add(res!);
break;
default:
}
}
if (rebuildStack) {
refresh();
}
return activePage.route?.completer?.future as Future<T?>?;
}
@override
Future<void> setNewRoutePath(RouteDecoder configuration) async {
final page = configuration.route;
if (page == null) {
goToUnknownPage();
return;
} else {
_push(configuration);
}
}
@override
RouteDecoder? get currentConfiguration {
if (_activePages.isEmpty) return null;
final route = _activePages.last;
return route;
}
Future<bool> handlePopupRoutes({
Object? result,
}) async {
Route? currentRoute;
navigatorKey.currentState!.popUntil((route) {
currentRoute = route;
return true;
});
if (currentRoute is PopupRoute) {
return await navigatorKey.currentState!.maybePop(result);
}
return false;
}
@override
Future<bool> popRoute({
Object? result,
PopMode? popMode,
}) async {
//Returning false will cause the entire app to be popped.
final wasPopup = await handlePopupRoutes(result: result);
if (wasPopup) return true;
final _popped = await _pop(popMode ?? backButtonPopMode, result);
refresh();
if (_popped != null) {
//emulate the old pop with result
return true;
}
return false;
}
bool _onPopVisualRoute(Route<dynamic> route, dynamic result) {
final didPop = route.didPop(result);
if (!didPop) {
return false;
}
final settings = route.settings;
if (settings is GetPage) {
final config = _activePages.cast<RouteDecoder?>().firstWhere(
(element) => element?.route == settings,
orElse: () => null,
);
if (config != null) {
_removeHistoryEntry(config, result);
}
}
refresh();
return true;
}
}
... ...
export 'circular_reveal_clipper.dart';
export 'custom_transition.dart';
export 'default_route.dart';
export 'default_transitions.dart';
export 'get_information_parser.dart';
export 'get_navigation_interface.dart';
export 'get_navigator.dart';
export 'get_route.dart';
export 'get_router_delegate.dart';
export 'get_transition_mixin.dart';
export 'modules.dart';
export 'observers/route_observer.dart';
export 'page_settings.dart';
export 'parse_route.dart';
export 'route_middleware.dart';
export 'route_report.dart';
export 'router_outlet.dart';
export 'transitions_type.dart';
export 'url_strategy/url_strategy.dart';
... ...
... ... @@ -98,7 +98,7 @@ class GetObserver extends NavigatorObserver {
}
RouterReportManager.instance.reportCurrentRoute(route);
_routeSend?.update((value) {
_routeSend!.update((value) {
// Only PageRoute is allowed to change current value
if (route is PageRoute) {
value.current = newRoute.name ?? '';
... ... @@ -216,7 +216,6 @@ class Routing {
/// This is basically a util for rules about 'what a route is'
class _RouteData {
final bool isGetPageRoute;
//final bool isSnackbar;
final bool isBottomSheet;
final bool isDialog;
final String? name;
... ... @@ -224,7 +223,6 @@ class _RouteData {
_RouteData({
required this.name,
required this.isGetPageRoute,
// required this.isSnackbar,
required this.isBottomSheet,
required this.isDialog,
});
... ... @@ -233,7 +231,6 @@ class _RouteData {
return _RouteData(
name: _extractRouteName(route),
isGetPageRoute: route is GetPageRoute,
// isSnackbar: route is SnackRoute,
isDialog: route is GetDialogRoute,
isBottomSheet: route is GetModalBottomSheetRoute,
);
... ...
import 'package:flutter/widgets.dart';
import '../../../route_manager.dart';
extension PageArgExt on BuildContext {
RouteSettings? get settings {
return ModalRoute.of(this)!.settings;
}
PageSettings? get pageSettings {
final args = ModalRoute.of(this)?.settings.arguments;
if (args is PageSettings) {
return args;
}
return null;
}
dynamic get arguments {
final args = settings?.arguments;
if (args is PageSettings) {
return args.arguments;
} else {
return args;
}
}
Map<String, String> get params {
final args = settings?.arguments;
if (args is PageSettings) {
return args.params;
} else {
return {};
}
}
Router get router {
return Router.of(this);
}
String get location {
final parser = router.routeInformationParser;
final config = delegate.currentConfiguration;
return parser?.restoreRouteInformation(config)?.location ?? '/';
}
RouterDelegate get delegate {
return router.routerDelegate;
}
GetDelegate get navigation {
return router.routerDelegate as GetDelegate;
}
}
class PageSettings extends RouteSettings {
PageSettings(
this.uri, [
this.arguments,
]);
@override
String get name => '$uri';
@override
late final Object? arguments;
final Uri uri;
final params = <String, String>{};
String get path => uri.path;
List<String> get paths => uri.pathSegments;
Map<String, String> get query => uri.queryParameters;
Map<String, List<String>> get queries => uri.queryParametersAll;
@override
String toString() => name;
PageSettings copy({
Uri? uri,
Object? arguments,
}) {
return PageSettings(
uri ?? this.uri,
arguments ?? this.arguments,
);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is PageSettings &&
other.uri == uri &&
other.arguments == arguments;
}
@override
int get hashCode => uri.hashCode ^ arguments.hashCode;
}
... ...
import '../../get_navigation.dart';
import '../../../route_manager.dart';
class RouteDecoder {
final List<GetPage> treeBranch;
GetPage? get route => treeBranch.isEmpty ? null : treeBranch.last;
final Map<String, String> parameters;
final Object? arguments;
const RouteDecoder(
this.treeBranch,
this.parameters,
this.arguments,
this.currentTreeBranch,
this.pageSettings,
);
void replaceArguments(Object? arguments) {
final _route = route;
if (_route != null) {
final index = treeBranch.indexOf(_route);
treeBranch[index] = _route.copy(arguments: arguments);
final List<GetPage> currentTreeBranch;
final PageSettings? pageSettings;
factory RouteDecoder.fromRoute(String location) {
var uri = Uri.parse(location);
final args = PageSettings(uri);
final decoder = Get.routeTree.matchRoute(location, arguments: args);
decoder.route = decoder.route?.copy(
completer: null,
arguments: args,
parameters: args.params,
);
return decoder;
}
GetPage? get route =>
currentTreeBranch.isEmpty ? null : currentTreeBranch.last;
GetPage routeOrUnknown(GetPage onUnknow) =>
currentTreeBranch.isEmpty ? onUnknow : currentTreeBranch.last;
set route(GetPage? getPage) {
if (getPage == null) return;
if (currentTreeBranch.isEmpty) {
currentTreeBranch.add(getPage);
} else {
currentTreeBranch[currentTreeBranch.length - 1] = getPage;
}
}
List<GetPage>? get currentChildrens => route?.children;
Map<String, String> get parameters => pageSettings?.params ?? {};
dynamic get args {
return pageSettings?.arguments;
}
T? arguments<T>() {
final args = pageSettings?.arguments;
if (args is T) {
return pageSettings?.arguments as T;
} else {
return null;
}
}
void replaceParameters(Object? arguments) {
void replaceArguments(Object? arguments) {
final _route = route;
if (_route != null) {
final index = treeBranch.indexOf(_route);
treeBranch[index] = _route.copy(parameters: parameters);
final index = currentTreeBranch.indexOf(_route);
currentTreeBranch[index] = _route.copy(arguments: arguments);
}
}
}
... ... @@ -34,7 +68,7 @@ class ParseRouteTree {
final List<GetPage> routes;
RouteDecoder matchRoute(String name, {Object? arguments}) {
RouteDecoder matchRoute(String name, {PageSettings? arguments}) {
final uri = Uri.parse(name);
// /home/profile/123 => home,profile,123 => /,/home,/home/profile,/home/profile/123
final split = uri.path.split('/').where((element) => element.isNotEmpty);
... ... @@ -77,28 +111,44 @@ class ParseRouteTree {
),
)
.toList();
arguments?.params.clear();
arguments?.params.addAll(params);
return RouteDecoder(
mappedTreeBranch,
params,
arguments,
);
}
arguments?.params.clear();
arguments?.params.addAll(params);
//route not found
return RouteDecoder(
treeBranch.map((e) => e.value).toList(),
params,
arguments,
);
}
void addRoutes(List<GetPage> getPages) {
void addRoutes<T>(List<GetPage<T>> getPages) {
for (final route in getPages) {
addRoute(route);
}
}
void addRoute(GetPage route) {
void removeRoutes<T>(List<GetPage<T>> getPages) {
for (final route in getPages) {
removeRoute(route);
}
}
void removeRoute<T>(GetPage<T> route) {
routes.remove(route);
for (var page in _flattenPage(route)) {
removeRoute(page);
}
}
void addRoute<T>(GetPage<T> route) {
routes.add(route);
// Add Page children.
... ... @@ -152,9 +202,11 @@ class ParseRouteTree {
);
GetPage? _findRoute(String name) {
return routes.firstWhereOrNull(
final value = routes.firstWhereOrNull(
(route) => route.path.regex.hasMatch(name),
);
return value;
}
Map<String, String> _parseParams(String path, PathDecoded routePath) {
... ... @@ -177,7 +229,7 @@ class ParseRouteTree {
}
}
extension FirstWhereExt<T> on List<T> {
extension FirstWhereOrNullExt<T> on List<T> {
/// The first element satisfying [test], or `null` if there are none.
T? firstWhereOrNull(bool Function(T element) test) {
for (var element in this) {
... ...
... ... @@ -20,21 +20,6 @@ abstract class _RouteMiddleware {
/// {@end-tool}
int? priority;
/// This function will be called when the page of
/// the called route is being searched for.
/// It take RouteSettings as a result an redirect to the new settings or
/// give it null and there will be no redirecting.
/// {@tool snippet}
/// ```dart
/// GetPage redirect(String route) {
/// final authService = Get.find<AuthService>();
/// return authService.authed.value ? null : RouteSettings(name: '/login');
/// }
/// ```
/// {@end-tool}
RouteSettings? redirect(String route);
/// Similar to [redirect],
/// This function will be called when the router delegate changes the
/// current route.
///
... ... @@ -45,13 +30,13 @@ abstract class _RouteMiddleware {
/// and no new routes are pushed.
/// {@tool snippet}
/// ```dart
/// GetNavConfig? redirect(GetNavConfig route) {
/// RouteDecoder? redirect(RouteDecoder route) {
/// final authService = Get.find<AuthService>();
/// return authService.authed.value ? null : RouteSettings(name: '/login');
/// return authService.authed.value ? null : RouteDecoder.fromRoute('/login');
/// }
/// ```
/// {@end-tool}
Future<GetNavConfig?> redirectDelegate(GetNavConfig route);
Future<RouteDecoder?> redirect(RouteDecoder route);
/// This function will be called when this Page is called
/// you can use it to change something about the page or give it new page
... ... @@ -101,8 +86,8 @@ class GetMiddleware implements _RouteMiddleware {
GetMiddleware({this.priority});
@override
RouteSettings? redirect(String? route) => null;
// @override
// RouteSettings? redirect(String? route) => null;
@override
GetPage? onPageCalled(GetPage? page) => page;
... ... @@ -120,7 +105,7 @@ class GetMiddleware implements _RouteMiddleware {
void onPageDispose() {}
@override
Future<GetNavConfig?> redirectDelegate(GetNavConfig route) =>
Future<RouteDecoder?> redirect(RouteDecoder route) =>
SynchronousFuture(route);
}
... ... @@ -144,17 +129,17 @@ class MiddlewareRunner {
return page;
}
RouteSettings? runRedirect(String? route) {
RouteSettings? to;
for (final element in _getMiddlewares()) {
to = element.redirect(route);
if (to != null) {
break;
}
}
Get.log('Redirect to $to');
return to;
}
// RouteSettings? runRedirect(String? route) {
// RouteSettings? to;
// for (final element in _getMiddlewares()) {
// to = element.redirect(route);
// if (to != null) {
// break;
// }
// }
// Get.log('Redirect to $to');
// return to;
// }
List<R>? runOnBindingsStart<R>(List<R>? bindings) {
_getMiddlewares().forEach((element) {
... ... @@ -212,7 +197,7 @@ class PageRedirect {
showCupertinoParallax: _r.showCupertinoParallax,
gestureWidth: _r.gestureWidth,
customTransition: _r.customTransition,
binding: _r.binding,
bindings: _r.bindings,
binds: _r.binds,
transitionDuration:
_r.transitionDuration ?? Get.defaultTransitionDuration,
... ... @@ -235,13 +220,13 @@ class PageRedirect {
title: _r.title,
maintainState: _r.maintainState,
routeName: _r.name,
settings: _r,
settings: settings,
curve: _r.curve,
showCupertinoParallax: _r.showCupertinoParallax,
gestureWidth: _r.gestureWidth,
opaque: _r.opaque,
customTransition: _r.customTransition,
binding: _r.binding,
bindings: _r.bindings,
binds: _r.binds,
transitionDuration:
_r.transitionDuration ?? Get.defaultTransitionDuration,
... ... @@ -274,19 +259,19 @@ class PageRedirect {
if (match.route!.middlewares == null || match.route!.middlewares!.isEmpty) {
return false;
}
final newSettings = runner.runRedirect(settings!.name);
if (newSettings == null) {
return false;
}
settings = newSettings;
// final newSettings = runner.runRedirect(settings!.name);
// if (newSettings == null) {
// return false;
// }
// settings = newSettings;
return true;
}
void addPageParameter(GetPage route) {
if (route.parameters == null) return;
final parameters = Get.parameters;
final parameters = Map<String, String?>.from(Get.parameters);
parameters.addEntries(route.parameters!.entries);
Get.parameters = parameters;
// Get.parameters = parameters;
}
}
... ...
... ... @@ -74,12 +74,12 @@ class _RouterOutletState<TDelegate extends RouterDelegate<T>, T extends Object>
}
}
class GetRouterOutlet extends RouterOutlet<GetDelegate, GetNavConfig> {
class GetRouterOutlet extends RouterOutlet<GetDelegate, RouteDecoder> {
GetRouterOutlet({
String? anchorRoute,
required String initialRoute,
Iterable<GetPage> Function(Iterable<GetPage> afterAnchor)? filterPages,
GlobalKey<NavigatorState>? key,
// GlobalKey<NavigatorState>? key,
GetDelegate? delegate,
}) : this.pickPages(
pickPages: (config) {
... ... @@ -102,13 +102,13 @@ class GetRouterOutlet extends RouterOutlet<GetDelegate, GetNavConfig> {
emptyPage: (delegate) =>
Get.routeTree.matchRoute(initialRoute).route ??
delegate.notFoundRoute,
key: key,
key: Get.nestedKey(anchorRoute)?.navigatorKey,
delegate: delegate,
);
GetRouterOutlet.pickPages({
Widget Function(GetDelegate delegate)? emptyWidget,
GetPage Function(GetDelegate delegate)? emptyPage,
required Iterable<GetPage> Function(GetNavConfig currentNavStack) pickPages,
required Iterable<GetPage> Function(RouteDecoder currentNavStack) pickPages,
bool Function(Route<dynamic>, dynamic)? onPopPage,
GlobalKey<NavigatorState>? key,
GetDelegate? delegate,
... ... @@ -137,14 +137,14 @@ class GetRouterOutlet extends RouterOutlet<GetDelegate, GetNavConfig> {
return (emptyWidget?.call(rDelegate) ?? SizedBox.shrink());
},
pickPages: pickPages,
delegate: delegate ?? Get.rootDelegate,
delegate: delegate ?? GetMaterialController.to.rootDelegate,
);
GetRouterOutlet.builder({
required Widget Function(
BuildContext context,
GetDelegate delegate,
GetNavConfig? currentRoute,
RouteDecoder? currentRoute,
)
builder,
GetDelegate? routerDelegate,
... ...
void removeHash() {}
void removeLastHistory(String? url) {}
... ...
import 'web/web_url.dart' if (dart.library.io) 'io/io_url.dart';
void setUrlStrategy() {
removeHash();
}
void removeLastHistory(String? url) {
removeLastHistory(url);
}
... ...
import 'dart:html';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
void removeHash() {
setUrlStrategy(PathUrlStrategy());
}
void removeLastHistory(String? url) {
window.location.replace(null);
}
... ...
... ... @@ -154,16 +154,18 @@ extension ListExtension<E> on List<E> {
// (this as RxList)._value;
// }
clear();
if (this is RxList) {
(this as RxList).value.clear();
}
add(item);
}
/// Replaces all existing items of this list with [items]
void assignAll(Iterable<E> items) {
// if (this is RxList) {
// (this as RxList)._value;
// }
clear();
if (this is RxList) {
(this as RxList).value.clear();
}
//clear();
addAll(items);
}
}
... ...
... ... @@ -26,25 +26,25 @@ extension _Empty on Object {
mixin StateMixin<T> on ListNotifier {
late T _value;
GetState<T>? _status;
GetStatus<T>? _status;
void _fillInitialStatus() {
_status = (value == null || value!._isEmpty())
? GetState<T>.loading()
: GetState<T>.success(_value);
? GetStatus<T>.loading()
: GetStatus<T>.success(_value);
}
GetState<T> get status {
GetStatus<T> get status {
reportRead();
return _status ??= _status = GetState.loading();
return _status ??= _status = GetStatus.loading();
}
T get state => value;
set status(GetState<T> newStatus) {
set status(GetStatus<T> newStatus) {
if (newStatus == status) return;
_status = newStatus;
if (newStatus is SuccessState<T>) {
if (newStatus is SuccessStatus<T>) {
_value = newStatus.data!;
return;
}
... ... @@ -69,12 +69,15 @@ mixin StateMixin<T> on ListNotifier {
final compute = body();
compute().then((newValue) {
if ((newValue == null || newValue._isEmpty()) && useEmpty) {
status = GetState<T>.loading();
status = GetStatus<T>.loading();
} else {
status = GetState<T>.success(newValue);
status = GetStatus<T>.success(newValue);
}
refresh();
}, onError: (err) {
status = GetState.error(errorMessage ?? err.toString());
status = GetStatus.error(errorMessage ?? err.toString());
refresh();
});
}
}
... ... @@ -88,6 +91,8 @@ class GetListenable<T> extends ListNotifierSingle implements RxInterface<T> {
if (_controller == null) {
_controller = StreamController<T>.broadcast();
addListener(_streamListener);
///TODO: report to controller dispose
}
return _controller!;
}
... ... @@ -228,39 +233,39 @@ extension StateExt<T> on StateMixin<T> {
typedef NotifierBuilder<T> = Widget Function(T state);
abstract class GetState<T> {
const GetState();
factory GetState.loading() => LoadingState();
factory GetState.error(String message) => ErrorState(message);
factory GetState.empty() => EmptyState();
factory GetState.success(T data) => SuccessState(data);
abstract class GetStatus<T> {
const GetStatus();
factory GetStatus.loading() => LoadingStatus();
factory GetStatus.error(String message) => ErrorStatus(message);
factory GetStatus.empty() => EmptyStatus();
factory GetStatus.success(T data) => SuccessStatus(data);
}
class LoadingState<T> extends GetState<T> {}
class LoadingStatus<T> extends GetStatus<T> {}
class SuccessState<T> extends GetState<T> {
class SuccessStatus<T> extends GetStatus<T> {
final T data;
SuccessState(this.data);
SuccessStatus(this.data);
}
class ErrorState<T, S> extends GetState<T> {
class ErrorStatus<T, S> extends GetStatus<T> {
final S? error;
ErrorState([this.error]);
ErrorStatus([this.error]);
}
class EmptyState<T> extends GetState<T> {}
class EmptyStatus<T> extends GetStatus<T> {}
extension StatusDataExt<T> on GetState<T> {
bool get isLoading => this is LoadingState;
bool get isSuccess => this is SuccessState;
bool get isError => this is ErrorState;
bool get isEmpty => this is EmptyState;
extension StatusDataExt<T> on GetStatus<T> {
bool get isLoading => this is LoadingStatus;
bool get isSuccess => this is SuccessStatus;
bool get isError => this is ErrorStatus;
bool get isEmpty => this is EmptyStatus;
bool get isCustom => !isLoading && !isSuccess && !isError && !isEmpty;
String get errorMessage {
final isError = this is ErrorState;
final isError = this is ErrorStatus;
if (isError) {
final err = this as ErrorState;
final err = this as ErrorStatus;
if (err.error != null && err.error is String) {
return err.error as String;
}
... ... @@ -270,8 +275,8 @@ extension StatusDataExt<T> on GetState<T> {
}
T? get data {
if (this is SuccessState<T>) {
final success = this as SuccessState<T>;
if (this is SuccessStatus<T>) {
final success = this as SuccessStatus<T>;
return success.data;
}
return null;
... ...
... ... @@ -10,13 +10,13 @@ typedef GetControllerBuilder<T extends GetLifeCycleMixin> = Widget Function(
T controller);
extension WatchExt on BuildContext {
T listen<T extends GetxController>() {
T listen<T>() {
return Bind.of(this, rebuild: true);
}
}
extension ReadExt on BuildContext {
T get<T extends GetxController>() {
T get<T>() {
return Bind.of(this);
}
}
... ... @@ -212,7 +212,7 @@ abstract class Bind<T> extends StatelessWidget {
child: child,
);
static T of<T extends GetxController>(
static T of<T>(
BuildContext context, {
bool rebuild = false,
// Object Function(T value)? filter,
... ...
... ... @@ -21,7 +21,7 @@ class ListNotifierGroup = ListNotifier with ListNotifierGroupMixin;
/// This mixin add to Listenable the addListener, removerListener and
/// containsListener implementation
mixin ListNotifierSingleMixin on Listenable {
List<GetStateUpdate?>? _updaters = <GetStateUpdate?>[];
List<GetStateUpdate>? _updaters = <GetStateUpdate>[];
@override
Disposer addListener(GetStateUpdate listener) {
... ... @@ -57,8 +57,9 @@ mixin ListNotifierSingleMixin on Listenable {
}
void _notifyUpdate() {
for (var element in _updaters!) {
element!();
final list = _updaters?.toList() ?? [];
for (var element in list) {
element();
}
}
... ...
import 'dart:async';
import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart';
import 'list_notifier.dart';
... ... @@ -95,8 +96,26 @@ mixin ObserverComponent on ComponentElement {
void getUpdate() {
if (disposers != null) {
_safeRebuild();
}
}
Future<bool> _safeRebuild() async {
if (dirty) return false;
if (SchedulerBinding.instance == null) {
markNeedsBuild();
} else {
// refresh was called during the building
if (SchedulerBinding.instance!.schedulerPhase != SchedulerPhase.idle) {
// Await for the end of build
await SchedulerBinding.instance!.endOfFrame;
if (dirty) return false;
}
markNeedsBuild();
}
return true;
}
@override
... ...
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
extension ContextExt on BuildContext {
... ... @@ -170,3 +169,12 @@ extension ContextExt on BuildContext {
return strictValues.firstOrNull ?? looseValues.first;
}
}
extension IterableExt<T> on Iterable<T> {
/// The first element, or `null` if the iterable is empty.
T? get firstOrNull {
var iterator = this.iterator;
if (iterator.moveNext()) return iterator.current;
return null;
}
}
... ...
name: get
description: Open screens/snackbars/dialogs without context, manage states and inject dependencies easily with GetX.
version: 4.6.1
version: 5.0.0-beta.14
homepage: https://github.com/jonataslaw/getx
environment:
sdk: '>=2.12.0 <3.0.0'
sdk: '>=2.13.0 <3.0.0'
dependencies:
flutter:
sdk: flutter
flutter_web_plugins:
sdk: flutter
dev_dependencies:
flutter_test:
... ...
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:get/get.dart';
import 'utils/wrapper.dart';
void main() {
... ... @@ -9,6 +10,8 @@ void main() {
Wrapper(child: Container()),
);
await tester.pump();
Get.bottomSheet(Container(
child: Wrap(
children: <Widget>[
... ... @@ -31,6 +34,8 @@ void main() {
Wrapper(child: Container()),
);
await tester.pump();
Get.bottomSheet(Container(
child: Wrap(
children: <Widget>[
... ... @@ -43,9 +48,13 @@ void main() {
),
));
await tester.pumpAndSettle();
expect(Get.isBottomSheetOpen, true);
Get.back();
await tester.pumpAndSettle();
expect(Get.isBottomSheetOpen, false);
// expect(() => Get.bottomSheet(Container(), isScrollControlled: null),
... ...
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:get/get.dart';
import 'utils/wrapper.dart';
void main() {
... ... @@ -9,6 +10,8 @@ void main() {
Wrapper(child: Container()),
);
await tester.pump();
Get.defaultDialog(
onConfirm: () => print("Ok"),
middleText: "Dialog made in 3 lines of code");
... ... @@ -23,6 +26,8 @@ void main() {
Wrapper(child: Container()),
);
await tester.pump();
Get.dialog(YourDialogWidget());
await tester.pumpAndSettle();
... ... @@ -35,11 +40,20 @@ void main() {
Wrapper(child: Container()),
);
await tester.pump();
Get.dialog(YourDialogWidget());
expect(Get.isDialogOpen, true);
await tester.pumpAndSettle();
expect(find.byType(YourDialogWidget), findsOneWidget);
// expect(Get.isDialogOpen, true);
Get.back();
expect(Get.isDialogOpen, false);
await tester.pumpAndSettle();
expect(find.byType(YourDialogWidget), findsNothing);
// expect(Get.isDialogOpen, false);
// await tester.pumpAndSettle();
});
}
... ...
... ... @@ -13,7 +13,7 @@ void main() {
expect(Get.isRegistered<Controller2>(), false);
expect(Get.isRegistered<Controller>(), false);
Get.to(First());
Get.to(() => First());
await tester.pumpAndSettle();
... ... @@ -21,7 +21,7 @@ void main() {
expect(Get.isRegistered<Controller>(), true);
Get.to(Second());
Get.to(() => Second());
await tester.pumpAndSettle();
... ...
... ... @@ -8,7 +8,7 @@ void main() {
testWidgets("Get.to navigates to provided route", (tester) async {
await tester.pumpWidget(Wrapper(child: Container()));
Get.to(FirstScreen());
Get.to(() => FirstScreen());
await tester.pumpAndSettle();
... ... @@ -47,13 +47,16 @@ void main() {
await tester.pumpAndSettle();
expect(Get.currentRoute, '/404');
expect(
GetMaterialController.to.rootDelegate.currentConfiguration?.route?.name,
'/404');
});
testWidgets("Get.off navigates to provided route", (tester) async {
await tester.pumpWidget(Wrapper(child: FirstScreen()));
// await tester.pump();
Get.off(SecondScreen());
Get.off(() => SecondScreen());
await tester.pumpAndSettle();
... ... @@ -62,8 +65,9 @@ void main() {
testWidgets("Get.off removes current route", (tester) async {
await tester.pumpWidget(Wrapper(child: FirstScreen()));
await tester.pump();
Get.off(SecondScreen());
Get.off(() => SecondScreen());
Get.back();
await tester.pumpAndSettle();
... ... @@ -81,6 +85,8 @@ void main() {
],
));
await tester.pump();
Get.offNamed('/second');
await tester.pumpAndSettle();
... ... @@ -98,7 +104,10 @@ void main() {
],
));
await tester.pump();
Get.offNamed('/second');
await tester.pumpAndSettle();
Get.back();
await tester.pumpAndSettle();
... ... @@ -116,19 +125,24 @@ void main() {
],
));
// await tester.pump();
Get.toNamed('/second');
await tester.pumpAndSettle();
Get.offNamed('/third');
await tester.pumpAndSettle();
Get.back();
await tester.pumpAndSettle();
expect(find.byType(FirstScreen), findsOneWidget);
await tester.pumpAndSettle();
});
testWidgets("Get.offAll navigates to provided route", (tester) async {
await tester.pumpWidget(Wrapper(child: FirstScreen()));
await tester.pump();
Get.offAll(SecondScreen());
Get.offAll(() => SecondScreen());
await tester.pumpAndSettle();
... ... @@ -137,11 +151,13 @@ void main() {
testWidgets("Get.offAll removes all previous routes", (tester) async {
await tester.pumpWidget(Wrapper(child: FirstScreen()));
await tester.pump();
Get.to(SecondScreen());
Get.offAll(ThirdScreen());
Get.to(() => SecondScreen());
await tester.pumpAndSettle();
Get.offAll(() => ThirdScreen());
await tester.pumpAndSettle();
Get.back();
await tester.pumpAndSettle();
expect(find.byType(SecondScreen), findsNothing);
... ... @@ -164,6 +180,8 @@ void main() {
],
));
await tester.pump();
Get.toNamed('/second');
await tester.pumpAndSettle();
... ... @@ -181,10 +199,13 @@ void main() {
],
));
await tester.pump();
Get.toNamed('/second');
await tester.pumpAndSettle();
Get.offAllNamed('/third');
await tester.pumpAndSettle();
Get.back();
await tester.pumpAndSettle();
expect(find.byType(SecondScreen), findsNothing);
... ... @@ -224,6 +245,8 @@ void main() {
));
Get.offAndToNamed('/second');
await tester.pumpAndSettle();
Get.back();
await tester.pumpAndSettle();
... ... @@ -234,10 +257,11 @@ void main() {
testWidgets("Get.offUntil navigates to provided route", (tester) async {
await tester.pumpWidget(Wrapper(child: Container()));
Get.to(FirstScreen());
Get.to(() => FirstScreen());
await tester.pumpAndSettle();
Get.offUntil(GetPageRoute(page: () => ThirdScreen()),
(route) => (route as GetPageRoute).routeName == '/FirstScreen');
Get.offUntil(() => ThirdScreen(), (route) => route.name == '/FirstScreen');
await tester.pumpAndSettle();
... ... @@ -249,10 +273,12 @@ void main() {
(tester) async {
await tester.pumpWidget(Wrapper(child: Container()));
Get.to(FirstScreen());
Get.to(SecondScreen());
Get.offUntil(GetPageRoute(page: () => ThirdScreen()),
(route) => (route as GetPageRoute).routeName == '/FirstScreen');
Get.to(() => FirstScreen());
await tester.pumpAndSettle();
Get.to(() => SecondScreen());
await tester.pumpAndSettle();
Get.offUntil(() => ThirdScreen(), (route) => route.name == '/FirstScreen');
await tester.pumpAndSettle();
Get.back();
await tester.pumpAndSettle();
... ... @@ -265,10 +291,12 @@ void main() {
(tester) async {
await tester.pumpWidget(Wrapper(child: Container()));
Get.to(FirstScreen());
Get.to(SecondScreen());
Get.offUntil(GetPageRoute(page: () => ThirdScreen()),
(route) => (route as GetPageRoute).routeName == '/FirstScreen');
Get.to(() => FirstScreen());
await tester.pumpAndSettle();
Get.to(() => SecondScreen());
await tester.pumpAndSettle();
Get.offUntil(() => ThirdScreen(), (route) => route.name == '/FirstScreen');
await tester.pumpAndSettle();
Get.back();
await tester.pumpAndSettle();
... ... @@ -286,7 +314,7 @@ void main() {
],
));
Get.offNamedUntil('/second', ModalRoute.withName('/first'));
Get.offNamedUntil('/second', (route) => route.name == '/first');
await tester.pumpAndSettle();
... ... @@ -306,43 +334,52 @@ void main() {
));
Get.toNamed('/second');
Get.offNamedUntil('/third', ModalRoute.withName('/first'));
await tester.pumpAndSettle();
Get.offNamedUntil('/third', (route) => route.name == '/first');
await tester.pumpAndSettle();
expect(find.byType(SecondScreen), findsNothing);
});
testWidgets(
"Get.offNamedUntil leaves previous routes that match provided predicate",
(tester) async {
await tester.pumpWidget(WrapperNamed(
initialRoute: '/first',
namedRoutes: [
GetPage(page: () => FirstScreen(), name: '/first'),
GetPage(page: () => SecondScreen(), name: '/second'),
GetPage(page: () => ThirdScreen(), name: '/third'),
],
));
Get.toNamed('/second');
Get.offNamedUntil('/third', ModalRoute.withName('/first'));
Get.back();
await tester.pumpAndSettle();
expect(find.byType(FirstScreen), findsOneWidget);
});
// testWidgets(
// "Get.offNamedUntil leaves previous routes that match provided predicate",
// (tester) async {
// await tester.pumpWidget(WrapperNamed(
// initialRoute: '/first',
// namedRoutes: [
// GetPage(page: () => FirstScreen(), name: '/first'),
// GetPage(page: () => SecondScreen(), name: '/second'),
// GetPage(page: () => ThirdScreen(), name: '/third'),
// ],
// ));
// Get.toNamed('/second');
// await tester.pumpAndSettle();
// Get.offNamedUntil('/third', (route) => route.name == '/first');
// await tester.pumpAndSettle();
// Get.back();
// await tester.pumpAndSettle();
// expect(find.byType(FirstScreen), findsOneWidget);
// });
testWidgets("Get.back navigates back", (tester) async {
await tester.pumpWidget(
Wrapper(
child: FirstScreen(),
child: Container(),
defaultTransition: Transition.circularReveal,
),
);
Get.to(SecondScreen());
// await tester.pump();
Get.to(() => FirstScreen());
await tester.pumpAndSettle();
Get.to(() => SecondScreen());
await tester.pumpAndSettle();
Get.back();
await tester.pumpAndSettle();
... ... @@ -353,135 +390,180 @@ void main() {
testWidgets(
"Get.back with closeOverlays pops both snackbar and current route",
(tester) async {
await tester.pumpWidget(Wrapper(child: FirstScreen()));
await tester.pumpWidget(
Wrapper(
child: Container(),
defaultTransition: Transition.circularReveal,
),
);
Get.to(SecondScreen());
// await tester.pump();
Get.to(() => FirstScreen());
await tester.pumpAndSettle();
Get.to(() => SecondScreen());
await tester.pumpAndSettle();
Get.snackbar('title', "message");
await tester.pumpAndSettle();
Get.back(closeOverlays: true);
await tester.pumpAndSettle();
expect(Get.isSnackbarOpen, false);
expect(find.byType(FirstScreen), findsOneWidget);
});
testWidgets("Get.defaultTransition smoke test", (tester) async {
await tester.pumpWidget(
Wrapper(
child: Container(),
defaultTransition: Transition.fadeIn,
),
);
group("Get.defaultTransition smoke test", () {
testWidgets("fadeIn", (tester) async {
await tester.pumpWidget(
Wrapper(
child: Container(),
defaultTransition: Transition.fadeIn,
),
);
Get.to(FirstScreen());
Get.to(() => FirstScreen());
await tester.pumpAndSettle();
await tester.pumpAndSettle();
expect(find.byType(FirstScreen), findsOneWidget);
expect(find.byType(FirstScreen), findsOneWidget);
});
await tester.pumpWidget(
Wrapper(
child: Container(),
defaultTransition: Transition.downToUp,
),
);
testWidgets("downToUp", (tester) async {
await tester.pumpWidget(
Wrapper(
child: Container(),
defaultTransition: Transition.downToUp,
),
);
Get.to(FirstScreen());
Get.to(() => FirstScreen());
await tester.pumpAndSettle();
await tester.pumpAndSettle();
expect(find.byType(FirstScreen), findsOneWidget);
expect(find.byType(FirstScreen), findsOneWidget);
});
await tester.pumpWidget(
Wrapper(
child: Container(),
defaultTransition: Transition.fade,
),
);
testWidgets("fade", (tester) async {
await tester.pumpWidget(
Wrapper(
child: Container(),
defaultTransition: Transition.fade,
),
);
Get.to(FirstScreen());
Get.to(() => FirstScreen());
await tester.pumpAndSettle();
await tester.pumpAndSettle();
expect(find.byType(FirstScreen), findsOneWidget);
expect(find.byType(FirstScreen), findsOneWidget);
});
await tester.pumpWidget(
Wrapper(
child: Container(),
defaultTransition: Transition.leftToRight,
),
);
testWidgets("leftToRight", (tester) async {
await tester.pumpWidget(
Wrapper(
child: Container(),
defaultTransition: Transition.leftToRight,
),
);
Get.to(FirstScreen());
Get.to(() => FirstScreen());
await tester.pumpAndSettle();
await tester.pumpAndSettle();
expect(find.byType(FirstScreen), findsOneWidget);
expect(find.byType(FirstScreen), findsOneWidget);
});
await tester.pumpWidget(
Wrapper(
child: Container(),
defaultTransition: Transition.leftToRightWithFade,
),
);
testWidgets("leftToRightWithFade", (tester) async {
await tester.pumpWidget(
Wrapper(
child: Container(),
defaultTransition: Transition.leftToRightWithFade,
),
);
Get.to(FirstScreen());
Get.to(() => FirstScreen());
await tester.pumpAndSettle();
await tester.pumpAndSettle();
expect(find.byType(FirstScreen), findsOneWidget);
expect(find.byType(FirstScreen), findsOneWidget);
});
await tester.pumpWidget(
Wrapper(
child: Container(),
defaultTransition: Transition.rightToLeft,
),
);
testWidgets("leftToRightWithFade", (tester) async {
await tester.pumpWidget(
Wrapper(
child: Container(),
defaultTransition: Transition.rightToLeft,
),
);
Get.to(FirstScreen());
Get.to(() => FirstScreen());
await tester.pumpAndSettle();
await tester.pumpAndSettle();
expect(find.byType(FirstScreen), findsOneWidget);
expect(find.byType(FirstScreen), findsOneWidget);
});
await tester.pumpWidget(
Wrapper(
child: Container(),
defaultTransition: Transition.rightToLeftWithFade,
),
);
testWidgets("defaultTransition", (tester) async {
await tester.pumpWidget(
Wrapper(
child: Container(),
defaultTransition: Transition.rightToLeft,
),
);
Get.to(FirstScreen());
Get.to(() => FirstScreen());
await tester.pumpAndSettle();
await tester.pumpAndSettle();
expect(find.byType(FirstScreen), findsOneWidget);
expect(find.byType(FirstScreen), findsOneWidget);
});
await tester.pumpWidget(
Wrapper(
child: Container(),
defaultTransition: Transition.cupertino,
),
);
testWidgets("rightToLeftWithFade", (tester) async {
await tester.pumpWidget(
Wrapper(
child: Container(),
defaultTransition: Transition.rightToLeftWithFade,
),
);
Get.to(FirstScreen());
Get.to(() => FirstScreen());
await tester.pumpAndSettle();
await tester.pumpAndSettle();
expect(find.byType(FirstScreen), findsOneWidget);
expect(find.byType(FirstScreen), findsOneWidget);
});
await tester.pumpWidget(
Wrapper(
child: Container(),
defaultTransition: Transition.size,
),
);
testWidgets("cupertino", (tester) async {
await tester.pumpWidget(
Wrapper(
child: Container(),
defaultTransition: Transition.cupertino,
),
);
Get.to(FirstScreen());
Get.to(() => FirstScreen());
await tester.pumpAndSettle();
await tester.pumpAndSettle();
expect(find.byType(FirstScreen), findsOneWidget);
expect(find.byType(FirstScreen), findsOneWidget);
});
testWidgets("size", (tester) async {
await tester.pumpWidget(
Wrapper(
child: Container(),
defaultTransition: Transition.size,
),
);
Get.to(() => FirstScreen());
await tester.pumpAndSettle();
expect(find.byType(FirstScreen), findsOneWidget);
});
});
}
... ...
... ... @@ -5,8 +5,15 @@ import 'package:get/get.dart';
import 'get_main_test.dart';
class RedirectMiddleware extends GetMiddleware {
// @override
// RouteSettings redirect(String? route) {
// return RouteSettings(name: '/second');
// }
@override
RouteSettings redirect(String? route) => RouteSettings(name: '/second');
Future<RouteDecoder?> redirectDelegate(RouteDecoder route) async {
return RouteDecoder.fromRoute('/second');
}
}
void main() {
... ... @@ -16,10 +23,9 @@ void main() {
initialRoute: '/',
getPages: [
GetPage(name: '/', page: () => Container()),
GetPage(
name: '/first',
page: () => FirstScreen(),
middlewares: [RedirectMiddleware()]),
GetPage(name: '/first', page: () => FirstScreen(), middlewares: [
RedirectMiddleware(),
]),
GetPage(name: '/second', page: () => SecondScreen()),
GetPage(name: '/third', page: () => ThirdScreen()),
],
... ... @@ -29,7 +35,7 @@ void main() {
Get.toNamed('/first');
await tester.pumpAndSettle();
print(Get.routing.current);
print(Get.rootController.rootDelegate.currentConfiguration?.route?.name);
expect(find.byType(SecondScreen), findsOneWidget);
});
}
... ...
import 'package:flutter/cupertino.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:get/get.dart';
import 'package:get/get_navigation/src/root/parse_route.dart';
void main() {
test('Parse Page with children', () {
... ...
... ... @@ -14,9 +14,12 @@ void main() {
child: Center(
child: CupertinoButton(
onPressed: () {
Get.to(() => CupertinoPageScaffold(
child: Center(child: Text('route')),
));
Get.to(
() => CupertinoPageScaffold(
child: Center(child: Text('route')),
),
preventDuplicateHandlingMode:
PreventDuplicateHandlingMode.Recreate);
},
child: const Text('push'),
),
... ... @@ -25,6 +28,8 @@ void main() {
),
);
await tester.pumpAndSettle();
// Check the basic iOS back-swipe dismiss transition. Dragging the pushed
// route halfway across the screen will trigger the iOS dismiss animation
... ... @@ -52,7 +57,7 @@ void main() {
of: find.text('push'),
matching: find.byType(CupertinoPageScaffold)))
.dx,
0,
moreOrLessEquals(-(400 / 3), epsilon: 1),
);
await tester.pumpAndSettle();
expect(find.text('push'), findsOneWidget);
... ... @@ -99,22 +104,5 @@ void main() {
.dx,
moreOrLessEquals(798, epsilon: 1),
);
// Use the navigator to push a route instead of tapping the 'push' button.
// The topmost route (the one that's animating away), ignores input while
// the pop is underway because route.navigator.userGestureInProgress.
Get.to(() => const CupertinoPageScaffold(
child: Center(child: Text('route')),
));
await tester.pumpAndSettle();
expect(find.text('route'), findsOneWidget);
expect(find.text('push'), findsNothing);
expect(
tester
.state<NavigatorState>(find.byType(Navigator))
.userGestureInProgress,
false,
);
});
}
... ...