Ravi Parmar
Committed by GitHub

Merge branch 'jonataslaw:master' into master

Too many changes to show.

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

No preview for this file type
  1 +## [4.5.1] - Big Update
  2 +Fix Snackbar when it have action and icon the same time
  3 +
  4 +## [4.5.0] - Big Update
  5 +To have a context-free, page-agnostic snackbar, we used OverlayRoute to display a partial route.
  6 +However this had several problems:
  7 +
  8 +1: There was no possibility to close the page without closing the snackbar
  9 +2: Get.back() could cause problems with tests of Get.isSnackbarOpen not being properly invoked
  10 +3: Sometimes when using iOS popGesture with an open snackbar, some visual inconsistency might appear.
  11 +4: When going to another route, the snackbar was not displayed on the new page, and if the user clicked on the new route as soon as he received a Snackbar, he could not read it.
  12 +
  13 +We remade the Snackbar from scratch, having its Api based on Overlay, and now opening a Snackbar won't be tied to a route, you can normally navigate routes while a Snackbar is shown at the top (or bottom), and even the PopGesture of the iOS is not influenced by it.
  14 +
  15 +Using Get.back() is handy, it's a small command, which closes routes, dialogs, snackbars, bottomsheets, etc, however Getx 5 will prioritize code safety, and splitting will reduce the check code as well. Currently we have to check if a snackbar is open, to close the snackbar and prevent the app from going back a page, all this boilerplate code will be removed, at the cost of having what it closes in front of Get.back command.
  16 +
  17 +For backwards compatibility, Get.back() still works for closing routes and overlays, however two new commands have been added: Get.closeCurrentSnackbar() and Get.closeAllSnackbars().
  18 +Maybe we will have a clearer api in GetX 5, and maybe Get.back() will continue to do everything like it does today. The community will be consulted about the desired api. However version 5 will definitely have commands like: Get.closeCurrentSnackbar, Get.closeCurrentDialog etc. There is also the possibility to close a specific snackbar using the return of Get.snackbar, which will no longer return a void, and now return a SnackbarController.
  19 +
  20 +Snackbars now also have a Queue, and no longer stack one on top of the other, preventing viewing. GetX now has flexible, customizable, route-independent, and completely stable Snackbars.
  21 +
  22 +Fixed bugs where the snackbar showed an error in debug mode for a fraction of a second. We found that Flutter has a bug with blur below 0.001, so we set the minimum overlayBlur value to this value if it is ==true.
  23 +
  24 +Errors with internationalization were also fixed, where if you are in UK, and the app had the en_US language, you didn't have American English by default. Now, if the country code is not present, it will automatically fetch the language code before fetching a fallbackLanguage.
  25 +
  26 +Update locale also now returns a Future, allowing you to perform an action only when the language has already changed (@MHosssam)
  27 +
  28 +We are very happy to announce that GetX is now documented in Japanese as well, thanks to (@toshi-kuji)
  29 +
  30 +GetX has always been focused on transparency. You can tell what's going on with your app just by reading the logs on the console. However, these logs shouldn't appear in production, so it now only appears in debug mode (@maxzod)
  31 +
  32 +@maxzod has also started translating the docs into Arabic, we hope the documentation will be complete soon.
  33 +
  34 +Some remaining package logs have been moved to Get.log (@gairick-saha)
  35 +
  36 +RxList.removeWhere received performance optimizations (@zuvola)
  37 +
  38 +Optimizations in GetConnect and added the ability to modify all request items in GetConnect (@rodrigorahman)
  39 +
  40 +The current route could be inconsistent if a dialog were opened after a transition, fixed by @xiangzy1
  41 +
  42 +Fixed try/catch case missed in socket_notifier (@ShookLyngs)
  43 +
  44 +Also we had fixes in the docs: @DeathGun3344 @pinguluk
  45 +
  46 +GetX also surpassed the incredible mark of more than 7000 likes, being the most liked package in all pub.dev, went from 99% to 100% popularity, and has more than 5.3k stars on github. Documentation is now available in 12 languages, and we're happy for all the engagement from your community.
  47 +
  48 +This update is a preparation update for version 5, which will be released later this year.
  49 +
  50 +Breaking and Depreciation:
  51 +GetBar is now deprecated, use GetSnackbar instead.
  52 +dismissDirection now gets a DismissDirection, making the Snackbar more customizable.
  53 +
  54 +
  55 +
  56 +
  57 +
