Jonny Borges
Committed by GitHub

Add files via upload

... ... @@ -189,3 +189,15 @@
- Added overlayContext
- Fix Duration transition
## [1.16.1-dev]
- Improve: GetObserver
## [1.17.0-dev]
- Added experimental APIs
## [1.18.0-dev]
- Added SafeArea to bottomsheets
- Added docs
## [1.19.0-dev]
- Added nested navigators
\ No newline at end of file
... ...
... ... @@ -4,14 +4,26 @@ A consistent navigation library that lets you navigate between screens, open dia
## Getting Started
Flutter's conventional navigation has a lot of unnecessary boilerplate, requires context to navigate between screens, open dialogs, and use snackbars on framework is really painful.
In addition, with each route navigation, all of your screens below MaterialApp are rebuilt, often causing RAM and CPU bottlenecks.
I worked on a pull to fix it in the framework, and seeing how things work I realized that a lot of cliche code could be avoided to get clean and concise code.
With that in mind, I created this library that will change the way you work with the Framework and save your life from cliche code,
increasing your productivity, and eliminating all the bugs present in Flutter's default navigation altogether.
In addition, when a route is pushed, the entire MaterialApp can be rebuilt causing freezes, this does not happen with Get.
This library that will change the way you work with the Framework and save your life from cliche code, increasing your productivity, and eliminating the rebuild bugs of your application.
##### If you use MODULAR, add on your MaterialApp this: navigatorKey: Get.addKey(Modular.navigatorKey)
```dart
// Default Flutter navigator
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return Home();
},
),
);
// Get sintax
Get.to(Home());
```
##### If you use master/dev branch of Flutter, use the version 1.16.0-dev.
##### If you use master/dev/beta branch of Flutter, use the version 1.19.0-dev.
* If you use MODULAR, add on your MaterialApp this: navigatorKey: Get.addKey(Modular.navigatorKey)
## How to use?
... ... @@ -19,7 +31,7 @@ Add this to your package's pubspec.yaml file:
```
dependencies:
get: ^1.12.0 // ^1.16.0-dev on dev/master
get: ^1.15.2 // ^1.19.0-dev on dev/master
```
And import it:
... ... @@ -74,14 +86,56 @@ ex:
if(data == 'sucess') madeAnything();
```
Don't you want to learn our syntax?
Just change the Navigator (uppercase) to navigator (lowercase), and you will have all the functions of the standard browser, without having to use context
Example:
```dart
// Default Flutter navigator
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return HomePage();
},
),
);
// Get with no context but Flutter sintax
navigator.push(
MaterialPageRoute(
builder: (context) {
return HomePage();
},
),
);
// Get sintax (Much better, but we respect your opinion)
Get.to(HomePage());
```
### SnackBars
To show a modern snackbar:
To have a simple SnackBar with Flutter, you must get the context of Scaffold, or you must use a GlobalKey attached to your Scaffold,
```dart
final snackBar = SnackBar(
content: Text('Hi!'),
action: SnackBarAction(
label: 'I am a old and ugly snackbar :(',
onPressed: (){}
),
// Find the Scaffold in the widget tree and use
// it to show a SnackBar.
Scaffold.of(context).showSnackBar(snackBar);
```
With Get:
```dart
Get.snackbar('Hi', 'i am a modern snackbar');
```
To have a simple SnackBar with Flutter, you must get the context of Scaffold, or you must use a GlobalKey attached to your Scaffold,
but with Get, all you have to do is call your Get.snackbar from anywhere in your code or customize it however you want!
With Get, all you have to do is call your Get.snackbar from anywhere in your code or customize it however you want!
```dart
Get.snackbar(
... ... @@ -131,7 +185,8 @@ but with Get, all you have to do is call your Get.snackbar from anywhere in your
// Form userInputForm
///////////////////////////////////
```
If you prefer the traditional snackbar, or want to customize it from scratch, including adding just one line (Get.snackbar makes use of a mandatory title and message), you can use GetBar (). Show () which provides the RAW API on which Get.snackbar was built.
If you prefer the traditional snackbar, or want to customize it from scratch, including adding just one line (Get.snackbar makes use of a mandatory title and message), you can use
`GetBar().show();` which provides the RAW API on which Get.snackbar was built.
### Dialogs
... ... @@ -192,6 +247,7 @@ Get.config(
defaultTransition = Transitions.cupertino}
```
## Navigate with named routes:
- Yes, and with no navigation bug, add "named" to Get. HOWEVER, TO MAKE THIS TYPE OF NAVIGATION, USE THE ROUTE MODEL FROM REPOSITORY.
Example of navigation with named routes:
... ... @@ -290,7 +346,7 @@ class Router {
}
```
And now, all you need to do is use Get.toNamed() to navigate your named routes, without any context (BLoC will love it), and when your app is compiled to the web, your routes will appear in the url beautifully <3
And now, all you need to do is use Get.toNamed() to navigate your named routes, without any context (you can call your routes directly from your BLoC or Controller class), and when your app is compiled to the web, your routes will appear in the url beautifully <3
```dart
class First extends StatelessWidget {
... ... @@ -363,6 +419,94 @@ class Third extends StatelessWidget {
}
```
### Advanced APIs
Each day Get gets further away from the standard Framework, and provides a wider range of features that are unthinkable to be executed using the standard Flutter.
With Get 1.17.0 a range of new APIs was launched, which allow access from arguments of a named route to whether there is a snackbar or dialog open at that moment, or which screen is being displayed.
This is a big step towards completely detaching the Flutter navigation from InheritedWidgets. Using context to access an InheritedWidget to access a simple navigation feature is one of the only boring things to do in this incredible framework, and now Get has solved this problem, it has become omniscient, and you will have access to basically any tool Flutter which is only available within the widget tree using it.
All APIs available here are in beta stage, so if you find any errors here, open an issue or offer a PR.
```dart
MaterialApp(
navigatorKey: Get.key,
navigatorObservers: [GetObserver()], // ADD THIS !!!!
);
```
You will also be able to use your own Middleware within GetObserver, this will not influence anything.
```dart
MaterialApp(
navigatorKey: Get.key,
navigatorObservers: [GetObserver(MiddleWare.observer)], // Here
);
```
```dart
Get.arguments // give the current args from currentScreen
Get.previousArguments // give arguments of previous route
Get.previousRoute // give name of previous route
Get.rawRoute // give the raw route to access for example, rawRoute.isFirst()
Get.routing // give access to Rounting API from GetObserver
Get.isSnackbarOpen // check if snackbar is open
Get.isDialogOpen // check if dialog is open
Get.isBottomSheetOpen // check if bottomsheet is open
```
### Nested Navigators
Get made Flutter's nested navigation even easier.
You don't need the context, and you will find your navigation stack by Id.
See how simple it is:
```dart
Navigator(
key: nestedKey(1), // create a key by index
initialRoute: '/',
onGenerateRoute: (settings) {
if (settings.name == '/') {
return GetRoute(
page: Scaffold(
appBar: AppBar(
title: Text("Main"),
),
body: Center(
child: FlatButton(
color: Colors.blue,
onPressed: () {
Get.toNamed('/second', 1); // navigate by your nested route by index
},
child: Text("Go to second")),
),
),
);
} else if (settings.name == '/second') {
return GetRoute(
page: Center(
child: Scaffold(
appBar: AppBar(
title: Text("Main"),
),
body: Center(
child: Text("second")
),
),
),
);
}
}),
```
### Others methods (docs will be added soon):
```dart
... ... @@ -380,6 +524,7 @@ Get.height / Get.width // Equivalent to the method: MediaQuery.of(context).size.
Get.context // Gives the context of the screen in the foreground anywhere in your code.
@deprecated on 1.17.0
var arguments = Get.args(context); // Gives current route arguments
```
... ...
... ... @@ -8,6 +8,10 @@ class GetModalBottomSheetRoute<T> extends PopupRoute<T> {
this.backgroundColor,
this.elevation,
this.shape,
this.removeBottom = false,
this.removeLeft = false,
this.removeRight = false,
this.removeTop = true,
this.clipBehavior,
this.modalBarrierColor,
this.isDismissible = true,
... ... @@ -30,6 +34,15 @@ class GetModalBottomSheetRoute<T> extends PopupRoute<T> {
final bool isDismissible;
final bool enableDrag;
// remove safearea from top
final bool removeTop;
// remove safearea from bottom
final bool removeBottom;
// remove safearea from left
final bool removeLeft;
// remove safearea from right
final bool removeRight;
@override
Duration get transitionDuration => Duration(milliseconds: 700);
... ... @@ -61,7 +74,10 @@ class GetModalBottomSheetRoute<T> extends PopupRoute<T> {
// and isn't exposed to the top padding of the MediaQuery.
Widget bottomSheet = MediaQuery.removePadding(
context: context,
removeTop: true,
removeTop: removeTop,
removeBottom: removeBottom,
removeLeft: removeLeft,
removeRight: removeRight,
child: _GetModalBottomSheet<T>(
route: this,
backgroundColor: backgroundColor ??
... ...
... ... @@ -56,74 +56,121 @@ class Get {
{bool opaque,
Transition transition,
Duration duration,
int id,
bool popGesture}) {
// if (key.currentState.mounted) // add this if appear problems on future with route navigate
// when widget don't mounted
if (id == null) {
return key.currentState.push(GetRoute(
opaque: opaque ?? true,
page: page,
popGesture: popGesture ?? _defaultPopGesture,
transition: transition ?? _defaultTransition,
transitionDuration: duration ?? const Duration(milliseconds: 400)));
} else {
return global(id).currentState.push(GetRoute(
opaque: opaque ?? true,
page: page,
popGesture: popGesture ?? _defaultPopGesture,
transition: transition ?? _defaultTransition,
transitionDuration: duration ?? const Duration(milliseconds: 400)));
}
}
/// It replaces Navigator.pushNamed, but needs no context, and it doesn't have the Navigator.pushNamed
/// routes rebuild bug present in Flutter. If for some strange reason you want the default behavior
/// of rebuilding every app after a route, use opaque = true as the parameter.
static Future<T> toNamed<T>(String page, {arguments}) {
static Future<T> toNamed<T>(String page, {arguments, int id}) {
// if (key.currentState.mounted) // add this if appear problems on future with route navigate
// when widget don't mounted
if (id == null) {
return key.currentState.pushNamed(page, arguments: arguments);
} else {
return global(id).currentState.pushNamed(page, arguments: arguments);
}
}
/// It replaces Navigator.pushReplacementNamed, but needs no context.
static Future<T> offNamed<T>(String page, {arguments}) {
static Future<T> offNamed<T>(String page, {arguments, int id}) {
// if (key.currentState.mounted) // add this if appear problems on future with route navigate
// when widget don't mounted
if (id == null) {
return key.currentState.pushReplacementNamed(page, arguments: arguments);
} else {
return global(id)
.currentState
.pushReplacementNamed(page, arguments: arguments);
}
}
/// It replaces Navigator.popUntil, but needs no context.
static void until(String page, predicate) {
static void until(String page, predicate, {int id}) {
// if (key.currentState.mounted) // add this if appear problems on future with route navigate
// when widget don't mounted
if (id == null) {
return key.currentState.popUntil(predicate);
} else {
return global(id).currentState.popUntil(predicate);
}
}
/// It replaces Navigator.pushAndRemoveUntil, but needs no context.
static Future<T> offUntil<T>(page, predicate) {
static Future<T> offUntil<T>(page, predicate, {int id}) {
// if (key.currentState.mounted) // add this if appear problems on future with route navigate
// when widget don't mounted
if (id == null) {
return key.currentState.pushAndRemoveUntil(page, predicate);
} else {
return global(id).currentState.pushAndRemoveUntil(page, predicate);
}
}
/// It replaces Navigator.pushNamedAndRemoveUntil, but needs no context.
static Future<T> offNamedUntil<T>(page, predicate) {
static Future<T> offNamedUntil<T>(page, predicate, {int id}) {
// if (key.currentState.mounted) // add this if appear problems on future with route navigate
// when widget don't mounted
if (id == null) {
return key.currentState.pushNamedAndRemoveUntil(page, predicate);
} else {
return global(id).currentState.pushNamedAndRemoveUntil(page, predicate);
}
}
/// It replaces Navigator.removeRoute, but needs no context.
static void removeRoute(route) {
static void removeRoute(route, {int id}) {
if (id == null) {
return key.currentState.removeRoute(route);
} else {
return global(id).currentState.removeRoute(route);
}
}
/// It replaces Navigator.pushNamedAndRemoveUntil, but needs no context.
static Future<T> offAllNamed<T>(
String newRouteName, {
RoutePredicate predicate,
arguments,
}) {
static Future<T> offAllNamed<T>(String newRouteName,
{RoutePredicate predicate, arguments, int id}) {
var route = (Route<dynamic> rota) => false;
if (id == null) {
return key.currentState.pushNamedAndRemoveUntil(
newRouteName, predicate ?? route,
arguments: arguments);
} else {
return global(id).currentState.pushNamedAndRemoveUntil(
newRouteName, predicate ?? route,
arguments: arguments);
}
}
/// It replaces Navigator.pop, but needs no context.
static back({dynamic result}) {
return key.currentState.pop(result);
static void back({dynamic result, int id}) {
if (id == null) {
key.currentState.pop(result);
} else {
global(id).currentState.pop(result);
}
}
/// Experimental API to back from overlay
static void backE({dynamic result}) {
Navigator.pop(overlayContext);
}
/// It will close as many screens as you define. Times must be> 0;
... ... @@ -145,13 +192,23 @@ class Get {
{bool opaque = false,
Transition transition,
bool popGesture,
int id,
Duration duration = const Duration(milliseconds: 400)}) {
if (id == null) {
return key.currentState.pushReplacement(GetRoute(
opaque: opaque ?? true,
page: page,
popGesture: popGesture ?? _defaultPopGesture,
transition: transition ?? _defaultTransition,
transitionDuration: duration));
} else {
return global(id).currentState.pushReplacement(GetRoute(
opaque: opaque ?? true,
page: page,
popGesture: popGesture ?? _defaultPopGesture,
transition: transition ?? _defaultTransition,
transitionDuration: duration));
}
}
/// It replaces Navigator.pushAndRemoveUntil, but needs no context
... ... @@ -159,8 +216,11 @@ class Get {
{RoutePredicate predicate,
bool opaque = false,
bool popGesture,
int id,
Transition transition}) {
var route = (Route<dynamic> rota) => false;
if (id == null) {
return key.currentState.pushAndRemoveUntil(
GetRoute(
opaque: opaque ?? true,
... ... @@ -169,6 +229,16 @@ class Get {
transition: transition ?? _defaultTransition,
),
predicate ?? route);
} else {
return global(id).currentState.pushAndRemoveUntil(
GetRoute(
opaque: opaque ?? true,
popGesture: popGesture ?? _defaultPopGesture,
page: page,
transition: transition ?? _defaultTransition,
),
predicate ?? route);
}
}
/// Show a dialog. You can choose color and opacity of background
... ... @@ -205,7 +275,7 @@ class Get {
);
}
static defaultDialog(
static Future<T> defaultDialog<T>(
{Color color,
double opacity = 0.2,
String title = "Alert dialog",
... ... @@ -221,7 +291,7 @@ class Get {
confirm: confirm,
);
dialog(child);
return dialog(child);
}
static Future<T> bottomSheet<T>({
... ... @@ -231,6 +301,7 @@ class Get {
ShapeBorder shape,
Clip clipBehavior,
Color barrierColor,
bool ignoreSafeArea,
bool isScrollControlled = false,
bool useRootNavigator = false,
bool isDismissible = true,
... ... @@ -248,9 +319,10 @@ class Get {
isScrollControlled: isScrollControlled,
barrierLabel: MaterialLocalizations.of(Get.key.currentContext)
.modalBarrierDismissLabel,
backgroundColor: backgroundColor,
backgroundColor: backgroundColor ?? Colors.transparent,
elevation: elevation,
shape: shape,
removeTop: ignoreSafeArea ?? true,
clipBehavior: clipBehavior,
isDismissible: isDismissible,
modalBarrierColor: barrierColor,
... ... @@ -259,10 +331,11 @@ class Get {
));
}
/// get arguments from current screen. You need of context
static args(context) {
return ModalRoute.of(context).settings.arguments;
}
// /// get arguments from current screen. You need of context
// @deprecated
// static args(context) {
// return ModalRoute.of(context).settings.arguments;
// }
static Future backdrop(Widget child,
{double radius = 20.0,
... ... @@ -395,18 +468,75 @@ class Get {
}
}
static Map<int, GlobalKey<NavigatorState>> _keys = {};
static GlobalKey<NavigatorState> nestedKey(int key) {
_keys.putIfAbsent(key, () => GlobalKey<NavigatorState>());
return _keys[key];
}
static GlobalKey<NavigatorState> global(int key) {
if (!_keys.containsKey(key)) {
throw 'route id not found';
}
final recoverKey = _keys[key];
return recoverKey;
}
/// give access to Routing API from GetObserver
static Routing get routing => _routing;
static Routing _routing;
static setRouting(Routing rt) {
_routing = rt;
}
/// give current arguments
static get arguments => _routing.args;
/// give arguments from previous route
static get previousArguments => _routing.previousArgs;
/// give name from current route
static get currentRoute => _routing.current;
/// give name from previous route
static get previousRoute => _routing.previous;
/// check if snackbar is open
static bool get isSnackbarOpen => _routing.isSnackbar;
/// check if dialog is open
static bool get isDialogOpen => _routing.isDialog;
/// check if bottomsheet is open
static bool get isBottomSheetOpen => _routing.isBottomSheet;
/// check a raw current route
static Route<dynamic> get rawRoute => _routing.route;
/// check if log is enable
static bool get isLogEnable => _enableLog;
/// check if popGesture is enable
static bool get isPopGestureEnable => _defaultPopGesture;
/// check if default opaque route is enable
static bool get isOpaqueRouteDefault => _defaultOpaqueRoute;
/// give access to currentContext
static BuildContext get context => key.currentContext;
/// give access to current Overlay Context
static BuildContext get overlayContext => key.currentState.overlay.context;
/// give access to Theme.of(context)
static ThemeData get theme => Theme.of(context);
/// give access to Mediaquery.of(context)
static MediaQueryData get mediaquery => MediaQuery.of(context);
/// give access to Theme.of(context).iconTheme.color
static Color get iconColor => Theme.of(context).iconTheme.color;
... ...
... ... @@ -8,6 +8,7 @@ class Routing {
final args;
final previousArgs;
final removed;
final Route<dynamic> route;
final bool isBack;
final bool isSnackbar;
final bool isBottomSheet;
... ... @@ -18,6 +19,7 @@ class Routing {
this.args,
this.previousArgs,
this.removed,
this.route,
this.isBack,
this.isSnackbar,
this.isBottomSheet,
... ... @@ -27,7 +29,7 @@ class Routing {
class GetObserver extends NavigatorObserver {
final Function(Routing) routing;
GetObserver(this.routing);
GetObserver([this.routing]);
@override
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
if ('${route?.settings?.name}' == 'snackbar') {
... ... @@ -40,14 +42,22 @@ class GetObserver extends NavigatorObserver {
if (Get.isLogEnable) print("[GOING TO ROUTE] ${route?.settings?.name}");
}
routing(Routing(
final routeSend = Routing(
removed: null,
isBack: false,
route: route,
current: '${route?.settings?.name}',
previous: '${previousRoute?.settings?.name}',
args: route?.settings?.arguments,
previousArgs: previousRoute?.settings?.arguments,
isSnackbar: '${route?.settings?.name}' == 'snackbar'));
isSnackbar: '${route?.settings?.name}' == 'snackbar',
isDialog: '${route?.settings?.name}' == 'dialog',
isBottomSheet: '${route?.settings?.name}' == 'bottomsheet',
);
if (routing != null) {
routing(routeSend);
}
Get.setRouting(routeSend);
}
@override
... ... @@ -67,14 +77,23 @@ class GetObserver extends NavigatorObserver {
if (Get.isLogEnable) print("[BACK ROUTE] ${route?.settings?.name}");
}
routing(Routing(
final routeSend = Routing(
removed: null,
isBack: true,
route: previousRoute,
current: '${previousRoute?.settings?.name}',
previous: '${route?.settings?.name}',
args: previousRoute?.settings?.arguments,
previousArgs: route?.settings?.arguments,
isSnackbar: '${route?.settings?.name}' == 'snackbar'));
isSnackbar: false, //'${route?.settings?.name}' == 'snackbar',
isDialog: false, //'${route?.settings?.name}' == 'dialog',
isBottomSheet: false, //'${route?.settings?.name}' == 'bottomsheet',
);
if (routing != null) {
routing(routeSend);
}
Get.setRouting(routeSend);
}
@override
... ... @@ -82,14 +101,22 @@ class GetObserver extends NavigatorObserver {
super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
if (Get.isLogEnable) print("[REPLACE ROUTE] ${oldRoute?.settings?.name}");
routing(Routing(
final routeSend = Routing(
removed: null, // add '${oldRoute?.settings?.name}' or remain null ???
isBack: false,
route: newRoute,
current: '${newRoute?.settings?.name}',
previous: '${oldRoute?.settings?.name}',
args: newRoute?.settings?.arguments,
isSnackbar: null,
previousArgs: null));
isBottomSheet: null,
isDialog: null,
previousArgs: null);
if (routing != null) {
routing(routeSend);
}
Get.setRouting(routeSend);
}
@override
... ... @@ -97,13 +124,21 @@ class GetObserver extends NavigatorObserver {
super.didRemove(route, previousRoute);
if (Get.isLogEnable) print("[REMOVING ROUTE] ${route?.settings?.name}");
routing(Routing(
final routeSend = Routing(
isBack: false,
route: previousRoute,
current: '${previousRoute?.settings?.name}',
removed: '${route?.settings?.name}',
previous: null,
isSnackbar: null,
isBottomSheet: null,
isDialog: null,
args: previousRoute?.settings?.arguments,
previousArgs: route?.settings?.arguments));
previousArgs: route?.settings?.arguments);
if (routing != null) {
routing(routeSend);
}
Get.setRouting(routeSend);
}
}
... ...
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
archive:
dependency: transitive
description:
name: archive
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.13"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.0"
async:
dependency: transitive
description:
... ... @@ -43,20 +29,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.14.12"
convert:
dependency: transitive
description:
name: convert
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.4"
flutter:
dependency: "direct main"
description: flutter
... ... @@ -67,13 +39,6 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
image:
dependency: transitive
description:
name: image
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.12"
matcher:
dependency: transitive
description:
... ... @@ -95,13 +60,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.4"
petitparser:
dependency: transitive
description:
name: petitparser
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.0"
quiver:
dependency: transitive
description:
... ... @@ -170,12 +128,5 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8"
xml:
dependency: transitive
description:
name: xml
url: "https://pub.dartlang.org"
source: hosted
version: "3.6.1"
sdks:
dart: ">=2.6.0 <3.0.0"
... ...
name: get
description: Navigate between screens, display snackbars, dialogs and bottomSheets, from anywhere in your code without context with Get.
version: 1.16.0-dev
version: 1.19.0-dev
homepage: https://github.com/jonataslaw/get
environment:
... ...