Jonny Borges
Committed by GitHub

Merge pull request #2087 from Bdaya-Dev/master

Minor improvements with small breaking changes
... ... @@ -75,9 +75,7 @@ class HomeView extends GetView<HomeController> {
shape: StadiumBorder(),
),
onPressed: () async {
final data =
await Get.rootDelegate.toNamed('/home/country');
print('DATA: $data');
await Get.rootDelegate.toNamed('/home/country');
},
child: Text(
'fetch_country'.tr,
... ...
import 'dart:async';
import 'package:get/get.dart';
import 'package:async/async.dart';
class SplashService extends GetxService {
final welcomeStr = ['GetX', 'Rules!'];
final activeStr = 0.obs;
final memo = AsyncMemoizer<void>();
Future<void> init() {
return memo.runOnce(_initFunction);
}
void _changeActiveString() {
activeStr.value = (activeStr.value + 1) % welcomeStr.length;
}
Future<void> _initFunction() async {
final t = Timer.periodic(
Duration(milliseconds: 500),
(t) => _changeActiveString(),
);
//simulate some long running operation
await Future.delayed(Duration(seconds: 5));
//cancel the timer once we are done
t.cancel();
}
}
... ...
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/splash_service.dart';
class SplashView extends GetView<SplashService> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Obx(
() => Text(
controller.welcomeStr[controller.activeStr.value],
style: TextStyle(fontSize: 20),
),
),
CircularProgressIndicator(),
],
),
),
);
}
}
... ...
import 'package:example_nav2/app/modules/splash/controllers/splash_service.dart';
import 'package:example_nav2/app/modules/splash/views/splash_view.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
... ... @@ -10,10 +12,23 @@ void main() {
title: "Application",
initialBinding: BindingsBuilder(
() {
Get.put(SplashService());
Get.put(AuthService());
},
),
getPages: AppPages.routes,
builder: (context, child) {
return FutureBuilder<void>(
key: ValueKey('initFuture'),
future: Get.find<SplashService>().init(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return child ?? SizedBox.shrink();
}
return SplashView();
},
);
},
// routeInformationParser: GetInformationParser(
// // initialRoute: Routes.HOME,
// ),
... ...
... ... @@ -2,8 +2,6 @@
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
... ...
... ... @@ -2,8 +2,6 @@
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
... ...
... ... @@ -162,29 +162,24 @@ class GetInstance {
}) {
final key = _getKey(S, name);
_InstanceBuilderFactory<S>? dep;
if (_singl.containsKey(key)) {
final dep = _singl[key];
if (dep != null && dep.isDirty) {
_singl[key] = _InstanceBuilderFactory<S>(
isSingleton,
builder,
permanent,
false,
fenix,
name,
lateRemove: dep as _InstanceBuilderFactory<S>,
);
final _dep = _singl[key];
if (_dep == null || !_dep.isDirty) {
return;
} else {
dep = _dep as _InstanceBuilderFactory<S>;
}
} else {
_singl[key] = _InstanceBuilderFactory<S>(
isSingleton,
builder,
permanent,
false,
fenix,
name,
);
}
_singl[key] = _InstanceBuilderFactory<S>(
isSingleton: isSingleton,
builderFunc: builder,
permanent: permanent,
isInit: false,
fenix: fenix,
tag: name,
lateRemove: dep,
);
}
/// Initializes the dependencies for a Class Instance [S] (or tag),
... ... @@ -519,14 +514,14 @@ class _InstanceBuilderFactory<S> {
String? tag;
_InstanceBuilderFactory(
this.isSingleton,
this.builderFunc,
this.permanent,
this.isInit,
this.fenix,
this.tag, {
this.lateRemove,
_InstanceBuilderFactory({
required this.isSingleton,
required this.builderFunc,
required this.permanent,
required this.isInit,
required this.fenix,
required this.tag,
required this.lateRemove,
});
void _showInitLog() {
... ...
... ... @@ -4,6 +4,7 @@ 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';
... ...
... ... @@ -56,5 +56,5 @@ class GetNavConfig extends RouteInformation {
@override
String toString() => '''
======GetNavConfig=====\ncurrentTreeBranch: $currentTreeBranch\ncurrentPage: $currentPage\n======GetNavConfig=====''';
======GetNavConfig=====\nlocation: $location\ncurrentTreeBranch: $currentTreeBranch\n======GetNavConfig=====''';
}
... ...
import 'package:flutter/widgets.dart';
import '../routes/default_route.dart';
import '../routes/get_route.dart';
class GetNavigator extends Navigator {
GetNavigator.onGenerateRoute({
GlobalKey<NavigatorState>? key,
bool Function(Route<dynamic>, dynamic)? onPopPage,
required List<GetPage> pages,
List<NavigatorObserver>? observers,
bool reportsRouteUpdateToEngine = false,
TransitionDelegate? transitionDelegate,
String? initialRoute,
}) : super(
//keys should be optional
key: key,
initialRoute: initialRoute,
onPopPage: onPopPage ??
(route, result) {
final didPop = route.didPop(result);
if (!didPop) {
return false;
}
return true;
},
onGenerateRoute: (settings) {
final selectedPageList =
pages.where((element) => element.name == settings.name);
if (selectedPageList.isNotEmpty) {
final selectedPage = selectedPageList.first;
return GetPageRoute(
page: selectedPage.page,
settings: settings,
);
}
},
reportsRouteUpdateToEngine: reportsRouteUpdateToEngine,
pages: pages,
observers: [
// GetObserver(),
...?observers,
],
transitionDelegate:
transitionDelegate ?? const DefaultTransitionDelegate<dynamic>(),
);
GetNavigator({
GlobalKey<NavigatorState>? key,
bool Function(Route<dynamic>, dynamic)? onPopPage,
required List<GetPage> pages,
List<NavigatorObserver>? observers,
bool reportsRouteUpdateToEngine = false,
TransitionDelegate? transitionDelegate,
String? initialRoute,
}) : super(
//keys should be optional
key: key,
initialRoute: initialRoute,
onPopPage: onPopPage ??
(route, result) {
final didPop = route.didPop(result);
if (!didPop) {
return false;
}
return true;
},
reportsRouteUpdateToEngine: reportsRouteUpdateToEngine,
pages: pages,
observers: [
// GetObserver(),
...?observers,
],
transitionDelegate:
transitionDelegate ?? const DefaultTransitionDelegate<dynamic>(),
);
}
... ...
... ... @@ -2,9 +2,50 @@ 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';
import 'get_navigator.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 {
... ... @@ -17,7 +58,10 @@ class GetDelegate extends RouterDelegate<GetNavConfig>
final List<NavigatorObserver>? navigatorObservers;
final TransitionDelegate<dynamic>? transitionDelegate;
final _allCompleters = <GetPage, Completer>{};
final Iterable<GetPage> Function(GetNavConfig currentNavStack)?
pickPagesForRootNavigator;
GlobalKey<NavigatorState> get navigatorKey => Get.key;
GetDelegate({
GetPage? notFoundRoute,
... ... @@ -26,6 +70,7 @@ class GetDelegate extends RouterDelegate<GetNavConfig>
this.backButtonPopMode = PopMode.History,
this.preventDuplicateHandlingMode =
PreventDuplicateHandlingMode.ReorderRoutes,
this.pickPagesForRootNavigator,
}) : notFoundRoute = notFoundRoute ??
GetPage(
name: '/404',
... ... @@ -36,191 +81,232 @@ class GetDelegate extends RouterDelegate<GetNavConfig>
Get.log('GetDelegate is created !');
}
@override
GetNavConfig? get currentConfiguration {
if (history.isEmpty) return null;
final route = history.last;
return route;
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;
}
GlobalKey<NavigatorState> get navigatorKey => Get.key;
Future<void> _unsafeHistoryAdd(GetNavConfig config) async {
final res = await runMiddleware(config);
if (res == null) return;
history.add(res);
}
Map<String, String> get parameters {
return currentConfiguration?.currentPage?.parameters ?? {};
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;
}
/// Removes routes according to [PopMode]
/// until it reaches the specifc [fullRoute],
/// DOES NOT remove the [fullRoute]
Future<void> backUntil(
String fullRoute, {
PopMode popMode = PopMode.Page,
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 {
// 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;
//this changes the currentConfiguration
await _pushHistory(config);
if (rebuildStack) {
refresh();
}
refresh();
}
@override
Widget build(BuildContext context) {
final pages = getVisualPages();
if (pages.length == 0) return SizedBox.shrink();
final extraObservers = navigatorObservers;
return GetNavigator(
key: navigatorKey,
onPopPage: _onPopVisualRoute,
pages: pages,
observers: [
GetObserver(),
if (extraObservers != null) ...extraObservers,
],
transitionDelegate:
transitionDelegate ?? const DefaultTransitionDelegate<dynamic>(),
);
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();
}
// void _unsafeHistoryClear() {
// history.clear();
// }
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 [participatesInRootNavigator] set to true
List<GetPage> getVisualPages() {
final currentHistory = currentConfiguration;
if (currentHistory == null) return <GetPage>[];
/// 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!).toList();
return history.map((e) => e.currentPage!);
} else {
//user specified at least one participatesInRootNavigator
return res
.where((element) => element.participatesInRootNavigator == true)
.toList();
.where((element) => element.participatesInRootNavigator == true);
}
}
// GetPageRoute getPageRoute(RouteSettings? settings) {
// return PageRedirect(settings ?? RouteSettings(name: '/404'), _notFound())
// .page();
// }
@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>(),
);
}
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;
}
Future<T?>? offAndToNamed<T>(
String page, {
dynamic arguments,
int? id,
dynamic result,
Map<String, String>? parameters,
PopMode popMode = PopMode.History,
}) async {
if (parameters != null) {
final uri = Uri(path: page, queryParameters: parameters);
page = uri.toString();
}
await popRoute(result: result);
return toNamed(page, arguments: arguments, parameters: parameters);
}
Future<T> offNamed<T>(
String page, {
dynamic arguments,
Map<String, String>? parameters,
}) async {
history.removeLast();
return toNamed<T>(page, arguments: arguments, parameters: parameters);
}
Future<GetNavConfig?> popHistory() async {
return await _popHistory();
}
// returns the popped page
@override
Future<bool> popRoute({
Object? result,
PopMode popMode = PopMode.Page,
}) 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);
refresh();
if (_popped != null) {
//emulate the old pop with result
return true;
}
return false;
}
/// 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<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> setNewRoutePath(GetNavConfig configuration) async {
await pushHistory(configuration);
}
@override
Future<void> setNewRoutePath(GetNavConfig configuration) async {
await pushHistory(configuration);
GetNavConfig? get currentConfiguration {
if (history.isEmpty) return null;
final route = history.last;
return route;
}
Future<T> toNamed<T>(
Future<void> toNamed(
String page, {
dynamic arguments,
Map<String, String>? parameters,
... ... @@ -233,10 +319,7 @@ class GetDelegate extends RouterDelegate<GetNavConfig>
final decoder = Get.routeTree.matchRoute(page, arguments: arguments);
decoder.replaceArguments(arguments);
final completer = Completer<T>();
if (decoder.route != null) {
_allCompleters[decoder.route!] = completer;
await pushHistory(
GetNavConfig(
currentTreeBranch: decoder.treeBranch,
... ... @@ -244,80 +327,76 @@ class GetDelegate extends RouterDelegate<GetNavConfig>
state: null, //TODO: persist state?
),
);
return completer.future;
} else {
///TODO: IMPLEMENT ROUTE NOT FOUND
return Future.value();
}
}
bool _canPop(PopMode mode) {
switch (mode) {
case PopMode.History:
return _canPopHistory();
case PopMode.Page:
default:
return _canPopPage();
await pushHistory(
GetNavConfig(
currentTreeBranch: [notFoundRoute],
location: notFoundRoute.name,
state: null, //TODO: persist state?
),
);
}
}
bool _canPopHistory() {
return history.length > 1;
//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);
}
bool _canPopPage() {
final currentTreeBranch = currentConfiguration?.currentTreeBranch;
if (currentTreeBranch == null) return false;
return currentTreeBranch.length > 1 ? true : _canPopHistory();
/// 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<GetNavConfig?> _doPopHistory() async {
return await _unsafeHistoryRemoveAt(history.length - 1);
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<void> setInitialRoutePath(GetNavConfig configuration) async {
// //no need to clear history with Reorder route strategy
// // _unsafeHistoryClear();
// // _resultCompleter.clear();
// await pushHistory(configuration);
// }
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();
@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) {
... ... @@ -334,153 +413,9 @@ class GetDelegate extends RouterDelegate<GetNavConfig>
if (config != null) {
_removeHistoryEntry(config);
}
if (_allCompleters.containsKey(settings)) {
_allCompleters[settings]?.complete(route.popped);
}
}
refresh();
return true;
}
Future<GetNavConfig?> _pop(PopMode mode) async {
switch (mode) {
case PopMode.History:
return await _popHistory();
case PopMode.Page:
return await _popPage();
default:
return null;
}
}
Future<GetNavConfig?> _popHistory() async {
if (!_canPopHistory()) return null;
return await _doPopHistory();
}
Future<GetNavConfig?> _popPage() async {
if (!_canPopPage()) return null;
return await _doPopPage();
}
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<void> _removeHistoryEntry(GetNavConfig entry) async {
await _unsafeHistoryRemove(entry);
}
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);
}
}
class GetNavigator extends Navigator {
GetNavigator({
GlobalKey<NavigatorState>? key,
bool Function(Route<dynamic>, dynamic)? onPopPage,
required List<Page> pages,
List<NavigatorObserver>? observers,
bool reportsRouteUpdateToEngine = false,
TransitionDelegate? transitionDelegate,
}) : super(
//keys should be optional
key: key,
onPopPage: onPopPage ??
(route, result) {
final didPop = route.didPop(result);
if (!didPop) {
return false;
}
return true;
},
reportsRouteUpdateToEngine: reportsRouteUpdateToEngine,
pages: pages,
observers: [
// GetObserver(),
if (observers != null) ...observers,
],
transitionDelegate:
transitionDelegate ?? const DefaultTransitionDelegate<dynamic>(),
);
}
/// 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
}
... ...
import 'package:flutter/widgets.dart';
import 'default_route.dart';
enum Transition {
fade,
fadeIn,
... ... @@ -20,3 +22,4 @@ enum Transition {
}
typedef GetPageBuilder = Widget Function();
typedef GetRouteAwarePageBuilder<T> = Widget Function([GetPageRoute<T>? route]);
... ...
... ... @@ -569,7 +569,4 @@ class BindError<T> extends Error {
/// instance of Bindings to manage the
/// dependencies() (via Get.put()) for the Route you are opening.
// ignore: one_member_abstracts
abstract class Binding extends BindingsInterface<List<Bind>> {
@override
List<Bind> dependencies();
}
abstract class Binding extends BindingsInterface<Iterable<Bind>> {}
... ...
... ... @@ -24,36 +24,32 @@ typedef ValueBuilderBuilder<T> = Widget Function(
/// ),
/// ```
class ValueBuilder<T> extends StatefulWidget {
final T? initialValue;
final T initialValue;
final ValueBuilderBuilder<T> builder;
final void Function()? onDispose;
final void Function(T)? onUpdate;
const ValueBuilder({
Key? key,
this.initialValue,
required this.initialValue,
this.onDispose,
this.onUpdate,
required this.builder,
}) : super(key: key);
@override
_ValueBuilderState<T> createState() => _ValueBuilderState<T>();
_ValueBuilderState<T> createState() => _ValueBuilderState<T>(initialValue);
}
class _ValueBuilderState<T> extends State<ValueBuilder<T?>> {
T? value;
class _ValueBuilderState<T> extends State<ValueBuilder<T>> {
T value;
_ValueBuilderState(this.value);
@override
void initState() {
super.initState();
value = widget.initialValue;
}
@override
Widget build(BuildContext context) => widget.builder(value, updater);
void updater(T? newValue) {
void updater(T newValue) {
if (widget.onUpdate != null) {
widget.onUpdate!(newValue);
}
... ... @@ -71,7 +67,6 @@ class _ValueBuilderState<T> extends State<ValueBuilder<T?>> {
} else if (value is StreamController) {
(value as StreamController?)?.close();
}
value = null;
}
}
... ...
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import '../platform/platform.dart';
extension ContextExtensionss on BuildContext {
/// The same of [MediaQuery.of(context).size]
Size get mediaQuerySize => MediaQuery.of(this).size;
... ... @@ -100,17 +99,44 @@ extension ContextExtensionss on BuildContext {
/// True if width be larger than 800
bool get showNavbar => (width > 800);
/// True if the shortestSide is smaller than 600p
bool get isPhone => (mediaQueryShortestSide < 600);
/// True if the width is smaller than 600p
bool get isPhoneOrLess => width <= 600;
/// True if the width is higher than 600p
bool get isPhoneOrWider => width >= 600;
/// same as [isPhoneOrLess]
bool get isPhone => isPhoneOrLess;
/// True if the width is smaller than 600p
bool get isSmallTabletOrLess => width <= 600;
/// True if the width is higher than 600p
bool get isSmallTabletOrWider => width >= 600;
/// same as [isSmallTabletOrLess]
bool get isSmallTablet => isSmallTabletOrLess;
/// True if the shortestSide is largest than 600p
bool get isSmallTablet => (mediaQueryShortestSide >= 600);
/// True if the width is smaller than 720p
bool get isLargeTabletOrLess => width <= 720;
/// True if the shortestSide is largest than 720p
bool get isLargeTablet => (mediaQueryShortestSide >= 720);
/// True if the width is higher than 720p
bool get isLargeTabletOrWider => width >= 720;
/// same as [isLargeTabletOrLess]
bool get isLargeTablet => isLargeTabletOrLess;
/// True if the current device is Tablet
bool get isTablet => isSmallTablet || isLargeTablet;
bool get isTablet => isSmallTablet;
/// True if the width is smaller than 1200p
bool get isDesktopOrLess => width <= 1200;
/// True if the width is higher than 1200p
bool get isDesktopOrWider => width >= 1200;
/// same as [isDesktopOrLess]
bool get isDesktop => isDesktopOrLess;
/// Returns a specific value according to the screen size
/// if the device width is higher than or equal to 1200 return
... ... @@ -119,23 +145,28 @@ extension ContextExtensionss on BuildContext {
/// if the device width is less than 300 return [watch] value.
/// in other cases return [mobile] value.
T responsiveValue<T>({
T? watch,
T? mobile,
T? tablet,
T? desktop,
T? watch,
}) {
var deviceWidth = mediaQuerySize.shortestSide;
if (GetPlatform.isDesktop) {
deviceWidth = mediaQuerySize.width;
}
if (deviceWidth >= 1200 && desktop != null) {
return desktop;
} else if (deviceWidth >= 600 && tablet != null) {
return tablet;
} else if (deviceWidth < 300 && watch != null) {
return watch;
} else {
return mobile!;
}
assert(
watch != null || mobile != null || tablet != null || desktop != null);
var deviceWidth = mediaQuerySize.width;
//big screen width can display smaller sizes
final strictValues = [
if (deviceWidth >= 1200) desktop, //desktop is allowed
if (deviceWidth >= 600) tablet, //tablet is allowed
if (deviceWidth >= 300) mobile, //mobile is allowed
watch, //watch is allowed
].whereType<T>();
final looseValues = [
watch,
mobile,
tablet,
desktop,
].whereType<T>();
return strictValues.firstOrNull ?? looseValues.first;
}
}
... ...
... ... @@ -34,16 +34,19 @@ void main() {
expect(isLandscape, context.isLandscape);
var mediaQueryShortestSide = mediaQuerySize.shortestSide;
expect(mediaQueryShortestSide, context.mediaQueryShortestSide);
var isLargeTablet = (mediaQueryShortestSide >= 720);
expect(isLargeTablet, context.isLargeTablet);
var isPhone = (mediaQueryShortestSide < 600);
expect(isPhone, context.isPhone);
var width = mediaQuerySize.width;
expect(width, context.width);
var isLargeTabletOrWider = (width >= 720);
expect(isLargeTabletOrWider, context.isLargeTabletOrWider);
var isPhoneOrLess = (width < 600);
expect(isPhoneOrLess, context.isPhoneOrLess);
var isPortrait = orientation == Orientation.portrait;
expect(isPortrait, context.isPortrait);
var isSmallTablet = (mediaQueryShortestSide >= 600);
expect(isSmallTablet, context.isSmallTablet);
var isTablet = isSmallTablet || isLargeTablet;
expect(isTablet, context.isTablet);
var isSmallTabletOrWider = (width >= 600);
expect(isSmallTabletOrWider, context.isSmallTabletOrWider);
var isTablet = isSmallTabletOrWider || isLargeTabletOrWider;
expect(isTablet, context.isSmallTabletOrWider);
var mediaQueryPadding = mediaQuery.padding;
expect(mediaQueryPadding, context.mediaQueryPadding);
var mediaQueryViewInsets = mediaQuery.viewInsets;
... ... @@ -55,8 +58,7 @@ void main() {
expect(widthTransformer, context.widthTransformer());
var ratio = heightTransformer / widthTransformer;
expect(ratio, context.ratio());
var width = mediaQuerySize.width;
expect(width, context.width);
var showNavbar = (width > 800);
expect(showNavbar, context.showNavbar);
var textScaleFactor = mediaQuery.textScaleFactor;
... ...