Jonny Borges
Committed by GitHub

Constraint BottomSheet to keyboard

  1 +## [2.6.3]
  2 +- Flutter currently has a problem on some devices where using showModalBottomSheet() can cause TextFields to be hidden behind the keyboard (https://github.com/flutter/flutter/issues/18564) this issue is closed, even users reporting that the problem still occurs.
  3 +The problem happens casually, as well as the problem of the snackbar on the iPhone SE 2, and checking the code, I realized that a padding with MediaQuery.of(context).viewInsets.bottom is missing inside the bottomSheet to make it work correctly, since it does not have any constraint with the keyboard.
  4 +For stability, I decided not to use the standard Flutter bottomSheet, which contains many bugs, mainly related to keyboard padding, and the lack of respect for topBar's safeArea, and to use a proprietary bottomSheet implementation that is more stable. The Flutter dialog has no problem, so it will be used as the basis for Get.dialog. The bottomSheet will be based on the Flutter bottomSheet Raw API (_ModalBottomSheetRoute), applying bug fixes.
  5 +- Added Get.isSnackbarOpen tests
  6 +
  7 +## [2.6.2]
  8 +- Refactor Bindings API
  9 +
  10 +## [2.6.1]
  11 +- Expose Bindings API
  12 +
1 ## [2.6.0] 13 ## [2.6.0]
2 - Added bindings. 14 - Added bindings.
3 You can now add bindings from your controllers to your routes, to prepare GetBuilder or GetX to create a dependency already declared in a Binding class. This feature is in an experimental phase, and will not be documented until the end of the tests. 15 You can now add bindings from your controllers to your routes, to prepare GetBuilder or GetX to create a dependency already declared in a Binding class. This feature is in an experimental phase, and will not be documented until the end of the tests.
@@ -278,7 +278,7 @@ What performance improvements does Get bring? @@ -278,7 +278,7 @@ What performance improvements does Get bring?
278 278
279 2- Does not use changeNotifier, it is the state manager that uses less memory (close to 0mb for until). 279 2- Does not use changeNotifier, it is the state manager that uses less memory (close to 0mb for until).
280 280
281 -3- Forget StatefulWidget! With Get you will never need it again (if you need to use it, you are using Get incorrectly). With the other state managers, you will probably have to use a StatefulWidget to get the instance of your Provider, BLoC, MobX Controller, etc. But have you ever stopped to think that your appBar, your scaffold, and most of the widgets that are in your class are stateless? So why save the state of an entire class, if you can only save the state of the Widget that is stateful? Get solves that, too. Create a Stateless class, make everything stateless. If you need to update a single component, wrap it with GetBuilder, and its state will be maintained. 281 +3- Forget StatefulWidget! With Get you will never need it. With the other state managers, you will probably have to use a StatefulWidget to get the instance of your Provider, BLoC, MobX Controller, etc. But have you ever stopped to think that your appBar, your scaffold, and most of the widgets that are in your class are stateless? So why save the state of an entire class, if you can only save the state of the Widget that is stateful? Get solves that, too. Create a Stateless class, make everything stateless. If you need to update a single component, wrap it with GetBuilder, and its state will be maintained.
282 282
283 4- Organize your project for real! Controllers must not be in your UI, place your TextEditController, or any controller you use within your Controller class. 283 4- Organize your project for real! Controllers must not be in your UI, place your TextEditController, or any controller you use within your Controller class.
284 284
@@ -288,7 +288,7 @@ What performance improvements does Get bring? @@ -288,7 +288,7 @@ What performance improvements does Get bring?
288 288
289 7- Use streams only if necessary. You can use your StreamControllers inside your controller normally, and use StreamBuilder also normally, but remember, a stream reasonably consumes memory, reactive programming is beautiful, but you shouldn't abuse it. 30 streams open simultaneously can be worse than changeNotifier (and changeNotifier is very bad). 289 7- Use streams only if necessary. You can use your StreamControllers inside your controller normally, and use StreamBuilder also normally, but remember, a stream reasonably consumes memory, reactive programming is beautiful, but you shouldn't abuse it. 30 streams open simultaneously can be worse than changeNotifier (and changeNotifier is very bad).
290 290
291 -8- Update widgets without spending ram for that. Get stores only the GetBuilder creator ID, and updates that GetBuilder when necessary. The memory consumption of the get ID storage in memory is close to 0 even for thousands of GetBuilders. When you create a new GetBuilder, you are actually sharing the state of GetBuilder that has a creator ID. A new state is not created for each GetBuilder, which saves A LOT OF ram for large applications. Basically your application will be entirely Stateless, and the few Widgets that will be Stateful (within GetBuilder) will have a single state, and therefore updating one will update them all. The state is just one. 291 +8- Update widgets without spending ram for that. Get stores only the GetBuilder creator ID, and updates that GetBuilder when necessary. The memory consumption of the get ID storage in memory is very low even for thousands of GetBuilders. When you create a new GetBuilder, you are actually sharing the state of GetBuilder that has a creator ID. A new state is not created for each GetBuilder, which saves A LOT OF ram for large applications. Basically your application will be entirely Stateless, and the few Widgets that will be Stateful (within GetBuilder) will have a single state, and therefore updating one will update them all. The state is just one.
292 292
293 9- Get is omniscient and in most cases it knows exactly the time to take a controller out of memory. You should not worry about when to dispose of a controller, Get knows the best time to do this. Example: 293 9- Get is omniscient and in most cases it knows exactly the time to take a controller out of memory. You should not worry about when to dispose of a controller, Get knows the best time to do this. Example:
294 294
@@ -318,6 +318,9 @@ GetBuilder<Controller>( @@ -318,6 +318,9 @@ GetBuilder<Controller>(
318 **Done!** 318 **Done!**
319 - You have already learned how to manage states with Get. 319 - You have already learned how to manage states with Get.
320 320
  321 +- Note: You may want a larger organization, and not use the init property. For that, you can create a class and extends Bindings class, and within it mention the controllers that will be created within that route. Controllers will not be created at that time, on the contrary, this is just a statement, so that the first time you use a Controller, Get will know where to look. Get will remain lazyLoad, and will continue to dispose Controllers when they are no longer needed. See the pub.dev example to see how it works.
  322 +
  323 +
321 If you navigate many routes and need data that was in your previously used controller, you just need to use GetBuilder Again (with no init): 324 If you navigate many routes and need data that was in your previously used controller, you just need to use GetBuilder Again (with no init):
322 325
323 ```dart 326 ```dart

38.3 KB | W: | H:

38.6 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
@@ -13,7 +13,7 @@ export 'src/rx/rx_getbuilder.dart'; @@ -13,7 +13,7 @@ export 'src/rx/rx_getbuilder.dart';
13 export 'src/root/root_widget.dart'; 13 export 'src/root/root_widget.dart';
14 export 'src/routes/default_route.dart'; 14 export 'src/routes/default_route.dart';
15 export 'src/routes/get_route.dart'; 15 export 'src/routes/get_route.dart';
16 -export 'src/routes/get_route.dart'; 16 +export 'src/routes/bindings_interface.dart';
17 export 'src/routes/observers/route_observer.dart'; 17 export 'src/routes/observers/route_observer.dart';
18 export 'src/routes/transitions_type.dart'; 18 export 'src/routes/transitions_type.dart';
19 export 'src/platform/platform.dart'; 19 export 'src/platform/platform.dart';
@@ -8,9 +8,6 @@ class GetModalBottomSheetRoute<T> extends PopupRoute<T> { @@ -8,9 +8,6 @@ class GetModalBottomSheetRoute<T> extends PopupRoute<T> {
8 this.backgroundColor, 8 this.backgroundColor,
9 this.elevation, 9 this.elevation,
10 this.shape, 10 this.shape,
11 - this.removeBottom = false,  
12 - this.removeLeft = false,  
13 - this.removeRight = false,  
14 this.removeTop = true, 11 this.removeTop = true,
15 this.clipBehavior, 12 this.clipBehavior,
16 this.modalBarrierColor, 13 this.modalBarrierColor,
@@ -36,12 +33,6 @@ class GetModalBottomSheetRoute<T> extends PopupRoute<T> { @@ -36,12 +33,6 @@ class GetModalBottomSheetRoute<T> extends PopupRoute<T> {
36 33
37 // remove safearea from top 34 // remove safearea from top
38 final bool removeTop; 35 final bool removeTop;
39 - // remove safearea from bottom  
40 - final bool removeBottom;  
41 - // remove safearea from left  
42 - final bool removeLeft;  
43 - // remove safearea from right  
44 - final bool removeRight;  
45 36
46 @override 37 @override
47 Duration get transitionDuration => Duration(milliseconds: 700); 38 Duration get transitionDuration => Duration(milliseconds: 700);
@@ -75,9 +66,9 @@ class GetModalBottomSheetRoute<T> extends PopupRoute<T> { @@ -75,9 +66,9 @@ class GetModalBottomSheetRoute<T> extends PopupRoute<T> {
75 Widget bottomSheet = MediaQuery.removePadding( 66 Widget bottomSheet = MediaQuery.removePadding(
76 context: context, 67 context: context,
77 removeTop: removeTop, 68 removeTop: removeTop,
78 - removeBottom: removeBottom,  
79 - removeLeft: removeLeft,  
80 - removeRight: removeRight, 69 + child: Padding(
  70 + padding:
  71 + EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
81 child: _GetModalBottomSheet<T>( 72 child: _GetModalBottomSheet<T>(
82 route: this, 73 route: this,
83 backgroundColor: backgroundColor ?? 74 backgroundColor: backgroundColor ??
@@ -90,6 +81,7 @@ class GetModalBottomSheetRoute<T> extends PopupRoute<T> { @@ -90,6 +81,7 @@ class GetModalBottomSheetRoute<T> extends PopupRoute<T> {
90 isScrollControlled: isScrollControlled, 81 isScrollControlled: isScrollControlled,
91 enableDrag: enableDrag, 82 enableDrag: enableDrag,
92 ), 83 ),
  84 + ),
93 ); 85 );
94 if (theme != null) bottomSheet = Theme(data: theme, child: bottomSheet); 86 if (theme != null) bottomSheet = Theme(data: theme, child: bottomSheet);
95 return bottomSheet; 87 return bottomSheet;
@@ -366,7 +366,8 @@ class Get { @@ -366,7 +366,8 @@ class Get {
366 assert(isDismissible != null); 366 assert(isDismissible != null);
367 assert(enableDrag != null); 367 assert(enableDrag != null);
368 368
369 - return navigator.push<T>(GetModalBottomSheetRoute<T>( 369 + return Navigator.of(overlayContext, rootNavigator: useRootNavigator)
  370 + .push(GetModalBottomSheetRoute<T>(
370 builder: (_) => bottomsheet, 371 builder: (_) => bottomsheet,
371 theme: Theme.of(Get.key.currentContext, shadowThemeOnly: true), 372 theme: Theme.of(Get.key.currentContext, shadowThemeOnly: true),
372 isScrollControlled: isScrollControlled, 373 isScrollControlled: isScrollControlled,
@@ -135,6 +135,7 @@ class GetMaterialApp extends StatelessWidget { @@ -135,6 +135,7 @@ class GetMaterialApp extends StatelessWidget {
135 curve: newNamedRoutes[settingsName].curve, 135 curve: newNamedRoutes[settingsName].curve,
136 alignment: newNamedRoutes[settingsName].alignment, 136 alignment: newNamedRoutes[settingsName].alignment,
137 opaque: newNamedRoutes[settingsName].opaque, 137 opaque: newNamedRoutes[settingsName].opaque,
  138 + binding: newNamedRoutes[settingsName].binding,
138 transitionDuration: (transitionDuration == null 139 transitionDuration: (transitionDuration == null
139 ? newNamedRoutes[settingsName].transitionDuration 140 ? newNamedRoutes[settingsName].transitionDuration
140 : transitionDuration), 141 : transitionDuration),
@@ -159,6 +160,7 @@ class GetMaterialApp extends StatelessWidget { @@ -159,6 +160,7 @@ class GetMaterialApp extends StatelessWidget {
159 alignment: unknownRoute.alignment, 160 alignment: unknownRoute.alignment,
160 parameter: unknownRoute.parameter, 161 parameter: unknownRoute.parameter,
161 opaque: unknownRoute.opaque, 162 opaque: unknownRoute.opaque,
  163 + binding: unknownRoute.binding,
162 transitionDuration: unknownRoute.transitionDuration, 164 transitionDuration: unknownRoute.transitionDuration,
163 popGesture: unknownRoute.popGesture, 165 popGesture: unknownRoute.popGesture,
164 transition: unknownRoute.transition, 166 transition: unknownRoute.transition,
@@ -41,7 +41,12 @@ class GetRouteBase<T> extends PageRoute<T> { @@ -41,7 +41,12 @@ class GetRouteBase<T> extends PageRoute<T> {
41 assert(maintainState != null), 41 assert(maintainState != null),
42 assert(fullscreenDialog != null), 42 assert(fullscreenDialog != null),
43 // assert(opaque), 43 // assert(opaque),
44 - super(settings: settings, fullscreenDialog: fullscreenDialog); 44 + super(settings: settings, fullscreenDialog: fullscreenDialog) {
  45 + /// prebuild dependencies
  46 + if (binding != null) {
  47 + binding.dependencies();
  48 + }
  49 + }
45 50
46 /// Builds the primary contents of the route. 51 /// Builds the primary contents of the route.
47 final Widget page; 52 final Widget page;
@@ -468,11 +473,6 @@ class GetRouteBase<T> extends PageRoute<T> { @@ -468,11 +473,6 @@ class GetRouteBase<T> extends PageRoute<T> {
468 @override 473 @override
469 Widget buildTransitions(BuildContext context, Animation<double> animation, 474 Widget buildTransitions(BuildContext context, Animation<double> animation,
470 Animation<double> secondaryAnimation, Widget child) { 475 Animation<double> secondaryAnimation, Widget child) {
471 - /// prebuild dependencies  
472 - if (binding != null) {  
473 - binding.dependencies();  
474 - }  
475 -  
476 return buildPageTransitions<T>( 476 return buildPageTransitions<T>(
477 this, 477 this,
478 context, 478 context,
@@ -12,7 +12,7 @@ class GetRoute { @@ -12,7 +12,7 @@ class GetRoute {
12 final Alignment alignment; 12 final Alignment alignment;
13 final bool maintainState; 13 final bool maintainState;
14 final bool opaque; 14 final bool opaque;
15 - final Bindings bindings; 15 + final Bindings binding;
16 final Widget customTransition; 16 final Widget customTransition;
17 final Duration transitionDuration; 17 final Duration transitionDuration;
18 final bool fullscreenDialog; 18 final bool fullscreenDialog;
@@ -29,7 +29,7 @@ class GetRoute { @@ -29,7 +29,7 @@ class GetRoute {
29 this.opaque = true, 29 this.opaque = true,
30 this.transitionDuration = const Duration(milliseconds: 400), 30 this.transitionDuration = const Duration(milliseconds: 400),
31 this.popGesture, 31 this.popGesture,
32 - this.bindings, 32 + this.binding,
33 this.transition, 33 this.transition,
34 this.customTransition, 34 this.customTransition,
35 this.fullscreenDialog = false, 35 this.fullscreenDialog = false,
1 name: get 1 name: get
2 description: Open screens/snackbars/dialogs/bottomSheets without context, manage states and inject dependencies easily with Get. 2 description: Open screens/snackbars/dialogs/bottomSheets without context, manage states and inject dependencies easily with Get.
3 -version: 2.6.0 3 +version: 2.6.3
4 homepage: https://github.com/jonataslaw/get 4 homepage: https://github.com/jonataslaw/get
5 5
6 environment: 6 environment:
@@ -330,6 +330,51 @@ void main() { @@ -330,6 +330,51 @@ void main() {
330 330
331 expect(find.byType(FirstScreen), findsOneWidget); 331 expect(find.byType(FirstScreen), findsOneWidget);
332 }); 332 });
  333 +
  334 + testWidgets("Get.snackbar test", (tester) async {
  335 + await tester.pumpWidget(
  336 + Wrapper(
  337 + child: RaisedButton(
  338 + child: Text('Open Snackbar'),
  339 + onPressed: () {
  340 + Get.snackbar('title', "message",
  341 + duration: Duration(seconds: 1), instantInit: true);
  342 + },
  343 + ),
  344 + ),
  345 + );
  346 +
  347 + expect(Get.isSnackbarOpen, false);
  348 + await tester.tap(find.text('Open Snackbar'));
  349 +
  350 + expect(Get.isSnackbarOpen, true);
  351 + await tester.pump(const Duration(seconds: 1));
  352 + });
  353 +
  354 + testWidgets("Get.rawSnackbar test", (tester) async {
  355 + await tester.pumpWidget(
  356 + Wrapper(
  357 + child: RaisedButton(
  358 + child: Text('Open Snackbar'),
  359 + onPressed: () {
  360 + Get.rawSnackbar(
  361 + title: 'title',
  362 + message: "message",
  363 + duration: Duration(seconds: 1),
  364 + instantInit: true);
  365 + },
  366 + ),
  367 + ),
  368 + );
  369 +
  370 + expect(Get.isSnackbarOpen, false);
  371 + await tester.tap(find.text('Open Snackbar'));
  372 +
  373 + expect(Get.isSnackbarOpen, true);
  374 + await tester.pump(const Duration(seconds: 1));
  375 + });
  376 +
  377 +
333 } 378 }
334 379
335 class FirstScreen extends StatelessWidget { 380 class FirstScreen extends StatelessWidget {