1 ## [4.3.8] 58 ## [4.3.8]
2 - Fix nav2 toNamed remove the route 59 - Fix nav2 toNamed remove the route
3 60
1 ![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png) 1 ![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
2 2
3 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get) 3 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
  4 +[![popularity](https://badges.bar/get/popularity)](https://pub.dev/packages/sentry/score)
4 [![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score) 5 [![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score)
  6 +[![pub points](https://badges.bar/get/pub%20points)](https://pub.dev/packages/get/score)
5 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg) 7 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
6 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart) 8 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart)
7 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N) 9 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N)
@@ -3,6 +3,9 @@ @@ -3,6 +3,9 @@
3 *Idiomas: Español (este archivo), [Vietnamita](README-vi.md), [Indonesio](README.id-ID.md), [Urdu](README.ur-PK.md), [Lengua china](README.zh-cn.md), [Inglés](README.md), [Portugués de Brasil](README.pt-br.md), [Ruso](README.ru.md), [Polaco](README.pl.md), [Coreano](README.ko-kr.md), [Francés](README-fr.md).* 3 *Idiomas: Español (este archivo), [Vietnamita](README-vi.md), [Indonesio](README.id-ID.md), [Urdu](README.ur-PK.md), [Lengua china](README.zh-cn.md), [Inglés](README.md), [Portugués de Brasil](README.pt-br.md), [Ruso](README.ru.md), [Polaco](README.pl.md), [Coreano](README.ko-kr.md), [Francés](README-fr.md).*
4 4
5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get) 5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
  6 +[![popularity](https://badges.bar/get/popularity)](https://pub.dev/packages/sentry/score)
  7 +[![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score)
  8 +[![pub points](https://badges.bar/get/pub%20points)](https://pub.dev/packages/get/score)
6 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg) 9 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
7 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart) 10 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart)
8 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N) 11 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N)
@@ -3,7 +3,9 @@ @@ -3,7 +3,9 @@
3 **Langues: Français (Ce fichier), [Anglais](README.md), [Vietnamien](README-vi.md), [Indonésien](README.id-ID.md), [Urdu](README.ur-PK.md), [Chinois](README.zh-cn.md), [Portuguais du Brésil](README.pt-br.md), [Espagnol](README-es.md), [Russe](README.ru.md), [Polonais](README.pl.md), [Koréen](README.ko-kr.md).** 3 **Langues: Français (Ce fichier), [Anglais](README.md), [Vietnamien](README-vi.md), [Indonésien](README.id-ID.md), [Urdu](README.ur-PK.md), [Chinois](README.zh-cn.md), [Portuguais du Brésil](README.pt-br.md), [Espagnol](README-es.md), [Russe](README.ru.md), [Polonais](README.pl.md), [Koréen](README.ko-kr.md).**
4 4
5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get) 5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
  6 +[![popularity](https://badges.bar/get/popularity)](https://pub.dev/packages/sentry/score)
6 [![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score) 7 [![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score)
  8 +[![pub points](https://badges.bar/get/pub%20points)](https://pub.dev/packages/get/score)
7 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg) 9 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
8 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart) 10 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart)
9 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N) 11 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N)
@@ -3,7 +3,9 @@ @@ -3,7 +3,9 @@
3 **Ngôn ngữ: Tiếng Việt (file này), [English](README.md), [Indonesian](README.id-ID.md), [Urdu](README.ur-PK.md), [Chinese](README.zh-cn.md), [Brazilian Portuguese](README.pt-br.md), [Spanish](README-es.md), [Russian](README.ru.md), [Polish](README.pl.md), [Korean](README.ko-kr.md), [French](README-fr.md).** 3 **Ngôn ngữ: Tiếng Việt (file này), [English](README.md), [Indonesian](README.id-ID.md), [Urdu](README.ur-PK.md), [Chinese](README.zh-cn.md), [Brazilian Portuguese](README.pt-br.md), [Spanish](README-es.md), [Russian](README.ru.md), [Polish](README.pl.md), [Korean](README.ko-kr.md), [French](README-fr.md).**
4 4
5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get) 5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
  6 +[![popularity](https://badges.bar/get/popularity)](https://pub.dev/packages/sentry/score)
6 [![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score) 7 [![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score)
  8 +[![pub points](https://badges.bar/get/pub%20points)](https://pub.dev/packages/get/score)
7 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg) 9 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
8 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart) 10 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart)
9 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N) 11 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N)
@@ -3,7 +3,9 @@ @@ -3,7 +3,9 @@
3 **Bahasa: Indonesia (file ini), [Inggris](README.md), [Orang Vietnam](README-vi.md), [Urdu](README.ur-PK.md), [China](README.zh-cn.md), [Portugis (Brazil)](README.pt-br.md), [Spanyol](README-es.md), [Russia](README.ru.md), [Polandia](README.pl.md), [Korea](README.ko-kr.md), [French](README-fr.md)** 3 **Bahasa: Indonesia (file ini), [Inggris](README.md), [Orang Vietnam](README-vi.md), [Urdu](README.ur-PK.md), [China](README.zh-cn.md), [Portugis (Brazil)](README.pt-br.md), [Spanyol](README-es.md), [Russia](README.ru.md), [Polandia](README.pl.md), [Korea](README.ko-kr.md), [French](README-fr.md)**
4 4
5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get) 5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
  6 +[![popularity](https://badges.bar/get/popularity)](https://pub.dev/packages/sentry/score)
6 [![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score) 7 [![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score)
  8 +[![pub points](https://badges.bar/get/pub%20points)](https://pub.dev/packages/get/score)
7 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg) 9 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
8 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart) 10 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart)
9 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N) 11 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N)
1 ![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png) 1 ![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
2 2
3 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get) 3 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
  4 +[![popularity](https://badges.bar/get/popularity)](https://pub.dev/packages/sentry/score)
4 [![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score) 5 [![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score)
  6 +[![pub points](https://badges.bar/get/pub%20points)](https://pub.dev/packages/get/score)
5 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg) 7 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
6 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart) 8 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart)
7 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N) 9 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N)
1 ![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png) 1 ![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
2 2
3 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get) 3 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
  4 +[![popularity](https://badges.bar/get/popularity)](https://pub.dev/packages/sentry/score)
4 [![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score) 5 [![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score)
  6 +[![pub points](https://badges.bar/get/pub%20points)](https://pub.dev/packages/get/score)
5 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg) 7 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
6 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart) 8 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart)
7 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N) 9 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N)
@@ -326,7 +328,7 @@ Text(controller.textFromApi); @@ -326,7 +328,7 @@ Text(controller.textFromApi);
326 328
327 ### 종속성 관리에 대한 자세한 내용 329 ### 종속성 관리에 대한 자세한 내용
328 330
329 -**종속성 관리에 대한 더 제사한 사항은 [여기](./documentation/kr_KO/dependency_management.md)에 있습니다.** 331 +**종속성 관리에 대한 더 자세한 사항은 [여기](./documentation/kr_KO/dependency_management.md)에 있습니다.**
330 332
331 # 기능들 333 # 기능들
332 334
@@ -1090,6 +1092,73 @@ class SettingsService extends GetxService { @@ -1090,6 +1092,73 @@ class SettingsService extends GetxService {
1090 따라서 앱 실행중 절대로 유지되어야 하는 클래스 인스턴스가 필요하면 1092 따라서 앱 실행중 절대로 유지되어야 하는 클래스 인스턴스가 필요하면
1091 `GetxService`를 사용하세요. 1093 `GetxService`를 사용하세요.
1092 1094
  1095 +### 테스트
  1096 +
  1097 +당신은 당신의 컨트롤러들을 생성주기를 포함하여 다른 어떤 클래스처럼 테스트할 수 있습니다 :
  1098 +
  1099 +```dart
  1100 +class Controller extends GetxController {
  1101 + @override
  1102 + void onInit() {
  1103 + super.onInit();
  1104 + //name2로 값 변경
  1105 + name.value = 'name2';
  1106 + }
  1107 +
  1108 + @override
  1109 + void onClose() {
  1110 + name.value = '';
  1111 + super.onClose();
  1112 + }
  1113 +
  1114 + final name = 'name1'.obs;
  1115 +
  1116 + void changeName() => name.value = 'name3';
  1117 +}
  1118 +
  1119 +void main() {
  1120 + test('''
  1121 +Test the state of the reactive variable "name" across all of its lifecycles''',
  1122 + () {
  1123 + /// 당신은 생성주기를 제외하고 컨트롤러를 테스트할 수 있습니다,
  1124 + /// 그러나 당신이 사용하지 않는다면 추천되지 않습니다
  1125 + /// GetX 종속성 주입
  1126 + final controller = Controller();
  1127 + expect(controller.name.value, 'name1');
  1128 +
  1129 + /// 당신이 그것을 사용한다면, 당신은 모든 것을 테스트할 수 있습니다,
  1130 + /// 각각의 생성주기 이후 어플리케이션의 상태를 포함하여.
  1131 + Get.put(controller); // onInit was called
  1132 + expect(controller.name.value, 'name2');
  1133 +
  1134 + /// 당신의 함수를 테스트하세요
  1135 + controller.changeName();
  1136 + expect(controller.name.value, 'name3');
  1137 +
  1138 + /// onClose 호출됨
  1139 + Get.delete<Controller>();
  1140 +
  1141 + expect(controller.name.value, '');
  1142 + });
  1143 +}
  1144 +```
  1145 +
  1146 +#### 팁들
  1147 +
  1148 +##### Mockito 또는 mocktail
  1149 +당신이 당신의 GetxController/GetxService를 모킹하려고 한다면, 당신은 GetxController를 extend 하고, Mock과 mixin 하라, 그렇게 되면
  1150 +
  1151 +```dart
  1152 +class NotificationServiceMock extends GetxService with Mock implements NotificationService {}
  1153 +```
  1154 +
  1155 +##### Get.reset() 사용하기
  1156 +당신이 위젯 또는 테스트 그룹을 테스트하고 있다면, 당신의 테스트의 마지막 또는 해제 때 당신의 이전 테스트에서 모든 설정을 리셋하기 위해 Get.rest을 사용하십시오
  1157 +
  1158 +##### Get.testMode
  1159 +당신이 당신의 컨트롤러에서 당신의 네비게이션을 사용하고 있다면, 당신의 메인의 시작에 `Get.testMode = true` 를 사용하십시오.
  1160 +
  1161 +
1093 # 2.0의 주요 변경점 1162 # 2.0의 주요 변경점
1094 1163
1095 1- Rx 타입들: 1164 1- Rx 타입들:
1 ![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png) 1 ![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
2 2
3 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get) 3 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
  4 +[![popularity](https://badges.bar/get/popularity)](https://pub.dev/packages/sentry/score)
4 [![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score) 5 [![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score)
  6 +[![pub points](https://badges.bar/get/pub%20points)](https://pub.dev/packages/get/score)
5 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg) 7 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
6 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart) 8 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart)
7 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N) 9 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N)
@@ -3,6 +3,9 @@ @@ -3,6 +3,9 @@
3 *Languages: [English](README.md), [Wietnamski](README-vi.md), [Indonezyjski](README.id-ID.md), [Urdu](README.ur-PK.md), [Język chiński](README.zh-cn.md), [Brazilian Portuguese](README.pt-br.md), [Spanish](README-es.md), [Russian](README.ru.md), Polish (Jesteś tu), [Koreański](README.ko-kr.md), [French](README-fr.md)* 3 *Languages: [English](README.md), [Wietnamski](README-vi.md), [Indonezyjski](README.id-ID.md), [Urdu](README.ur-PK.md), [Język chiński](README.zh-cn.md), [Brazilian Portuguese](README.pt-br.md), [Spanish](README-es.md), [Russian](README.ru.md), Polish (Jesteś tu), [Koreański](README.ko-kr.md), [French](README-fr.md)*
4 4
5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get) 5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
  6 +[![popularity](https://badges.bar/get/popularity)](https://pub.dev/packages/sentry/score)
  7 +[![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score)
  8 +[![pub points](https://badges.bar/get/pub%20points)](https://pub.dev/packages/get/score)
6 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg) 9 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
7 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart) 10 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart)
8 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N) 11 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N)
@@ -3,7 +3,9 @@ @@ -3,7 +3,9 @@
3 **Idiomas: [Inglês](README.md), [Vietnamita](README-vi.md), [Indonésia](README.id-ID.md), [Urdu](README.ur-PK.md), [Chinês](README.zh-cn.md), Português (este arquivo), [Espanhol](README-es.md), [Russo](README.ru.md), [Polonês](README.pl.md), [Coreano](README.ko-kr.md), [Francês](README-fr.md)** 3 **Idiomas: [Inglês](README.md), [Vietnamita](README-vi.md), [Indonésia](README.id-ID.md), [Urdu](README.ur-PK.md), [Chinês](README.zh-cn.md), Português (este arquivo), [Espanhol](README-es.md), [Russo](README.ru.md), [Polonês](README.pl.md), [Coreano](README.ko-kr.md), [Francês](README-fr.md)**
4 4
5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get) 5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
  6 +[![popularity](https://badges.bar/get/popularity)](https://pub.dev/packages/sentry/score)
6 [![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score) 7 [![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score)
  8 +[![pub points](https://badges.bar/get/pub%20points)](https://pub.dev/packages/get/score)
7 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg) 9 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
8 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart) 10 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart)
9 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N) 11 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N)
@@ -3,6 +3,9 @@ @@ -3,6 +3,9 @@
3 _Языки: Русский (этот файл), [вьетнамский](README-vi.md), [индонезийский](README.id-ID.md), [урду](README.ur-PK.md), [Английский](README.md), [Китайский](README.zh-cn.md), [Бразильский Португальский](README.pt-br.md), [Испанский](README-es.md), [Польский](README.pl.md), [Kорейский](README.ko-kr.md), [French](README-fr.md)._ 3 _Языки: Русский (этот файл), [вьетнамский](README-vi.md), [индонезийский](README.id-ID.md), [урду](README.ur-PK.md), [Английский](README.md), [Китайский](README.zh-cn.md), [Бразильский Португальский](README.pt-br.md), [Испанский](README-es.md), [Польский](README.pl.md), [Kорейский](README.ko-kr.md), [French](README-fr.md)._
4 4
5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get) 5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
  6 +[![popularity](https://badges.bar/get/popularity)](https://pub.dev/packages/sentry/score)
  7 +[![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score)
  8 +[![pub points](https://badges.bar/get/pub%20points)](https://pub.dev/packages/get/score)
6 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg) 9 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
7 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart) 10 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart)
8 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N) 11 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N)
@@ -3,7 +3,9 @@ @@ -3,7 +3,9 @@
3 **🌎 اردو ( Selected ✔) [| انگریزی |](README.md) [| ویتنامی |](README-vi.md) [| انڈونیشی |](README.id-ID.md) [چینی |](README.zh-cn.md) [برازیلی پرتگالی |](README.pt-br.md) [ہسپانوی |](README-es.md) [روسی |](README.ru.md) [پولش |](README.pl.md) [کورین |](README.ko-kr.md), [French](README-fr.md)** 3 **🌎 اردو ( Selected ✔) [| انگریزی |](README.md) [| ویتنامی |](README-vi.md) [| انڈونیشی |](README.id-ID.md) [چینی |](README.zh-cn.md) [برازیلی پرتگالی |](README.pt-br.md) [ہسپانوی |](README-es.md) [روسی |](README.ru.md) [پولش |](README.pl.md) [کورین |](README.ko-kr.md), [French](README-fr.md)**
4 4
5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get) 5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
  6 +[![popularity](https://badges.bar/get/popularity)](https://pub.dev/packages/sentry/score)
6 [![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score) 7 [![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score)
  8 +[![pub points](https://badges.bar/get/pub%20points)](https://pub.dev/packages/get/score)
7 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg) 9 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
8 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart) 10 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart)
9 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N) 11 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N)
@@ -3,6 +3,9 @@ @@ -3,6 +3,9 @@
3 _语言: 中文, [英文](README.md), [越南文](README-vi.md), [印度尼西亚](README.id-ID.md), [乌尔都语](README.ur-PK.md), [巴西葡萄牙语](README.pt-br.md), [俄语](README.ru.md), [西班牙语](README-es.md), [波兰语](README.pl.md), [韩国语](README.ko-kr.md), [法语](README-fr.md), [French](README-fr.md)._ 3 _语言: 中文, [英文](README.md), [越南文](README-vi.md), [印度尼西亚](README.id-ID.md), [乌尔都语](README.ur-PK.md), [巴西葡萄牙语](README.pt-br.md), [俄语](README.ru.md), [西班牙语](README-es.md), [波兰语](README.pl.md), [韩国语](README.ko-kr.md), [法语](README-fr.md), [French](README-fr.md)._
4 4
5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get) 5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
  6 +[![popularity](https://badges.bar/get/popularity)](https://pub.dev/packages/sentry/score)
  7 +[![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score)
  8 +[![pub points](https://badges.bar/get/pub%20points)](https://pub.dev/packages/get/score)
6 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg) 9 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
7 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart) 10 [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart)
8 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N) 11 [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N)
  1 +// ignore_for_file: file_names
  2 +
1 const Map<String, String> en_US = { 3 const Map<String, String> en_US = {
2 'covid': 'Corona Virus', 4 'covid': 'Corona Virus',
3 'total_confirmed': 'Total Confirmed', 5 'total_confirmed': 'Total Confirmed',
  1 +// ignore_for_file: file_names
  2 +
1 const Map<String, String> pt_BR = { 3 const Map<String, String> pt_BR = {
2 'covid': 'Corona Vírus', 4 'covid': 'Corona Vírus',
3 'total_confirmed': 'Total confirmado', 5 'total_confirmed': 'Total confirmado',
@@ -7,4 +9,4 @@ const Map<String, String> pt_BR = { @@ -7,4 +9,4 @@ const Map<String, String> pt_BR = {
7 'total_infecteds': 'Total de infectados', 9 'total_infecteds': 'Total de infectados',
8 'details': 'Detalhes', 10 'details': 'Detalhes',
9 'total_recovered': 'Total de recuperados' 11 'total_recovered': 'Total de recuperados'
10 -};  
  12 +};
1 import 'package:flutter/material.dart'; 1 import 'package:flutter/material.dart';
2 import 'package:get/get.dart'; 2 import 'package:get/get.dart';
3 3
4 -import 'en_US.dart';  
5 -import 'pt_BR.dart'; 4 +import 'en_us.dart';
  5 +import 'pt_br.dart';
6 6
7 class TranslationService extends Translations { 7 class TranslationService extends Translations {
8 static Locale? get locale => Get.deviceLocale; 8 static Locale? get locale => Get.deviceLocale;
@@ -21,6 +21,12 @@ class HomeView extends GetView<HomeController> { @@ -21,6 +21,12 @@ class HomeView extends GetView<HomeController> {
21 child: Scaffold( 21 child: Scaffold(
22 backgroundColor: Colors.transparent, 22 backgroundColor: Colors.transparent,
23 appBar: AppBar( 23 appBar: AppBar(
  24 + leading: IconButton(
  25 + icon: Icon(Icons.add),
  26 + onPressed: () {
  27 + Get.snackbar('title', 'message');
  28 + },
  29 + ),
24 title: Text('covid'.tr), 30 title: Text('covid'.tr),
25 backgroundColor: Colors.white10, 31 backgroundColor: Colors.white10,
26 elevation: 0, 32 elevation: 0,
@@ -3,7 +3,6 @@ import 'package:get/get.dart'; @@ -3,7 +3,6 @@ import 'package:get/get.dart';
3 3
4 import '../controllers/dashboard_controller.dart'; 4 import '../controllers/dashboard_controller.dart';
5 5
6 -  
7 class DashboardView extends GetView<DashboardController> { 6 class DashboardView extends GetView<DashboardController> {
8 @override 7 @override
9 Widget build(BuildContext context) { 8 Widget build(BuildContext context) {
  1 +// ignore_for_file: non_constant_identifier_names
  2 +
1 part of 'app_pages.dart'; 3 part of 'app_pages.dart';
2 // DO NOT EDIT. This is code generated via package:get_cli/get_cli.dart 4 // DO NOT EDIT. This is code generated via package:get_cli/get_cli.dart
3 5
4 abstract class Routes { 6 abstract class Routes {
5 - Routes._();  
6 -  
7 static const HOME = _Paths.HOME; 7 static const HOME = _Paths.HOME;
8 - static const PROFILE = _Paths.HOME + _Paths.PROFILE;  
9 8
  9 + static const PROFILE = _Paths.HOME + _Paths.PROFILE;
10 static const SETTINGS = _Paths.SETTINGS; 10 static const SETTINGS = _Paths.SETTINGS;
11 11
12 static const PRODUCTS = _Paths.HOME + _Paths.PRODUCTS; 12 static const PRODUCTS = _Paths.HOME + _Paths.PRODUCTS;
13 - static String PRODUCT_DETAILS(String productId) => '$PRODUCTS/$productId'; 13 +
14 static const LOGIN = _Paths.LOGIN; 14 static const LOGIN = _Paths.LOGIN;
  15 + static const DASHBOARD = _Paths.HOME + _Paths.DASHBOARD;
  16 + Routes._();
15 static String LOGIN_THEN(String afterSuccessfulLogin) => 17 static String LOGIN_THEN(String afterSuccessfulLogin) =>
16 '$LOGIN?then=${Uri.encodeQueryComponent(afterSuccessfulLogin)}'; 18 '$LOGIN?then=${Uri.encodeQueryComponent(afterSuccessfulLogin)}';
17 - static const DASHBOARD = _Paths.HOME + _Paths.DASHBOARD; 19 + static String PRODUCT_DETAILS(String productId) => '$PRODUCTS/$productId';
18 } 20 }
19 21
20 abstract class _Paths { 22 abstract class _Paths {
@@ -115,6 +115,7 @@ class GetConnect extends GetConnectInterface { @@ -115,6 +115,7 @@ class GetConnect extends GetConnectInterface {
115 Decoder? defaultDecoder; 115 Decoder? defaultDecoder;
116 Duration timeout; 116 Duration timeout;
117 List<TrustedCertificate>? trustedCertificates; 117 List<TrustedCertificate>? trustedCertificates;
  118 + String Function(Uri url)? findProxy;
118 GetHttpClient? _httpClient; 119 GetHttpClient? _httpClient;
119 List<GetSocket>? _sockets; 120 List<GetSocket>? _sockets;
120 bool withCredentials; 121 bool withCredentials;
@@ -134,6 +135,7 @@ class GetConnect extends GetConnectInterface { @@ -134,6 +135,7 @@ class GetConnect extends GetConnectInterface {
134 baseUrl: baseUrl, 135 baseUrl: baseUrl,
135 trustedCertificates: trustedCertificates, 136 trustedCertificates: trustedCertificates,
136 withCredentials: withCredentials, 137 withCredentials: withCredentials,
  138 + findProxy: findProxy
137 ); 139 );
138 140
139 @override 141 @override
@@ -39,6 +39,8 @@ class GetHttpClient { @@ -39,6 +39,8 @@ class GetHttpClient {
39 39
40 final GetModifier _modifier; 40 final GetModifier _modifier;
41 41
  42 + String Function(Uri url)? findProxy;
  43 +
42 GetHttpClient({ 44 GetHttpClient({
43 this.userAgent = 'getx-client', 45 this.userAgent = 'getx-client',
44 this.timeout = const Duration(seconds: 8), 46 this.timeout = const Duration(seconds: 8),
@@ -50,10 +52,12 @@ class GetHttpClient { @@ -50,10 +52,12 @@ class GetHttpClient {
50 this.baseUrl, 52 this.baseUrl,
51 List<TrustedCertificate>? trustedCertificates, 53 List<TrustedCertificate>? trustedCertificates,
52 bool withCredentials = false, 54 bool withCredentials = false,
  55 + String Function(Uri url)? findProxy,
53 }) : _httpClient = HttpRequestImpl( 56 }) : _httpClient = HttpRequestImpl(
54 allowAutoSignedCert: allowAutoSignedCert, 57 allowAutoSignedCert: allowAutoSignedCert,
55 trustedCertificates: trustedCertificates, 58 trustedCertificates: trustedCertificates,
56 withCredentials: withCredentials, 59 withCredentials: withCredentials,
  60 + findProxy: findProxy,
57 ), 61 ),
58 _modifier = GetModifier(); 62 _modifier = GetModifier();
59 63
@@ -195,7 +199,6 @@ class GetHttpClient { @@ -195,7 +199,6 @@ class GetHttpClient {
195 int requestNumber = 1, 199 int requestNumber = 1,
196 Map<String, String>? headers, 200 Map<String, String>? headers,
197 }) async { 201 }) async {
198 - try {  
199 var request = await handler(); 202 var request = await handler();
200 203
201 headers?.forEach((key, value) { 204 headers?.forEach((key, value) {
@@ -206,6 +209,7 @@ class GetHttpClient { @@ -206,6 +209,7 @@ class GetHttpClient {
206 final newRequest = await _modifier.modifyRequest<T>(request); 209 final newRequest = await _modifier.modifyRequest<T>(request);
207 210
208 _httpClient.timeout = timeout; 211 _httpClient.timeout = timeout;
  212 + try {
209 var response = await _httpClient.send<T>(newRequest); 213 var response = await _httpClient.send<T>(newRequest);
210 214
211 final newResponse = 215 final newResponse =
@@ -242,7 +246,7 @@ class GetHttpClient { @@ -242,7 +246,7 @@ class GetHttpClient {
242 throw GetHttpException(err.toString()); 246 throw GetHttpException(err.toString());
243 } else { 247 } else {
244 return Response<T>( 248 return Response<T>(
245 - request: null, 249 + request: newRequest,
246 headers: null, 250 headers: null,
247 statusCode: null, 251 statusCode: null,
248 body: null, 252 body: null,
@@ -268,6 +272,8 @@ class GetHttpClient { @@ -268,6 +272,8 @@ class GetHttpClient {
268 headers: headers, 272 headers: headers,
269 decoder: decoder ?? (defaultDecoder as Decoder<T>?), 273 decoder: decoder ?? (defaultDecoder as Decoder<T>?),
270 contentLength: 0, 274 contentLength: 0,
  275 + followRedirects: followRedirects,
  276 + maxRedirects: maxRedirects,
271 )); 277 ));
272 } 278 }
273 279
@@ -17,6 +17,7 @@ class HttpRequestImpl extends HttpRequestBase { @@ -17,6 +17,7 @@ class HttpRequestImpl extends HttpRequestBase {
17 bool allowAutoSignedCert = true, 17 bool allowAutoSignedCert = true,
18 List<TrustedCertificate>? trustedCertificates, 18 List<TrustedCertificate>? trustedCertificates,
19 bool withCredentials = false, 19 bool withCredentials = false,
  20 + String Function(Uri url)? findProxy,
20 }) { 21 }) {
21 _httpClient = io.HttpClient(); 22 _httpClient = io.HttpClient();
22 if (trustedCertificates != null) { 23 if (trustedCertificates != null) {
@@ -29,6 +30,7 @@ class HttpRequestImpl extends HttpRequestBase { @@ -29,6 +30,7 @@ class HttpRequestImpl extends HttpRequestBase {
29 30
30 _httpClient = io.HttpClient(context: _securityContext); 31 _httpClient = io.HttpClient(context: _securityContext);
31 _httpClient!.badCertificateCallback = (_, __, ___) => allowAutoSignedCert; 32 _httpClient!.badCertificateCallback = (_, __, ___) => allowAutoSignedCert;
  33 + _httpClient!.findProxy = findProxy;
32 } 34 }
33 35
34 @override 36 @override
@@ -8,6 +8,7 @@ class HttpRequestImpl extends HttpRequestBase { @@ -8,6 +8,7 @@ class HttpRequestImpl extends HttpRequestBase {
8 bool allowAutoSignedCert = true, 8 bool allowAutoSignedCert = true,
9 List<TrustedCertificate>? trustedCertificates, 9 List<TrustedCertificate>? trustedCertificates,
10 bool withCredentials = false, 10 bool withCredentials = false,
  11 + String Function(Uri url)? findProxy,
11 }); 12 });
12 @override 13 @override
13 void close() {} 14 void close() {}
@@ -138,7 +138,11 @@ class HeaderValue { @@ -138,7 +138,11 @@ class HeaderValue {
138 stringBuffer.write(_value); 138 stringBuffer.write(_value);
139 if (parameters != null && parameters!.isNotEmpty) { 139 if (parameters != null && parameters!.isNotEmpty) {
140 _parameters!.forEach((name, value) { 140 _parameters!.forEach((name, value) {
141 - stringBuffer..write('; ')..write(name)..write('=')..write(value); 141 + stringBuffer
  142 + ..write('; ')
  143 + ..write(name)
  144 + ..write('=')
  145 + ..write(value);
142 }); 146 });
143 } 147 }
144 return stringBuffer.toString(); 148 return stringBuffer.toString();
1 import 'dart:convert'; 1 import 'dart:convert';
2 2
  3 +/// Signature for [SocketNotifier.addCloses].
  4 +typedef CloseSocket = void Function(Close);
  5 +
  6 +/// Signature for [SocketNotifier.addMessages].
  7 +typedef MessageSocket = void Function(dynamic val);
  8 +
  9 +/// Signature for [SocketNotifier.open].
  10 +typedef OpenSocket = void Function();
  11 +
  12 +/// Wrapper class to message and reason from SocketNotifier
3 class Close { 13 class Close {
4 final String? message; 14 final String? message;
5 final int? reason; 15 final int? reason;
@@ -12,12 +22,8 @@ class Close { @@ -12,12 +22,8 @@ class Close {
12 } 22 }
13 } 23 }
14 24
15 -typedef OpenSocket = void Function();  
16 -  
17 -typedef CloseSocket = void Function(Close);  
18 -  
19 -typedef MessageSocket = void Function(dynamic val);  
20 - 25 +/// This class manages the transmission of messages over websockets using
  26 +/// GetConnect
21 class SocketNotifier { 27 class SocketNotifier {
22 List<void Function(dynamic)>? _onMessages = <MessageSocket>[]; 28 List<void Function(dynamic)>? _onMessages = <MessageSocket>[];
23 Map<String, void Function(dynamic)>? _onEvents = <String, MessageSocket>{}; 29 Map<String, void Function(dynamic)>? _onEvents = <String, MessageSocket>{};
@@ -26,22 +32,42 @@ class SocketNotifier { @@ -26,22 +32,42 @@ class SocketNotifier {
26 32
27 late OpenSocket open; 33 late OpenSocket open;
28 34
29 - void addMessages(MessageSocket socket) {  
30 - _onMessages!.add((socket)); 35 + /// subscribe to close events
  36 + void addCloses(CloseSocket socket) {
  37 + _onCloses!.add(socket);
  38 + }
  39 +
  40 + /// subscribe to error events
  41 + void addErrors(CloseSocket socket) {
  42 + _onErrors!.add((socket));
31 } 43 }
32 44
  45 + /// subscribe to named events
33 void addEvents(String event, MessageSocket socket) { 46 void addEvents(String event, MessageSocket socket) {
34 _onEvents![event] = socket; 47 _onEvents![event] = socket;
35 } 48 }
36 49
37 - void addCloses(CloseSocket socket) {  
38 - _onCloses!.add(socket); 50 + /// subscribe to message events
  51 + void addMessages(MessageSocket socket) {
  52 + _onMessages!.add((socket));
39 } 53 }
40 54
41 - void addErrors(CloseSocket socket) {  
42 - _onErrors!.add((socket)); 55 + /// Dispose messages, events, closes and errors subscriptions
  56 + void dispose() {
  57 + _onMessages = null;
  58 + _onEvents = null;
  59 + _onCloses = null;
  60 + _onErrors = null;
43 } 61 }
44 62
  63 + /// Notify all subscriptions on [addCloses]
  64 + void notifyClose(Close err) {
  65 + for (var item in _onCloses!) {
  66 + item(err);
  67 + }
  68 + }
  69 +
  70 + /// Notify all subscriptions on [addMessages]
45 void notifyData(dynamic data) { 71 void notifyData(dynamic data) {
46 for (var item in _onMessages!) { 72 for (var item in _onMessages!) {
47 item(data); 73 item(data);
@@ -51,12 +77,7 @@ class SocketNotifier { @@ -51,12 +77,7 @@ class SocketNotifier {
51 } 77 }
52 } 78 }
53 79
54 - void notifyClose(Close err) {  
55 - for (var item in _onCloses!) {  
56 - item(err);  
57 - }  
58 - }  
59 - 80 + /// Notify all subscriptions on [addErrors]
60 void notifyError(Close err) { 81 void notifyError(Close err) {
61 // rooms.removeWhere((key, value) => value.contains(_ws)); 82 // rooms.removeWhere((key, value) => value.contains(_ws));
62 for (var item in _onErrors!) { 83 for (var item in _onErrors!) {
@@ -72,15 +93,9 @@ class SocketNotifier { @@ -72,15 +93,9 @@ class SocketNotifier {
72 if (_onEvents!.containsKey(event)) { 93 if (_onEvents!.containsKey(event)) {
73 _onEvents![event]!(data); 94 _onEvents![event]!(data);
74 } 95 }
  96 + // ignore: avoid_catches_without_on_clauses
75 } catch (_) { 97 } catch (_) {
76 return; 98 return;
77 } 99 }
78 } 100 }
79 -  
80 - void dispose() {  
81 - _onMessages = null;  
82 - _onEvents = null;  
83 - _onCloses = null;  
84 - _onErrors = null;  
85 - }  
86 } 101 }
@@ -4,15 +4,8 @@ import 'dart:convert'; @@ -4,15 +4,8 @@ import 'dart:convert';
4 import 'dart:html'; 4 import 'dart:html';
5 5
6 import '../../../get_core/get_core.dart'; 6 import '../../../get_core/get_core.dart';
7 -  
8 import 'socket_notifier.dart'; 7 import 'socket_notifier.dart';
9 8
10 -enum ConnectionStatus {  
11 - connecting,  
12 - connected,  
13 - closed,  
14 -}  
15 -  
16 class BaseWebSocket { 9 class BaseWebSocket {
17 String url; 10 String url;
18 WebSocket? socket; 11 WebSocket? socket;
@@ -21,6 +14,8 @@ class BaseWebSocket { @@ -21,6 +14,8 @@ class BaseWebSocket {
21 bool isDisposed = false; 14 bool isDisposed = false;
22 bool allowSelfSigned; 15 bool allowSelfSigned;
23 16
  17 + ConnectionStatus? connectionStatus;
  18 + Timer? _t;
24 BaseWebSocket( 19 BaseWebSocket(
25 this.url, { 20 this.url, {
26 this.ping = const Duration(seconds: 5), 21 this.ping = const Duration(seconds: 5),
@@ -30,9 +25,12 @@ class BaseWebSocket { @@ -30,9 +25,12 @@ class BaseWebSocket {
30 ? url.replaceAll('https:', 'wss:') 25 ? url.replaceAll('https:', 'wss:')
31 : url.replaceAll('http:', 'ws:'); 26 : url.replaceAll('http:', 'ws:');
32 } 27 }
33 - ConnectionStatus? connectionStatus;  
34 - Timer? _t;  
35 28
  29 + void close([int? status, String? reason]) {
  30 + socket?.close(status, reason);
  31 + }
  32 +
  33 + // ignore: use_setters_to_change_properties
36 void connect() { 34 void connect() {
37 try { 35 try {
38 connectionStatus = ConnectionStatus.connecting; 36 connectionStatus = ConnectionStatus.connecting;
@@ -68,9 +66,18 @@ class BaseWebSocket { @@ -68,9 +66,18 @@ class BaseWebSocket {
68 } 66 }
69 } 67 }
70 68
71 - // ignore: use_setters_to_change_properties  
72 - void onOpen(OpenSocket fn) {  
73 - socketNotifier!.open = fn; 69 + void dispose() {
  70 + socketNotifier!.dispose();
  71 + socketNotifier = null;
  72 + isDisposed = true;
  73 + }
  74 +
  75 + void emit(String event, dynamic data) {
  76 + send(jsonEncode({'type': event, 'data': data}));
  77 + }
  78 +
  79 + void on(String event, MessageSocket message) {
  80 + socketNotifier!.addEvents(event, message);
74 } 81 }
75 82
76 void onClose(CloseSocket fn) { 83 void onClose(CloseSocket fn) {
@@ -85,12 +92,9 @@ class BaseWebSocket { @@ -85,12 +92,9 @@ class BaseWebSocket {
85 socketNotifier!.addMessages(fn); 92 socketNotifier!.addMessages(fn);
86 } 93 }
87 94
88 - void on(String event, MessageSocket message) {  
89 - socketNotifier!.addEvents(event, message);  
90 - }  
91 -  
92 - void close([int? status, String? reason]) {  
93 - socket?.close(status, reason); 95 + // ignore: use_setters_to_change_properties
  96 + void onOpen(OpenSocket fn) {
  97 + socketNotifier!.open = fn;
94 } 98 }
95 99
96 void send(dynamic data) { 100 void send(dynamic data) {
@@ -103,14 +107,10 @@ class BaseWebSocket { @@ -103,14 +107,10 @@ class BaseWebSocket {
103 Get.log('WebSocket not connected, message $data not sent'); 107 Get.log('WebSocket not connected, message $data not sent');
104 } 108 }
105 } 109 }
  110 +}
106 111
107 - void emit(String event, dynamic data) {  
108 - send(jsonEncode({'type': event, 'data': data}));  
109 - }  
110 -  
111 - void dispose() {  
112 - socketNotifier!.dispose();  
113 - socketNotifier = null;  
114 - isDisposed = true;  
115 - } 112 +enum ConnectionStatus {
  113 + connecting,
  114 + connected,
  115 + closed,
116 } 116 }
@@ -4,30 +4,28 @@ import 'dart:io'; @@ -4,30 +4,28 @@ import 'dart:io';
4 import 'dart:math'; 4 import 'dart:math';
5 5
6 import '../../../get_core/get_core.dart'; 6 import '../../../get_core/get_core.dart';
7 -  
8 import 'socket_notifier.dart'; 7 import 'socket_notifier.dart';
9 8
10 -enum ConnectionStatus {  
11 - connecting,  
12 - connected,  
13 - closed,  
14 -}  
15 -  
16 class BaseWebSocket { 9 class BaseWebSocket {
17 String url; 10 String url;
18 WebSocket? socket; 11 WebSocket? socket;
19 SocketNotifier? socketNotifier = SocketNotifier(); 12 SocketNotifier? socketNotifier = SocketNotifier();
20 bool isDisposed = false; 13 bool isDisposed = false;
  14 + Duration ping;
  15 + bool allowSelfSigned;
  16 + ConnectionStatus? connectionStatus;
  17 +
21 BaseWebSocket( 18 BaseWebSocket(
22 this.url, { 19 this.url, {
23 this.ping = const Duration(seconds: 5), 20 this.ping = const Duration(seconds: 5),
24 this.allowSelfSigned = true, 21 this.allowSelfSigned = true,
25 }); 22 });
26 - Duration ping;  
27 - bool allowSelfSigned;  
28 23
29 - ConnectionStatus? connectionStatus; 24 + void close([int? status, String? reason]) {
  25 + socket?.close(status, reason);
  26 + }
30 27
  28 + // ignore: use_setters_to_change_properties
31 Future connect() async { 29 Future connect() async {
32 if (isDisposed) { 30 if (isDisposed) {
33 socketNotifier = SocketNotifier(); 31 socketNotifier = SocketNotifier();
@@ -60,9 +58,18 @@ class BaseWebSocket { @@ -60,9 +58,18 @@ class BaseWebSocket {
60 } 58 }
61 } 59 }
62 60
63 - // ignore: use_setters_to_change_properties  
64 - void onOpen(OpenSocket fn) {  
65 - socketNotifier!.open = fn; 61 + void dispose() {
  62 + socketNotifier!.dispose();
  63 + socketNotifier = null;
  64 + isDisposed = true;
  65 + }
  66 +
  67 + void emit(String event, dynamic data) {
  68 + send(jsonEncode({'type': event, 'data': data}));
  69 + }
  70 +
  71 + void on(String event, MessageSocket message) {
  72 + socketNotifier!.addEvents(event, message);
66 } 73 }
67 74
68 void onClose(CloseSocket fn) { 75 void onClose(CloseSocket fn) {
@@ -77,12 +84,9 @@ class BaseWebSocket { @@ -77,12 +84,9 @@ class BaseWebSocket {
77 socketNotifier!.addMessages(fn); 84 socketNotifier!.addMessages(fn);
78 } 85 }
79 86
80 - void on(String event, MessageSocket message) {  
81 - socketNotifier!.addEvents(event, message);  
82 - }  
83 -  
84 - void close([int? status, String? reason]) {  
85 - socket?.close(status, reason); 87 + // ignore: use_setters_to_change_properties
  88 + void onOpen(OpenSocket fn) {
  89 + socketNotifier!.open = fn;
86 } 90 }
87 91
88 void send(dynamic data) async { 92 void send(dynamic data) async {
@@ -95,16 +99,6 @@ class BaseWebSocket { @@ -95,16 +99,6 @@ class BaseWebSocket {
95 } 99 }
96 } 100 }
97 101
98 - void dispose() {  
99 - socketNotifier!.dispose();  
100 - socketNotifier = null;  
101 - isDisposed = true;  
102 - }  
103 -  
104 - void emit(String event, dynamic data) {  
105 - send(jsonEncode({'type': event, 'data': data}));  
106 - }  
107 -  
108 Future<WebSocket> _connectForSelfSignedCert(String url) async { 102 Future<WebSocket> _connectForSelfSignedCert(String url) async {
109 try { 103 try {
110 var r = Random(); 104 var r = Random();
@@ -136,3 +130,9 @@ class BaseWebSocket { @@ -136,3 +130,9 @@ class BaseWebSocket {
136 } 130 }
137 } 131 }
138 } 132 }
  133 +
  134 +enum ConnectionStatus {
  135 + connecting,
  136 + connected,
  137 + closed,
  138 +}
@@ -239,7 +239,7 @@ class GetInstance { @@ -239,7 +239,7 @@ class GetInstance {
239 final newKey = key ?? _getKey(S, tag); 239 final newKey = key ?? _getKey(S, tag);
240 if (_singl.containsKey(newKey)) { 240 if (_singl.containsKey(newKey)) {
241 final dep = _singl[newKey]; 241 final dep = _singl[newKey];
242 - if (dep != null) { 242 + if (dep != null && !dep.permanent) {
243 dep.isDirty = true; 243 dep.isDirty = true;
244 } 244 }
245 } 245 }
@@ -16,5 +16,5 @@ export 'src/routes/get_route.dart'; @@ -16,5 +16,5 @@ export 'src/routes/get_route.dart';
16 export 'src/routes/observers/route_observer.dart'; 16 export 'src/routes/observers/route_observer.dart';
17 export 'src/routes/route_middleware.dart'; 17 export 'src/routes/route_middleware.dart';
18 export 'src/routes/transitions_type.dart'; 18 export 'src/routes/transitions_type.dart';
19 -export 'src/snackbar/snack.dart';  
20 -export 'src/snackbar/snack_route.dart'; 19 +export 'src/snackbar/snackbar.dart';
  20 +export 'src/snackbar/snackbar_controller.dart';
@@ -11,243 +11,56 @@ import 'dialog/dialog_route.dart'; @@ -11,243 +11,56 @@ import 'dialog/dialog_route.dart';
11 import 'root/parse_route.dart'; 11 import 'root/parse_route.dart';
12 import 'root/root_controller.dart'; 12 import 'root/root_controller.dart';
13 import 'routes/transitions_type.dart'; 13 import 'routes/transitions_type.dart';
  14 +import 'snackbar/snackbar_controller.dart';
14 15
15 -extension ExtensionSnackbar on GetInterface {  
16 - void rawSnackbar({  
17 - String? title,  
18 - String? message,  
19 - Widget? titleText,  
20 - Widget? messageText,  
21 - Widget? icon,  
22 - bool instantInit = true,  
23 - bool shouldIconPulse = true,  
24 - double? maxWidth,  
25 - EdgeInsets margin = const EdgeInsets.all(0.0),  
26 - EdgeInsets padding = const EdgeInsets.all(16),  
27 - double borderRadius = 0.0,  
28 - Color? borderColor,  
29 - double borderWidth = 1.0,  
30 - Color backgroundColor = const Color(0xFF303030),  
31 - Color? leftBarIndicatorColor,  
32 - List<BoxShadow>? boxShadows,  
33 - Gradient? backgroundGradient,  
34 - Widget? mainButton,  
35 - OnTap? onTap,  
36 - Duration duration = const Duration(seconds: 3),  
37 - bool isDismissible = true,  
38 - SnackDismissDirection dismissDirection = SnackDismissDirection.VERTICAL,  
39 - bool showProgressIndicator = false,  
40 - AnimationController? progressIndicatorController,  
41 - Color? progressIndicatorBackgroundColor,  
42 - Animation<Color>? progressIndicatorValueColor,  
43 - SnackPosition snackPosition = SnackPosition.BOTTOM,  
44 - SnackStyle snackStyle = SnackStyle.FLOATING,  
45 - Curve forwardAnimationCurve = Curves.easeOutCirc,  
46 - Curve reverseAnimationCurve = Curves.easeOutCirc,  
47 - Duration animationDuration = const Duration(seconds: 1),  
48 - SnackbarStatusCallback? snackbarStatus,  
49 - double? barBlur = 0.0,  
50 - double overlayBlur = 0.0,  
51 - Color? overlayColor,  
52 - Form? userInputForm,  
53 - }) async {  
54 - final getBar = GetBar(  
55 - snackbarStatus: snackbarStatus,  
56 - title: title,  
57 - message: message,  
58 - titleText: titleText,  
59 - messageText: messageText,  
60 - snackPosition: snackPosition,  
61 - borderRadius: borderRadius,  
62 - margin: margin,  
63 - duration: duration,  
64 - barBlur: barBlur,  
65 - backgroundColor: backgroundColor,  
66 - icon: icon,  
67 - shouldIconPulse: shouldIconPulse,  
68 - maxWidth: maxWidth,  
69 - padding: padding,  
70 - borderColor: borderColor,  
71 - borderWidth: borderWidth,  
72 - leftBarIndicatorColor: leftBarIndicatorColor,  
73 - boxShadows: boxShadows,  
74 - backgroundGradient: backgroundGradient,  
75 - mainButton: mainButton,  
76 - onTap: onTap,  
77 - isDismissible: isDismissible,  
78 - dismissDirection: dismissDirection,  
79 - showProgressIndicator: showProgressIndicator,  
80 - progressIndicatorController: progressIndicatorController,  
81 - progressIndicatorBackgroundColor: progressIndicatorBackgroundColor,  
82 - progressIndicatorValueColor: progressIndicatorValueColor,  
83 - snackStyle: snackStyle,  
84 - forwardAnimationCurve: forwardAnimationCurve,  
85 - reverseAnimationCurve: reverseAnimationCurve,  
86 - animationDuration: animationDuration,  
87 - overlayBlur: overlayBlur,  
88 - overlayColor: overlayColor,  
89 - userInputForm: userInputForm,  
90 - );  
91 -  
92 - if (instantInit) {  
93 - getBar.show();  
94 - } else {  
95 - SchedulerBinding.instance!.addPostFrameCallback((_) {  
96 - getBar.show();  
97 - });  
98 - }  
99 - }  
100 -  
101 - Future<T?>? showSnackbar<T>(GetBar snackbar) {  
102 - return key.currentState?.push(SnackRoute<T>(snack: snackbar));  
103 - }  
104 -  
105 - void snackbar<T>(  
106 - String title,  
107 - String message, {  
108 - Color? colorText,  
109 - Duration? duration, 16 +/// It replaces the Flutter Navigator, but needs no context.
  17 +/// You can to use navigator.push(YourRoute()) rather
  18 +/// Navigator.push(context, YourRoute());
  19 +NavigatorState? get navigator => GetNavigation(Get).key.currentState;
110 20
111 - /// with instantInit = false you can put snackbar on initState  
112 - bool instantInit = true,  
113 - SnackPosition? snackPosition,  
114 - Widget? titleText,  
115 - Widget? messageText,  
116 - Widget? icon,  
117 - bool? shouldIconPulse,  
118 - double? maxWidth,  
119 - EdgeInsets? margin,  
120 - EdgeInsets? padding,  
121 - double? borderRadius,  
122 - Color? borderColor,  
123 - double? borderWidth, 21 +extension ExtensionBottomSheet on GetInterface {
  22 + Future<T?> bottomSheet<T>(
  23 + Widget bottomsheet, {
124 Color? backgroundColor, 24 Color? backgroundColor,
125 - Color? leftBarIndicatorColor,  
126 - List<BoxShadow>? boxShadows,  
127 - Gradient? backgroundGradient,  
128 - TextButton? mainButton,  
129 - OnTap? onTap,  
130 - bool? isDismissible,  
131 - bool? showProgressIndicator,  
132 - SnackDismissDirection? dismissDirection,  
133 - AnimationController? progressIndicatorController,  
134 - Color? progressIndicatorBackgroundColor,  
135 - Animation<Color>? progressIndicatorValueColor,  
136 - SnackStyle? snackStyle,  
137 - Curve? forwardAnimationCurve,  
138 - Curve? reverseAnimationCurve,  
139 - Duration? animationDuration,  
140 - double? barBlur,  
141 - double? overlayBlur,  
142 - SnackbarStatusCallback? snackbarStatus,  
143 - Color? overlayColor,  
144 - Form? userInputForm,  
145 - }) async {  
146 - final getBar = GetBar(  
147 - snackbarStatus: snackbarStatus,  
148 - titleText: titleText ??  
149 - Text(  
150 - title,  
151 - style: TextStyle(  
152 - color: colorText ?? iconColor ?? Colors.black,  
153 - fontWeight: FontWeight.w800,  
154 - fontSize: 16,  
155 - ),  
156 - ),  
157 - messageText: messageText ??  
158 - Text(  
159 - message,  
160 - style: TextStyle(  
161 - color: colorText ?? iconColor ?? Colors.black,  
162 - fontWeight: FontWeight.w300,  
163 - fontSize: 14,  
164 - ),  
165 - ),  
166 - snackPosition: snackPosition ?? SnackPosition.TOP,  
167 - borderRadius: borderRadius ?? 15,  
168 - margin: margin ?? EdgeInsets.symmetric(horizontal: 10),  
169 - duration: duration ?? Duration(seconds: 3),  
170 - barBlur: barBlur ?? 7.0,  
171 - backgroundColor: backgroundColor ?? Colors.grey.withOpacity(0.2),  
172 - icon: icon,  
173 - shouldIconPulse: shouldIconPulse ?? true,  
174 - maxWidth: maxWidth,  
175 - padding: padding ?? EdgeInsets.all(16),  
176 - borderColor: borderColor,  
177 - borderWidth: borderWidth,  
178 - leftBarIndicatorColor: leftBarIndicatorColor,  
179 - boxShadows: boxShadows,  
180 - backgroundGradient: backgroundGradient,  
181 - mainButton: mainButton,  
182 - onTap: onTap,  
183 - isDismissible: isDismissible ?? true,  
184 - dismissDirection: dismissDirection ?? SnackDismissDirection.VERTICAL,  
185 - showProgressIndicator: showProgressIndicator ?? false,  
186 - progressIndicatorController: progressIndicatorController,  
187 - progressIndicatorBackgroundColor: progressIndicatorBackgroundColor,  
188 - progressIndicatorValueColor: progressIndicatorValueColor,  
189 - snackStyle: snackStyle ?? SnackStyle.FLOATING,  
190 - forwardAnimationCurve: forwardAnimationCurve ?? Curves.easeOutCirc,  
191 - reverseAnimationCurve: reverseAnimationCurve ?? Curves.easeOutCirc,  
192 - animationDuration: animationDuration ?? Duration(seconds: 1),  
193 - overlayBlur: overlayBlur ?? 0.0,  
194 - overlayColor: overlayColor ?? Colors.transparent,  
195 - userInputForm: userInputForm);  
196 -  
197 - if (instantInit) {  
198 - showSnackbar<T>(getBar);  
199 - } else {  
200 - routing.isSnackbar = true;  
201 - SchedulerBinding.instance!.addPostFrameCallback((_) {  
202 - showSnackbar<T>(getBar);  
203 - });  
204 - }  
205 - }  
206 -}  
207 -  
208 -extension OverlayExt on GetInterface {  
209 - Future<T> showOverlay<T>({  
210 - required Future<T> Function() asyncFunction,  
211 - Color opacityColor = Colors.black,  
212 - Widget? loadingWidget,  
213 - double opacity = .5,  
214 - }) async {  
215 - final navigatorState =  
216 - Navigator.of(Get.overlayContext!, rootNavigator: false);  
217 - final overlayState = navigatorState.overlay!;  
218 -  
219 - final overlayEntryOpacity = OverlayEntry(builder: (context) {  
220 - return Opacity(  
221 - opacity: opacity,  
222 - child: Container(  
223 - color: opacityColor,  
224 - ));  
225 - });  
226 - final overlayEntryLoader = OverlayEntry(builder: (context) {  
227 - return loadingWidget ??  
228 - Center(  
229 - child: Container(  
230 - height: 90,  
231 - width: 90,  
232 - child: Text('Loading...'),  
233 - ));  
234 - });  
235 - overlayState.insert(overlayEntryOpacity);  
236 - overlayState.insert(overlayEntryLoader);  
237 -  
238 - T data; 25 + double? elevation,
  26 + bool persistent = true,
  27 + ShapeBorder? shape,
  28 + Clip? clipBehavior,
  29 + Color? barrierColor,
  30 + bool? ignoreSafeArea,
  31 + bool isScrollControlled = false,
  32 + bool useRootNavigator = false,
  33 + bool isDismissible = true,
  34 + bool enableDrag = true,
  35 + RouteSettings? settings,
  36 + Duration? enterBottomSheetDuration,
  37 + Duration? exitBottomSheetDuration,
  38 + }) {
  39 + return Navigator.of(overlayContext!, rootNavigator: useRootNavigator)
  40 + .push(GetModalBottomSheetRoute<T>(
  41 + builder: (_) => bottomsheet,
  42 + isPersistent: persistent,
  43 + // theme: Theme.of(key.currentContext, shadowThemeOnly: true),
  44 + theme: Theme.of(key.currentContext!),
  45 + isScrollControlled: isScrollControlled,
239 46
240 - try {  
241 - data = await asyncFunction();  
242 - } on Exception catch (_) {  
243 - overlayEntryLoader.remove();  
244 - overlayEntryOpacity.remove();  
245 - rethrow;  
246 - } 47 + barrierLabel: MaterialLocalizations.of(key.currentContext!)
  48 + .modalBarrierDismissLabel,
247 49
248 - overlayEntryLoader.remove();  
249 - overlayEntryOpacity.remove();  
250 - return data; 50 + backgroundColor: backgroundColor ?? Colors.transparent,
  51 + elevation: elevation,
  52 + shape: shape,
  53 + removeTop: ignoreSafeArea ?? true,
  54 + clipBehavior: clipBehavior,
  55 + isDismissible: isDismissible,
  56 + modalBarrierColor: barrierColor,
  57 + settings: settings,
  58 + enableDrag: enableDrag,
  59 + enterBottomSheetDuration:
  60 + enterBottomSheetDuration ?? const Duration(milliseconds: 250),
  61 + exitBottomSheetDuration:
  62 + exitBottomSheetDuration ?? const Duration(milliseconds: 200),
  63 + ));
251 } 64 }
252 } 65 }
253 66
@@ -378,7 +191,7 @@ extension ExtensionDialog on GetInterface { @@ -378,7 +191,7 @@ extension ExtensionDialog on GetInterface {
378 padding: EdgeInsets.symmetric(horizontal: 10, vertical: 8), 191 padding: EdgeInsets.symmetric(horizontal: 10, vertical: 8),
379 shape: RoundedRectangleBorder( 192 shape: RoundedRectangleBorder(
380 side: BorderSide( 193 side: BorderSide(
381 - color: buttonColor ?? theme.accentColor, 194 + color: buttonColor ?? theme.colorScheme.secondary,
382 width: 2, 195 width: 2,
383 style: BorderStyle.solid), 196 style: BorderStyle.solid),
384 borderRadius: BorderRadius.circular(100)), 197 borderRadius: BorderRadius.circular(100)),
@@ -389,7 +202,8 @@ extension ExtensionDialog on GetInterface { @@ -389,7 +202,8 @@ extension ExtensionDialog on GetInterface {
389 }, 202 },
390 child: Text( 203 child: Text(
391 textCancel ?? "Cancel", 204 textCancel ?? "Cancel",
392 - style: TextStyle(color: cancelTextColor ?? theme.accentColor), 205 + style: TextStyle(
  206 + color: cancelTextColor ?? theme.colorScheme.secondary),
393 ), 207 ),
394 )); 208 ));
395 } 209 }
@@ -401,7 +215,7 @@ extension ExtensionDialog on GetInterface { @@ -401,7 +215,7 @@ extension ExtensionDialog on GetInterface {
401 actions.add(TextButton( 215 actions.add(TextButton(
402 style: TextButton.styleFrom( 216 style: TextButton.styleFrom(
403 tapTargetSize: MaterialTapTargetSize.shrinkWrap, 217 tapTargetSize: MaterialTapTargetSize.shrinkWrap,
404 - backgroundColor: buttonColor ?? theme.accentColor, 218 + backgroundColor: buttonColor ?? theme.colorScheme.secondary,
405 shape: RoundedRectangleBorder( 219 shape: RoundedRectangleBorder(
406 borderRadius: BorderRadius.circular(100)), 220 borderRadius: BorderRadius.circular(100)),
407 ), 221 ),
@@ -416,94 +230,249 @@ extension ExtensionDialog on GetInterface { @@ -416,94 +230,249 @@ extension ExtensionDialog on GetInterface {
416 } 230 }
417 } 231 }
418 232
419 - Widget baseAlertDialog = AlertDialog(  
420 - titlePadding: titlePadding ?? EdgeInsets.all(8),  
421 - contentPadding: contentPadding ?? EdgeInsets.all(8), 233 + Widget baseAlertDialog = AlertDialog(
  234 + titlePadding: titlePadding ?? EdgeInsets.all(8),
  235 + contentPadding: contentPadding ?? EdgeInsets.all(8),
  236 +
  237 + backgroundColor: backgroundColor ?? theme.dialogBackgroundColor,
  238 + shape: RoundedRectangleBorder(
  239 + borderRadius: BorderRadius.all(Radius.circular(radius))),
  240 + title: Text(title, textAlign: TextAlign.center, style: titleStyle),
  241 + content: Column(
  242 + crossAxisAlignment: CrossAxisAlignment.center,
  243 + mainAxisSize: MainAxisSize.min,
  244 + children: [
  245 + content ??
  246 + Text(middleText,
  247 + textAlign: TextAlign.center, style: middleTextStyle),
  248 + SizedBox(height: 16),
  249 + ButtonTheme(
  250 + minWidth: 78.0,
  251 + height: 34.0,
  252 + child: Wrap(
  253 + alignment: WrapAlignment.center,
  254 + spacing: 8,
  255 + runSpacing: 8,
  256 + children: actions,
  257 + ),
  258 + )
  259 + ],
  260 + ),
  261 + // actions: actions, // ?? <Widget>[cancelButton, confirmButton],
  262 + buttonPadding: EdgeInsets.zero,
  263 + );
  264 +
  265 + return dialog<T>(
  266 + onWillPop != null
  267 + ? WillPopScope(
  268 + onWillPop: onWillPop,
  269 + child: baseAlertDialog,
  270 + )
  271 + : baseAlertDialog,
  272 + barrierDismissible: barrierDismissible,
  273 + navigatorKey: navigatorKey,
  274 + );
  275 + }
  276 +}
  277 +
  278 +extension ExtensionSnackbar on GetInterface {
  279 + SnackbarController rawSnackbar({
  280 + String? title,
  281 + String? message,
  282 + Widget? titleText,
  283 + Widget? messageText,
  284 + Widget? icon,
  285 + bool instantInit = true,
  286 + bool shouldIconPulse = true,
  287 + double? maxWidth,
  288 + EdgeInsets margin = const EdgeInsets.all(0.0),
  289 + EdgeInsets padding = const EdgeInsets.all(16),
  290 + double borderRadius = 0.0,
  291 + Color? borderColor,
  292 + double borderWidth = 1.0,
  293 + Color backgroundColor = const Color(0xFF303030),
  294 + Color? leftBarIndicatorColor,
  295 + List<BoxShadow>? boxShadows,
  296 + Gradient? backgroundGradient,
  297 + Widget? mainButton,
  298 + OnTap? onTap,
  299 + Duration? duration = const Duration(seconds: 3),
  300 + bool isDismissible = true,
  301 + DismissDirection? dismissDirection,
  302 + bool showProgressIndicator = false,
  303 + AnimationController? progressIndicatorController,
  304 + Color? progressIndicatorBackgroundColor,
  305 + Animation<Color>? progressIndicatorValueColor,
  306 + SnackPosition snackPosition = SnackPosition.BOTTOM,
  307 + SnackStyle snackStyle = SnackStyle.FLOATING,
  308 + Curve forwardAnimationCurve = Curves.easeOutCirc,
  309 + Curve reverseAnimationCurve = Curves.easeOutCirc,
  310 + Duration animationDuration = const Duration(seconds: 1),
  311 + SnackbarStatusCallback? snackbarStatus,
  312 + double barBlur = 0.0,
  313 + double overlayBlur = 0.0,
  314 + Color? overlayColor,
  315 + Form? userInputForm,
  316 + }) {
  317 + final getSnackBar = GetSnackBar(
  318 + snackbarStatus: snackbarStatus,
  319 + title: title,
  320 + message: message,
  321 + titleText: titleText,
  322 + messageText: messageText,
  323 + snackPosition: snackPosition,
  324 + borderRadius: borderRadius,
  325 + margin: margin,
  326 + duration: duration,
  327 + barBlur: barBlur,
  328 + backgroundColor: backgroundColor,
  329 + icon: icon,
  330 + shouldIconPulse: shouldIconPulse,
  331 + maxWidth: maxWidth,
  332 + padding: padding,
  333 + borderColor: borderColor,
  334 + borderWidth: borderWidth,
  335 + leftBarIndicatorColor: leftBarIndicatorColor,
  336 + boxShadows: boxShadows,
  337 + backgroundGradient: backgroundGradient,
  338 + mainButton: mainButton,
  339 + onTap: onTap,
  340 + isDismissible: isDismissible,
  341 + dismissDirection: dismissDirection,
  342 + showProgressIndicator: showProgressIndicator,
  343 + progressIndicatorController: progressIndicatorController,
  344 + progressIndicatorBackgroundColor: progressIndicatorBackgroundColor,
  345 + progressIndicatorValueColor: progressIndicatorValueColor,
  346 + snackStyle: snackStyle,
  347 + forwardAnimationCurve: forwardAnimationCurve,
  348 + reverseAnimationCurve: reverseAnimationCurve,
  349 + animationDuration: animationDuration,
  350 + overlayBlur: overlayBlur,
  351 + overlayColor: overlayColor,
  352 + userInputForm: userInputForm,
  353 + );
  354 +
  355 + final controller = SnackbarController(getSnackBar);
422 356
423 - backgroundColor: backgroundColor ?? theme.dialogBackgroundColor,  
424 - shape: RoundedRectangleBorder(  
425 - borderRadius: BorderRadius.all(Radius.circular(radius))),  
426 - title: Text(title, textAlign: TextAlign.center, style: titleStyle),  
427 - content: Column(  
428 - crossAxisAlignment: CrossAxisAlignment.center,  
429 - mainAxisSize: MainAxisSize.min,  
430 - children: [  
431 - content ??  
432 - Text(middleText,  
433 - textAlign: TextAlign.center, style: middleTextStyle),  
434 - SizedBox(height: 16),  
435 - ButtonTheme(  
436 - minWidth: 78.0,  
437 - height: 34.0,  
438 - child: Wrap(  
439 - alignment: WrapAlignment.center,  
440 - spacing: 8,  
441 - runSpacing: 8,  
442 - children: actions,  
443 - ),  
444 - )  
445 - ],  
446 - ),  
447 - // actions: actions, // ?? <Widget>[cancelButton, confirmButton],  
448 - buttonPadding: EdgeInsets.zero,  
449 - ); 357 + if (instantInit) {
  358 + controller.show();
  359 + } else {
  360 + SchedulerBinding.instance!.addPostFrameCallback((_) {
  361 + controller.show();
  362 + });
  363 + }
  364 + return controller;
  365 + }
450 366
451 - return dialog<T>(  
452 - onWillPop != null  
453 - ? WillPopScope(  
454 - onWillPop: onWillPop,  
455 - child: baseAlertDialog,  
456 - )  
457 - : baseAlertDialog,  
458 - barrierDismissible: barrierDismissible,  
459 - navigatorKey: navigatorKey,  
460 - ); 367 + SnackbarController showSnackbar(GetSnackBar snackbar) {
  368 + final controller = SnackbarController(snackbar);
  369 + controller.show();
  370 + return controller;
461 } 371 }
462 -}  
463 372
464 -extension ExtensionBottomSheet on GetInterface {  
465 - Future<T?> bottomSheet<T>(  
466 - Widget bottomsheet, { 373 + SnackbarController snackbar(
  374 + String title,
  375 + String message, {
  376 + Color? colorText,
  377 + Duration? duration = const Duration(seconds: 3),
  378 +
  379 + /// with instantInit = false you can put snackbar on initState
  380 + bool instantInit = true,
  381 + SnackPosition? snackPosition,
  382 + Widget? titleText,
  383 + Widget? messageText,
  384 + Widget? icon,
  385 + bool? shouldIconPulse,
  386 + double? maxWidth,
  387 + EdgeInsets? margin,
  388 + EdgeInsets? padding,
  389 + double? borderRadius,
  390 + Color? borderColor,
  391 + double? borderWidth,
467 Color? backgroundColor, 392 Color? backgroundColor,
468 - double? elevation,  
469 - bool persistent = true,  
470 - ShapeBorder? shape,  
471 - Clip? clipBehavior,  
472 - Color? barrierColor,  
473 - bool? ignoreSafeArea,  
474 - bool isScrollControlled = false,  
475 - bool useRootNavigator = false,  
476 - bool isDismissible = true,  
477 - bool enableDrag = true,  
478 - RouteSettings? settings,  
479 - Duration? enterBottomSheetDuration,  
480 - Duration? exitBottomSheetDuration, 393 + Color? leftBarIndicatorColor,
  394 + List<BoxShadow>? boxShadows,
  395 + Gradient? backgroundGradient,
  396 + TextButton? mainButton,
  397 + OnTap? onTap,
  398 + bool? isDismissible,
  399 + bool? showProgressIndicator,
  400 + DismissDirection? dismissDirection,
  401 + AnimationController? progressIndicatorController,
  402 + Color? progressIndicatorBackgroundColor,
  403 + Animation<Color>? progressIndicatorValueColor,
  404 + SnackStyle? snackStyle,
  405 + Curve? forwardAnimationCurve,
  406 + Curve? reverseAnimationCurve,
  407 + Duration? animationDuration,
  408 + double? barBlur,
  409 + double? overlayBlur,
  410 + SnackbarStatusCallback? snackbarStatus,
  411 + Color? overlayColor,
  412 + Form? userInputForm,
481 }) { 413 }) {
482 - return Navigator.of(overlayContext!, rootNavigator: useRootNavigator)  
483 - .push(GetModalBottomSheetRoute<T>(  
484 - builder: (_) => bottomsheet,  
485 - isPersistent: persistent,  
486 - // theme: Theme.of(key.currentContext, shadowThemeOnly: true),  
487 - theme: Theme.of(key.currentContext!),  
488 - isScrollControlled: isScrollControlled, 414 + final getSnackBar = GetSnackBar(
  415 + snackbarStatus: snackbarStatus,
  416 + titleText: titleText ??
  417 + Text(
  418 + title,
  419 + style: TextStyle(
  420 + color: colorText ?? iconColor ?? Colors.black,
  421 + fontWeight: FontWeight.w800,
  422 + fontSize: 16,
  423 + ),
  424 + ),
  425 + messageText: messageText ??
  426 + Text(
  427 + message,
  428 + style: TextStyle(
  429 + color: colorText ?? iconColor ?? Colors.black,
  430 + fontWeight: FontWeight.w300,
  431 + fontSize: 14,
  432 + ),
  433 + ),
  434 + snackPosition: snackPosition ?? SnackPosition.TOP,
  435 + borderRadius: borderRadius ?? 15,
  436 + margin: margin ?? EdgeInsets.symmetric(horizontal: 10),
  437 + duration: duration,
  438 + barBlur: barBlur ?? 7.0,
  439 + backgroundColor: backgroundColor ?? Colors.grey.withOpacity(0.2),
  440 + icon: icon,
  441 + shouldIconPulse: shouldIconPulse ?? true,
  442 + maxWidth: maxWidth,
  443 + padding: padding ?? EdgeInsets.all(16),
  444 + borderColor: borderColor,
  445 + borderWidth: borderWidth,
  446 + leftBarIndicatorColor: leftBarIndicatorColor,
  447 + boxShadows: boxShadows,
  448 + backgroundGradient: backgroundGradient,
  449 + mainButton: mainButton,
  450 + onTap: onTap,
  451 + isDismissible: isDismissible ?? true,
  452 + dismissDirection: dismissDirection,
  453 + showProgressIndicator: showProgressIndicator ?? false,
  454 + progressIndicatorController: progressIndicatorController,
  455 + progressIndicatorBackgroundColor: progressIndicatorBackgroundColor,
  456 + progressIndicatorValueColor: progressIndicatorValueColor,
  457 + snackStyle: snackStyle ?? SnackStyle.FLOATING,
  458 + forwardAnimationCurve: forwardAnimationCurve ?? Curves.easeOutCirc,
  459 + reverseAnimationCurve: reverseAnimationCurve ?? Curves.easeOutCirc,
  460 + animationDuration: animationDuration ?? Duration(seconds: 1),
  461 + overlayBlur: overlayBlur ?? 0.0,
  462 + overlayColor: overlayColor ?? Colors.transparent,
  463 + userInputForm: userInputForm);
489 464
490 - barrierLabel: MaterialLocalizations.of(key.currentContext!)  
491 - .modalBarrierDismissLabel, 465 + final controller = SnackbarController(getSnackBar);
492 466
493 - backgroundColor: backgroundColor ?? Colors.transparent,  
494 - elevation: elevation,  
495 - shape: shape,  
496 - removeTop: ignoreSafeArea ?? true,  
497 - clipBehavior: clipBehavior,  
498 - isDismissible: isDismissible,  
499 - modalBarrierColor: barrierColor,  
500 - settings: settings,  
501 - enableDrag: enableDrag,  
502 - enterBottomSheetDuration:  
503 - enterBottomSheetDuration ?? const Duration(milliseconds: 250),  
504 - exitBottomSheetDuration:  
505 - exitBottomSheetDuration ?? const Duration(milliseconds: 200),  
506 - )); 467 + if (instantInit) {
  468 + controller.show();
  469 + } else {
  470 + //routing.isSnackbar = true;
  471 + SchedulerBinding.instance!.addPostFrameCallback((_) {
  472 + controller.show();
  473 + });
  474 + }
  475 + return controller;
507 } 476 }
508 } 477 }
509 478
@@ -826,11 +795,11 @@ you can only use widgets and widget functions here'''; @@ -826,11 +795,11 @@ you can only use widgets and widget functions here''';
826 795
827 /// Returns true if a Snackbar, Dialog or BottomSheet is currently OPEN 796 /// Returns true if a Snackbar, Dialog or BottomSheet is currently OPEN
828 bool get isOverlaysOpen => 797 bool get isOverlaysOpen =>
829 - (isSnackbarOpen! || isDialogOpen! || isBottomSheetOpen!); 798 + (isSnackbarOpen || isDialogOpen! || isBottomSheetOpen!);
830 799
831 /// Returns true if there is no Snackbar, Dialog or BottomSheet open 800 /// Returns true if there is no Snackbar, Dialog or BottomSheet open
832 bool get isOverlaysClosed => 801 bool get isOverlaysClosed =>
833 - (!isSnackbarOpen! && !isDialogOpen! && !isBottomSheetOpen!); 802 + (!isSnackbarOpen && !isDialogOpen! && !isBottomSheetOpen!);
834 803
835 /// **Navigation.popUntil()** shortcut.<br><br> 804 /// **Navigation.popUntil()** shortcut.<br><br>
836 /// 805 ///
@@ -850,9 +819,21 @@ you can only use widgets and widget functions here'''; @@ -850,9 +819,21 @@ you can only use widgets and widget functions here''';
850 bool canPop = true, 819 bool canPop = true,
851 int? id, 820 int? id,
852 }) { 821 }) {
  822 + //TODO: This code brings compatibility of the new snackbar with GetX 4,
  823 + // remove this code in version 5
  824 + if (isSnackbarOpen && !closeOverlays) {
  825 + closeCurrentSnackbar();
  826 + return;
  827 + }
  828 +
853 if (closeOverlays && isOverlaysOpen) { 829 if (closeOverlays && isOverlaysOpen) {
  830 + //TODO: This code brings compatibility of the new snackbar with GetX 4,
  831 + // remove this code in version 5
  832 + if (isSnackbarOpen) {
  833 + closeAllSnackbars();
  834 + }
854 navigator?.popUntil((route) { 835 navigator?.popUntil((route) {
855 - return (isOverlaysClosed); 836 + return (!isDialogOpen! && !isBottomSheetOpen!);
856 }); 837 });
857 } 838 }
858 if (canPop) { 839 if (canPop) {
@@ -1135,7 +1116,16 @@ you can only use widgets and widget functions here'''; @@ -1135,7 +1116,16 @@ you can only use widgets and widget functions here''';
1135 String get previousRoute => routing.previous; 1116 String get previousRoute => routing.previous;
1136 1117
1137 /// check if snackbar is open 1118 /// check if snackbar is open
1138 - bool? get isSnackbarOpen => routing.isSnackbar; 1119 + bool get isSnackbarOpen =>
  1120 + SnackbarController.isSnackbarBeingShown; //routing.isSnackbar;
  1121 +
  1122 + void closeAllSnackbars() {
  1123 + SnackbarController.cancelAllSnackbars();
  1124 + }
  1125 +
  1126 + Future<void> closeCurrentSnackbar() async {
  1127 + await SnackbarController.closeCurrentSnackbar();
  1128 + }
1139 1129
1140 /// check if dialog is open 1130 /// check if dialog is open
1141 bool? get isDialogOpen => routing.isDialog; 1131 bool? get isDialogOpen => routing.isDialog;
@@ -1341,7 +1331,48 @@ extension NavTwoExt on GetInterface { @@ -1341,7 +1331,48 @@ extension NavTwoExt on GetInterface {
1341 } 1331 }
1342 } 1332 }
1343 1333
1344 -/// It replaces the Flutter Navigator, but needs no context.  
1345 -/// You can to use navigator.push(YourRoute()) rather  
1346 -/// Navigator.push(context, YourRoute());  
1347 -NavigatorState? get navigator => GetNavigation(Get).key.currentState; 1334 +extension OverlayExt on GetInterface {
  1335 + Future<T> showOverlay<T>({
  1336 + required Future<T> Function() asyncFunction,
  1337 + Color opacityColor = Colors.black,
  1338 + Widget? loadingWidget,
  1339 + double opacity = .5,
  1340 + }) async {
  1341 + final navigatorState =
  1342 + Navigator.of(Get.overlayContext!, rootNavigator: false);
  1343 + final overlayState = navigatorState.overlay!;
  1344 +
  1345 + final overlayEntryOpacity = OverlayEntry(builder: (context) {
  1346 + return Opacity(
  1347 + opacity: opacity,
  1348 + child: Container(
  1349 + color: opacityColor,
  1350 + ));
  1351 + });
  1352 + final overlayEntryLoader = OverlayEntry(builder: (context) {
  1353 + return loadingWidget ??
  1354 + Center(
  1355 + child: Container(
  1356 + height: 90,
  1357 + width: 90,
  1358 + child: Text('Loading...'),
  1359 + ));
  1360 + });
  1361 + overlayState.insert(overlayEntryOpacity);
  1362 + overlayState.insert(overlayEntryLoader);
  1363 +
  1364 + T data;
  1365 +
  1366 + try {
  1367 + data = await asyncFunction();
  1368 + } on Exception catch (_) {
  1369 + overlayEntryLoader.remove();
  1370 + overlayEntryOpacity.remove();
  1371 + rethrow;
  1372 + }
  1373 +
  1374 + overlayEntryLoader.remove();
  1375 + overlayEntryOpacity.remove();
  1376 + return data;
  1377 + }
  1378 +}
1 import 'package:flutter/foundation.dart'; 1 import 'package:flutter/foundation.dart';
2 import 'package:flutter/widgets.dart'; 2 import 'package:flutter/widgets.dart';
  3 +
3 import '../../../get.dart'; 4 import '../../../get.dart';
4 5
5 class GetInformationParser extends RouteInformationParser<GetNavConfig> { 6 class GetInformationParser extends RouteInformationParser<GetNavConfig> {
@@ -14,7 +15,6 @@ class GetInformationParser extends RouteInformationParser<GetNavConfig> { @@ -14,7 +15,6 @@ class GetInformationParser extends RouteInformationParser<GetNavConfig> {
14 SynchronousFuture<GetNavConfig> parseRouteInformation( 15 SynchronousFuture<GetNavConfig> parseRouteInformation(
15 RouteInformation routeInformation, 16 RouteInformation routeInformation,
16 ) { 17 ) {
17 - Get.log('GetInformationParser: route location: ${routeInformation.location}');  
18 var location = routeInformation.location; 18 var location = routeInformation.location;
19 if (location == '/') { 19 if (location == '/') {
20 //check if there is a corresponding page 20 //check if there is a corresponding page
@@ -24,6 +24,8 @@ class GetInformationParser extends RouteInformationParser<GetNavConfig> { @@ -24,6 +24,8 @@ class GetInformationParser extends RouteInformationParser<GetNavConfig> {
24 } 24 }
25 } 25 }
26 26
  27 + Get.log('GetInformationParser: route location: $location');
  28 +
27 final matchResult = Get.routeTree.matchRoute(location ?? initialRoute); 29 final matchResult = Get.routeTree.matchRoute(location ?? initialRoute);
28 30
29 return SynchronousFuture( 31 return SynchronousFuture(
@@ -2,50 +2,10 @@ import 'dart:async'; @@ -2,50 +2,10 @@ import 'dart:async';
2 2
3 import 'package:flutter/foundation.dart'; 3 import 'package:flutter/foundation.dart';
4 import 'package:flutter/material.dart'; 4 import 'package:flutter/material.dart';
  5 +
5 import '../../../get.dart'; 6 import '../../../get.dart';
6 import '../../../get_state_manager/src/simple/list_notifier.dart'; 7 import '../../../get_state_manager/src/simple/list_notifier.dart';
7 8
8 -/// Enables the user to customize the intended pop behavior  
9 -///  
10 -/// Goes to either the previous history entry or the previous page entry  
11 -///  
12 -/// e.g. if the user navigates to these pages  
13 -/// 1) /home  
14 -/// 2) /home/products/1234  
15 -///  
16 -/// when popping on [History] mode, it will emulate a browser back button.  
17 -///  
18 -/// so the new history stack will be:  
19 -/// 1) /home  
20 -///  
21 -/// when popping on [Page] mode, it will only remove the last part of the route  
22 -/// so the new history stack will be:  
23 -/// 1) /home  
24 -/// 2) /home/products  
25 -///  
26 -/// another pop will change the history stack to:  
27 -/// 1) /home  
28 -enum PopMode {  
29 - History,  
30 - Page,  
31 -}  
32 -  
33 -/// Enables the user to customize the behavior when pushing multiple routes that  
34 -/// shouldn't be duplicates  
35 -enum PreventDuplicateHandlingMode {  
36 - /// Removes the history entries until it reaches the old route  
37 - PopUntilOriginalRoute,  
38 -  
39 - /// Simply don't push the new route  
40 - DoNothing,  
41 -  
42 - /// Recommended - Moves the old route entry to the front  
43 - ///  
44 - /// With this mode, you guarantee there will be only one  
45 - /// route entry for each location  
46 - ReorderRoutes  
47 -}  
48 -  
49 class GetDelegate extends RouterDelegate<GetNavConfig> 9 class GetDelegate extends RouterDelegate<GetNavConfig>
50 with ListenableMixin, ListNotifierMixin { 10 with ListenableMixin, ListNotifierMixin {
51 final List<GetNavConfig> history = <GetNavConfig>[]; 11 final List<GetNavConfig> history = <GetNavConfig>[];
@@ -57,7 +17,7 @@ class GetDelegate extends RouterDelegate<GetNavConfig> @@ -57,7 +17,7 @@ class GetDelegate extends RouterDelegate<GetNavConfig>
57 final List<NavigatorObserver>? navigatorObservers; 17 final List<NavigatorObserver>? navigatorObservers;
58 final TransitionDelegate<dynamic>? transitionDelegate; 18 final TransitionDelegate<dynamic>? transitionDelegate;
59 19
60 - GlobalKey<NavigatorState> get navigatorKey => Get.key; 20 + final _allCompleters = <GetPage, Completer>{};
61 21
62 GetDelegate({ 22 GetDelegate({
63 GetPage? notFoundRoute, 23 GetPage? notFoundRoute,
@@ -76,191 +36,72 @@ class GetDelegate extends RouterDelegate<GetNavConfig> @@ -76,191 +36,72 @@ class GetDelegate extends RouterDelegate<GetNavConfig>
76 Get.log('GetDelegate is created !'); 36 Get.log('GetDelegate is created !');
77 } 37 }
78 38
79 - Future<GetNavConfig?> runMiddleware(GetNavConfig config) async {  
80 - final middlewares = config.currentTreeBranch.last.middlewares;  
81 - if (middlewares == null) {  
82 - return config;  
83 - }  
84 - var iterator = config;  
85 - for (var item in middlewares) {  
86 - var redirectRes = await item.redirectDelegate(iterator);  
87 - if (redirectRes == null) return null;  
88 - iterator = redirectRes;  
89 - }  
90 - return iterator;  
91 - }  
92 -  
93 - Future<void> _unsafeHistoryAdd(GetNavConfig config) async {  
94 - final res = await runMiddleware(config);  
95 - if (res == null) return;  
96 - history.add(res); 39 + @override
  40 + GetNavConfig? get currentConfiguration {
  41 + if (history.isEmpty) return null;
  42 + final route = history.last;
  43 + return route;
97 } 44 }
98 45
99 - Future<void> _unsafeHistoryRemove(GetNavConfig config) async {  
100 - var index = history.indexOf(config);  
101 - if (index >= 0) await _unsafeHistoryRemoveAt(index);  
102 - } 46 + GlobalKey<NavigatorState> get navigatorKey => Get.key;
103 47
104 - Future<GetNavConfig?> _unsafeHistoryRemoveAt(int index) async {  
105 - if (index == history.length - 1 && history.length > 1) {  
106 - //removing WILL update the current route  
107 - final toCheck = history[history.length - 2];  
108 - final resMiddleware = await runMiddleware(toCheck);  
109 - if (resMiddleware == null) return null;  
110 - history[history.length - 2] = resMiddleware;  
111 - }  
112 - return history.removeAt(index); 48 + Map<String, String> get parameters {
  49 + return currentConfiguration?.currentPage?.parameters ?? {};
113 } 50 }
114 51
115 T arguments<T>() { 52 T arguments<T>() {
116 return currentConfiguration?.currentPage?.arguments as T; 53 return currentConfiguration?.currentPage?.arguments as T;
117 } 54 }
118 55
119 - Map<String, String> get parameters {  
120 - return currentConfiguration?.currentPage?.parameters ?? {};  
121 - }  
122 -  
123 - // void _unsafeHistoryClear() {  
124 - // history.clear();  
125 - // }  
126 -  
127 - /// Adds a new history entry and waits for the result  
128 - Future<void> pushHistory(  
129 - GetNavConfig config, {  
130 - bool rebuildStack = true, 56 + /// Removes routes according to [PopMode]
  57 + /// until it reaches the specifc [fullRoute],
  58 + /// DOES NOT remove the [fullRoute]
  59 + Future<void> backUntil(
  60 + String fullRoute, {
  61 + PopMode popMode = PopMode.Page,
131 }) async { 62 }) async {
132 - //this changes the currentConfiguration  
133 - await _pushHistory(config);  
134 - if (rebuildStack) {  
135 - refresh(); 63 + // remove history or page entries until you meet route
  64 + var iterator = currentConfiguration;
  65 + while (_canPop(popMode) &&
  66 + iterator != null &&
  67 + iterator.location != fullRoute) {
  68 + await _pop(popMode);
  69 + // replace iterator
  70 + iterator = currentConfiguration;
136 } 71 }
  72 + refresh();
137 } 73 }
138 74
139 - Future<void> _removeHistoryEntry(GetNavConfig entry) async {  
140 - await _unsafeHistoryRemove(entry);  
141 - }  
142 -  
143 - Future<void> _pushHistory(GetNavConfig config) async {  
144 - if (config.currentPage!.preventDuplicates) {  
145 - final originalEntryIndex =  
146 - history.indexWhere((element) => element.location == config.location);  
147 - if (originalEntryIndex >= 0) {  
148 - switch (preventDuplicateHandlingMode) {  
149 - case PreventDuplicateHandlingMode.PopUntilOriginalRoute:  
150 - await backUntil(config.location!, popMode: PopMode.Page);  
151 - break;  
152 - case PreventDuplicateHandlingMode.ReorderRoutes:  
153 - await _unsafeHistoryRemoveAt(originalEntryIndex);  
154 - await _unsafeHistoryAdd(config);  
155 - break;  
156 - case PreventDuplicateHandlingMode.DoNothing:  
157 - default:  
158 - break;  
159 - }  
160 - return;  
161 - }  
162 - }  
163 - await _unsafeHistoryAdd(config); 75 + @override
  76 + Widget build(BuildContext context) {
  77 + final pages = getVisualPages();
  78 + if (pages.length == 0) return SizedBox.shrink();
  79 + final extraObservers = navigatorObservers;
  80 + return GetNavigator(
  81 + key: navigatorKey,
  82 + onPopPage: _onPopVisualRoute,
  83 + pages: pages,
  84 + observers: [
  85 + GetObserver(),
  86 + if (extraObservers != null) ...extraObservers,
  87 + ],
  88 + transitionDelegate:
  89 + transitionDelegate ?? const DefaultTransitionDelegate<dynamic>(),
  90 + );
164 } 91 }
165 92
166 - // GetPageRoute getPageRoute(RouteSettings? settings) {  
167 - // return PageRedirect(settings ?? RouteSettings(name: '/404'), _notFound())  
168 - // .page(); 93 + // void _unsafeHistoryClear() {
  94 + // history.clear();
169 // } 95 // }
170 96
171 - Future<GetNavConfig?> _popHistory() async {  
172 - if (!_canPopHistory()) return null;  
173 - return await _doPopHistory();  
174 - }  
175 -  
176 - Future<GetNavConfig?> _doPopHistory() async {  
177 - return await _unsafeHistoryRemoveAt(history.length - 1);  
178 - }  
179 -  
180 - Future<GetNavConfig?> _popPage() async {  
181 - if (!_canPopPage()) return null;  
182 - return await _doPopPage();  
183 - }  
184 -  
185 - Future<GetNavConfig?> _pop(PopMode mode) async {  
186 - switch (mode) {  
187 - case PopMode.History:  
188 - return await _popHistory();  
189 - case PopMode.Page:  
190 - return await _popPage();  
191 - default:  
192 - return null;  
193 - }  
194 - }  
195 -  
196 - // returns the popped page  
197 - Future<GetNavConfig?> _doPopPage() async {  
198 - final currentBranch = currentConfiguration?.currentTreeBranch;  
199 - if (currentBranch != null && currentBranch.length > 1) {  
200 - //remove last part only  
201 - final remaining = currentBranch.take(currentBranch.length - 1);  
202 - final prevHistoryEntry =  
203 - history.length > 1 ? history[history.length - 2] : null;  
204 -  
205 - //check if current route is the same as the previous route  
206 - if (prevHistoryEntry != null) {  
207 - //if so, pop the entire history entry  
208 - final newLocation = remaining.last.name;  
209 - final prevLocation = prevHistoryEntry.location;  
210 - if (newLocation == prevLocation) {  
211 - //pop the entire history entry  
212 - return await _popHistory();  
213 - }  
214 - }  
215 -  
216 - //create a new route with the remaining tree branch  
217 - final res = await _popHistory();  
218 - await _pushHistory(  
219 - GetNavConfig(  
220 - currentTreeBranch: remaining.toList(),  
221 - location: remaining.last.name,  
222 - state: null, //TOOD: persist state??  
223 - ),  
224 - );  
225 - return res;  
226 - } else {  
227 - //remove entire entry  
228 - return await _popHistory();  
229 - }  
230 - }  
231 -  
232 - Future<GetNavConfig?> popHistory() async {  
233 - return await _popHistory();  
234 - }  
235 -  
236 - bool _canPopHistory() {  
237 - return history.length > 1;  
238 - }  
239 -  
240 Future<bool> canPopHistory() { 97 Future<bool> canPopHistory() {
241 return SynchronousFuture(_canPopHistory()); 98 return SynchronousFuture(_canPopHistory());
242 } 99 }
243 100
244 - bool _canPopPage() {  
245 - final currentTreeBranch = currentConfiguration?.currentTreeBranch;  
246 - if (currentTreeBranch == null) return false;  
247 - return currentTreeBranch.length > 1 ? true : _canPopHistory();  
248 - }  
249 -  
250 Future<bool> canPopPage() { 101 Future<bool> canPopPage() {
251 return SynchronousFuture(_canPopPage()); 102 return SynchronousFuture(_canPopPage());
252 } 103 }
253 104
254 - bool _canPop(PopMode mode) {  
255 - switch (mode) {  
256 - case PopMode.History:  
257 - return _canPopHistory();  
258 - case PopMode.Page:  
259 - default:  
260 - return _canPopPage();  
261 - }  
262 - }  
263 -  
264 /// gets the visual pages from the current history entry 105 /// gets the visual pages from the current history entry
265 /// 106 ///
266 /// visual pages must have [participatesInRootNavigator] set to true 107 /// visual pages must have [participatesInRootNavigator] set to true
@@ -281,44 +122,104 @@ class GetDelegate extends RouterDelegate<GetNavConfig> @@ -281,44 +122,104 @@ class GetDelegate extends RouterDelegate<GetNavConfig>
281 } 122 }
282 } 123 }
283 124
  125 + // GetPageRoute getPageRoute(RouteSettings? settings) {
  126 + // return PageRedirect(settings ?? RouteSettings(name: '/404'), _notFound())
  127 + // .page();
  128 + // }
  129 +
  130 + Future<bool> handlePopupRoutes({
  131 + Object? result,
  132 + }) async {
  133 + Route? currentRoute;
  134 + navigatorKey.currentState!.popUntil((route) {
  135 + currentRoute = route;
  136 + return true;
  137 + });
  138 + if (currentRoute is PopupRoute) {
  139 + return await navigatorKey.currentState!.maybePop(result);
  140 + }
  141 + return false;
  142 + }
  143 +
  144 + Future<T?>? offAndToNamed<T>(
  145 + String page, {
  146 + dynamic arguments,
  147 + int? id,
  148 + dynamic result,
  149 + Map<String, String>? parameters,
  150 + PopMode popMode = PopMode.History,
  151 + }) async {
  152 + if (parameters != null) {
  153 + final uri = Uri(path: page, queryParameters: parameters);
  154 + page = uri.toString();
  155 + }
  156 +
  157 + await popRoute(result: result);
  158 + return toNamed(page, arguments: arguments, parameters: parameters);
  159 + }
  160 +
  161 + Future<T> offNamed<T>(
  162 + String page, {
  163 + dynamic arguments,
  164 + Map<String, String>? parameters,
  165 + }) async {
  166 + history.removeLast();
  167 + return toNamed<T>(page, arguments: arguments, parameters: parameters);
  168 + }
  169 +
  170 + Future<GetNavConfig?> popHistory() async {
  171 + return await _popHistory();
  172 + }
  173 +
  174 + // returns the popped page
284 @override 175 @override
285 - Widget build(BuildContext context) {  
286 - final pages = getVisualPages();  
287 - if (pages.length == 0) return SizedBox.shrink();  
288 - final extraObservers = navigatorObservers;  
289 - return GetNavigator(  
290 - key: navigatorKey,  
291 - onPopPage: _onPopVisualRoute,  
292 - pages: pages,  
293 - observers: [  
294 - GetObserver(),  
295 - if (extraObservers != null) ...extraObservers,  
296 - ],  
297 - transitionDelegate:  
298 - transitionDelegate ?? const DefaultTransitionDelegate<dynamic>(),  
299 - ); 176 + Future<bool> popRoute({
  177 + Object? result,
  178 + PopMode popMode = PopMode.Page,
  179 + }) async {
  180 + //Returning false will cause the entire app to be popped.
  181 + final wasPopup = await handlePopupRoutes(result: result);
  182 + if (wasPopup) return true;
  183 + final _popped = await _pop(popMode);
  184 + refresh();
  185 + if (_popped != null) {
  186 + //emulate the old pop with result
  187 + return true;
  188 + }
  189 + return false;
  190 + }
  191 +
  192 + /// Adds a new history entry and waits for the result
  193 + Future<void> pushHistory(
  194 + GetNavConfig config, {
  195 + bool rebuildStack = true,
  196 + }) async {
  197 + //this changes the currentConfiguration
  198 + await _pushHistory(config);
  199 + if (rebuildStack) {
  200 + refresh();
  201 + }
  202 + }
  203 +
  204 + Future<GetNavConfig?> runMiddleware(GetNavConfig config) async {
  205 + final middlewares = config.currentTreeBranch.last.middlewares;
  206 + if (middlewares == null) {
  207 + return config;
  208 + }
  209 + var iterator = config;
  210 + for (var item in middlewares) {
  211 + var redirectRes = await item.redirectDelegate(iterator);
  212 + if (redirectRes == null) return null;
  213 + iterator = redirectRes;
  214 + }
  215 + return iterator;
300 } 216 }
301 217
302 - // @override  
303 - // Future<void> setInitialRoutePath(GetNavConfig configuration) async {  
304 - // //no need to clear history with Reorder route strategy  
305 - // // _unsafeHistoryClear();  
306 - // // _resultCompleter.clear();  
307 - // await pushHistory(configuration);  
308 - // }  
309 -  
310 @override 218 @override
311 Future<void> setNewRoutePath(GetNavConfig configuration) async { 219 Future<void> setNewRoutePath(GetNavConfig configuration) async {
312 await pushHistory(configuration); 220 await pushHistory(configuration);
313 } 221 }
314 222
315 - @override  
316 - GetNavConfig? get currentConfiguration {  
317 - if (history.isEmpty) return null;  
318 - final route = history.last;  
319 - return route;  
320 - }  
321 -  
322 Future<T> toNamed<T>( 223 Future<T> toNamed<T>(
323 String page, { 224 String page, {
324 dynamic arguments, 225 dynamic arguments,
@@ -352,84 +253,73 @@ class GetDelegate extends RouterDelegate<GetNavConfig> @@ -352,84 +253,73 @@ class GetDelegate extends RouterDelegate<GetNavConfig>
352 } 253 }
353 } 254 }
354 255
355 - Future<T?>? offAndToNamed<T>(  
356 - String page, {  
357 - dynamic arguments,  
358 - int? id,  
359 - dynamic result,  
360 - Map<String, String>? parameters,  
361 - PopMode popMode = PopMode.History,  
362 - }) async {  
363 - if (parameters != null) {  
364 - final uri = Uri(path: page, queryParameters: parameters);  
365 - page = uri.toString(); 256 + bool _canPop(PopMode mode) {
  257 + switch (mode) {
  258 + case PopMode.History:
  259 + return _canPopHistory();
  260 + case PopMode.Page:
  261 + default:
  262 + return _canPopPage();
366 } 263 }
367 -  
368 - await popRoute(result: result);  
369 - return toNamed(page, arguments: arguments, parameters: parameters);  
370 } 264 }
371 265
372 - Future<T> offNamed<T>(  
373 - String page, {  
374 - dynamic arguments,  
375 - Map<String, String>? parameters,  
376 - }) async {  
377 - history.removeLast();  
378 - return toNamed<T>(page, arguments: arguments, parameters: parameters); 266 + bool _canPopHistory() {
  267 + return history.length > 1;
379 } 268 }
380 269
381 - /// Removes routes according to [PopMode]  
382 - /// until it reaches the specifc [fullRoute],  
383 - /// DOES NOT remove the [fullRoute]  
384 - Future<void> backUntil(  
385 - String fullRoute, {  
386 - PopMode popMode = PopMode.Page,  
387 - }) async {  
388 - // remove history or page entries until you meet route  
389 - var iterator = currentConfiguration;  
390 - while (_canPop(popMode) &&  
391 - iterator != null &&  
392 - iterator.location != fullRoute) {  
393 - await _pop(popMode);  
394 - // replace iterator  
395 - iterator = currentConfiguration;  
396 - }  
397 - refresh(); 270 + bool _canPopPage() {
  271 + final currentTreeBranch = currentConfiguration?.currentTreeBranch;
  272 + if (currentTreeBranch == null) return false;
  273 + return currentTreeBranch.length > 1 ? true : _canPopHistory();
398 } 274 }
399 275
400 - Future<bool> handlePopupRoutes({  
401 - Object? result,  
402 - }) async {  
403 - Route? currentRoute;  
404 - navigatorKey.currentState!.popUntil((route) {  
405 - currentRoute = route;  
406 - return true;  
407 - });  
408 - if (currentRoute is PopupRoute) {  
409 - return await navigatorKey.currentState!.maybePop(result);  
410 - }  
411 - return false; 276 + Future<GetNavConfig?> _doPopHistory() async {
  277 + return await _unsafeHistoryRemoveAt(history.length - 1);
412 } 278 }
413 279
414 - @override  
415 - Future<bool> popRoute({  
416 - Object? result,  
417 - PopMode popMode = PopMode.Page,  
418 - }) async {  
419 - //Returning false will cause the entire app to be popped.  
420 - final wasPopup = await handlePopupRoutes(result: result);  
421 - if (wasPopup) return true;  
422 - final _popped = await _pop(popMode);  
423 - refresh();  
424 - if (_popped != null) {  
425 - //emulate the old pop with result  
426 - return true; 280 + // @override
  281 + // Future<void> setInitialRoutePath(GetNavConfig configuration) async {
  282 + // //no need to clear history with Reorder route strategy
  283 + // // _unsafeHistoryClear();
  284 + // // _resultCompleter.clear();
  285 + // await pushHistory(configuration);
  286 + // }
  287 +
  288 + Future<GetNavConfig?> _doPopPage() async {
  289 + final currentBranch = currentConfiguration?.currentTreeBranch;
  290 + if (currentBranch != null && currentBranch.length > 1) {
  291 + //remove last part only
  292 + final remaining = currentBranch.take(currentBranch.length - 1);
  293 + final prevHistoryEntry =
  294 + history.length > 1 ? history[history.length - 2] : null;
  295 +
  296 + //check if current route is the same as the previous route
  297 + if (prevHistoryEntry != null) {
  298 + //if so, pop the entire history entry
  299 + final newLocation = remaining.last.name;
  300 + final prevLocation = prevHistoryEntry.location;
  301 + if (newLocation == prevLocation) {
  302 + //pop the entire history entry
  303 + return await _popHistory();
  304 + }
  305 + }
  306 +
  307 + //create a new route with the remaining tree branch
  308 + final res = await _popHistory();
  309 + await _pushHistory(
  310 + GetNavConfig(
  311 + currentTreeBranch: remaining.toList(),
  312 + location: remaining.last.name,
  313 + state: null, //TOOD: persist state??
  314 + ),
  315 + );
  316 + return res;
  317 + } else {
  318 + //remove entire entry
  319 + return await _popHistory();
427 } 320 }
428 - return false;  
429 } 321 }
430 322
431 - final _allCompleters = <GetPage, Completer>{};  
432 -  
433 bool _onPopVisualRoute(Route<dynamic> route, dynamic result) { 323 bool _onPopVisualRoute(Route<dynamic> route, dynamic result) {
434 final didPop = route.didPop(result); 324 final didPop = route.didPop(result);
435 if (!didPop) { 325 if (!didPop) {
@@ -452,21 +342,89 @@ class GetDelegate extends RouterDelegate<GetNavConfig> @@ -452,21 +342,89 @@ class GetDelegate extends RouterDelegate<GetNavConfig>
452 342
453 return true; 343 return true;
454 } 344 }
  345 +
  346 + Future<GetNavConfig?> _pop(PopMode mode) async {
  347 + switch (mode) {
  348 + case PopMode.History:
  349 + return await _popHistory();
  350 + case PopMode.Page:
  351 + return await _popPage();
  352 + default:
  353 + return null;
  354 + }
  355 + }
  356 +
  357 + Future<GetNavConfig?> _popHistory() async {
  358 + if (!_canPopHistory()) return null;
  359 + return await _doPopHistory();
  360 + }
  361 +
  362 + Future<GetNavConfig?> _popPage() async {
  363 + if (!_canPopPage()) return null;
  364 + return await _doPopPage();
  365 + }
  366 +
  367 + Future<void> _pushHistory(GetNavConfig config) async {
  368 + if (config.currentPage!.preventDuplicates) {
  369 + final originalEntryIndex =
  370 + history.indexWhere((element) => element.location == config.location);
  371 + if (originalEntryIndex >= 0) {
  372 + switch (preventDuplicateHandlingMode) {
  373 + case PreventDuplicateHandlingMode.PopUntilOriginalRoute:
  374 + await backUntil(config.location!, popMode: PopMode.Page);
  375 + break;
  376 + case PreventDuplicateHandlingMode.ReorderRoutes:
  377 + await _unsafeHistoryRemoveAt(originalEntryIndex);
  378 + await _unsafeHistoryAdd(config);
  379 + break;
  380 + case PreventDuplicateHandlingMode.DoNothing:
  381 + default:
  382 + break;
  383 + }
  384 + return;
  385 + }
  386 + }
  387 + await _unsafeHistoryAdd(config);
  388 + }
  389 +
  390 + Future<void> _removeHistoryEntry(GetNavConfig entry) async {
  391 + await _unsafeHistoryRemove(entry);
  392 + }
  393 +
  394 + Future<void> _unsafeHistoryAdd(GetNavConfig config) async {
  395 + final res = await runMiddleware(config);
  396 + if (res == null) return;
  397 + history.add(res);
  398 + }
  399 +
  400 + Future<void> _unsafeHistoryRemove(GetNavConfig config) async {
  401 + var index = history.indexOf(config);
  402 + if (index >= 0) await _unsafeHistoryRemoveAt(index);
  403 + }
  404 +
  405 + Future<GetNavConfig?> _unsafeHistoryRemoveAt(int index) async {
  406 + if (index == history.length - 1 && history.length > 1) {
  407 + //removing WILL update the current route
  408 + final toCheck = history[history.length - 2];
  409 + final resMiddleware = await runMiddleware(toCheck);
  410 + if (resMiddleware == null) return null;
  411 + history[history.length - 2] = resMiddleware;
  412 + }
  413 + return history.removeAt(index);
  414 + }
455 } 415 }
456 416
457 class GetNavigator extends Navigator { 417 class GetNavigator extends Navigator {
458 - GetNavigator(  
459 - {GlobalKey<NavigatorState>? key,  
460 - bool Function(Route<dynamic>, dynamic)? onPopPage,  
461 - required List<GetPage> pages,  
462 - List<NavigatorObserver>? observers,  
463 - bool reportsRouteUpdateToEngine = false,  
464 - TransitionDelegate? transitionDelegate,  
465 - String? initialRoute})  
466 - : super( 418 + GetNavigator({
  419 + GlobalKey<NavigatorState>? key,
  420 + bool Function(Route<dynamic>, dynamic)? onPopPage,
  421 + required List<Page> pages,
  422 + List<NavigatorObserver>? observers,
  423 + bool reportsRouteUpdateToEngine = false,
  424 + TransitionDelegate? transitionDelegate,
  425 + }) : super(
467 //keys should be optional 426 //keys should be optional
468 key: key, 427 key: key,
469 - initialRoute: initialRoute ?? '/',  
470 onPopPage: onPopPage ?? 428 onPopPage: onPopPage ??
471 (route, result) { 429 (route, result) {
472 final didPop = route.didPop(result); 430 final didPop = route.didPop(result);
@@ -475,17 +433,6 @@ class GetNavigator extends Navigator { @@ -475,17 +433,6 @@ class GetNavigator extends Navigator {
475 } 433 }
476 return true; 434 return true;
477 }, 435 },
478 - onGenerateRoute: (RouteSettings settings) {  
479 - final selectedPageList =  
480 - pages.where((element) => element.name == settings.name);  
481 - if (selectedPageList.isNotEmpty) {  
482 - final selectedPage = selectedPageList.first;  
483 - return GetPageRoute(  
484 - page: selectedPage.page,  
485 - settings: settings,  
486 - );  
487 - }  
488 - },  
489 reportsRouteUpdateToEngine: reportsRouteUpdateToEngine, 436 reportsRouteUpdateToEngine: reportsRouteUpdateToEngine,
490 pages: pages, 437 pages: pages,
491 observers: [ 438 observers: [
@@ -496,3 +443,44 @@ class GetNavigator extends Navigator { @@ -496,3 +443,44 @@ class GetNavigator extends Navigator {
496 transitionDelegate ?? const DefaultTransitionDelegate<dynamic>(), 443 transitionDelegate ?? const DefaultTransitionDelegate<dynamic>(),
497 ); 444 );
498 } 445 }
  446 +
  447 +/// Enables the user to customize the intended pop behavior
  448 +///
  449 +/// Goes to either the previous history entry or the previous page entry
  450 +///
  451 +/// e.g. if the user navigates to these pages
  452 +/// 1) /home
  453 +/// 2) /home/products/1234
  454 +///
  455 +/// when popping on [History] mode, it will emulate a browser back button.
  456 +///
  457 +/// so the new history stack will be:
  458 +/// 1) /home
  459 +///
  460 +/// when popping on [Page] mode, it will only remove the last part of the route
  461 +/// so the new history stack will be:
  462 +/// 1) /home
  463 +/// 2) /home/products
  464 +///
  465 +/// another pop will change the history stack to:
  466 +/// 1) /home
  467 +enum PopMode {
  468 + History,
  469 + Page,
  470 +}
  471 +
  472 +/// Enables the user to customize the behavior when pushing multiple routes that
  473 +/// shouldn't be duplicates
  474 +enum PreventDuplicateHandlingMode {
  475 + /// Removes the history entries until it reaches the old route
  476 + PopUntilOriginalRoute,
  477 +
  478 + /// Simply don't push the new route
  479 + DoNothing,
  480 +
  481 + /// Recommended - Moves the old route entry to the front
  482 + ///
  483 + /// With this mode, you guarantee there will be only one
  484 + /// route entry for each location
  485 + ReorderRoutes
  486 +}
1 import 'package:flutter/cupertino.dart'; 1 import 'package:flutter/cupertino.dart';
2 import 'package:flutter/foundation.dart'; 2 import 'package:flutter/foundation.dart';
3 import 'package:flutter/material.dart'; 3 import 'package:flutter/material.dart';
  4 +
4 import '../../../get_core/get_core.dart'; 5 import '../../../get_core/get_core.dart';
5 import '../../../get_instance/get_instance.dart'; 6 import '../../../get_instance/get_instance.dart';
6 import '../../../get_state_manager/get_state_manager.dart'; 7 import '../../../get_state_manager/get_state_manager.dart';
@@ -9,6 +10,59 @@ import '../../get_navigation.dart'; @@ -9,6 +10,59 @@ import '../../get_navigation.dart';
9 import 'root_controller.dart'; 10 import 'root_controller.dart';
10 11
11 class GetCupertinoApp extends StatelessWidget { 12 class GetCupertinoApp extends StatelessWidget {
  13 + final GlobalKey<NavigatorState>? navigatorKey;
  14 +
  15 + final Widget? home;
  16 + final Map<String, WidgetBuilder>? routes;
  17 + final String? initialRoute;
  18 + final RouteFactory? onGenerateRoute;
  19 + final InitialRouteListFactory? onGenerateInitialRoutes;
  20 + final RouteFactory? onUnknownRoute;
  21 + final List<NavigatorObserver>? navigatorObservers;
  22 + final TransitionBuilder? builder;
  23 + final String title;
  24 + final GenerateAppTitle? onGenerateTitle;
  25 + final CustomTransition? customTransition;
  26 + final Color? color;
  27 + final Map<String, Map<String, String>>? translationsKeys;
  28 + final Translations? translations;
  29 + final TextDirection? textDirection;
  30 + final Locale? locale;
  31 + final Locale? fallbackLocale;
  32 + final Iterable<LocalizationsDelegate<dynamic>>? localizationsDelegates;
  33 + final LocaleListResolutionCallback? localeListResolutionCallback;
  34 + final LocaleResolutionCallback? localeResolutionCallback;
  35 + final Iterable<Locale> supportedLocales;
  36 + final bool showPerformanceOverlay;
  37 + final bool checkerboardRasterCacheImages;
  38 + final bool checkerboardOffscreenLayers;
  39 + final bool showSemanticsDebugger;
  40 + final bool debugShowCheckedModeBanner;
  41 + final Map<LogicalKeySet, Intent>? shortcuts;
  42 + final ThemeData? highContrastTheme;
  43 + final ThemeData? highContrastDarkTheme;
  44 + final Map<Type, Action<Intent>>? actions;
  45 + final Function(Routing?)? routingCallback;
  46 + final Transition? defaultTransition;
  47 + final bool? opaqueRoute;
  48 + final VoidCallback? onInit;
  49 + final VoidCallback? onReady;
  50 + final VoidCallback? onDispose;
  51 + final bool? enableLog;
  52 + final LogWriterCallback? logWriterCallback;
  53 + final bool? popGesture;
  54 + final SmartManagement smartManagement;
  55 + final Bindings? initialBinding;
  56 + final Duration? transitionDuration;
  57 + final bool? defaultGlobalState;
  58 + final List<GetPage>? getPages;
  59 + final GetPage? unknownRoute;
  60 + final RouteInformationProvider? routeInformationProvider;
  61 + final RouteInformationParser<Object>? routeInformationParser;
  62 + final RouterDelegate<Object>? routerDelegate;
  63 + final BackButtonDispatcher? backButtonDispatcher;
  64 + final CupertinoThemeData? theme;
  65 + final bool useInheritedMediaQuery;
12 const GetCupertinoApp({ 66 const GetCupertinoApp({
13 Key? key, 67 Key? key,
14 this.theme, 68 this.theme,
@@ -46,6 +100,7 @@ class GetCupertinoApp extends StatelessWidget { @@ -46,6 +100,7 @@ class GetCupertinoApp extends StatelessWidget {
46 this.shortcuts, 100 this.shortcuts,
47 this.smartManagement = SmartManagement.full, 101 this.smartManagement = SmartManagement.full,
48 this.initialBinding, 102 this.initialBinding,
  103 + this.useInheritedMediaQuery = false,
49 this.unknownRoute, 104 this.unknownRoute,
50 this.routingCallback, 105 this.routingCallback,
51 this.defaultTransition, 106 this.defaultTransition,
@@ -66,58 +121,6 @@ class GetCupertinoApp extends StatelessWidget { @@ -66,58 +121,6 @@ class GetCupertinoApp extends StatelessWidget {
66 backButtonDispatcher = null, 121 backButtonDispatcher = null,
67 super(key: key); 122 super(key: key);
68 123
69 - final GlobalKey<NavigatorState>? navigatorKey;  
70 - final Widget? home;  
71 - final Map<String, WidgetBuilder>? routes;  
72 - final String? initialRoute;  
73 - final RouteFactory? onGenerateRoute;  
74 - final InitialRouteListFactory? onGenerateInitialRoutes;  
75 - final RouteFactory? onUnknownRoute;  
76 - final List<NavigatorObserver>? navigatorObservers;  
77 - final TransitionBuilder? builder;  
78 - final String title;  
79 - final GenerateAppTitle? onGenerateTitle;  
80 - final CustomTransition? customTransition;  
81 - final Color? color;  
82 - final Map<String, Map<String, String>>? translationsKeys;  
83 - final Translations? translations;  
84 - final TextDirection? textDirection;  
85 - final Locale? locale;  
86 - final Locale? fallbackLocale;  
87 - final Iterable<LocalizationsDelegate<dynamic>>? localizationsDelegates;  
88 - final LocaleListResolutionCallback? localeListResolutionCallback;  
89 - final LocaleResolutionCallback? localeResolutionCallback;  
90 - final Iterable<Locale> supportedLocales;  
91 - final bool showPerformanceOverlay;  
92 - final bool checkerboardRasterCacheImages;  
93 - final bool checkerboardOffscreenLayers;  
94 - final bool showSemanticsDebugger;  
95 - final bool debugShowCheckedModeBanner;  
96 - final Map<LogicalKeySet, Intent>? shortcuts;  
97 - final ThemeData? highContrastTheme;  
98 - final ThemeData? highContrastDarkTheme;  
99 - final Map<Type, Action<Intent>>? actions;  
100 - final Function(Routing?)? routingCallback;  
101 - final Transition? defaultTransition;  
102 - final bool? opaqueRoute;  
103 - final VoidCallback? onInit;  
104 - final VoidCallback? onReady;  
105 - final VoidCallback? onDispose;  
106 - final bool? enableLog;  
107 - final LogWriterCallback? logWriterCallback;  
108 - final bool? popGesture;  
109 - final SmartManagement smartManagement;  
110 - final Bindings? initialBinding;  
111 - final Duration? transitionDuration;  
112 - final bool? defaultGlobalState;  
113 - final List<GetPage>? getPages;  
114 - final GetPage? unknownRoute;  
115 - final RouteInformationProvider? routeInformationProvider;  
116 - final RouteInformationParser<Object>? routeInformationParser;  
117 - final RouterDelegate<Object>? routerDelegate;  
118 - final BackButtonDispatcher? backButtonDispatcher;  
119 - final CupertinoThemeData? theme;  
120 -  
121 GetCupertinoApp.router({ 124 GetCupertinoApp.router({
122 Key? key, 125 Key? key,
123 this.theme, 126 this.theme,
@@ -128,6 +131,7 @@ class GetCupertinoApp extends StatelessWidget { @@ -128,6 +131,7 @@ class GetCupertinoApp extends StatelessWidget {
128 this.builder, 131 this.builder,
129 this.title = '', 132 this.title = '',
130 this.onGenerateTitle, 133 this.onGenerateTitle,
  134 + this.useInheritedMediaQuery = false,
131 this.color, 135 this.color,
132 this.highContrastTheme, 136 this.highContrastTheme,
133 this.highContrastDarkTheme, 137 this.highContrastDarkTheme,
@@ -183,31 +187,6 @@ class GetCupertinoApp extends StatelessWidget { @@ -183,31 +187,6 @@ class GetCupertinoApp extends StatelessWidget {
183 Get.routeInformationParser = routeInformationParser; 187 Get.routeInformationParser = routeInformationParser;
184 } 188 }
185 189
186 - Route<dynamic> generator(RouteSettings settings) {  
187 - return PageRedirect(settings: settings, unknownRoute: unknownRoute).page();  
188 - }  
189 -  
190 - List<Route<dynamic>> initialRoutesGenerate(String name) {  
191 - return [  
192 - PageRedirect(  
193 - settings: RouteSettings(name: name),  
194 - unknownRoute: unknownRoute,  
195 - ).page()  
196 - ];  
197 - }  
198 -  
199 - Widget defaultBuilder(BuildContext context, Widget? child) {  
200 - return Directionality(  
201 - textDirection: textDirection ??  
202 - (rtlLanguages.contains(Get.locale?.languageCode)  
203 - ? TextDirection.rtl  
204 - : TextDirection.ltr),  
205 - child: builder == null  
206 - ? (child ?? Material())  
207 - : builder!(context, child ?? Material()),  
208 - );  
209 - }  
210 -  
211 @override 190 @override
212 Widget build(BuildContext context) => GetBuilder<GetMaterialController>( 191 Widget build(BuildContext context) => GetBuilder<GetMaterialController>(
213 init: Get.rootController, 192 init: Get.rootController,
@@ -271,6 +250,7 @@ class GetCupertinoApp extends StatelessWidget { @@ -271,6 +250,7 @@ class GetCupertinoApp extends StatelessWidget {
271 showSemanticsDebugger: showSemanticsDebugger, 250 showSemanticsDebugger: showSemanticsDebugger,
272 debugShowCheckedModeBanner: debugShowCheckedModeBanner, 251 debugShowCheckedModeBanner: debugShowCheckedModeBanner,
273 shortcuts: shortcuts, 252 shortcuts: shortcuts,
  253 + useInheritedMediaQuery: useInheritedMediaQuery,
274 ) 254 )
275 : CupertinoApp( 255 : CupertinoApp(
276 key: _.unikey, 256 key: _.unikey,
@@ -310,7 +290,33 @@ class GetCupertinoApp extends StatelessWidget { @@ -310,7 +290,33 @@ class GetCupertinoApp extends StatelessWidget {
310 showSemanticsDebugger: showSemanticsDebugger, 290 showSemanticsDebugger: showSemanticsDebugger,
311 debugShowCheckedModeBanner: debugShowCheckedModeBanner, 291 debugShowCheckedModeBanner: debugShowCheckedModeBanner,
312 shortcuts: shortcuts, 292 shortcuts: shortcuts,
  293 + useInheritedMediaQuery: useInheritedMediaQuery,
313 // actions: actions, 294 // actions: actions,
314 ), 295 ),
315 ); 296 );
  297 +
  298 + Widget defaultBuilder(BuildContext context, Widget? child) {
  299 + return Directionality(
  300 + textDirection: textDirection ??
  301 + (rtlLanguages.contains(Get.locale?.languageCode)
  302 + ? TextDirection.rtl
  303 + : TextDirection.ltr),
  304 + child: builder == null
  305 + ? (child ?? Material())
  306 + : builder!(context, child ?? Material()),
  307 + );
  308 + }
  309 +
  310 + Route<dynamic> generator(RouteSettings settings) {
  311 + return PageRedirect(settings: settings, unknownRoute: unknownRoute).page();
  312 + }
  313 +
  314 + List<Route<dynamic>> initialRoutesGenerate(String name) {
  315 + return [
  316 + PageRedirect(
  317 + settings: RouteSettings(name: name),
  318 + unknownRoute: unknownRoute,
  319 + ).page()
  320 + ];
  321 + }
316 } 322 }
@@ -10,6 +10,64 @@ import '../../get_navigation.dart'; @@ -10,6 +10,64 @@ import '../../get_navigation.dart';
10 import 'root_controller.dart'; 10 import 'root_controller.dart';
11 11
12 class GetMaterialApp extends StatelessWidget { 12 class GetMaterialApp extends StatelessWidget {
  13 + final GlobalKey<NavigatorState>? navigatorKey;
  14 +
  15 + final GlobalKey<ScaffoldMessengerState>? scaffoldMessengerKey;
  16 + final Widget? home;
  17 + final Map<String, WidgetBuilder>? routes;
  18 + final String? initialRoute;
  19 + final RouteFactory? onGenerateRoute;
  20 + final InitialRouteListFactory? onGenerateInitialRoutes;
  21 + final RouteFactory? onUnknownRoute;
  22 + final List<NavigatorObserver>? navigatorObservers;
  23 + final TransitionBuilder? builder;
  24 + final String title;
  25 + final GenerateAppTitle? onGenerateTitle;
  26 + final ThemeData? theme;
  27 + final ThemeData? darkTheme;
  28 + final ThemeMode themeMode;
  29 + final CustomTransition? customTransition;
  30 + final Color? color;
  31 + final Map<String, Map<String, String>>? translationsKeys;
  32 + final Translations? translations;
  33 + final TextDirection? textDirection;
  34 + final Locale? locale;
  35 + final Locale? fallbackLocale;
  36 + final Iterable<LocalizationsDelegate<dynamic>>? localizationsDelegates;
  37 + final LocaleListResolutionCallback? localeListResolutionCallback;
  38 + final LocaleResolutionCallback? localeResolutionCallback;
  39 + final Iterable<Locale> supportedLocales;
  40 + final bool showPerformanceOverlay;
  41 + final bool checkerboardRasterCacheImages;
  42 + final bool checkerboardOffscreenLayers;
  43 + final bool showSemanticsDebugger;
  44 + final bool debugShowCheckedModeBanner;
  45 + final Map<LogicalKeySet, Intent>? shortcuts;
  46 + final ScrollBehavior? scrollBehavior;
  47 + final ThemeData? highContrastTheme;
  48 + final ThemeData? highContrastDarkTheme;
  49 + final Map<Type, Action<Intent>>? actions;
  50 + final bool debugShowMaterialGrid;
  51 + final ValueChanged<Routing?>? routingCallback;
  52 + final Transition? defaultTransition;
  53 + final bool? opaqueRoute;
  54 + final VoidCallback? onInit;
  55 + final VoidCallback? onReady;
  56 + final VoidCallback? onDispose;
  57 + final bool? enableLog;
  58 + final LogWriterCallback? logWriterCallback;
  59 + final bool? popGesture;
  60 + final SmartManagement smartManagement;
  61 + final Bindings? initialBinding;
  62 + final Duration? transitionDuration;
  63 + final bool? defaultGlobalState;
  64 + final List<GetPage>? getPages;
  65 + final GetPage? unknownRoute;
  66 + final RouteInformationProvider? routeInformationProvider;
  67 + final RouteInformationParser<Object>? routeInformationParser;
  68 + final RouterDelegate<Object>? routerDelegate;
  69 + final BackButtonDispatcher? backButtonDispatcher;
  70 + final bool useInheritedMediaQuery;
13 const GetMaterialApp({ 71 const GetMaterialApp({
14 Key? key, 72 Key? key,
15 this.navigatorKey, 73 this.navigatorKey,
@@ -21,6 +79,7 @@ class GetMaterialApp extends StatelessWidget { @@ -21,6 +79,7 @@ class GetMaterialApp extends StatelessWidget {
21 this.onGenerateRoute, 79 this.onGenerateRoute,
22 this.onGenerateInitialRoutes, 80 this.onGenerateInitialRoutes,
23 this.onUnknownRoute, 81 this.onUnknownRoute,
  82 + this.useInheritedMediaQuery = false,
24 List<NavigatorObserver> this.navigatorObservers = 83 List<NavigatorObserver> this.navigatorObservers =
25 const <NavigatorObserver>[], 84 const <NavigatorObserver>[],
26 this.builder, 85 this.builder,
@@ -72,63 +131,6 @@ class GetMaterialApp extends StatelessWidget { @@ -72,63 +131,6 @@ class GetMaterialApp extends StatelessWidget {
72 backButtonDispatcher = null, 131 backButtonDispatcher = null,
73 super(key: key); 132 super(key: key);
74 133
75 - final GlobalKey<NavigatorState>? navigatorKey;  
76 - final GlobalKey<ScaffoldMessengerState>? scaffoldMessengerKey;  
77 - final Widget? home;  
78 - final Map<String, WidgetBuilder>? routes;  
79 - final String? initialRoute;  
80 - final RouteFactory? onGenerateRoute;  
81 - final InitialRouteListFactory? onGenerateInitialRoutes;  
82 - final RouteFactory? onUnknownRoute;  
83 - final List<NavigatorObserver>? navigatorObservers;  
84 - final TransitionBuilder? builder;  
85 - final String title;  
86 - final GenerateAppTitle? onGenerateTitle;  
87 - final ThemeData? theme;  
88 - final ThemeData? darkTheme;  
89 - final ThemeMode themeMode;  
90 - final CustomTransition? customTransition;  
91 - final Color? color;  
92 - final Map<String, Map<String, String>>? translationsKeys;  
93 - final Translations? translations;  
94 - final TextDirection? textDirection;  
95 - final Locale? locale;  
96 - final Locale? fallbackLocale;  
97 - final Iterable<LocalizationsDelegate<dynamic>>? localizationsDelegates;  
98 - final LocaleListResolutionCallback? localeListResolutionCallback;  
99 - final LocaleResolutionCallback? localeResolutionCallback;  
100 - final Iterable<Locale> supportedLocales;  
101 - final bool showPerformanceOverlay;  
102 - final bool checkerboardRasterCacheImages;  
103 - final bool checkerboardOffscreenLayers;  
104 - final bool showSemanticsDebugger;  
105 - final bool debugShowCheckedModeBanner;  
106 - final Map<LogicalKeySet, Intent>? shortcuts;  
107 - final ScrollBehavior? scrollBehavior;  
108 - final ThemeData? highContrastTheme;  
109 - final ThemeData? highContrastDarkTheme;  
110 - final Map<Type, Action<Intent>>? actions;  
111 - final bool debugShowMaterialGrid;  
112 - final ValueChanged<Routing?>? routingCallback;  
113 - final Transition? defaultTransition;  
114 - final bool? opaqueRoute;  
115 - final VoidCallback? onInit;  
116 - final VoidCallback? onReady;  
117 - final VoidCallback? onDispose;  
118 - final bool? enableLog;  
119 - final LogWriterCallback? logWriterCallback;  
120 - final bool? popGesture;  
121 - final SmartManagement smartManagement;  
122 - final Bindings? initialBinding;  
123 - final Duration? transitionDuration;  
124 - final bool? defaultGlobalState;  
125 - final List<GetPage>? getPages;  
126 - final GetPage? unknownRoute;  
127 - final RouteInformationProvider? routeInformationProvider;  
128 - final RouteInformationParser<Object>? routeInformationParser;  
129 - final RouterDelegate<Object>? routerDelegate;  
130 - final BackButtonDispatcher? backButtonDispatcher;  
131 -  
132 GetMaterialApp.router({ 134 GetMaterialApp.router({
133 Key? key, 135 Key? key,
134 this.routeInformationProvider, 136 this.routeInformationProvider,
@@ -142,6 +144,7 @@ class GetMaterialApp extends StatelessWidget { @@ -142,6 +144,7 @@ class GetMaterialApp extends StatelessWidget {
142 this.color, 144 this.color,
143 this.theme, 145 this.theme,
144 this.darkTheme, 146 this.darkTheme,
  147 + this.useInheritedMediaQuery = false,
145 this.highContrastTheme, 148 this.highContrastTheme,
146 this.highContrastDarkTheme, 149 this.highContrastDarkTheme,
147 this.themeMode = ThemeMode.system, 150 this.themeMode = ThemeMode.system,
@@ -200,31 +203,6 @@ class GetMaterialApp extends StatelessWidget { @@ -200,31 +203,6 @@ class GetMaterialApp extends StatelessWidget {
200 Get.routeInformationParser = routeInformationParser; 203 Get.routeInformationParser = routeInformationParser;
201 } 204 }
202 205
203 - Route<dynamic> generator(RouteSettings settings) {  
204 - return PageRedirect(settings: settings, unknownRoute: unknownRoute).page();  
205 - }  
206 -  
207 - List<Route<dynamic>> initialRoutesGenerate(String name) {  
208 - return [  
209 - PageRedirect(  
210 - settings: RouteSettings(name: name),  
211 - unknownRoute: unknownRoute,  
212 - ).page()  
213 - ];  
214 - }  
215 -  
216 - Widget defaultBuilder(BuildContext context, Widget? child) {  
217 - return Directionality(  
218 - textDirection: textDirection ??  
219 - (rtlLanguages.contains(Get.locale?.languageCode)  
220 - ? TextDirection.rtl  
221 - : TextDirection.ltr),  
222 - child: builder == null  
223 - ? (child ?? Material())  
224 - : builder!(context, child ?? Material()),  
225 - );  
226 - }  
227 -  
228 @override 206 @override
229 Widget build(BuildContext context) => GetBuilder<GetMaterialController>( 207 Widget build(BuildContext context) => GetBuilder<GetMaterialController>(
230 init: Get.rootController, 208 init: Get.rootController,
@@ -270,7 +248,6 @@ class GetMaterialApp extends StatelessWidget { @@ -270,7 +248,6 @@ class GetMaterialApp extends StatelessWidget {
270 ? MaterialApp.router( 248 ? MaterialApp.router(
271 routerDelegate: routerDelegate!, 249 routerDelegate: routerDelegate!,
272 routeInformationParser: routeInformationParser!, 250 routeInformationParser: routeInformationParser!,
273 - scaffoldMessengerKey: scaffoldMessengerKey,  
274 backButtonDispatcher: backButtonDispatcher, 251 backButtonDispatcher: backButtonDispatcher,
275 routeInformationProvider: routeInformationProvider, 252 routeInformationProvider: routeInformationProvider,
276 key: _.unikey, 253 key: _.unikey,
@@ -283,6 +260,8 @@ class GetMaterialApp extends StatelessWidget { @@ -283,6 +260,8 @@ class GetMaterialApp extends StatelessWidget {
283 _.darkTheme ?? darkTheme ?? theme ?? ThemeData.fallback(), 260 _.darkTheme ?? darkTheme ?? theme ?? ThemeData.fallback(),
284 themeMode: _.themeMode ?? themeMode, 261 themeMode: _.themeMode ?? themeMode,
285 locale: Get.locale ?? locale, 262 locale: Get.locale ?? locale,
  263 + scaffoldMessengerKey:
  264 + scaffoldMessengerKey ?? _.scaffoldMessengerKey,
286 localizationsDelegates: localizationsDelegates, 265 localizationsDelegates: localizationsDelegates,
287 localeListResolutionCallback: localeListResolutionCallback, 266 localeListResolutionCallback: localeListResolutionCallback,
288 localeResolutionCallback: localeResolutionCallback, 267 localeResolutionCallback: localeResolutionCallback,
@@ -295,13 +274,15 @@ class GetMaterialApp extends StatelessWidget { @@ -295,13 +274,15 @@ class GetMaterialApp extends StatelessWidget {
295 debugShowCheckedModeBanner: debugShowCheckedModeBanner, 274 debugShowCheckedModeBanner: debugShowCheckedModeBanner,
296 shortcuts: shortcuts, 275 shortcuts: shortcuts,
297 scrollBehavior: scrollBehavior, 276 scrollBehavior: scrollBehavior,
  277 + useInheritedMediaQuery: useInheritedMediaQuery,
298 ) 278 )
299 : MaterialApp( 279 : MaterialApp(
300 key: _.unikey, 280 key: _.unikey,
301 navigatorKey: (navigatorKey == null 281 navigatorKey: (navigatorKey == null
302 ? Get.key 282 ? Get.key
303 : Get.addKey(navigatorKey!)), 283 : Get.addKey(navigatorKey!)),
304 - scaffoldMessengerKey: scaffoldMessengerKey, 284 + scaffoldMessengerKey:
  285 + scaffoldMessengerKey ?? _.scaffoldMessengerKey,
305 home: home, 286 home: home,
306 routes: routes ?? const <String, WidgetBuilder>{}, 287 routes: routes ?? const <String, WidgetBuilder>{},
307 initialRoute: initialRoute, 288 initialRoute: initialRoute,
@@ -340,7 +321,33 @@ class GetMaterialApp extends StatelessWidget { @@ -340,7 +321,33 @@ class GetMaterialApp extends StatelessWidget {
340 debugShowCheckedModeBanner: debugShowCheckedModeBanner, 321 debugShowCheckedModeBanner: debugShowCheckedModeBanner,
341 shortcuts: shortcuts, 322 shortcuts: shortcuts,
342 scrollBehavior: scrollBehavior, 323 scrollBehavior: scrollBehavior,
  324 + useInheritedMediaQuery: useInheritedMediaQuery,
343 // actions: actions, 325 // actions: actions,
344 ), 326 ),
345 ); 327 );
  328 +
  329 + Widget defaultBuilder(BuildContext context, Widget? child) {
  330 + return Directionality(
  331 + textDirection: textDirection ??
  332 + (rtlLanguages.contains(Get.locale?.languageCode)
  333 + ? TextDirection.rtl
  334 + : TextDirection.ltr),
  335 + child: builder == null
  336 + ? (child ?? Material())
  337 + : builder!(context, child ?? Material()),
  338 + );
  339 + }
  340 +
  341 + Route<dynamic> generator(RouteSettings settings) {
  342 + return PageRedirect(settings: settings, unknownRoute: unknownRoute).page();
  343 + }
  344 +
  345 + List<Route<dynamic>> initialRoutesGenerate(String name) {
  346 + return [
  347 + PageRedirect(
  348 + settings: RouteSettings(name: name),
  349 + unknownRoute: unknownRoute,
  350 + ).page()
  351 + ];
  352 + }
346 } 353 }
1 import 'package:flutter/material.dart'; 1 import 'package:flutter/material.dart';
  2 +
  3 +import '../../../get.dart';
2 import '../../../get_state_manager/get_state_manager.dart'; 4 import '../../../get_state_manager/get_state_manager.dart';
3 import '../../../get_utils/get_utils.dart'; 5 import '../../../get_utils/get_utils.dart';
4 import '../routes/custom_transition.dart'; 6 import '../routes/custom_transition.dart';
5 import '../routes/observers/route_observer.dart'; 7 import '../routes/observers/route_observer.dart';
6 import '../routes/transitions_type.dart'; 8 import '../routes/transitions_type.dart';
7 9
8 -class GetMaterialController extends GetxController { 10 +class GetMaterialController extends SuperController {
9 bool testMode = false; 11 bool testMode = false;
10 Key? unikey; 12 Key? unikey;
11 ThemeData? theme; 13 ThemeData? theme;
12 ThemeData? darkTheme; 14 ThemeData? darkTheme;
13 ThemeMode? themeMode; 15 ThemeMode? themeMode;
14 16
  17 + final scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
  18 +
15 bool defaultPopGesture = GetPlatform.isIOS; 19 bool defaultPopGesture = GetPlatform.isIOS;
16 bool defaultOpaqueRoute = true; 20 bool defaultOpaqueRoute = true;
17 21
@@ -31,6 +35,8 @@ class GetMaterialController extends GetxController { @@ -31,6 +35,8 @@ class GetMaterialController extends GetxController {
31 35
32 var _key = GlobalKey<NavigatorState>(debugLabel: 'Key Created by default'); 36 var _key = GlobalKey<NavigatorState>(debugLabel: 'Key Created by default');
33 37
  38 + Map<dynamic, GlobalKey<NavigatorState>> keys = {};
  39 +
34 GlobalKey<NavigatorState> get key => _key; 40 GlobalKey<NavigatorState> get key => _key;
35 41
36 GlobalKey<NavigatorState>? addKey(GlobalKey<NavigatorState> newKey) { 42 GlobalKey<NavigatorState>? addKey(GlobalKey<NavigatorState> newKey) {
@@ -38,7 +44,32 @@ class GetMaterialController extends GetxController { @@ -38,7 +44,32 @@ class GetMaterialController extends GetxController {
38 return key; 44 return key;
39 } 45 }
40 46
41 - Map<dynamic, GlobalKey<NavigatorState>> keys = {}; 47 + @override
  48 + void didChangeLocales(List<Locale>? locales) {
  49 + Get.asap(() {
  50 + final locale = Get.deviceLocale;
  51 + if (locale != null) {
  52 + Get.updateLocale(locale);
  53 + }
  54 + });
  55 + }
  56 +
  57 + @override
  58 + void onDetached() {}
  59 +
  60 + @override
  61 + void onInactive() {}
  62 +
  63 + @override
  64 + void onPaused() {}
  65 +
  66 + @override
  67 + void onResumed() {}
  68 +
  69 + void restartApp() {
  70 + unikey = UniqueKey();
  71 + update();
  72 + }
42 73
43 void setTheme(ThemeData value) { 74 void setTheme(ThemeData value) {
44 if (darkTheme == null) { 75 if (darkTheme == null) {
@@ -57,9 +88,4 @@ class GetMaterialController extends GetxController { @@ -57,9 +88,4 @@ class GetMaterialController extends GetxController {
57 themeMode = value; 88 themeMode = value;
58 update(); 89 update();
59 } 90 }
60 -  
61 - void restartApp() {  
62 - unikey = UniqueKey();  
63 - update();  
64 - }  
65 } 91 }
@@ -9,24 +9,6 @@ import '../../get_navigation.dart'; @@ -9,24 +9,6 @@ import '../../get_navigation.dart';
9 import 'custom_transition.dart'; 9 import 'custom_transition.dart';
10 import 'transitions_type.dart'; 10 import 'transitions_type.dart';
11 11
12 -@immutable  
13 -class PathDecoded {  
14 - const PathDecoded(this.regex, this.keys);  
15 - final RegExp regex;  
16 - final List<String?> keys;  
17 -  
18 - @override  
19 - bool operator ==(Object other) {  
20 - if (identical(this, other)) return true;  
21 -  
22 - return other is PathDecoded &&  
23 - other.regex == regex; // && listEquals(other.keys, keys);  
24 - }  
25 -  
26 - @override  
27 - int get hashCode => regex.hashCode;  
28 -}  
29 -  
30 class GetPage<T> extends Page<T> { 12 class GetPage<T> extends Page<T> {
31 final GetPageBuilder page; 13 final GetPageBuilder page;
32 final bool? popGesture; 14 final bool? popGesture;
@@ -98,27 +80,6 @@ class GetPage<T> extends Page<T> { @@ -98,27 +80,6 @@ class GetPage<T> extends Page<T> {
98 ); 80 );
99 // settings = RouteSettings(name: name, arguments: Get.arguments); 81 // settings = RouteSettings(name: name, arguments: Get.arguments);
100 82
101 - static PathDecoded _nameToRegex(String path) {  
102 - var keys = <String?>[];  
103 -  
104 - String _replace(Match pattern) {  
105 - var buffer = StringBuffer('(?:');  
106 -  
107 - if (pattern[1] != null) buffer.write('\.');  
108 - buffer.write('([\\w%+-._~!\$&\'()*,;=:@]+))');  
109 - if (pattern[3] != null) buffer.write('?');  
110 -  
111 - keys.add(pattern[2]);  
112 - return "$buffer";  
113 - }  
114 -  
115 - var stringPath = '$path/?'  
116 - .replaceAllMapped(RegExp(r'(\.)?:(\w+)(\?)?'), _replace)  
117 - .replaceAll('//', '/');  
118 -  
119 - return PathDecoded(RegExp('^$stringPath\$'), keys);  
120 - }  
121 -  
122 GetPage<T> copy({ 83 GetPage<T> copy({
123 String? name, 84 String? name,
124 GetPageBuilder? page, 85 GetPageBuilder? page,
@@ -174,8 +135,6 @@ class GetPage<T> extends Page<T> { @@ -174,8 +135,6 @@ class GetPage<T> extends Page<T> {
174 ); 135 );
175 } 136 }
176 137
177 - late Future<T?> popped;  
178 -  
179 @override 138 @override
180 Route<T> createRoute(BuildContext context) { 139 Route<T> createRoute(BuildContext context) {
181 // return GetPageRoute<T>(settings: this, page: page); 140 // return GetPageRoute<T>(settings: this, page: page);
@@ -185,7 +144,45 @@ class GetPage<T> extends Page<T> { @@ -185,7 +144,45 @@ class GetPage<T> extends Page<T> {
185 unknownRoute: unknownRoute, 144 unknownRoute: unknownRoute,
186 ).getPageToRoute<T>(this, unknownRoute); 145 ).getPageToRoute<T>(this, unknownRoute);
187 146
188 - popped = _page.popped;  
189 return _page; 147 return _page;
190 } 148 }
  149 +
  150 + static PathDecoded _nameToRegex(String path) {
  151 + var keys = <String?>[];
  152 +
  153 + String _replace(Match pattern) {
  154 + var buffer = StringBuffer('(?:');
  155 +
  156 + if (pattern[1] != null) buffer.write('\.');
  157 + buffer.write('([\\w%+-._~!\$&\'()*,;=:@]+))');
  158 + if (pattern[3] != null) buffer.write('?');
  159 +
  160 + keys.add(pattern[2]);
  161 + return "$buffer";
  162 + }
  163 +
  164 + var stringPath = '$path/?'
  165 + .replaceAllMapped(RegExp(r'(\.)?:(\w+)(\?)?'), _replace)
  166 + .replaceAll('//', '/');
  167 +
  168 + return PathDecoded(RegExp('^$stringPath\$'), keys);
  169 + }
  170 +}
  171 +
  172 +@immutable
  173 +class PathDecoded {
  174 + final RegExp regex;
  175 + final List<String?> keys;
  176 + const PathDecoded(this.regex, this.keys);
  177 +
  178 + @override
  179 + int get hashCode => regex.hashCode;
  180 +
  181 + @override
  182 + bool operator ==(Object other) {
  183 + if (identical(this, other)) return true;
  184 +
  185 + return other is PathDecoded &&
  186 + other.regex == regex; // && listEquals(other.keys, keys);
  187 + }
191 } 188 }
@@ -5,111 +5,247 @@ import 'package:flutter/cupertino.dart'; @@ -5,111 +5,247 @@ import 'package:flutter/cupertino.dart';
5 import 'package:flutter/foundation.dart'; 5 import 'package:flutter/foundation.dart';
6 import 'package:flutter/gestures.dart'; 6 import 'package:flutter/gestures.dart';
7 import 'package:flutter/material.dart'; 7 import 'package:flutter/material.dart';
8 -import '../../../get.dart';  
9 8
  9 +import '../../../get.dart';
10 import 'default_transitions.dart'; 10 import 'default_transitions.dart';
11 import 'transitions_type.dart'; 11 import 'transitions_type.dart';
12 12
13 const double _kBackGestureWidth = 20.0; 13 const double _kBackGestureWidth = 20.0;
14 -const double _kMinFlingVelocity = 1.0; // Screen widths per second. 14 +const int _kMaxDroppedSwipePageForwardAnimationTime =
  15 + 800; // Screen widths per second.
15 16
16 // An eyeballed value for the maximum time it takes 17 // An eyeballed value for the maximum time it takes
17 //for a page to animate forward 18 //for a page to animate forward
18 // if the user releases a page mid swipe. 19 // if the user releases a page mid swipe.
19 -const int _kMaxDroppedSwipePageForwardAnimationTime = 800; // Milliseconds. 20 +const int _kMaxPageBackAnimationTime = 300; // Milliseconds.
20 21
21 // The maximum time for a page to get reset to it's original position if the 22 // The maximum time for a page to get reset to it's original position if the
22 // user releases a page mid swipe. 23 // user releases a page mid swipe.
23 -const int _kMaxPageBackAnimationTime = 300; // Milliseconds. 24 +const double _kMinFlingVelocity = 1.0; // Milliseconds.
24 25
25 -mixin GetPageRouteTransitionMixin<T> on PageRoute<T> {  
26 - /// Builds the primary contents of the route.  
27 - @protected  
28 - Widget buildContent(BuildContext context); 26 +class CupertinoBackGestureController<T> {
  27 + final AnimationController controller;
29 28
30 - /// {@template flutter.cupertino.CupertinoRouteTransitionMixin.title}  
31 - /// A title string for this route. 29 + final NavigatorState navigator;
  30 +
  31 + /// Creates a controller for an iOS-style back gesture.
32 /// 32 ///
33 - /// Used to auto-populate [CupertinoNavigationBar] and  
34 - /// [CupertinoSliverNavigationBar]'s `middle`/`largeTitle` widgets when  
35 - /// one is not manually supplied.  
36 - /// {@endtemplate}  
37 - String? get title; 33 + /// The [navigator] and [controller] arguments must not be null.
  34 + CupertinoBackGestureController({
  35 + required this.navigator,
  36 + required this.controller,
  37 + }) {
  38 + navigator.didStartUserGesture();
  39 + }
38 40
39 - double Function(BuildContext context)? get gestureWidth; 41 + /// The drag gesture has ended with a horizontal motion of
  42 + /// [fractionalVelocity] as a fraction of screen width per second.
  43 + void dragEnd(double velocity) {
  44 + // Fling in the appropriate direction.
  45 + // AnimationController.fling is guaranteed to
  46 + // take at least one frame.
  47 + //
  48 + // This curve has been determined through rigorously eyeballing native iOS
  49 + // animations.
  50 + const Curve animationCurve = Curves.fastLinearToSlowEaseIn;
  51 + final bool animateForward;
40 52
41 - ValueNotifier<String?>? _previousTitle; 53 + // If the user releases the page before mid screen with sufficient velocity,
  54 + // or after mid screen, we should animate the page out. Otherwise, the page
  55 + // should be animated back in.
  56 + if (velocity.abs() >= _kMinFlingVelocity) {
  57 + animateForward = velocity <= 0;
  58 + } else {
  59 + animateForward = controller.value > 0.5;
  60 + }
42 61
43 - /// The title string of the previous [CupertinoPageRoute].  
44 - ///  
45 - /// The [ValueListenable]'s value is readable after the route is installed  
46 - /// onto a [Navigator]. The [ValueListenable] will also notify its listeners  
47 - /// if the value changes (such as by replacing the previous route).  
48 - ///  
49 - /// The [ValueListenable] itself will be null before the route is installed.  
50 - /// Its content value will be null if the previous route has no title or  
51 - /// is not a [CupertinoPageRoute].  
52 - ///  
53 - /// See also:  
54 - ///  
55 - /// * [ValueListenableBuilder], which can be used to listen and rebuild  
56 - /// widgets based on a ValueListenable.  
57 - ValueListenable<String?> get previousTitle {  
58 - assert(  
59 - _previousTitle != null,  
60 - '''  
61 -Cannot read the previousTitle for a route that has not yet been installed''',  
62 - );  
63 - return _previousTitle!;  
64 - } 62 + if (animateForward) {
  63 + // The closer the panel is to dismissing, the shorter the animation is.
  64 + // We want to cap the animation time, but we want to use a linear curve
  65 + // to determine it.
  66 + final droppedPageForwardAnimationTime = min(
  67 + lerpDouble(
  68 + _kMaxDroppedSwipePageForwardAnimationTime, 0, controller.value)!
  69 + .floor(),
  70 + _kMaxPageBackAnimationTime,
  71 + );
  72 + controller.animateTo(1.0,
  73 + duration: Duration(milliseconds: droppedPageForwardAnimationTime),
  74 + curve: animationCurve);
  75 + } else {
  76 + // This route is destined to pop at this point. Reuse navigator's pop.
  77 + navigator.pop();
65 78
66 - @override  
67 - void didChangePrevious(Route<dynamic>? previousRoute) {  
68 - final previousTitleString = previousRoute is CupertinoRouteTransitionMixin  
69 - ? previousRoute.title  
70 - : null;  
71 - if (_previousTitle == null) {  
72 - _previousTitle = ValueNotifier<String?>(previousTitleString); 79 + // The popping may have finished inline if already at the
  80 + // target destination.
  81 + if (controller.isAnimating) {
  82 + // Otherwise, use a custom popping animation duration and curve.
  83 + final droppedPageBackAnimationTime = lerpDouble(
  84 + 0, _kMaxDroppedSwipePageForwardAnimationTime, controller.value)!
  85 + .floor();
  86 + controller.animateBack(0.0,
  87 + duration: Duration(milliseconds: droppedPageBackAnimationTime),
  88 + curve: animationCurve);
  89 + }
  90 + }
  91 +
  92 + if (controller.isAnimating) {
  93 + // Keep the userGestureInProgress in true state so we don't change the
  94 + // curve of the page transition mid-flight since CupertinoPageTransition
  95 + // depends on userGestureInProgress.
  96 + late AnimationStatusListener animationStatusCallback;
  97 + animationStatusCallback = (status) {
  98 + navigator.didStopUserGesture();
  99 + controller.removeStatusListener(animationStatusCallback);
  100 + };
  101 + controller.addStatusListener(animationStatusCallback);
73 } else { 102 } else {
74 - _previousTitle!.value = previousTitleString; 103 + navigator.didStopUserGesture();
75 } 104 }
76 - super.didChangePrevious(previousRoute);  
77 } 105 }
78 106
79 - @override  
80 - // A relatively rigorous eyeball estimation.  
81 - Duration get transitionDuration => const Duration(milliseconds: 400); 107 + /// The drag gesture has changed by [fractionalDelta]. The total range of the
  108 + /// drag should be 0.0 to 1.0.
  109 + void dragUpdate(double delta) {
  110 + controller.value -= delta;
  111 + }
  112 +}
  113 +
  114 +class CupertinoBackGestureDetector<T> extends StatefulWidget {
  115 + final Widget child;
  116 +
  117 + final double gestureWidth;
  118 + final ValueGetter<bool> enabledCallback;
  119 +
  120 + final ValueGetter<CupertinoBackGestureController<T>> onStartPopGesture;
  121 +
  122 + const CupertinoBackGestureDetector({
  123 + Key? key,
  124 + required this.enabledCallback,
  125 + required this.onStartPopGesture,
  126 + required this.child,
  127 + required this.gestureWidth,
  128 + }) : super(key: key);
82 129
83 @override 130 @override
84 - Color? get barrierColor => null; 131 + CupertinoBackGestureDetectorState<T> createState() =>
  132 + CupertinoBackGestureDetectorState<T>();
  133 +}
  134 +
  135 +class CupertinoBackGestureDetectorState<T>
  136 + extends State<CupertinoBackGestureDetector<T>> {
  137 + CupertinoBackGestureController<T>? _backGestureController;
  138 +
  139 + late HorizontalDragGestureRecognizer _recognizer;
85 140
86 @override 141 @override
87 - String? get barrierLabel => null; 142 + Widget build(BuildContext context) {
  143 + assert(debugCheckHasDirectionality(context));
  144 + // For devices with notches, the drag area needs to be larger on the side
  145 + // that has the notch.
  146 + var dragAreaWidth = Directionality.of(context) == TextDirection.ltr
  147 + ? MediaQuery.of(context).padding.left
  148 + : MediaQuery.of(context).padding.right;
  149 + dragAreaWidth = max(dragAreaWidth, widget.gestureWidth);
  150 + return Stack(
  151 + fit: StackFit.passthrough,
  152 + children: <Widget>[
  153 + widget.child,
  154 + PositionedDirectional(
  155 + start: 0.0,
  156 + width: dragAreaWidth,
  157 + top: 0.0,
  158 + bottom: 0.0,
  159 + child: Listener(
  160 + onPointerDown: _handlePointerDown,
  161 + behavior: HitTestBehavior.translucent,
  162 + ),
  163 + ),
  164 + ],
  165 + );
  166 + }
88 167
89 - bool get showCupertinoParallax; 168 + @override
  169 + void dispose() {
  170 + _recognizer.dispose();
  171 + super.dispose();
  172 + }
90 173
91 @override 174 @override
92 - bool canTransitionTo(TransitionRoute<dynamic> nextRoute) {  
93 - // Don't perform outgoing animation if the next route is a  
94 - // fullscreen dialog. 175 + void initState() {
  176 + super.initState();
  177 + _recognizer = HorizontalDragGestureRecognizer(debugOwner: this)
  178 + ..onStart = _handleDragStart
  179 + ..onUpdate = _handleDragUpdate
  180 + ..onEnd = _handleDragEnd
  181 + ..onCancel = _handleDragCancel;
  182 + }
95 183
96 - return nextRoute is GetPageRouteTransitionMixin &&  
97 - !nextRoute.fullscreenDialog &&  
98 - nextRoute.showCupertinoParallax; 184 + double _convertToLogical(double value) {
  185 + switch (Directionality.of(context)) {
  186 + case TextDirection.rtl:
  187 + return -value;
  188 + case TextDirection.ltr:
  189 + return value;
  190 + }
99 } 191 }
100 192
101 - /// True if an iOS-style back swipe pop gesture is currently  
102 - /// underway for [route]. 193 + void _handleDragCancel() {
  194 + assert(mounted);
  195 + // This can be called even if start is not called, paired with
  196 + // the "down" event
  197 + // that we don't consider here.
  198 + _backGestureController?.dragEnd(0.0);
  199 + _backGestureController = null;
  200 + }
  201 +
  202 + void _handleDragEnd(DragEndDetails details) {
  203 + assert(mounted);
  204 + assert(_backGestureController != null);
  205 + _backGestureController!.dragEnd(_convertToLogical(
  206 + details.velocity.pixelsPerSecond.dx / context.size!.width));
  207 + _backGestureController = null;
  208 + }
  209 +
  210 + void _handleDragStart(DragStartDetails details) {
  211 + assert(mounted);
  212 + assert(_backGestureController == null);
  213 + _backGestureController = widget.onStartPopGesture();
  214 + }
  215 +
  216 + void _handleDragUpdate(DragUpdateDetails details) {
  217 + assert(mounted);
  218 + assert(_backGestureController != null);
  219 + _backGestureController!.dragUpdate(
  220 + _convertToLogical(details.primaryDelta! / context.size!.width));
  221 + }
  222 +
  223 + void _handlePointerDown(PointerDownEvent event) {
  224 + if (widget.enabledCallback()) _recognizer.addPointer(event);
  225 + }
  226 +}
  227 +
  228 +mixin GetPageRouteTransitionMixin<T> on PageRoute<T> {
  229 + ValueNotifier<String?>? _previousTitle;
  230 +
  231 + @override
  232 + Color? get barrierColor => null;
  233 +
  234 + @override
  235 + String? get barrierLabel => null;
  236 +
  237 + double Function(BuildContext context)? get gestureWidth;
  238 +
  239 + /// Whether a pop gesture can be started by the user.
103 /// 240 ///
104 - /// This just check the route's [NavigatorState.userGestureInProgress]. 241 + /// Returns true if the user can edge-swipe to a previous route.
105 /// 242 ///
106 - /// See also: 243 + /// Returns false once [isPopGestureInProgress] is true, but
  244 + /// [isPopGestureInProgress] can only become true if [popGestureEnabled] was
  245 + /// true first.
107 /// 246 ///
108 - /// * [popGestureEnabled], which returns true if a user-triggered pop gesture  
109 - /// would be allowed.  
110 - static bool isPopGestureInProgress(PageRoute<dynamic> route) {  
111 - return route.navigator!.userGestureInProgress;  
112 - } 247 + /// This should only be used between frames, not during build.
  248 + bool get popGestureEnabled => _isPopGestureEnabled(this);
113 249
114 /// True if an iOS-style back swipe pop gesture is currently 250 /// True if an iOS-style back swipe pop gesture is currently
115 /// underway for this route. 251 /// underway for this route.
@@ -122,44 +258,47 @@ Cannot read the previousTitle for a route that has not yet been installed''', @@ -122,44 +258,47 @@ Cannot read the previousTitle for a route that has not yet been installed''',
122 /// would be allowed. 258 /// would be allowed.
123 bool get popGestureInProgress => isPopGestureInProgress(this); 259 bool get popGestureInProgress => isPopGestureInProgress(this);
124 260
125 - /// Whether a pop gesture can be started by the user. 261 + /// The title string of the previous [CupertinoPageRoute].
126 /// 262 ///
127 - /// Returns true if the user can edge-swipe to a previous route. 263 + /// The [ValueListenable]'s value is readable after the route is installed
  264 + /// onto a [Navigator]. The [ValueListenable] will also notify its listeners
  265 + /// if the value changes (such as by replacing the previous route).
128 /// 266 ///
129 - /// Returns false once [isPopGestureInProgress] is true, but  
130 - /// [isPopGestureInProgress] can only become true if [popGestureEnabled] was  
131 - /// true first. 267 + /// The [ValueListenable] itself will be null before the route is installed.
  268 + /// Its content value will be null if the previous route has no title or
  269 + /// is not a [CupertinoPageRoute].
132 /// 270 ///
133 - /// This should only be used between frames, not during build.  
134 - bool get popGestureEnabled => _isPopGestureEnabled(this); 271 + /// See also:
  272 + ///
  273 + /// * [ValueListenableBuilder], which can be used to listen and rebuild
  274 + /// widgets based on a ValueListenable.
  275 + ValueListenable<String?> get previousTitle {
  276 + assert(
  277 + _previousTitle != null,
  278 + '''
  279 +Cannot read the previousTitle for a route that has not yet been installed''',
  280 + );
  281 + return _previousTitle!;
  282 + }
135 283
136 - static bool _isPopGestureEnabled<T>(PageRoute<T> route) {  
137 - // If there's nothing to go back to, then obviously we don't support  
138 - // the back gesture.  
139 - if (route.isFirst) return false;  
140 - // If the route wouldn't actually pop if we popped it, then the gesture  
141 - // would be really confusing (or would skip internal routes),  
142 - //so disallow it.  
143 - if (route.willHandlePopInternally) return false;  
144 - // If attempts to dismiss this route might be vetoed such as in a page  
145 - // with forms, then do not allow the user to dismiss the route with a swipe.  
146 - if (route.hasScopedWillPopCallback) return false;  
147 - // Fullscreen dialogs aren't dismissible by back swipe.  
148 - if (route.fullscreenDialog) return false;  
149 - // If we're in an animation already, we cannot be manually swiped.  
150 - if (route.animation!.status != AnimationStatus.completed) return false;  
151 - // If we're being popped into, we also cannot be swiped until the pop above  
152 - // it completes. This translates to our secondary animation being  
153 - // dismissed.  
154 - if (route.secondaryAnimation!.status != AnimationStatus.dismissed) {  
155 - return false;  
156 - }  
157 - // If we're in a gesture already, we cannot start another.  
158 - if (isPopGestureInProgress(route)) return false; 284 + bool get showCupertinoParallax;
159 285
160 - // Looks like a back gesture would be welcome!  
161 - return true;  
162 - } 286 + /// {@template flutter.cupertino.CupertinoRouteTransitionMixin.title}
  287 + /// A title string for this route.
  288 + ///
  289 + /// Used to auto-populate [CupertinoNavigationBar] and
  290 + /// [CupertinoSliverNavigationBar]'s `middle`/`largeTitle` widgets when
  291 + /// one is not manually supplied.
  292 + /// {@endtemplate}
  293 + String? get title;
  294 +
  295 + @override
  296 + // A relatively rigorous eyeball estimation.
  297 + Duration get transitionDuration => const Duration(milliseconds: 400);
  298 +
  299 + /// Builds the primary contents of the route.
  300 + @protected
  301 + Widget buildContent(BuildContext context);
163 302
164 @override 303 @override
165 Widget buildPage(BuildContext context, Animation<double> animation, 304 Widget buildPage(BuildContext context, Animation<double> animation,
@@ -173,17 +312,36 @@ Cannot read the previousTitle for a route that has not yet been installed''', @@ -173,17 +312,36 @@ Cannot read the previousTitle for a route that has not yet been installed''',
173 return result; 312 return result;
174 } 313 }
175 314
176 - // Called by CupertinoBackGestureDetector when a pop ("back") drag start  
177 - // gesture is detected. The returned controller handles all of the subsequent  
178 - // drag events.  
179 - static CupertinoBackGestureController<T> _startPopGesture<T>(  
180 - PageRoute<T> route) {  
181 - assert(_isPopGestureEnabled(route)); 315 + @override
  316 + Widget buildTransitions(BuildContext context, Animation<double> animation,
  317 + Animation<double> secondaryAnimation, Widget child) {
  318 + return buildPageTransitions<T>(
  319 + this, context, animation, secondaryAnimation, child);
  320 + }
182 321
183 - return CupertinoBackGestureController<T>(  
184 - navigator: route.navigator!,  
185 - controller: route.controller!, // protected access  
186 - ); 322 + @override
  323 + bool canTransitionTo(TransitionRoute<dynamic> nextRoute) {
  324 + // Don't perform outgoing animation if the next route is a
  325 + // fullscreen dialog.
  326 +
  327 + return (nextRoute is GetPageRouteTransitionMixin &&
  328 + !nextRoute.fullscreenDialog &&
  329 + nextRoute.showCupertinoParallax) ||
  330 + (nextRoute is CupertinoRouteTransitionMixin &&
  331 + !nextRoute.fullscreenDialog);
  332 + }
  333 +
  334 + @override
  335 + void didChangePrevious(Route<dynamic>? previousRoute) {
  336 + final previousTitleString = previousRoute is CupertinoRouteTransitionMixin
  337 + ? previousRoute.title
  338 + : null;
  339 + if (_previousTitle == null) {
  340 + _previousTitle = ValueNotifier<String?>(previousTitleString);
  341 + } else {
  342 + _previousTitle!.value = previousTitleString;
  343 + }
  344 + super.didChangePrevious(previousRoute);
187 } 345 }
188 346
189 /// Returns a [CupertinoFullscreenDialogTransition] if [route] is a full 347 /// Returns a [CupertinoFullscreenDialogTransition] if [route] is a full
@@ -501,211 +659,57 @@ Cannot read the previousTitle for a route that has not yet been installed''', @@ -501,211 +659,57 @@ Cannot read the previousTitle for a route that has not yet been installed''',
501 } 659 }
502 } 660 }
503 661
504 - @override  
505 - Widget buildTransitions(BuildContext context, Animation<double> animation,  
506 - Animation<double> secondaryAnimation, Widget child) {  
507 - return buildPageTransitions<T>(  
508 - this, context, animation, secondaryAnimation, child);  
509 - }  
510 -}  
511 -  
512 -class CupertinoBackGestureDetector<T> extends StatefulWidget {  
513 - const CupertinoBackGestureDetector({  
514 - Key? key,  
515 - required this.enabledCallback,  
516 - required this.onStartPopGesture,  
517 - required this.child,  
518 - required this.gestureWidth,  
519 - }) : super(key: key);  
520 -  
521 - final Widget child;  
522 - final double gestureWidth;  
523 -  
524 - final ValueGetter<bool> enabledCallback;  
525 -  
526 - final ValueGetter<CupertinoBackGestureController<T>> onStartPopGesture;  
527 -  
528 - @override  
529 - CupertinoBackGestureDetectorState<T> createState() =>  
530 - CupertinoBackGestureDetectorState<T>();  
531 -}  
532 -  
533 -class CupertinoBackGestureDetectorState<T>  
534 - extends State<CupertinoBackGestureDetector<T>> {  
535 - CupertinoBackGestureController<T>? _backGestureController;  
536 -  
537 - late HorizontalDragGestureRecognizer _recognizer;  
538 -  
539 - @override  
540 - void initState() {  
541 - super.initState();  
542 - _recognizer = HorizontalDragGestureRecognizer(debugOwner: this)  
543 - ..onStart = _handleDragStart  
544 - ..onUpdate = _handleDragUpdate  
545 - ..onEnd = _handleDragEnd  
546 - ..onCancel = _handleDragCancel;  
547 - }  
548 -  
549 - @override  
550 - void dispose() {  
551 - _recognizer.dispose();  
552 - super.dispose();  
553 - }  
554 -  
555 - void _handleDragStart(DragStartDetails details) {  
556 - assert(mounted);  
557 - assert(_backGestureController == null);  
558 - _backGestureController = widget.onStartPopGesture();  
559 - }  
560 -  
561 - void _handleDragUpdate(DragUpdateDetails details) {  
562 - assert(mounted);  
563 - assert(_backGestureController != null);  
564 - _backGestureController!.dragUpdate(  
565 - _convertToLogical(details.primaryDelta! / context.size!.width));  
566 - }  
567 -  
568 - void _handleDragEnd(DragEndDetails details) {  
569 - assert(mounted);  
570 - assert(_backGestureController != null);  
571 - _backGestureController!.dragEnd(_convertToLogical(  
572 - details.velocity.pixelsPerSecond.dx / context.size!.width));  
573 - _backGestureController = null;  
574 - }  
575 -  
576 - void _handleDragCancel() {  
577 - assert(mounted);  
578 - // This can be called even if start is not called, paired with  
579 - // the "down" event  
580 - // that we don't consider here.  
581 - _backGestureController?.dragEnd(0.0);  
582 - _backGestureController = null;  
583 - }  
584 -  
585 - void _handlePointerDown(PointerDownEvent event) {  
586 - if (widget.enabledCallback()) _recognizer.addPointer(event);  
587 - }  
588 -  
589 - double _convertToLogical(double value) {  
590 - switch (Directionality.of(context)) {  
591 - case TextDirection.rtl:  
592 - return -value;  
593 - case TextDirection.ltr:  
594 - return value;  
595 - }  
596 - }  
597 -  
598 - @override  
599 - Widget build(BuildContext context) {  
600 - assert(debugCheckHasDirectionality(context));  
601 - // For devices with notches, the drag area needs to be larger on the side  
602 - // that has the notch.  
603 - var dragAreaWidth = Directionality.of(context) == TextDirection.ltr  
604 - ? MediaQuery.of(context).padding.left  
605 - : MediaQuery.of(context).padding.right;  
606 - dragAreaWidth = max(dragAreaWidth, widget.gestureWidth);  
607 - return Stack(  
608 - fit: StackFit.passthrough,  
609 - children: <Widget>[  
610 - widget.child,  
611 - PositionedDirectional(  
612 - start: 0.0,  
613 - width: dragAreaWidth,  
614 - top: 0.0,  
615 - bottom: 0.0,  
616 - child: Listener(  
617 - onPointerDown: _handlePointerDown,  
618 - behavior: HitTestBehavior.translucent,  
619 - ),  
620 - ),  
621 - ],  
622 - );  
623 - }  
624 -}  
625 -  
626 -class CupertinoBackGestureController<T> {  
627 - /// Creates a controller for an iOS-style back gesture. 662 + // Called by CupertinoBackGestureDetector when a pop ("back") drag start
  663 + // gesture is detected. The returned controller handles all of the subsequent
  664 + // drag events.
  665 + /// True if an iOS-style back swipe pop gesture is currently
  666 + /// underway for [route].
628 /// 667 ///
629 - /// The [navigator] and [controller] arguments must not be null.  
630 - CupertinoBackGestureController({  
631 - required this.navigator,  
632 - required this.controller,  
633 - }) {  
634 - navigator.didStartUserGesture();  
635 - }  
636 -  
637 - final AnimationController controller;  
638 - final NavigatorState navigator;  
639 -  
640 - /// The drag gesture has changed by [fractionalDelta]. The total range of the  
641 - /// drag should be 0.0 to 1.0.  
642 - void dragUpdate(double delta) {  
643 - controller.value -= delta; 668 + /// This just check the route's [NavigatorState.userGestureInProgress].
  669 + ///
  670 + /// See also:
  671 + ///
  672 + /// * [popGestureEnabled], which returns true if a user-triggered pop gesture
  673 + /// would be allowed.
  674 + static bool isPopGestureInProgress(PageRoute<dynamic> route) {
  675 + return route.navigator!.userGestureInProgress;
644 } 676 }
645 677
646 - /// The drag gesture has ended with a horizontal motion of  
647 - /// [fractionalVelocity] as a fraction of screen width per second.  
648 - void dragEnd(double velocity) {  
649 - // Fling in the appropriate direction.  
650 - // AnimationController.fling is guaranteed to  
651 - // take at least one frame.  
652 - //  
653 - // This curve has been determined through rigorously eyeballing native iOS  
654 - // animations.  
655 - const Curve animationCurve = Curves.fastLinearToSlowEaseIn;  
656 - final bool animateForward;  
657 -  
658 - // If the user releases the page before mid screen with sufficient velocity,  
659 - // or after mid screen, we should animate the page out. Otherwise, the page  
660 - // should be animated back in.  
661 - if (velocity.abs() >= _kMinFlingVelocity) {  
662 - animateForward = velocity <= 0;  
663 - } else {  
664 - animateForward = controller.value > 0.5; 678 + static bool _isPopGestureEnabled<T>(PageRoute<T> route) {
  679 + // If there's nothing to go back to, then obviously we don't support
  680 + // the back gesture.
  681 + if (route.isFirst) return false;
  682 + // If the route wouldn't actually pop if we popped it, then the gesture
  683 + // would be really confusing (or would skip internal routes),
  684 + //so disallow it.
  685 + if (route.willHandlePopInternally) return false;
  686 + // If attempts to dismiss this route might be vetoed such as in a page
  687 + // with forms, then do not allow the user to dismiss the route with a swipe.
  688 + if (route.hasScopedWillPopCallback) return false;
  689 + // Fullscreen dialogs aren't dismissible by back swipe.
  690 + if (route.fullscreenDialog) return false;
  691 + // If we're in an animation already, we cannot be manually swiped.
  692 + if (route.animation!.status != AnimationStatus.completed) return false;
  693 + // If we're being popped into, we also cannot be swiped until the pop above
  694 + // it completes. This translates to our secondary animation being
  695 + // dismissed.
  696 + if (route.secondaryAnimation!.status != AnimationStatus.dismissed) {
  697 + return false;
665 } 698 }
  699 + // If we're in a gesture already, we cannot start another.
  700 + if (isPopGestureInProgress(route)) return false;
666 701
667 - if (animateForward) {  
668 - // The closer the panel is to dismissing, the shorter the animation is.  
669 - // We want to cap the animation time, but we want to use a linear curve  
670 - // to determine it.  
671 - final droppedPageForwardAnimationTime = min(  
672 - lerpDouble(  
673 - _kMaxDroppedSwipePageForwardAnimationTime, 0, controller.value)!  
674 - .floor(),  
675 - _kMaxPageBackAnimationTime,  
676 - );  
677 - controller.animateTo(1.0,  
678 - duration: Duration(milliseconds: droppedPageForwardAnimationTime),  
679 - curve: animationCurve);  
680 - } else {  
681 - // This route is destined to pop at this point. Reuse navigator's pop.  
682 - navigator.pop(); 702 + // Looks like a back gesture would be welcome!
  703 + return true;
  704 + }
683 705
684 - // The popping may have finished inline if already at the  
685 - // target destination.  
686 - if (controller.isAnimating) {  
687 - // Otherwise, use a custom popping animation duration and curve.  
688 - final droppedPageBackAnimationTime = lerpDouble(  
689 - 0, _kMaxDroppedSwipePageForwardAnimationTime, controller.value)!  
690 - .floor();  
691 - controller.animateBack(0.0,  
692 - duration: Duration(milliseconds: droppedPageBackAnimationTime),  
693 - curve: animationCurve);  
694 - }  
695 - } 706 + static CupertinoBackGestureController<T> _startPopGesture<T>(
  707 + PageRoute<T> route) {
  708 + assert(_isPopGestureEnabled(route));
696 709
697 - if (controller.isAnimating) {  
698 - // Keep the userGestureInProgress in true state so we don't change the  
699 - // curve of the page transition mid-flight since CupertinoPageTransition  
700 - // depends on userGestureInProgress.  
701 - late AnimationStatusListener animationStatusCallback;  
702 - animationStatusCallback = (status) {  
703 - navigator.didStopUserGesture();  
704 - controller.removeStatusListener(animationStatusCallback);  
705 - };  
706 - controller.addStatusListener(animationStatusCallback);  
707 - } else {  
708 - navigator.didStopUserGesture();  
709 - } 710 + return CupertinoBackGestureController<T>(
  711 + navigator: route.navigator!,
  712 + controller: route.controller!, // protected access
  713 + );
710 } 714 }
711 } 715 }
@@ -5,37 +5,8 @@ import '../../../../instance_manager.dart'; @@ -5,37 +5,8 @@ import '../../../../instance_manager.dart';
5 import '../../../get_navigation.dart'; 5 import '../../../get_navigation.dart';
6 import '../../dialog/dialog_route.dart'; 6 import '../../dialog/dialog_route.dart';
7 import '../../router_report.dart'; 7 import '../../router_report.dart';
8 -import '../../snackbar/snack_route.dart';  
9 import '../default_route.dart'; 8 import '../default_route.dart';
10 9
11 -class Routing {  
12 - String current;  
13 - String previous;  
14 - dynamic args;  
15 - String removed;  
16 - Route<dynamic>? route;  
17 - bool? isBack;  
18 - bool? isSnackbar;  
19 - bool? isBottomSheet;  
20 - bool? isDialog;  
21 -  
22 - Routing({  
23 - this.current = '',  
24 - this.previous = '',  
25 - this.args,  
26 - this.removed = '',  
27 - this.route,  
28 - this.isBack,  
29 - this.isSnackbar,  
30 - this.isBottomSheet,  
31 - this.isDialog,  
32 - });  
33 -  
34 - void update(void fn(Routing value)) {  
35 - fn(this);  
36 - }  
37 -}  
38 -  
39 /// Extracts the name of a route based on it's instance type 10 /// Extracts the name of a route based on it's instance type
40 /// or null if not possible. 11 /// or null if not possible.
41 String? _extractRouteName(Route? route) { 12 String? _extractRouteName(Route? route) {
@@ -58,49 +29,70 @@ String? _extractRouteName(Route? route) { @@ -58,49 +29,70 @@ String? _extractRouteName(Route? route) {
58 return null; 29 return null;
59 } 30 }
60 31
61 -/// This is basically a util for rules about 'what a route is'  
62 -class _RouteData {  
63 - final bool isGetPageRoute;  
64 - final bool isSnackbar;  
65 - final bool isBottomSheet;  
66 - final bool isDialog;  
67 - final String? name;  
68 -  
69 - _RouteData({  
70 - required this.name,  
71 - required this.isGetPageRoute,  
72 - required this.isSnackbar,  
73 - required this.isBottomSheet,  
74 - required this.isDialog,  
75 - });  
76 -  
77 - factory _RouteData.ofRoute(Route? route) {  
78 - return _RouteData(  
79 - name: _extractRouteName(route),  
80 - isGetPageRoute: route is GetPageRoute,  
81 - isSnackbar: route is SnackRoute,  
82 - isDialog: route is GetDialogRoute,  
83 - isBottomSheet: route is GetModalBottomSheetRoute,  
84 - );  
85 - }  
86 -}  
87 -  
88 class GetObserver extends NavigatorObserver { 32 class GetObserver extends NavigatorObserver {
89 final Function(Routing?)? routing; 33 final Function(Routing?)? routing;
90 34
  35 + final Routing? _routeSend;
  36 +
91 GetObserver([this.routing, this._routeSend]); 37 GetObserver([this.routing, this._routeSend]);
92 38
93 - final Routing? _routeSend; 39 + @override
  40 + void didPop(Route route, Route? previousRoute) {
  41 + super.didPop(route, previousRoute);
  42 + final currentRoute = _RouteData.ofRoute(route);
  43 + final newRoute = _RouteData.ofRoute(previousRoute);
  44 +
  45 + // if (currentRoute.isSnackbar) {
  46 + // // Get.log("CLOSE SNACKBAR ${currentRoute.name}");
  47 + // Get.log("CLOSE SNACKBAR");
  48 + // } else
  49 +
  50 + if (currentRoute.isBottomSheet || currentRoute.isDialog) {
  51 + Get.log("CLOSE ${currentRoute.name}");
  52 + } else if (currentRoute.isGetPageRoute) {
  53 + Get.log("CLOSE TO ROUTE ${currentRoute.name}");
  54 + }
  55 + if (previousRoute != null) {
  56 + RouterReportManager.reportCurrentRoute(previousRoute);
  57 + }
  58 +
  59 + // Here we use a 'inverse didPush set', meaning that we use
  60 + // previous route instead of 'route' because this is
  61 + // a 'inverse push'
  62 + _routeSend?.update((value) {
  63 + // Only PageRoute is allowed to change current value
  64 + if (previousRoute is PageRoute) {
  65 + value.current = _extractRouteName(previousRoute) ?? '';
  66 + value.previous = newRoute.name ?? '';
  67 + } else if (value.previous.isNotEmpty) {
  68 + value.current = value.previous;
  69 + }
  70 +
  71 + value.args = previousRoute?.settings.arguments;
  72 + value.route = previousRoute;
  73 + value.isBack = true;
  74 + value.removed = '';
  75 + // value.isSnackbar = newRoute.isSnackbar;
  76 + value.isBottomSheet = newRoute.isBottomSheet;
  77 + value.isDialog = newRoute.isDialog;
  78 + });
  79 +
  80 + // print('currentRoute.isDialog ${currentRoute.isDialog}');
  81 +
  82 + routing?.call(_routeSend);
  83 + }
94 84
95 @override 85 @override
96 void didPush(Route route, Route? previousRoute) { 86 void didPush(Route route, Route? previousRoute) {
97 super.didPush(route, previousRoute); 87 super.didPush(route, previousRoute);
98 final newRoute = _RouteData.ofRoute(route); 88 final newRoute = _RouteData.ofRoute(route);
99 89
100 - if (newRoute.isSnackbar) {  
101 - // Get.log("OPEN SNACKBAR ${newRoute.name}");  
102 - Get.log("OPEN SNACKBAR");  
103 - } else if (newRoute.isBottomSheet || newRoute.isDialog) { 90 + // if (newRoute.isSnackbar) {
  91 + // // Get.log("OPEN SNACKBAR ${newRoute.name}");
  92 + // Get.log("OPEN SNACKBAR");
  93 + // } else
  94 +
  95 + if (newRoute.isBottomSheet || newRoute.isDialog) {
104 Get.log("OPEN ${newRoute.name}"); 96 Get.log("OPEN ${newRoute.name}");
105 } else if (newRoute.isGetPageRoute) { 97 } else if (newRoute.isGetPageRoute) {
106 Get.log("GOING TO ROUTE ${newRoute.name}"); 98 Get.log("GOING TO ROUTE ${newRoute.name}");
@@ -121,7 +113,6 @@ class GetObserver extends NavigatorObserver { @@ -121,7 +113,6 @@ class GetObserver extends NavigatorObserver {
121 value.route = route; 113 value.route = route;
122 value.isBack = false; 114 value.isBack = false;
123 value.removed = ''; 115 value.removed = '';
124 - value.isSnackbar = newRoute.isSnackbar ? true : value.isSnackbar ?? false;  
125 value.isBottomSheet = 116 value.isBottomSheet =
126 newRoute.isBottomSheet ? true : value.isBottomSheet ?? false; 117 newRoute.isBottomSheet ? true : value.isBottomSheet ?? false;
127 value.isDialog = newRoute.isDialog ? true : value.isDialog ?? false; 118 value.isDialog = newRoute.isDialog ? true : value.isDialog ?? false;
@@ -133,46 +124,27 @@ class GetObserver extends NavigatorObserver { @@ -133,46 +124,27 @@ class GetObserver extends NavigatorObserver {
133 } 124 }
134 125
135 @override 126 @override
136 - void didPop(Route route, Route? previousRoute) {  
137 - super.didPop(route, previousRoute); 127 + void didRemove(Route route, Route? previousRoute) {
  128 + super.didRemove(route, previousRoute);
  129 + final routeName = _extractRouteName(route);
138 final currentRoute = _RouteData.ofRoute(route); 130 final currentRoute = _RouteData.ofRoute(route);
139 - final newRoute = _RouteData.ofRoute(previousRoute);  
140 131
141 - if (currentRoute.isSnackbar) {  
142 - // Get.log("CLOSE SNACKBAR ${currentRoute.name}");  
143 - Get.log("CLOSE SNACKBAR");  
144 - } else if (currentRoute.isBottomSheet || currentRoute.isDialog) {  
145 - Get.log("CLOSE ${currentRoute.name}");  
146 - } else if (currentRoute.isGetPageRoute) {  
147 - Get.log("CLOSE TO ROUTE ${currentRoute.name}");  
148 - }  
149 - if (previousRoute != null) {  
150 - RouterReportManager.reportCurrentRoute(previousRoute);  
151 - } 132 + Get.log("REMOVING ROUTE $routeName");
152 133
153 - // Here we use a 'inverse didPush set', meaning that we use  
154 - // previous route instead of 'route' because this is  
155 - // a 'inverse push'  
156 _routeSend?.update((value) { 134 _routeSend?.update((value) {
157 - // Only PageRoute is allowed to change current value  
158 - if (previousRoute is PageRoute) {  
159 - value.current = _extractRouteName(previousRoute) ?? '';  
160 - value.previous = newRoute.name ?? '';  
161 - } else if (value.previous.isNotEmpty) {  
162 - value.current = value.previous;  
163 - }  
164 -  
165 - value.args = previousRoute?.settings.arguments;  
166 value.route = previousRoute; 135 value.route = previousRoute;
167 - value.isBack = true;  
168 - value.removed = '';  
169 - value.isSnackbar = newRoute.isSnackbar;  
170 - value.isBottomSheet = newRoute.isBottomSheet;  
171 - value.isDialog = newRoute.isDialog; 136 + value.isBack = false;
  137 + value.removed = routeName ?? '';
  138 + value.previous = routeName ?? '';
  139 + // value.isSnackbar = currentRoute.isSnackbar ? false : value.isSnackbar;
  140 + value.isBottomSheet =
  141 + currentRoute.isBottomSheet ? false : value.isBottomSheet;
  142 + value.isDialog = currentRoute.isDialog ? false : value.isDialog;
172 }); 143 });
173 144
174 - // print('currentRoute.isDialog ${currentRoute.isDialog}');  
175 - 145 + if (route is GetPageRoute) {
  146 + RouterReportManager.reportRouteWillDispose(route);
  147 + }
176 routing?.call(_routeSend); 148 routing?.call(_routeSend);
177 } 149 }
178 150
@@ -201,7 +173,7 @@ class GetObserver extends NavigatorObserver { @@ -201,7 +173,7 @@ class GetObserver extends NavigatorObserver {
201 value.isBack = false; 173 value.isBack = false;
202 value.removed = ''; 174 value.removed = '';
203 value.previous = '$oldName'; 175 value.previous = '$oldName';
204 - value.isSnackbar = currentRoute.isSnackbar ? false : value.isSnackbar; 176 + // value.isSnackbar = currentRoute.isSnackbar ? false : value.isSnackbar;
205 value.isBottomSheet = 177 value.isBottomSheet =
206 currentRoute.isBottomSheet ? false : value.isBottomSheet; 178 currentRoute.isBottomSheet ? false : value.isBottomSheet;
207 value.isDialog = currentRoute.isDialog ? false : value.isDialog; 179 value.isDialog = currentRoute.isDialog ? false : value.isDialog;
@@ -212,29 +184,59 @@ class GetObserver extends NavigatorObserver { @@ -212,29 +184,59 @@ class GetObserver extends NavigatorObserver {
212 184
213 routing?.call(_routeSend); 185 routing?.call(_routeSend);
214 } 186 }
  187 +}
215 188
216 - @override  
217 - void didRemove(Route route, Route? previousRoute) {  
218 - super.didRemove(route, previousRoute);  
219 - final routeName = _extractRouteName(route);  
220 - final currentRoute = _RouteData.ofRoute(route); 189 +class Routing {
  190 + String current;
  191 + String previous;
  192 + dynamic args;
  193 + String removed;
  194 + Route<dynamic>? route;
  195 + bool? isBack;
  196 + // bool? isSnackbar;
  197 + bool? isBottomSheet;
  198 + bool? isDialog;
221 199
222 - Get.log("REMOVING ROUTE $routeName"); 200 + Routing({
  201 + this.current = '',
  202 + this.previous = '',
  203 + this.args,
  204 + this.removed = '',
  205 + this.route,
  206 + this.isBack,
  207 + // this.isSnackbar,
  208 + this.isBottomSheet,
  209 + this.isDialog,
  210 + });
223 211
224 - _routeSend?.update((value) {  
225 - value.route = previousRoute;  
226 - value.isBack = false;  
227 - value.removed = routeName ?? '';  
228 - value.previous = routeName ?? '';  
229 - value.isSnackbar = currentRoute.isSnackbar ? false : value.isSnackbar;  
230 - value.isBottomSheet =  
231 - currentRoute.isBottomSheet ? false : value.isBottomSheet;  
232 - value.isDialog = currentRoute.isDialog ? false : value.isDialog;  
233 - }); 212 + void update(void fn(Routing value)) {
  213 + fn(this);
  214 + }
  215 +}
234 216
235 - if (route is GetPageRoute) {  
236 - RouterReportManager.reportRouteWillDispose(route);  
237 - }  
238 - routing?.call(_routeSend); 217 +/// This is basically a util for rules about 'what a route is'
  218 +class _RouteData {
  219 + final bool isGetPageRoute;
  220 + //final bool isSnackbar;
  221 + final bool isBottomSheet;
  222 + final bool isDialog;
  223 + final String? name;
  224 +
  225 + _RouteData({
  226 + required this.name,
  227 + required this.isGetPageRoute,
  228 + // required this.isSnackbar,
  229 + required this.isBottomSheet,
  230 + required this.isDialog,
  231 + });
  232 +
  233 + factory _RouteData.ofRoute(Route? route) {
  234 + return _RouteData(
  235 + name: _extractRouteName(route),
  236 + isGetPageRoute: route is GetPageRoute,
  237 + // isSnackbar: route is SnackRoute,
  238 + isDialog: route is GetDialogRoute,
  239 + isBottomSheet: route is GetModalBottomSheetRoute,
  240 + );
239 } 241 }
240 } 242 }
1 -import 'dart:async';  
2 -import 'dart:ui';  
3 -import 'package:flutter/widgets.dart';  
4 -import '../../../get_core/get_core.dart';  
5 -import '../../get_navigation.dart';  
6 -import 'snack.dart';  
7 -  
8 -class SnackRoute<T> extends OverlayRoute<T> {  
9 - late Animation<double> _filterBlurAnimation;  
10 - late Animation<Color?> _filterColorAnimation;  
11 -  
12 - SnackRoute({  
13 - required this.snack,  
14 - RouteSettings? settings,  
15 - }) : super(settings: settings) {  
16 - _builder = Builder(builder: (_) {  
17 - return GestureDetector(  
18 - child: snack,  
19 - onTap: snack.onTap != null ? () => snack.onTap!(snack) : null,  
20 - );  
21 - });  
22 -  
23 - _configureAlignment(snack.snackPosition);  
24 - _snackbarStatus = snack.snackbarStatus;  
25 - }  
26 -  
27 - _configureAlignment(SnackPosition snackPosition) {  
28 - switch (snack.snackPosition) {  
29 - case SnackPosition.TOP:  
30 - {  
31 - _initialAlignment = Alignment(-1.0, -2.0);  
32 - _endAlignment = Alignment(-1.0, -1.0);  
33 - break;  
34 - }  
35 - case SnackPosition.BOTTOM:  
36 - {  
37 - _initialAlignment = Alignment(-1.0, 2.0);  
38 - _endAlignment = Alignment(-1.0, 1.0);  
39 - break;  
40 - }  
41 - }  
42 - }  
43 -  
44 - GetBar snack;  
45 - Builder? _builder;  
46 -  
47 - final Completer<T> _transitionCompleter = Completer<T>();  
48 -  
49 - late SnackbarStatusCallback _snackbarStatus;  
50 - Alignment? _initialAlignment;  
51 - Alignment? _endAlignment;  
52 - bool _wasDismissedBySwipe = false;  
53 - bool _onTappedDismiss = false;  
54 -  
55 - Timer? _timer;  
56 -  
57 - bool get opaque => false;  
58 -  
59 - @override  
60 - Iterable<OverlayEntry> createOverlayEntries() {  
61 - return <OverlayEntry>[  
62 - if (snack.overlayBlur > 0.0) ...[  
63 - OverlayEntry(  
64 - builder: (context) {  
65 - return GestureDetector(  
66 - onTap: () {  
67 - if (snack.isDismissible && !_onTappedDismiss) {  
68 - _onTappedDismiss = true;  
69 - Get.back();  
70 - }  
71 - },  
72 - child: AnimatedBuilder(  
73 - animation: _filterBlurAnimation,  
74 - builder: (context, child) {  
75 - return BackdropFilter(  
76 - filter: ImageFilter.blur(  
77 - sigmaX: _filterBlurAnimation.value,  
78 - sigmaY: _filterBlurAnimation.value),  
79 - child: Container(  
80 - constraints: BoxConstraints.expand(),  
81 - color: _filterColorAnimation.value,  
82 - ),  
83 - );  
84 - },  
85 - ),  
86 - );  
87 - },  
88 - maintainState: false,  
89 - opaque: opaque,  
90 - ),  
91 - ],  
92 - OverlayEntry(  
93 - builder: (context) {  
94 - final Widget annotatedChild = Semantics(  
95 - child: AlignTransition(  
96 - alignment: _animation!,  
97 - child: snack.isDismissible  
98 - ? _getDismissibleSnack(_builder)  
99 - : _getSnack(),  
100 - ),  
101 - focused: false,  
102 - container: true,  
103 - explicitChildNodes: true,  
104 - );  
105 - return annotatedChild;  
106 - },  
107 - maintainState: false,  
108 - opaque: opaque,  
109 - ),  
110 - ];  
111 - }  
112 -  
113 - String dismissibleKeyGen = "";  
114 -  
115 - Widget _getDismissibleSnack(Widget? child) {  
116 - return Dismissible(  
117 - direction: _getDismissDirection(),  
118 - resizeDuration: null,  
119 - confirmDismiss: (_) {  
120 - if (currentStatus == SnackbarStatus.OPENING ||  
121 - currentStatus == SnackbarStatus.CLOSING) {  
122 - return Future.value(false);  
123 - }  
124 - return Future.value(true);  
125 - },  
126 - key: Key(dismissibleKeyGen),  
127 - onDismissed: (_) {  
128 - dismissibleKeyGen += "1";  
129 - _cancelTimer();  
130 - _wasDismissedBySwipe = true;  
131 -  
132 - if (isCurrent) {  
133 - navigator!.pop();  
134 - } else {  
135 - navigator!.removeRoute(this);  
136 - }  
137 - },  
138 - child: _getSnack(),  
139 - );  
140 - }  
141 -  
142 - Widget _getSnack() {  
143 - return Container(  
144 - margin: snack.margin,  
145 - child: _builder,  
146 - );  
147 - }  
148 -  
149 - DismissDirection _getDismissDirection() {  
150 - if (snack.dismissDirection == SnackDismissDirection.HORIZONTAL) {  
151 - return DismissDirection.horizontal;  
152 - } else {  
153 - if (snack.snackPosition == SnackPosition.TOP) {  
154 - return DismissDirection.up;  
155 - }  
156 - return DismissDirection.down;  
157 - }  
158 - }  
159 -  
160 - @override  
161 - bool get finishedWhenPopped =>  
162 - _controller!.status == AnimationStatus.dismissed;  
163 -  
164 - /// The animation that drives the route's transition and the previous route's  
165 - /// forward transition.  
166 - Animation<Alignment>? _animation;  
167 -  
168 - /// The animation controller that the route uses to drive the transitions.  
169 - ///  
170 - /// The animation itself is exposed by the [animation] property.  
171 - AnimationController? _controller;  
172 -  
173 - /// Called to create the animation controller that will drive the transitions  
174 - /// to this route from the previous one, and back to the previous route  
175 - /// from this one.  
176 - AnimationController createAnimationController() {  
177 - assert(!_transitionCompleter.isCompleted,  
178 - 'Cannot reuse a $runtimeType after disposing it.');  
179 - assert(snack.animationDuration >= Duration.zero);  
180 - return AnimationController(  
181 - duration: snack.animationDuration,  
182 - debugLabel: debugLabel,  
183 - vsync: navigator!,  
184 - );  
185 - }  
186 -  
187 - /// Called to create the animation that exposes the current progress of  
188 - /// the transition controlled by the animation controller created by  
189 - /// `createAnimationController()`.  
190 - Animation<Alignment> createAnimation() {  
191 - assert(!_transitionCompleter.isCompleted,  
192 - 'Cannot reuse a $runtimeType after disposing it.');  
193 - assert(_controller != null);  
194 - return AlignmentTween(begin: _initialAlignment, end: _endAlignment).animate(  
195 - CurvedAnimation(  
196 - parent: _controller!,  
197 - curve: snack.forwardAnimationCurve,  
198 - reverseCurve: snack.reverseAnimationCurve,  
199 - ),  
200 - );  
201 - }  
202 -  
203 - Animation<double> createBlurFilterAnimation() {  
204 - return Tween(begin: 0.0, end: snack.overlayBlur).animate(  
205 - CurvedAnimation(  
206 - parent: _controller!,  
207 - curve: Interval(  
208 - 0.0,  
209 - 0.35,  
210 - curve: Curves.easeInOutCirc,  
211 - ),  
212 - ),  
213 - );  
214 - }  
215 -  
216 - Animation<Color?> createColorFilterAnimation() {  
217 - return ColorTween(begin: Color(0x00000000), end: snack.overlayColor)  
218 - .animate(  
219 - CurvedAnimation(  
220 - parent: _controller!,  
221 - curve: Interval(  
222 - 0.0,  
223 - 0.35,  
224 - curve: Curves.easeInOutCirc,  
225 - ),  
226 - ),  
227 - );  
228 - }  
229 -  
230 - T? _result;  
231 - SnackbarStatus? currentStatus;  
232 -  
233 - void _handleStatusChanged(AnimationStatus status) {  
234 - switch (status) {  
235 - case AnimationStatus.completed:  
236 - currentStatus = SnackbarStatus.OPEN;  
237 - _snackbarStatus(currentStatus);  
238 - if (overlayEntries.isNotEmpty) overlayEntries.first.opaque = opaque;  
239 -  
240 - break;  
241 - case AnimationStatus.forward:  
242 - currentStatus = SnackbarStatus.OPENING;  
243 - _snackbarStatus(currentStatus);  
244 - break;  
245 - case AnimationStatus.reverse:  
246 - currentStatus = SnackbarStatus.CLOSING;  
247 - _snackbarStatus(currentStatus);  
248 - if (overlayEntries.isNotEmpty) overlayEntries.first.opaque = false;  
249 - break;  
250 - case AnimationStatus.dismissed:  
251 - assert(!overlayEntries.first.opaque);  
252 - // We might still be the current route if a subclass is controlling the  
253 - // the transition and hits the dismissed status. For example, the iOS  
254 - // back gesture drives this animation to the dismissed status before  
255 - // popping the navigator.  
256 - currentStatus = SnackbarStatus.CLOSED;  
257 - _snackbarStatus(currentStatus);  
258 -  
259 - if (!isCurrent) {  
260 - navigator!.finalizeRoute(this);  
261 - // assert(overlayEntries.isEmpty);  
262 - }  
263 - break;  
264 - }  
265 - changedInternalState();  
266 - }  
267 -  
268 - @override  
269 - void install() {  
270 - assert(!_transitionCompleter.isCompleted,  
271 - 'Cannot install a $runtimeType after disposing it.');  
272 - _controller = createAnimationController();  
273 - assert(_controller != null,  
274 - '$runtimeType.createAnimationController() returned null.');  
275 - _filterBlurAnimation = createBlurFilterAnimation();  
276 - _filterColorAnimation = createColorFilterAnimation();  
277 - _animation = createAnimation();  
278 - assert(_animation != null, '$runtimeType.createAnimation() returned null.');  
279 - super.install();  
280 - }  
281 -  
282 - @override  
283 - TickerFuture didPush() {  
284 - super.didPush();  
285 - assert(  
286 - _controller != null,  
287 - // ignore: lines_longer_than_80_chars  
288 - '$runtimeType.didPush called before calling install() or after calling dispose().',  
289 - );  
290 - assert(  
291 - !_transitionCompleter.isCompleted,  
292 - 'Cannot reuse a $runtimeType after disposing it.',  
293 - );  
294 - _animation!.addStatusListener(_handleStatusChanged);  
295 - _configureTimer();  
296 - return _controller!.forward();  
297 - }  
298 -  
299 - @override  
300 - void didReplace(Route<dynamic>? oldRoute) {  
301 - assert(  
302 - _controller != null,  
303 - // ignore: lines_longer_than_80_chars  
304 - '$runtimeType.didReplace called before calling install() or after calling dispose().',  
305 - );  
306 - assert(  
307 - !_transitionCompleter.isCompleted,  
308 - 'Cannot reuse a $runtimeType after disposing it.',  
309 - );  
310 -  
311 - if (oldRoute is SnackRoute) {  
312 - _controller!.value = oldRoute._controller!.value;  
313 - }  
314 - _animation!.addStatusListener(_handleStatusChanged);  
315 - super.didReplace(oldRoute);  
316 - }  
317 -  
318 - @override  
319 - bool didPop(T? result) {  
320 - assert(  
321 - _controller != null,  
322 - // ignore: lines_longer_than_80_chars  
323 - '$runtimeType.didPop called before calling install() or after calling dispose().',  
324 - );  
325 - assert(  
326 - !_transitionCompleter.isCompleted,  
327 - 'Cannot reuse a $runtimeType after disposing it.',  
328 - );  
329 -  
330 - _result = result;  
331 - _cancelTimer();  
332 -  
333 - if (_wasDismissedBySwipe) {  
334 - Timer(Duration(milliseconds: 200), () {  
335 - _controller!.reset();  
336 - });  
337 -  
338 - _wasDismissedBySwipe = false;  
339 - } else {  
340 - _controller!.reverse();  
341 - }  
342 -  
343 - return super.didPop(result);  
344 - }  
345 -  
346 - void _configureTimer() {  
347 - if (snack.duration != null) {  
348 - if (_timer != null && _timer!.isActive) {  
349 - _timer!.cancel();  
350 - }  
351 - _timer = Timer(snack.duration!, () {  
352 - if (isCurrent) {  
353 - navigator!.pop();  
354 - } else if (isActive) {  
355 - navigator!.removeRoute(this);  
356 - }  
357 - });  
358 - } else {  
359 - if (_timer != null) {  
360 - _timer!.cancel();  
361 - }  
362 - }  
363 - }  
364 -  
365 - void _cancelTimer() {  
366 - if (_timer != null && _timer!.isActive) {  
367 - _timer!.cancel();  
368 - }  
369 - }  
370 -  
371 - /// Whether this route can perform a transition to the given route.  
372 - /// Subclasses can override this method to restrict the set of routes they  
373 - /// need to coordinate transitions with.  
374 - bool canTransitionTo(SnackRoute<dynamic> nextRoute) => true;  
375 -  
376 - /// Whether this route can perform a transition from the given route.  
377 - ///  
378 - /// Subclasses can override this method to restrict the set of routes they  
379 - /// need to coordinate transitions with.  
380 - bool canTransitionFrom(SnackRoute<dynamic> previousRoute) => true;  
381 -  
382 - @override  
383 - void dispose() {  
384 - assert(!_transitionCompleter.isCompleted,  
385 - 'Cannot dispose a $runtimeType twice.');  
386 - _controller?.dispose();  
387 - _transitionCompleter.complete(_result);  
388 - super.dispose();  
389 - }  
390 -  
391 - /// A short description of this route useful for debugging.  
392 - String get debugLabel => '$runtimeType';  
393 -}