Jonny Borges
Committed by GitHub

Merge branch 'master' into master

Showing 65 changed files with 1291 additions and 1307 deletions

Too many changes to show.

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

No preview for this file type
## [4.5.1] - Big Update
## [4.6.1]
Fix GetConnect on Flutter web
## [4.6.0]
Add useInheritedMediaQuery to GetMaterialApp and GetCupertinoApp (@davidhole)
Add Circular reveal Transition (@parmarravi)
Add request to failed response (@heftekharm)
Fix internationalization with only country code (@codercengiz)
Add GetTickerProviderStateMixin when multiple AnimationController objects are used (@NatsuOnFire)
Add the followRedirects and maxRedirects fields to the Request object (@wei53881)
Fix to rx.trigger fires twice (@gslender)
Add proxy setting support to GetConnect (@jtans)
Fix markAsDirty used on permanent controllers (@zenalex)
Update Korean readme (@dumbokim)
## [4.5.1]
Fix Snackbar when it have action and icon the same time
## [4.5.0] - Big Update
To have a context-free, page-agnostic snackbar, we used OverlayRoute to display a partial route.
To have a page-agnostic snackbar, we used OverlayRoute to display a partial route.
However this had several problems:
1: There was no possibility to close the page without closing the snackbar
... ...
... ... @@ -37,6 +37,17 @@ _语言: 中文, [英文](README.md), [越南文](README-vi.md), [印度尼西
- [改变语言](#改变语言)
- [系统语言](#系统语言)
- [改变主题](#改变主题)
- [GetConnect](#getconnect)
- [默认配置](#默认配置)
- [自定义配置](#自定义配置)
- [GetPage 中间件](#getpage-中间件)
- [优先级](#优先级)
- [Redirect](#redirect)
- [onPageCalled](#onpagecalled)
- [OnBindingsStart](#onbindingsstart)
- [OnPageBuildStart](#onpagebuildstart)
- [OnPageBuilt](#onpagebuilt)
- [OnPageDispose](#onpagedispose)
- [其他高级API](#其他高级api)
- [可选的全局设置和手动配置](#可选的全局设置和手动配置)
- [局部状态组件](#局部状态组件)
... ... @@ -394,6 +405,163 @@ Get.changeTheme(Get.isDarkMode? ThemeData.light(): ThemeData.dark());
`.darkmode`被激活时,它将切换到light主题,当light主题被激活时,它将切换到dark主题。
## GetConnect
GetConnect可以便捷的通过http或websockets进行前后台通信。
### 默认配置
你能轻松的通过extend GetConnect就能使用GET/POST/PUT/DELETE/SOCKET方法与你的Rest API或websockets通信。
```dart
class UserProvider extends GetConnect {
// Get request
Future<Response> getUser(int id) => get('http://youapi/users/$id');
// Post request
Future<Response> postUser(Map data) => post('http://youapi/users', body: data);
// Post request with File
Future<Response<CasesModel>> postCases(List<int> image) {
final form = FormData({
'file': MultipartFile(image, filename: 'avatar.png'),
'otherFile': MultipartFile(image, filename: 'cover.png'),
});
return post('http://youapi/users/upload', form);
}
GetSocket userMessages() {
return socket('https://yourapi/users/socket');
}
}
```
### 自定义配置
GetConnect具有多种自定义配置。你可以配置base Url,配置响应,配置请求,添加权限验证,甚至是尝试认证的次数,除此之外,还可以定义一个标准的解码器,该解码器将把您的所有请求转换为您的模型,而不需要任何额外的配置。
```dart
class HomeProvider extends GetConnect {
@override
void onInit() {
// All request will pass to jsonEncode so CasesModel.fromJson()
httpClient.defaultDecoder = CasesModel.fromJson;
httpClient.baseUrl = 'https://api.covid19api.com';
// baseUrl = 'https://api.covid19api.com'; // It define baseUrl to
// Http and websockets if used with no [httpClient] instance
// It's will attach 'apikey' property on header from all requests
httpClient.addRequestModifier((request) {
request.headers['apikey'] = '12345678';
return request;
});
// Even if the server sends data from the country "Brazil",
// it will never be displayed to users, because you remove
// that data from the response, even before the response is delivered
httpClient.addResponseModifier<CasesModel>((request, response) {
CasesModel model = response.body;
if (model.countries.contains('Brazil')) {
model.countries.remove('Brazilll');
}
});
httpClient.addAuthenticator((request) async {
final response = await get("http://yourapi/token");
final token = response.body['token'];
// Set the header
request.headers['Authorization'] = "$token";
return request;
});
//Autenticator will be called 3 times if HttpStatus is
//HttpStatus.unauthorized
httpClient.maxAuthRetries = 3;
}
}
@override
Future<Response<CasesModel>> getCases(String path) => get(path);
}
```
## GetPage 中间件
GetPage现在有个新的参数可以把列表中的Get中间件按指定顺序执行。
**注意**: 当GetPage有中间件时,所有的子page会自动有相同的中间件。
### 优先级
设置中间件的优先级定义Get中间件的执行顺序。
```dart
final middlewares = [
GetMiddleware(priority: 2),
GetMiddleware(priority: 5),
GetMiddleware(priority: 4),
GetMiddleware(priority: -8),
];
```
这些中间件会按这个顺序执行 **-8 => 2 => 4 => 5**
### Redirect
当被调用路由的页面被搜索时,这个函数将被调用。它将RouteSettings作为重定向的结果。或者给它null,就没有重定向了。
```dart
RouteSettings redirect(String route) {
final authService = Get.find<AuthService>();
return authService.authed.value ? null : RouteSettings(name: '/login')
}
```
### onPageCalled
在调用页面时,创建任何东西之前,这个函数会先被调用。
您可以使用它来更改页面的某些内容或给它一个新页面。
```dart
GetPage onPageCalled(GetPage page) {
final authService = Get.find<AuthService>();
return page.copyWith(title: 'Welcome ${authService.UserName}');
}
```
### OnBindingsStart
这个函数将在绑定初始化之前被调用。
在这里,您可以更改此页面的绑定。
```dart
List<Bindings> onBindingsStart(List<Bindings> bindings) {
final authService = Get.find<AuthService>();
if (authService.isAdmin) {
bindings.add(AdminBinding());
}
return bindings;
}
```
### OnPageBuildStart
这个函数将在绑定初始化之后被调用。
在这里,您可以在创建绑定之后和创建页面widget之前执行一些操作。
```dart
GetPageBuilder onPageBuildStart(GetPageBuilder page) {
print('bindings are ready');
return page;
}
```
### OnPageBuilt
这个函数将在GetPage.page调用后被调用,并给出函数的结果,并获取将要显示的widget。
### OnPageDispose
这个函数将在处理完页面的所有相关对象(Controllers, views, ...)之后被调用。
## 其他高级API
```dart
... ...
... ... @@ -59,11 +59,11 @@ var name = 'Jonatas Borges'.obs;
就这么简单!
我们把这个reactive-".obs"(ervables)变量称为_Rx_
我们把这个reactive-".obs"(ervables)变量称为 _Rx_
我们做了什么?我们创建了一个 "Stream "的 "String",分配了初始值 "Jonatas Borges",我们通知所有使用 "Jonatas Borges "的widgets,它们现在 "属于 "这个变量,当_Rx_的值发生变化时,它们也要随之改变。
这就是GetX**的**魔力,这要归功于Dart的能力。
这就是GetX **的** 魔力,这要归功于Dart的能力。
但是,我们知道,一个`Widget`只有在函数里面才能改变,因为静态类没有 "自动改变 "的能力。
... ... @@ -80,15 +80,15 @@ var name = 'Jonatas Borges'.obs;
Obx (() => Text (controller.name));
```
_你只需记住 `Obx(()=>`
你只需记住 `Obx(()=>`
你只需将Widget通过一个箭头函数传递给 `Obx()`(_Rx_的 "观察者")。
你只需将Widget通过一个箭头函数传递给 `Obx()`( _Rx_ 的 "观察者")。
`Obx`是相当聪明的,只有当`controller.name`的值发生变化时才会改变。
如果`name`是`"John"`,你把它改成了`"John"`(`name.value="John"`),因为它和之前的`value`是一样的,所以界面上不会有任何变化,而`Obx`为了节省资源,会直接忽略新的值,不重建Widget。**这是不是很神奇**
> 那么,如果我在一个`Obx`里有5个_Rx_(可观察的)变量呢?
> 那么,如果我在一个`Obx`里有5个 _Rx_ (可观察的)变量呢?
当其中**任何**一个变量发生变化时,它就会更新。
... ... @@ -96,7 +96,7 @@ _你只需记住 `Obx(()=>`
不会,只会更新使用那个 _Rx_ 变量的**特定 Widget**
所以,只有当_Rx_变量的值发生变化时,**GetX**才会更新界面。
所以,只有当 _Rx_ 变量的值发生变化时,**GetX**才会更新界面。
```
final isOpen = false.obs;
... ... @@ -114,13 +114,13 @@ void onButtonTap() => isOpen.value=false;
如果你需要一个**强大的**状态管理器,用**GetX**是不会错的。
它不能和变量一起工作,除了__flows__,它里面的东西本质都是`Streams`
你可以将_rxDart_与它结合使用,因为所有的东西都是`Streams`
你可以监听每个"_Rx_变量 "的 "事件"。
它不能和变量一起工作,除了 __flows__ ,它里面的东西本质都是`Streams`
你可以将 _rxDart_ 与它结合使用,因为所有的东西都是`Streams`
你可以监听每个" _Rx_ 变量 "的 "事件"。
因为里面的所有东西都是 "Streams"。
这实际上是一种_BLoC_方法,比_MobX_更容易,而且没有代码生成器或装饰。
你可以把**任何东西**变成一个_"Observable"_,只需要在它末尾加上`.obs`
这实际上是一种 _BLoC_ 方法,比 _MobX_ 更容易,而且没有代码生成器或装饰。
你可以把**任何东西**变成一个 _"Observable"_ ,只需要在它末尾加上`.obs`
### 最高性能
... ... @@ -129,8 +129,8 @@ void onButtonTap() => isOpen.value=false;
如果你的应用程序中遇到错误,并发送重复的状态变更,**GetX**将确保它不会崩溃。
使用**GetX**,只有当`value`改变时,状态才会改变。
这就是**GetX**,和使用MobX_的_`computed`的主要区别。
当加入两个__observable__,其中一个发生变化时,该_observable_的监听器也会发生变化。
这就是**GetX**,和使用MobX _的_ `computed`的主要区别。
当加入两个 __observable__ ,其中一个发生变化时,该 _observable_ 的监听器也会发生变化。
使用**GetX**,如果你连接了两个变量,`GetX()`(类似于`Observer()`)只有在它的状态真正变化时才会重建。
... ... @@ -183,13 +183,13 @@ final user = User().obs;
##### 有一个反应的状态,很容易。
我们知道,_Dart_现在正朝着_null safety_的方向发展。
为了做好准备,从现在开始,你应该总是用一个**初始值**来开始你的_Rx_变量。
我们知道, _Dart_ 现在正朝着 _null safety_ 的方向发展。
为了做好准备,从现在开始,你应该总是用一个**初始值**来开始你的 _Rx_ 变量。
> 用**GetX**将一个变量转化为一个_observable_ + _initial value_是最简单,也是最实用的方法。
> 用**GetX**将一个变量转化为一个 _observable_ + _initial value_ 是最简单,也是最实用的方法。
你只需在变量的末尾添加一个"`.obs`",即可把它变成可观察的变量,
然后它的`.value`就是_初始值_)。
然后它的`.value`就是 _初始值_ )。
### 使用视图中的值
... ... @@ -411,17 +411,17 @@ interval(count1, (_) => print("interval $_"), time: Duration(seconds: 1));
所有worker都会返回一个`Worker`实例,你可以用它来取消(通过`dispose()`)worker。
- **`ever`**
每当_Rx_变量发出一个新的值时,就会被调用。
每当 _Rx_ 变量发出一个新的值时,就会被调用。
- **`everAll`**
和 "ever "很像,但它需要一个_Rx_值的 "List",每次它的变量被改变时都会被调用。就是这样。
和 "ever "很像,但它需要一个 _Rx_ 值的 "List",每次它的变量被改变时都会被调用。就是这样。
- **`once`**
'once'只在变量第一次被改变时被调用。
- **`debounce`**
debounce'在搜索函数中非常有用,你只希望API在用户完成输入时被调用。如果用户输入 "Jonny",你将在API中进行5次搜索,分别是字母J、o、n、n和y。使用Get不会发生这种情况,因为你将有一个 "debounce "Worker,它只会在输入结束时触发。
'debounce'在搜索函数中非常有用,你只希望API在用户完成输入时被调用。如果用户输入 "Jonny",你将在API中进行5次搜索,分别是字母J、o、n、n和y。使用Get不会发生这种情况,因为你将有一个 "debounce "Worker,它只会在输入结束时触发。
- **`interval`**
'interval'与debouce不同,debouce如果用户在1秒内对一个变量进行了1000次修改,他将在规定的计时器(默认为800毫秒)后只发送最后一次修改。Interval则会忽略规定时间内的所有用户操作。如果你发送事件1分钟,每秒1000个,那么当用户停止DDOS事件时,debounce将只发送最后一个事件。建议这样做是为了避免滥用,在用户可以快速点击某样东西并获得一些好处的功能中(想象一下,用户点击某样东西可以赚取硬币,如果他在同一分钟内点击300次,他就会有300个硬币,使用间隔,你可以设置时间范围为3秒,无论是点击300次或100万次,1分钟内他最多获得20个硬币)。debounce适用于防DDOS,适用于搜索等功能,每次改变onChange都会调用你的api进行查询。Debounce会等待用户停止输入名称,进行请求。如果在上面提到的投币场景中使用它,用户只会赢得1个硬币,因为只有当用户 "暂停 "到既定时间时,它才会被执行。
... ...
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
... ... @@ -15,11 +14,11 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp.router(
return GetMaterialApp(
debugShowCheckedModeBanner: false,
enableLog: true,
logWriterCallback: Logger.write,
// initialRoute: AppPages.INITIAL,
initialRoute: AppPages.INITIAL,
getPages: AppPages.routes,
locale: TranslationService.locale,
fallbackLocale: TranslationService.fallbackLocale,
... ...
import 'package:get/get.dart';
import '../data/home_api_provider.dart';
import '../data/home_api_provider.dart';
import '../data/home_repository.dart';
import '../domain/adapters/repository_adapter.dart';
import '../presentation/controllers/home_controller.dart';
... ...
import 'package:get/get.dart';
import '../domain/entity/cases_model.dart';
// ignore: one_member_abstracts
... ... @@ -12,6 +13,7 @@ class HomeProvider extends GetConnect implements IHomeProvider {
httpClient.defaultDecoder =
(val) => CasesModel.fromJson(val as Map<String, dynamic>);
httpClient.baseUrl = 'https://api.covid19api.com';
super.onInit();
}
@override
... ...
... ... @@ -3,7 +3,7 @@ import 'package:get/get.dart';
import '../../domain/adapters/repository_adapter.dart';
import '../../domain/entity/cases_model.dart';
class HomeController extends SuperController<CasesModel> {
class HomeController extends StateController<CasesModel> {
HomeController({required this.homeRepository});
final IHomeRepository homeRepository;
... ... @@ -11,74 +11,12 @@ class HomeController extends SuperController<CasesModel> {
@override
void onInit() {
super.onInit();
//Loading, Success, Error handle with 1 line of code
append(() => homeRepository.getCases);
futurize(() => homeRepository.getCases);
}
Country getCountryById(String id) {
final index = int.tryParse(id);
if (index != null) {
return state!.countries[index];
}
return state!.countries.first;
}
@override
void onReady() {
print('The build method is done. '
'Your controller is ready to call dialogs and snackbars');
super.onReady();
}
@override
void onClose() {
print('onClose called');
super.onClose();
}
@override
void didChangeMetrics() {
print('the window size did change');
super.didChangeMetrics();
}
@override
void didChangePlatformBrightness() {
print('platform change ThemeMode');
super.didChangePlatformBrightness();
}
@override
Future<bool> didPushRoute(String route) {
print('the route $route will be open');
return super.didPushRoute(route);
}
@override
Future<bool> didPopRoute() {
print('the current route will be closed');
return super.didPopRoute();
}
@override
void onDetached() {
print('onDetached called');
}
@override
void onInactive() {
print('onInative called');
}
@override
void onPaused() {
print('onPaused called');
}
@override
void onResumed() {
print('onResumed called');
return index != null ? state.countries[index] : state.countries.first;
}
}
... ...
... ... @@ -6,6 +6,7 @@ import 'package:get/get.dart';
import '../controllers/home_controller.dart';
class CountryView extends GetView<HomeController> {
const CountryView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
... ... @@ -28,14 +29,15 @@ class CountryView extends GetView<HomeController> {
),
body: Center(
child: ListView.builder(
itemCount: controller.state!.countries.length,
itemCount: controller.state.countries.length,
itemBuilder: (context, index) {
final country = controller.state!.countries[index];
final country = controller.state.countries[index];
return ListTile(
onTap: () {
onTap: () async {
//Get.rootDelegate.toNamed('/home/country');
Get.rootDelegate
.toNamed('/home/country/details?id=$index');
final data = await Get.toNamed(
'/home/country/details?id=$index');
print(data);
},
trailing: CircleAvatar(
backgroundImage: NetworkImage(
... ...
... ... @@ -2,12 +2,14 @@ import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/home_controller.dart';
class DetailsView extends GetView<HomeController> {
const DetailsView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final parameter = Get.rootDelegate.parameters;
final parameter = context.params; //Get.parameters;
final country = controller.getCountryById(parameter['id'] ?? '');
return Container(
decoration: BoxDecoration(
... ... @@ -76,6 +78,11 @@ class DetailsView extends GetView<HomeController> {
'${country.totalRecovered}',
style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold),
),
TextButton(
onPressed: () {
Get.back(result: 'djsoidjsoidj');
},
child: Text('back'))
],
)),
),
... ...
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/home_controller.dart';
class HomeView extends GetView<HomeController> {
const HomeView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
... ... @@ -77,9 +77,8 @@ class HomeView extends GetView<HomeController> {
shape: StadiumBorder(),
),
onPressed: () async {
final data =
await Get.rootDelegate.toNamed('/home/country');
print('DATA: $data');
//await Navigation Get.rootDelegate.toNamed('/home/country');
Get.toNamed('/home/country');
},
child: Text(
'fetch_country'.tr,
... ...
... ... @@ -15,7 +15,7 @@ class AppPages {
GetPage(
name: Routes.HOME,
page: () => HomeView(),
binding: HomeBinding(),
bindings: [HomeBinding()],
children: [
GetPage(
name: Routes.COUNTRY,
... ... @@ -27,6 +27,7 @@ class AppPages {
),
],
),
]),
],
),
];
}
... ...
... ... @@ -4,4 +4,8 @@ abstract class Routes {
static const HOME = '/home';
static const COUNTRY = '/country';
static const DETAILS = '/details';
static const DASHBOARD = '/dashboard';
static const PROFILE = '/profile';
static const PRODUCTS = '/products';
}
... ...
class Logger {
mixin Logger {
// Sample of abstract logging function
static void write(String text, {bool isError = false}) {
Future.microtask(() => print('** $text. isError: [$isError]'));
... ...
... ... @@ -20,6 +20,10 @@ version: 1.0.0+1
environment:
sdk: ">=2.12.0 <3.0.0"
dependency_overrides:
get:
path: ../
dependencies:
flutter:
sdk: flutter
... ... @@ -33,6 +37,7 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
get_test: 4.0.1
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
... ...
import 'dart:io';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:get/get.dart';
... ... @@ -7,16 +7,16 @@ import 'package:get/get.dart';
// import 'package:get_test/get_test.dart';
import 'package:matcher/matcher.dart' as m;
import '../lib/pages/home/domain/adapters/repository_adapter.dart';
import '../lib/pages/home/domain/entity/cases_model.dart';
import '../lib/pages/home/presentation/controllers/home_controller.dart';
class MockRepository implements IHomeRepository {
class MockRepositorySuccess implements IHomeRepository {
@override
Future<CasesModel> getCases() async {
await Future.delayed(Duration(milliseconds: 100));
if (Random().nextBool()) {
return CasesModel(
global: Global(
totalDeaths: 100,
... ... @@ -32,7 +32,11 @@ class MockRepository implements IHomeRepository {
message: '',
);
}
}
class MockRepositoryFailure implements IHomeRepository {
@override
Future<CasesModel> getCases() async {
return Future<CasesModel>.error('error');
}
}
... ... @@ -41,28 +45,18 @@ void main() {
WidgetsFlutterBinding.ensureInitialized();
setUpAll(() => HttpOverrides.global = null);
final binding = BindingsBuilder(() {
Get.lazyPut<IHomeRepository>(() => MockRepository());
Get.lazyPut<IHomeRepository>(() => MockRepositorySuccess());
Get.lazyPut<HomeController>(
() => HomeController(homeRepository: Get.find()));
() => HomeController(homeRepository: Get.find()),
);
});
test('Test Binding', () {
expect(Get.isPrepared<HomeController>(), false);
expect(Get.isPrepared<IHomeRepository>(), false);
/// test you Binding class with BindingsBuilder
binding.builder();
expect(Get.isPrepared<HomeController>(), true);
expect(Get.isPrepared<IHomeRepository>(), true);
Get.reset();
});
test('Test Controller', () async {
/// Controller can't be on memory
expect(() => Get.find<HomeController>(), throwsA(m.TypeMatcher<String>()));
expect(() => Get.find<HomeController>(tag: 'success'),
throwsA(m.TypeMatcher<String>()));
/// build Binding
/// binding will put the controller on memory
binding.builder();
/// recover your controller
... ... @@ -77,24 +71,15 @@ void main() {
/// await time request
await Future.delayed(Duration(milliseconds: 100));
if (controller.status.isError) {
expect(controller.state, null);
}
/// test if status is success
expect(controller.status.isSuccess, true);
expect(controller.state.global.totalDeaths, 100);
expect(controller.state.global.totalConfirmed, 200);
if (controller.status.isSuccess) {
expect(controller.state!.global.totalDeaths, 100);
expect(controller.state!.global.totalConfirmed, 200);
}
});
test('ever', () async {
final count = ''.obs;
var result = '';
ever<String>(count, (value) {
result = value;
});
count.value = '1';
expect('1', result);
/// test if status is error
Get.lazyReplace<IHomeRepository>(() => MockRepositoryFailure());
expect(controller.status.isError, true);
expect(controller.state, null);
});
/// Tests with GetTests
... ... @@ -151,26 +136,3 @@ void main() {
},
);*/
}
class Controller extends GetxController {
final count = 0.obs;
void increment() => count.value++;
@override
void onInit() {
print('inittt');
super.onInit();
}
@override
void onReady() {
print('onReady');
super.onReady();
}
@override
void onClose() {
super.onClose();
print('onClose');
}
}
... ...
... ... @@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>8.0</string>
<string>9.0</string>
</dict>
</plist>
... ...
... ... @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
... ... @@ -127,7 +127,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1020;
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
... ...
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
... ...
... ... @@ -5,14 +5,14 @@ import '../routes/app_pages.dart';
class EnsureAuthMiddleware extends GetMiddleware {
@override
Future<GetNavConfig?> redirectDelegate(GetNavConfig route) async {
Future<RouteDecoder?> redirectDelegate(RouteDecoder route) async {
// you can do whatever you want here
// but it's preferable to make this method fast
// await Future.delayed(Duration(milliseconds: 500));
if (!AuthService.to.isLoggedInValue) {
final newRoute = Routes.LOGIN_THEN(route.location!);
return GetNavConfig.fromRoute(newRoute);
final newRoute = Routes.LOGIN_THEN(route.pageSettings!.name);
return RouteDecoder.fromRoute(newRoute);
}
return await super.redirectDelegate(route);
}
... ... @@ -20,13 +20,13 @@ class EnsureAuthMiddleware extends GetMiddleware {
class EnsureNotAuthedMiddleware extends GetMiddleware {
@override
Future<GetNavConfig?> redirectDelegate(GetNavConfig route) async {
Future<RouteDecoder?> redirectDelegate(RouteDecoder route) async {
if (AuthService.to.isLoggedInValue) {
//NEVER navigate to auth screen, when user is already authed
return null;
//OR redirect user to another screen
//return GetNavConfig.fromRoute(Routes.PROFILE);
//return RouteDecoder.fromRoute(Routes.PROFILE);
}
return await super.redirectDelegate(route);
}
... ...
... ... @@ -2,11 +2,13 @@ import 'package:get/get.dart';
import '../controllers/dashboard_controller.dart';
class DashboardBinding extends Bindings {
class DashboardBinding extends Binding {
@override
void dependencies() {
Get.lazyPut<DashboardController>(
List<Bind> dependencies() {
return [
Bind.lazyPut<DashboardController>(
() => DashboardController(),
);
)
];
}
}
... ...
... ... @@ -2,11 +2,13 @@ import 'package:get/get.dart';
import '../controllers/home_controller.dart';
class HomeBinding extends Bindings {
class HomeBinding extends Binding {
@override
void dependencies() {
Get.lazyPut<HomeController>(
List<Bind> dependencies() {
return [
Bind.lazyPut<HomeController>(
() => HomeController(),
);
)
];
}
}
... ...
... ... @@ -5,24 +5,29 @@ import '../../../routes/app_pages.dart';
import '../controllers/home_controller.dart';
class HomeView extends GetView<HomeController> {
const HomeView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return GetRouterOutlet.builder(
builder: (context, delegate, currentRoute) {
routerDelegate: Get.nestedKey(Routes.HOME),
builder: (context) {
final delegate = context.navigation;
//This router outlet handles the appbar and the bottom navigation bar
final currentLocation = currentRoute?.location;
final currentLocation = context.location;
var currentIndex = 0;
if (currentLocation?.startsWith(Routes.PRODUCTS) == true) {
if (currentLocation.startsWith(Routes.PRODUCTS) == true) {
currentIndex = 2;
}
if (currentLocation?.startsWith(Routes.PROFILE) == true) {
if (currentLocation.startsWith(Routes.PROFILE) == true) {
currentIndex = 1;
}
return Scaffold(
body: GetRouterOutlet(
initialRoute: Routes.DASHBOARD,
// anchorRoute: Routes.HOME,
key: Get.nestedKey(Routes.HOME),
anchorRoute: Routes.HOME,
//delegate: Get.nestedKey(Routes.HOME),
// key: Get.nestedKey(Routes.HOME),
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: currentIndex,
... ...
... ... @@ -2,11 +2,11 @@ import 'package:get/get.dart';
import '../controllers/login_controller.dart';
class LoginBinding extends Bindings {
class LoginBinding extends Binding {
@override
void dependencies() {
Get.lazyPut<LoginController>(
() => LoginController(),
);
List<Bind> dependencies() {
return [
Bind.lazyPut(() => LoginController()),
];
}
}
... ...
... ... @@ -31,9 +31,8 @@ class LoginView extends GetView<LoginController> {
),
onPressed: () {
AuthService.to.login();
final thenTo = Get.rootDelegate.currentConfiguration!
.currentPage!.parameters?['then'];
Get.rootDelegate.offNamed(thenTo ?? Routes.HOME);
final thenTo = context.params['then'];
Get.offNamed(thenTo ?? Routes.HOME);
},
),
],
... ...
... ... @@ -2,13 +2,15 @@ import 'package:get/get.dart';
import '../controllers/product_details_controller.dart';
class ProductDetailsBinding extends Bindings {
class ProductDetailsBinding extends Binding {
@override
void dependencies() {
Get.create<ProductDetailsController>(
List<Bind> dependencies() {
return [
Bind.create<ProductDetailsController>(
() => ProductDetailsController(
Get.parameters['productId'] ?? '',
),
);
)
];
}
}
... ...
... ... @@ -13,6 +13,7 @@ class ProductDetailsController extends GetxController {
@override
void onClose() {
Get.log('ProductDetailsController close with id: $productId');
super.onClose();
}
}
... ...
... ... @@ -5,11 +5,12 @@ import '../../../routes/app_pages.dart';
import '../controllers/products_controller.dart';
class ProductsView extends GetView<ProductsController> {
const ProductsView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton.extended(
onPressed: controller.loadDemoProductsFromSomeWhere,
onPressed: () => controller.loadDemoProductsFromSomeWhere(),
label: Text('Add'),
),
body: Column(
... ... @@ -31,8 +32,7 @@ class ProductsView extends GetView<ProductsController> {
final item = controller.products[index];
return ListTile(
onTap: () {
Get.rootDelegate
.toNamed(Routes.PRODUCT_DETAILS(item.id));
Get.toNamed(Routes.PRODUCT_DETAILS(item.id));
},
title: Text(item.name),
subtitle: Text(item.id),
... ...
... ... @@ -2,11 +2,13 @@ import 'package:get/get.dart';
import '../controllers/profile_controller.dart';
class ProfileBinding extends Bindings {
class ProfileBinding extends Binding {
@override
void dependencies() {
Get.lazyPut<ProfileController>(
List<Bind> dependencies() {
return [
Bind.lazyPut<ProfileController>(
() => ProfileController(),
);
)
];
}
}
... ...
... ... @@ -39,7 +39,8 @@ class ProfileView extends GetView<ProfileController> {
Get.defaultDialog(
title: 'Test Dialog In Home Outlet !!',
barrierDismissible: true,
navigatorKey: Get.nestedKey(Routes.HOME),
id: Routes.HOME,
// navigatorKey: Get.nestedKey(Routes.HOME),
);
},
)
... ...
... ... @@ -2,11 +2,13 @@ import 'package:get/get.dart';
import '../controllers/root_controller.dart';
class RootBinding extends Bindings {
class RootBinding extends Binding {
@override
void dependencies() {
Get.lazyPut<RootController>(
List<Bind> dependencies() {
return [
Bind.lazyPut<RootController>(
() => RootController(),
);
)
];
}
}
... ...
... ... @@ -21,7 +21,7 @@ class DrawerWidget extends StatelessWidget {
ListTile(
title: Text('Home'),
onTap: () {
Get.rootDelegate.toNamed(Routes.HOME);
Get.toNamed(Routes.HOME);
//to close the drawer
Navigator.of(context).pop();
... ... @@ -30,7 +30,7 @@ class DrawerWidget extends StatelessWidget {
ListTile(
title: Text('Settings'),
onTap: () {
Get.rootDelegate.toNamed(Routes.SETTINGS);
Get.toNamed(Routes.SETTINGS);
//to close the drawer
Navigator.of(context).pop();
... ... @@ -46,7 +46,7 @@ class DrawerWidget extends StatelessWidget {
),
onTap: () {
AuthService.to.logout();
Get.rootDelegate.toNamed(Routes.LOGIN);
Get.toNamed(Routes.LOGIN);
//to close the drawer
Navigator.of(context).pop();
... ... @@ -61,7 +61,7 @@ class DrawerWidget extends StatelessWidget {
),
),
onTap: () {
Get.rootDelegate.toNamed(Routes.LOGIN);
Get.toNamed(Routes.LOGIN);
//to close the drawer
Navigator.of(context).pop();
... ...
... ... @@ -8,21 +8,28 @@ import 'drawer.dart';
class RootView extends GetView<RootController> {
@override
Widget build(BuildContext context) {
return GetRouterOutlet.builder(
builder: (context, delegate, current) {
final title = current?.location;
return RouterOutlet.builder(
delegate: Get.nestedKey(null),
builder: (context) {
final title = context.location;
return Scaffold(
drawer: DrawerWidget(),
appBar: AppBar(
title: Text(title ?? ''),
title: Text(title),
centerTitle: true,
),
//body: HomeView(),
body: GetRouterOutlet(
initialRoute: Routes.HOME,
// anchorRoute: '/',
// filterPages: (afterAnchor) {
// return afterAnchor.take(1);
// },
delegate: Get.nestedKey(null),
anchorRoute: '/',
filterPages: (afterAnchor) {
// print(afterAnchor);
// print('dddddddddddddddddd');
// print(afterAnchor.take(1));
return afterAnchor.take(1);
},
),
);
},
... ...
... ... @@ -2,11 +2,13 @@ import 'package:get/get.dart';
import '../controllers/settings_controller.dart';
class SettingsBinding extends Bindings {
class SettingsBinding extends Binding {
@override
void dependencies() {
Get.lazyPut<SettingsController>(
List<Bind> dependencies() {
return [
Bind.lazyPut<SettingsController>(
() => SettingsController(),
);
)
];
}
}
... ...
import 'dart:async';
import 'package:async/async.dart';
import 'package:get/get.dart';
class SplashService extends GetxService {
final welcomeStr = ['GetX', 'Rules!'];
final activeStr = 0.obs;
final memo = AsyncMemoizer<void>();
Future<void> init() {
return memo.runOnce(_initFunction);
}
void _changeActiveString() {
activeStr.value = (activeStr.value + 1) % welcomeStr.length;
}
Future<void> _initFunction() async {
final t = Timer.periodic(
Duration(milliseconds: 500),
(t) => _changeActiveString(),
);
//simulate some long running operation
await Future.delayed(Duration(seconds: 5));
//cancel the timer once we are done
t.cancel();
}
}
... ...
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/splash_service.dart';
class SplashView extends GetView<SplashService> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Obx(
() => Text(
controller.welcomeStr[controller.activeStr.value],
style: TextStyle(fontSize: 20),
),
),
CircularProgressIndicator(),
],
),
),
);
}
}
... ...
... ... @@ -29,7 +29,7 @@ class AppPages {
GetPage(
name: '/',
page: () => RootView(),
binding: RootBinding(),
bindings: [RootBinding()],
participatesInRootNavigator: true,
preventDuplicates: true,
children: [
... ... @@ -40,12 +40,12 @@ class AppPages {
],
name: _Paths.LOGIN,
page: () => LoginView(),
binding: LoginBinding(),
bindings: [LoginBinding()],
),
GetPage(
preventDuplicates: true,
name: _Paths.HOME,
page: () => HomeView(),
page: () => const HomeView(),
bindings: [
HomeBinding(),
],
... ... @@ -54,7 +54,9 @@ class AppPages {
GetPage(
name: _Paths.DASHBOARD,
page: () => DashboardView(),
binding: DashboardBinding(),
bindings: [
DashboardBinding(),
],
),
GetPage(
middlewares: [
... ... @@ -65,19 +67,20 @@ class AppPages {
page: () => ProfileView(),
title: 'Profile',
transition: Transition.size,
binding: ProfileBinding(),
bindings: [ProfileBinding()],
),
GetPage(
name: _Paths.PRODUCTS,
page: () => ProductsView(),
page: () => const ProductsView(),
title: 'Products',
transition: Transition.zoom,
binding: ProductsBinding(),
participatesInRootNavigator: false,
bindings: [ProductsBinding()],
children: [
GetPage(
name: _Paths.PRODUCT_DETAILS,
page: () => ProductDetailsView(),
binding: ProductDetailsBinding(),
bindings: [ProductDetailsBinding()],
middlewares: [
//only enter this route when authed
EnsureAuthMiddleware(),
... ... @@ -90,7 +93,9 @@ class AppPages {
GetPage(
name: _Paths.SETTINGS,
page: () => SettingsView(),
binding: SettingsBinding(),
bindings: [
SettingsBinding(),
],
),
],
),
... ...
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'app/modules/splash/controllers/splash_service.dart';
import 'app/routes/app_pages.dart';
import 'services/auth_service.dart';
void main() {
runApp(
GetMaterialApp.router(
GetMaterialApp(
title: "Application",
initialBinding: BindingsBuilder(
() {
Get.put(SplashService());
Get.put(AuthService());
},
),
getPages: AppPages.routes,
initialRoute: AppPages.INITIAL,
// builder: (context, child) {
// return FutureBuilder<void>(
// key: ValueKey('initFuture'),
// future: Get.find<SplashService>().init(),
// builder: (context, snapshot) {
// if (snapshot.connectionState == ConnectionState.done) {
// return child ?? SizedBox.shrink();
// }
// return SplashView();
// },
// );
// },
// routeInformationParser: GetInformationParser(
// // initialRoute: Routes.HOME,
// ),
... ...
... ... @@ -7,12 +7,12 @@ environment:
dependencies:
cupertino_icons: ^1.0.2
effective_dart: 1.3.1
# get: ^4.1.4
get:
path: ../
flutter:
sdk: flutter
flutter_lints: ^1.0.4
dev_dependencies:
flutter_test:
... ...
... ... @@ -12,7 +12,7 @@ export 'http/src/multipart/multipart_file.dart';
export 'http/src/response/response.dart';
export 'sockets/sockets.dart';
abstract class GetConnectInterface with GetLifeCycleBase {
abstract class GetConnectInterface with GetLifeCycleMixin {
List<GetSocket>? sockets;
GetHttpClient get httpClient;
... ... @@ -100,9 +100,7 @@ class GetConnect extends GetConnectInterface {
this.maxAuthRetries = 1,
this.allowAutoSignedCert = false,
this.withCredentials = false,
}) {
$configureLifeCycle();
}
});
bool allowAutoSignedCert;
String userAgent;
... ... @@ -135,8 +133,7 @@ class GetConnect extends GetConnectInterface {
baseUrl: baseUrl,
trustedCertificates: trustedCertificates,
withCredentials: withCredentials,
findProxy: findProxy
);
findProxy: findProxy);
@override
Future<Response<T>> get<T>(
... ...
... ... @@ -28,6 +28,7 @@ class GetHttpClient {
int maxAuthRetries;
bool sendUserAgent;
bool sendContentLength;
Decoder? defaultDecoder;
... ... @@ -47,6 +48,7 @@ class GetHttpClient {
this.followRedirects = true,
this.maxRedirects = 5,
this.sendUserAgent = false,
this.sendContentLength = true,
this.maxAuthRetries = 1,
bool allowAutoSignedCert = false,
this.baseUrl,
... ... @@ -111,7 +113,7 @@ class GetHttpClient {
if (body is FormData) {
bodyBytes = await body.toBytes();
headers['content-length'] = bodyBytes.length.toString();
_setContentLenght(headers, bodyBytes.length);
headers['content-type'] =
'multipart/form-data; boundary=${body.boundary}';
} else if (contentType != null &&
... ... @@ -124,21 +126,21 @@ class GetHttpClient {
});
var formData = parts.join('&');
bodyBytes = utf8.encode(formData);
headers['content-length'] = bodyBytes.length.toString();
_setContentLenght(headers, bodyBytes.length);
headers['content-type'] = contentType;
} else if (body is Map || body is List) {
var jsonString = json.encode(body);
bodyBytes = utf8.encode(jsonString);
headers['content-length'] = bodyBytes.length.toString();
_setContentLenght(headers, bodyBytes.length);
headers['content-type'] = contentType ?? defaultContentType;
} else if (body is String) {
bodyBytes = utf8.encode(body);
headers['content-length'] = bodyBytes.length.toString();
_setContentLenght(headers, bodyBytes.length);
headers['content-type'] = contentType ?? defaultContentType;
} else if (body == null) {
_setContentLenght(headers, 0);
headers['content-type'] = contentType ?? defaultContentType;
headers['content-length'] = '0';
} else {
if (!errorSafety) {
throw UnexpectedFormat('body cannot be ${body.runtimeType}');
... ... @@ -162,6 +164,12 @@ class GetHttpClient {
);
}
void _setContentLenght(Map<String, String> headers, int contentLength) {
if (sendContentLength) {
headers['content-length'] = '$contentLength';
}
}
Stream<List<int>> _trackProgress(
List<int> bodyBytes,
Progress? uploadProgress,
... ...
... ... @@ -14,6 +14,7 @@ class HttpRequestImpl implements HttpRequestBase {
bool allowAutoSignedCert = true,
List<TrustedCertificate>? trustedCertificates,
this.withCredentials = false,
String Function(Uri url)? findProxy,
});
/// The currently active XHRs.
... ...
... ... @@ -110,11 +110,12 @@ class BaseWebSocket {
return true;
};
var request = await client.getUrl(Uri.parse(url));
request.headers.add('Connection', 'Upgrade');
request.headers.add('Upgrade', 'websocket');
request.headers.add('Sec-WebSocket-Version', '13');
request.headers.add('Sec-WebSocket-Key', key.toLowerCase());
var request = await client.getUrl(Uri.parse(url))
..headers.add('Connection', 'Upgrade')
..headers.add('Upgrade', 'websocket')
..headers.add('Cache-Control', 'no-cache')
..headers.add('Sec-WebSocket-Version', '13')
..headers.add('Sec-WebSocket-Key', key.toLowerCase());
var response = await request.close();
// ignore: close_sinks
... ...
typedef ValueUpdater<T> = T Function();
/// This allows a value of type T or T?
/// to be treated as a value of type T?.
///
/// We use this so that APIs that have become
/// non-nullable can still be used with `!` and `?`
/// to support older versions of the API as well.
T? ambiguate<T>(T? value) => value;
... ...
// ignore: one_member_abstracts
import 'get_instance.dart';
// ignore: one_member_abstracts
abstract class BindingsInterface<T> {
T dependencies();
}
/// [Bindings] should be extended or implemented.
/// When using `GetMaterialApp`, all `GetPage`s and navigation
/// methods (like Get.to()) have a `binding` property that takes an
/// instance of Bindings to manage the
/// dependencies() (via Get.put()) for the Route you are opening.
// ignore: one_member_abstracts
abstract class Bindings {
@Deprecated('Use Binding instead')
abstract class Bindings extends BindingsInterface<void> {
@override
void dependencies();
}
... ... @@ -58,8 +66,4 @@ class BindingsBuilder<T> extends Bindings {
}
}
// abstract class INavigation {}
// typedef Snack = Function();
// typedef Modal = Function();
// typedef Route = Function();
typedef BindingBuilderCallback = void Function();
... ...
import '../../get_core/src/get_interface.dart';
import '../../route_manager.dart';
import 'get_instance.dart';
... ...
... ... @@ -162,30 +162,25 @@ class GetInstance {
}) {
final key = _getKey(S, name);
_InstanceBuilderFactory<S>? dep;
if (_singl.containsKey(key)) {
final dep = _singl[key];
if (dep != null && dep.isDirty) {
_singl[key] = _InstanceBuilderFactory<S>(
isSingleton,
builder,
permanent,
false,
fenix,
name,
lateRemove: dep as _InstanceBuilderFactory<S>,
);
}
final _dep = _singl[key];
if (_dep == null || !_dep.isDirty) {
return;
} else {
dep = _dep as _InstanceBuilderFactory<S>;
}
}
_singl[key] = _InstanceBuilderFactory<S>(
isSingleton,
builder,
permanent,
false,
fenix,
name,
isSingleton: isSingleton,
builderFunc: builder,
permanent: permanent,
isInit: false,
fenix: fenix,
tag: name,
lateRemove: dep,
);
}
}
/// Initializes the dependencies for a Class Instance [S] (or tag),
/// If its a Controller, it starts the lifecycle process.
... ... @@ -205,7 +200,8 @@ class GetInstance {
if (_singl[key]!.isSingleton!) {
_singl[key]!.isInit = true;
if (Get.smartManagement != SmartManagement.onlyBuilder) {
RouterReportManager.reportDependencyLinkedToRoute(_getKey(S, name));
RouterReportManager.instance
.reportDependencyLinkedToRoute(_getKey(S, name));
}
}
}
... ... @@ -249,7 +245,7 @@ class GetInstance {
S _startController<S>({String? tag}) {
final key = _getKey(S, tag);
final i = _singl[key]!.getDependency() as S;
if (i is GetLifeCycleBase) {
if (i is GetLifeCycleMixin) {
i.onStart();
if (tag == null) {
Get.log('Instance "$S" has been initialized');
... ... @@ -257,7 +253,7 @@ class GetInstance {
Get.log('Instance "$S" with tag "$tag" has been initialized');
}
if (!_singl[key]!.isSingleton!) {
RouterReportManager.appendRouteByCreate(i);
RouterReportManager.instance.appendRouteByCreate(i);
}
}
return i;
... ... @@ -323,7 +319,7 @@ class GetInstance {
{@deprecated bool clearFactory = true, bool clearRouteBindings = true}) {
// if (clearFactory) _factory.clear();
// deleteAll(force: true);
if (clearRouteBindings) RouterReportManager.clearRouteKeys();
if (clearRouteBindings) RouterReportManager.instance.clearRouteKeys();
_singl.clear();
return true;
... ... @@ -378,7 +374,7 @@ class GetInstance {
return false;
}
if (i is GetLifeCycleBase) {
if (i is GetLifeCycleMixin) {
i.onDelete();
Get.log('"$newKey" onDelete() called');
}
... ... @@ -451,7 +447,7 @@ class GetInstance {
return;
}
if (i is GetLifeCycleBase) {
if (i is GetLifeCycleMixin) {
i.onDelete();
Get.log('"$newKey" onDelete() called');
}
... ... @@ -518,14 +514,14 @@ class _InstanceBuilderFactory<S> {
String? tag;
_InstanceBuilderFactory(
this.isSingleton,
this.builderFunc,
this.permanent,
this.isInit,
this.fenix,
this.tag, {
this.lateRemove,
_InstanceBuilderFactory({
required this.isSingleton,
required this.builderFunc,
required this.permanent,
required this.isInit,
required this.fenix,
required this.tag,
required this.lateRemove,
});
void _showInitLog() {
... ...
import '../../get_core/get_core.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/scheduler.dart';
/// Special callable class to keep the contract of a regular method, and avoid
/// overrides if you extend the class that uses it, as Dart has no final
/// methods.
/// Used in `DisposableInterface` to avoid the danger of overriding onStart.
class InternalFinalCallback<T> {
ValueUpdater<T>? _callback;
InternalFinalCallback({ValueUpdater<T>? callback}) : _callback = callback;
T call() => _callback!.call();
}
import '../../get.dart';
/// The [GetLifeCycle]
///
... ... @@ -21,26 +12,15 @@ class InternalFinalCallback<T> {
/// }
/// }
/// ```
mixin GetLifeCycleBase {
/// Called at the exact moment the widget is allocated in memory.
/// It uses an internal "callable" type, to avoid any @overrides in subclases.
/// This method should be internal and is required to define the
/// lifetime cycle of the subclass.
final onStart = InternalFinalCallback<void>();
// /// The `configureLifeCycle` works as a constructor for the [GetLifeCycle]
// ///
// /// This method must be invoked in the constructor of the implementation
// void configureLifeCycle() {
// if (_initialized) return;
// }
/// Internal callback that starts the cycle of this controller.
final onDelete = InternalFinalCallback<void>();
mixin GetLifeCycleMixin {
/// Called immediately after the widget is allocated in memory.
/// You might use this to initialize something for the controller.
void onInit() {}
@protected
@mustCallSuper
void onInit() {
ambiguate(SchedulerBinding.instance)
?.addPostFrameCallback((_) => onReady());
}
/// Called 1 frame after onInit(). It is the perfect place to enter
/// navigation events, like snackbar, dialogs, or a new route, or
... ... @@ -60,8 +40,15 @@ mixin GetLifeCycleBase {
/// Checks whether the controller has already been initialized.
bool get initialized => _initialized;
// Internal callback that starts the cycle of this controller.
void _onStart() {
/// Called at the exact moment the widget is allocated in memory.
/// It uses an internal "callable" type, to avoid any @overrides in subclases.
/// This method should be internal and is required to define the
/// lifetime cycle of the subclass.
// @protected
@mustCallSuper
@nonVirtual
void onStart() {
// _checkIfAlreadyConfigured();
if (_initialized) return;
onInit();
_initialized = true;
... ... @@ -72,33 +59,31 @@ mixin GetLifeCycleBase {
/// Checks whether the controller has already been closed.
bool get isClosed => _isClosed;
// Internal callback that starts the cycle of this controller.
void _onDelete() {
// Called when the controller is removed from memory.
@mustCallSuper
@nonVirtual
void onDelete() {
if (_isClosed) return;
_isClosed = true;
onClose();
}
void $configureLifeCycle() {
_checkIfAlreadyConfigured();
onStart._callback = _onStart;
onDelete._callback = _onDelete;
}
void _checkIfAlreadyConfigured() {
if (_initialized) {
throw """You can only call configureLifeCycle once.
The proper place to insert it is in your class's constructor
that inherits GetLifeCycle.""";
}
}
}
abstract class GetLifeCycle with GetLifeCycleBase {
GetLifeCycle() {
$configureLifeCycle();
}
// void _checkIfAlreadyConfigured() {
// if (_initialized) {
// throw """You can only call configureLifeCycle once.
// The proper place to insert it is in your class's constructor
// that inherits GetLifeCycle.""";
// }
// }
}
/// Allow track difference between GetxServices and GetxControllers
mixin GetxServiceMixin {}
/// Unlike GetxController, which serves to control events on each of its pages,
/// GetxService is not automatically disposed (nor can be removed with
/// Get.delete()).
/// It is ideal for situations where, once started, that service will
/// remain in memory, such as Auth control for example. Only way to remove
/// it is Get.reset().
abstract class GetxService with GetLifeCycleMixin, GetxServiceMixin {}
... ...
... ... @@ -2,10 +2,6 @@ library get_navigation;
export 'src/bottomsheet/bottomsheet.dart';
export 'src/extension_navigation.dart';
export 'src/nav2/get_information_parser.dart';
export 'src/nav2/get_nav_config.dart';
export 'src/nav2/get_router_delegate.dart';
export 'src/nav2/router_outlet.dart';
export 'src/root/get_cupertino_app.dart';
export 'src/root/get_material_app.dart';
export 'src/root/internacionalization.dart';
... ... @@ -13,6 +9,7 @@ export 'src/root/root_controller.dart';
export 'src/routes/custom_transition.dart';
export 'src/routes/default_route.dart';
export 'src/routes/get_route.dart';
export 'src/routes/index.dart';
export 'src/routes/observers/route_observer.dart';
export 'src/routes/route_middleware.dart';
export 'src/routes/transitions_type.dart';
... ...
import 'package:flutter/material.dart';
import '../../../get.dart';
import '../router_report.dart';
... ... @@ -21,7 +22,7 @@ class GetModalBottomSheetRoute<T> extends PopupRoute<T> {
this.enterBottomSheetDuration = const Duration(milliseconds: 250),
this.exitBottomSheetDuration = const Duration(milliseconds: 200),
}) : super(settings: settings) {
RouterReportManager.reportCurrentRoute(this);
RouterReportManager.instance.reportCurrentRoute(this);
}
final bool? isPersistent;
final WidgetBuilder? builder;
... ... @@ -56,7 +57,7 @@ class GetModalBottomSheetRoute<T> extends PopupRoute<T> {
@override
void dispose() {
RouterReportManager.reportRouteDispose(this);
RouterReportManager.instance.reportRouteDispose(this);
super.dispose();
}
... ...
import 'package:flutter/widgets.dart';
import '../router_report.dart';
class GetDialogRoute<T> extends PopupRoute<T> {
... ... @@ -17,7 +18,7 @@ class GetDialogRoute<T> extends PopupRoute<T> {
_transitionDuration = transitionDuration,
_transitionBuilder = transitionBuilder,
super(settings: settings) {
RouterReportManager.reportCurrentRoute(this);
RouterReportManager.instance.reportCurrentRoute(this);
}
final RoutePageBuilder widget;
... ... @@ -28,7 +29,7 @@ class GetDialogRoute<T> extends PopupRoute<T> {
@override
void dispose() {
RouterReportManager.reportRouteDispose(this);
RouterReportManager.instance.reportRouteDispose(this);
super.dispose();
}
... ...
... ... @@ -8,15 +8,11 @@ import '../../get_instance/src/bindings_interface.dart';
import '../../get_utils/get_utils.dart';
import '../get_navigation.dart';
import 'dialog/dialog_route.dart';
import 'root/parse_route.dart';
import 'root/root_controller.dart';
import 'routes/transitions_type.dart';
import 'snackbar/snackbar_controller.dart';
/// It replaces the Flutter Navigator, but needs no context.
/// You can to use navigator.push(YourRoute()) rather
/// Navigator.push(context, YourRoute());
NavigatorState? get navigator => GetNavigation(Get).key.currentState;
NavigatorState? get navigator => GetNavigationExt(Get).key.currentState;
extension ExtensionBottomSheet on GetInterface {
Future<T?> bottomSheet<T>(
... ... @@ -80,6 +76,7 @@ extension ExtensionDialog on GetInterface {
Curve? transitionCurve,
String? name,
RouteSettings? routeSettings,
String? id,
}) {
assert(debugCheckHasMaterialLocalizations(context!));
... ... @@ -112,12 +109,13 @@ extension ExtensionDialog on GetInterface {
navigatorKey: navigatorKey,
routeSettings:
routeSettings ?? RouteSettings(arguments: arguments, name: name),
id: id,
);
}
/// Api from showGeneralDialog with no context
Future<T?> generalDialog<T>({
required RoutePageBuilder pageBuilder,
Future<T?> generalDialog<T>(
{required RoutePageBuilder pageBuilder,
bool barrierDismissible = false,
String? barrierLabel,
Color barrierColor = const Color(0x80000000),
... ... @@ -125,9 +123,10 @@ extension ExtensionDialog on GetInterface {
RouteTransitionsBuilder? transitionBuilder,
GlobalKey<NavigatorState>? navigatorKey,
RouteSettings? routeSettings,
}) {
String? id}) {
assert(!barrierDismissible || barrierLabel != null);
final nav = navigatorKey?.currentState ??
final key = navigatorKey ?? Get.nestedKey(id)?.navigatorKey;
final nav = key?.currentState ??
Navigator.of(overlayContext!,
rootNavigator:
true); //overlay context will always return the root navigator
... ... @@ -150,6 +149,7 @@ extension ExtensionDialog on GetInterface {
EdgeInsetsGeometry? titlePadding,
TextStyle? titleStyle,
Widget? content,
String? id,
EdgeInsetsGeometry? contentPadding,
VoidCallback? onConfirm,
VoidCallback? onCancel,
... ... @@ -194,7 +194,7 @@ extension ExtensionDialog on GetInterface {
color: buttonColor ?? theme.colorScheme.secondary,
width: 2,
style: BorderStyle.solid),
borderRadius: BorderRadius.circular(100)),
borderRadius: BorderRadius.circular(radius)),
),
onPressed: () {
onCancel?.call();
... ... @@ -217,7 +217,7 @@ extension ExtensionDialog on GetInterface {
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
backgroundColor: buttonColor ?? theme.colorScheme.secondary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100)),
borderRadius: BorderRadius.circular(radius)),
),
child: Text(
textConfirm ?? "Ok",
... ... @@ -271,6 +271,7 @@ extension ExtensionDialog on GetInterface {
: baseAlertDialog,
barrierDismissible: barrierDismissible,
navigatorKey: navigatorKey,
id: id,
);
}
}
... ... @@ -357,7 +358,7 @@ extension ExtensionSnackbar on GetInterface {
if (instantInit) {
controller.show();
} else {
SchedulerBinding.instance!.addPostFrameCallback((_) {
ambiguate(SchedulerBinding.instance)!.addPostFrameCallback((_) {
controller.show();
});
}
... ... @@ -468,7 +469,7 @@ extension ExtensionSnackbar on GetInterface {
controller.show();
} else {
//routing.isSnackbar = true;
SchedulerBinding.instance!.addPostFrameCallback((_) {
ambiguate(SchedulerBinding.instance)!.addPostFrameCallback((_) {
controller.show();
});
}
... ... @@ -476,7 +477,7 @@ extension ExtensionSnackbar on GetInterface {
}
}
extension GetNavigation on GetInterface {
extension GetNavigationExt on GetInterface {
/// **Navigation.push()** shortcut.<br><br>
///
/// Pushes a new `page` to the stack
... ... @@ -501,9 +502,8 @@ extension GetNavigation on GetInterface {
///
/// By default, GetX will prevent you from push a route that you already in,
/// if you want to push anyway, set [preventDuplicates] to false
Future<T?>? to<T>(
dynamic page, {
bool? opaque,
Future<T?>? to<T>(Widget Function() page,
{bool? opaque,
Transition? transition,
Curve? curve,
Duration? duration,
... ... @@ -511,54 +511,53 @@ extension GetNavigation on GetInterface {
String? routeName,
bool fullscreenDialog = false,
dynamic arguments,
Bindings? binding,
List<BindingsInterface> bindings = const [],
bool preventDuplicates = true,
bool? popGesture,
bool showCupertinoParallax = true,
double Function(BuildContext context)? gestureWidth,
}) {
// var routeName = "/${page.runtimeType}";
routeName ??= "/${page.runtimeType}";
routeName = _cleanRouteName(routeName);
if (preventDuplicates && routeName == currentRoute) {
return null;
}
return global(id).currentState?.push<T>(
GetPageRoute<T>(
opaque: opaque ?? true,
page: _resolvePage(page, 'to'),
bool rebuildStack = true,
PreventDuplicateHandlingMode preventDuplicateHandlingMode =
PreventDuplicateHandlingMode.ReorderRoutes}) {
return searchDelegate(id).to(
page,
opaque: opaque,
transition: transition,
curve: curve,
duration: duration,
id: id,
routeName: routeName,
gestureWidth: gestureWidth,
settings: RouteSettings(
name: routeName,
arguments: arguments,
),
popGesture: popGesture ?? defaultPopGesture,
transition: transition ?? defaultTransition,
curve: curve ?? defaultTransitionCurve,
fullscreenDialog: fullscreenDialog,
binding: binding,
transitionDuration: duration ?? defaultTransitionDuration,
),
arguments: arguments,
bindings: bindings,
preventDuplicates: preventDuplicates,
popGesture: popGesture,
showCupertinoParallax: showCupertinoParallax,
gestureWidth: gestureWidth,
rebuildStack: rebuildStack,
preventDuplicateHandlingMode: preventDuplicateHandlingMode,
);
}
GetPageBuilder _resolvePage(dynamic page, String method) {
if (page is GetPageBuilder) {
return page;
} else if (page is Widget) {
Get.log(
'''WARNING, consider using: "Get.$method(() => Page())" instead of "Get.$method(Page())".
Using a widget function instead of a widget fully guarantees that the widget and its controllers will be removed from memory when they are no longer used.
''');
return () => page;
} else if (page is String) {
throw '''Unexpected String,
use toNamed() instead''';
} else {
throw '''Unexpected format,
you can only use widgets and widget functions here''';
}
}
// GetPageBuilder _resolvePage(dynamic page, String method) {
// if (page is GetPageBuilder) {
// return page;
// } else if (page is Widget) {
// Get.log(
// '''WARNING, consider using: "Get.$method(() => Page())"
//instead of "Get.$method(Page())".
// Using a widget function instead of a widget fully guarantees that the widget
//and its controllers will be removed from memory when they are no longer used.
// ''');
// return () => page;
// } else if (page is String) {
// throw '''Unexpected String,
// use toNamed() instead''';
// } else {
// throw '''Unexpected format,
// you can only use widgets and widget functions here''';
// }
// }
/// **Navigation.pushNamed()** shortcut.<br><br>
///
... ... @@ -579,22 +578,25 @@ you can only use widgets and widget functions here''';
Future<T?>? toNamed<T>(
String page, {
dynamic arguments,
int? id,
dynamic id,
bool preventDuplicates = true,
Map<String, String>? parameters,
}) {
if (preventDuplicates && page == currentRoute) {
return null;
}
// if (preventDuplicates && page == currentRoute) {
// return null;
// }
if (parameters != null) {
final uri = Uri(path: page, queryParameters: parameters);
page = uri.toString();
}
return global(id).currentState?.pushNamed<T>(
return searchDelegate(id).toNamed(
page,
arguments: arguments,
id: id,
preventDuplicates: preventDuplicates,
parameters: parameters,
);
}
... ... @@ -618,20 +620,22 @@ you can only use widgets and widget functions here''';
String page, {
dynamic arguments,
int? id,
bool preventDuplicates = true,
Map<String, String>? parameters,
}) {
if (preventDuplicates && page == currentRoute) {
return null;
}
// if (preventDuplicates && page == currentRoute) {
// return null;
// }
if (parameters != null) {
final uri = Uri(path: page, queryParameters: parameters);
page = uri.toString();
}
return global(id).currentState?.pushReplacementNamed(
return searchDelegate(id).offNamed(
page,
arguments: arguments,
id: id,
// preventDuplicates: preventDuplicates,
parameters: parameters,
);
}
... ... @@ -648,34 +652,10 @@ you can only use widgets and widget functions here''';
/// or also like this:
/// `Get.until((route) => !Get.isDialogOpen())`, to make sure the
/// dialog is closed
void until(RoutePredicate predicate, {int? id}) {
void until(bool Function(GetPage<dynamic>) predicate, {int? id}) {
// if (key.currentState.mounted) // add this if appear problems on future with route navigate
// when widget don't mounted
return global(id).currentState?.popUntil(predicate);
}
/// **Navigation.pushAndRemoveUntil()** shortcut.<br><br>
///
/// Push the given `page`, and then pop several pages in the stack until
/// [predicate] returns true
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
///
/// Obs: unlike other get methods, this one you need to send a function
/// that returns the widget to the page argument, like this:
/// Get.offUntil(GetPageRoute(page: () => HomePage()), predicate)
///
/// [predicate] can be used like this:
/// `Get.offUntil(page, (route) => (route as GetPageRoute).routeName == '/home')`
/// to pop routes in stack until home,
/// or also like this:
/// `Get.until((route) => !Get.isDialogOpen())`, to make sure the dialog
/// is closed
Future<T?>? offUntil<T>(Route<T> page, RoutePredicate predicate, {int? id}) {
// if (key.currentState.mounted) // add this if appear problems on future with route navigate
// when widget don't mounted
return global(id).currentState?.pushAndRemoveUntil<T>(page, predicate);
return searchDelegate(id).backUntil(predicate);
}
/// **Navigation.pushNamedAndRemoveUntil()** shortcut.<br><br>
... ... @@ -698,7 +678,7 @@ you can only use widgets and widget functions here''';
/// Note: Always put a slash on the route name ('/page1'), to avoid unexpected errors
Future<T?>? offNamedUntil<T>(
String page,
RoutePredicate predicate, {
bool Function(GetPage<dynamic>)? predicate, {
int? id,
dynamic arguments,
Map<String, String>? parameters,
... ... @@ -708,10 +688,12 @@ you can only use widgets and widget functions here''';
page = uri.toString();
}
return global(id).currentState?.pushNamedAndRemoveUntil<T>(
return searchDelegate(id).offNamedUntil<T>(
page,
predicate,
predicate: predicate,
id: id,
arguments: arguments,
parameters: parameters,
);
}
... ... @@ -737,7 +719,7 @@ you can only use widgets and widget functions here''';
final uri = Uri(path: page, queryParameters: parameters);
page = uri.toString();
}
return global(id).currentState?.popAndPushNamed(
return searchDelegate(id).backAndtoNamed(
page,
arguments: arguments,
result: result,
... ... @@ -750,8 +732,8 @@ you can only use widgets and widget functions here''';
///
/// [id] is for when you are using nested navigation,
/// as explained in documentation
void removeRoute(Route<dynamic> route, {int? id}) {
return global(id).currentState?.removeRoute(route);
void removeRoute(String name, {int? id}) {
return searchDelegate(id).removeRoute(name);
}
/// **Navigation.pushNamedAndRemoveUntil()** shortcut.<br><br>
... ... @@ -776,7 +758,7 @@ you can only use widgets and widget functions here''';
/// Note: Always put a slash on the route ('/page1'), to avoid unexpected errors
Future<T?>? offAllNamed<T>(
String newRouteName, {
RoutePredicate? predicate,
// bool Function(GetPage<dynamic>)? predicate,
dynamic arguments,
int? id,
Map<String, String>? parameters,
... ... @@ -786,10 +768,12 @@ you can only use widgets and widget functions here''';
newRouteName = uri.toString();
}
return global(id).currentState?.pushNamedAndRemoveUntil<T>(
return searchDelegate(id).offAllNamed<T>(
newRouteName,
predicate ?? (_) => false,
//predicate: predicate ?? (_) => false,
arguments: arguments,
id: id,
parameters: parameters,
);
}
... ... @@ -819,6 +803,13 @@ you can only use widgets and widget functions here''';
bool canPop = true,
int? id,
}) {
//TODO: remove this when change own api to Dialog and BottomSheets
//to declarative way
if (isDialogOpen! || isBottomSheetOpen!) {
searchDelegate(id).navigatorKey.currentState?.pop();
return;
}
//TODO: This code brings compatibility of the new snackbar with GetX 4,
// remove this code in version 5
if (isSnackbarOpen && !closeOverlays) {
... ... @@ -832,16 +823,21 @@ you can only use widgets and widget functions here''';
if (isSnackbarOpen) {
closeAllSnackbars();
}
navigator?.popUntil((route) {
return (!isDialogOpen! && !isBottomSheetOpen!);
});
while ((isDialogOpen! && isBottomSheetOpen!)) {
searchDelegate(id).navigatorKey.currentState?.pop();
}
// navigator?.popUntil((route) {
// return;
// });
}
if (canPop) {
if (global(id).currentState?.canPop() == true) {
global(id).currentState?.pop<T>(result);
if (searchDelegate(id).canBack == true) {
searchDelegate(id).back<T>(result);
}
} else {
global(id).currentState?.pop<T>(result);
searchDelegate(id).back<T>(result);
}
}
... ... @@ -856,7 +852,7 @@ you can only use widgets and widget functions here''';
times = 1;
}
var count = 0;
var back = global(id).currentState?.popUntil((route) => count++ == times);
var back = searchDelegate(id).backUntil((route) => count++ == times);
return back;
}
... ... @@ -887,15 +883,15 @@ you can only use widgets and widget functions here''';
/// By default, GetX will prevent you from push a route that you already in,
/// if you want to push anyway, set [preventDuplicates] to false
Future<T?>? off<T>(
dynamic page, {
bool opaque = false,
Widget Function() page, {
bool? opaque,
Transition? transition,
Curve? curve,
bool? popGesture,
int? id,
String? routeName,
dynamic arguments,
Bindings? binding,
List<BindingsInterface> bindings = const [],
bool fullscreenDialog = false,
bool preventDuplicates = true,
Duration? duration,
... ... @@ -906,21 +902,34 @@ you can only use widgets and widget functions here''';
if (preventDuplicates && routeName == currentRoute) {
return null;
}
return global(id).currentState?.pushReplacement(GetPageRoute(
opaque: opaque,
gestureWidth: gestureWidth,
page: _resolvePage(page, 'off'),
binding: binding,
settings: RouteSettings(
arguments: arguments,
name: routeName,
),
return searchDelegate(id).off(
page,
opaque: opaque ?? true,
transition: transition,
curve: curve,
popGesture: popGesture,
id: id,
routeName: routeName,
arguments: arguments,
bindings: bindings,
fullscreenDialog: fullscreenDialog,
popGesture: popGesture ?? defaultPopGesture,
transition: transition ?? defaultTransition,
curve: curve ?? defaultTransitionCurve,
transitionDuration: duration ?? defaultTransitionDuration));
preventDuplicates: preventDuplicates,
duration: duration,
gestureWidth: gestureWidth,
);
}
Future<T?> offUntil<T>(
Widget Function() page,
bool Function(GetPage) predicate, [
Object? arguments,
int? id,
]) {
return searchDelegate(id).offUntil(
page,
predicate,
arguments,
);
}
///
... ... @@ -954,14 +963,14 @@ you can only use widgets and widget functions here''';
/// By default, GetX will prevent you from push a route that you already in,
/// if you want to push anyway, set [preventDuplicates] to false
Future<T?>? offAll<T>(
dynamic page, {
RoutePredicate? predicate,
bool opaque = false,
Widget Function() page, {
bool Function(GetPage<dynamic>)? predicate,
bool? opaque,
bool? popGesture,
int? id,
String? routeName,
dynamic arguments,
Bindings? binding,
List<BindingsInterface> bindings = const [],
bool fullscreenDialog = false,
Transition? transition,
Curve? curve,
... ... @@ -970,24 +979,21 @@ you can only use widgets and widget functions here''';
}) {
routeName ??= "/${page.runtimeType.toString()}";
routeName = _cleanRouteName(routeName);
return global(id).currentState?.pushAndRemoveUntil<T>(
GetPageRoute<T>(
opaque: opaque,
popGesture: popGesture ?? defaultPopGesture,
page: _resolvePage(page, 'offAll'),
binding: binding,
gestureWidth: gestureWidth,
settings: RouteSettings(
name: routeName,
return searchDelegate(id).offAll<T>(
page,
predicate: predicate,
opaque: opaque ?? true,
popGesture: popGesture,
id: id,
// routeName routeName,
arguments: arguments,
),
bindings: bindings,
fullscreenDialog: fullscreenDialog,
routeName: routeName,
transition: transition ?? defaultTransition,
curve: curve ?? defaultTransitionCurve,
transitionDuration: duration ?? defaultTransitionDuration,
),
predicate ?? (route) => false);
transition: transition,
curve: curve,
duration: duration,
gestureWidth: gestureWidth,
);
}
/// Takes a route [name] String generated by [to], [off], [offAll]
... ... @@ -1055,7 +1061,7 @@ you can only use widgets and widget functions here''';
/// Your entire application will be rebuilt, and touch events will not
/// work until the end of rendering.
Future<void> forceAppUpdate() async {
await engine!.performReassemble();
await engine.performReassemble();
}
void appUpdate() => _getxController.update();
... ... @@ -1072,20 +1078,25 @@ you can only use widgets and widget functions here''';
return _getxController.addKey(newKey);
}
GlobalKey<NavigatorState>? nestedKey(dynamic key) {
GetDelegate? nestedKey(String? key) {
if (key == null) {
return routerDelegate as GetDelegate;
}
keys.putIfAbsent(
key,
() => GlobalKey<NavigatorState>(
debugLabel: 'Getx nested key: ${key.toString()}',
() => GetDelegate(
showHashOnUrl: true,
//debugLabel: 'Getx nested key: ${key.toString()}',
pages: RouteDecoder.fromRoute(key).currentChildrens ?? [],
),
);
return keys[key];
}
GlobalKey<NavigatorState> global(int? k) {
GlobalKey<NavigatorState> _key;
GetDelegate searchDelegate(dynamic k) {
GetDelegate _key;
if (k == null) {
_key = key;
_key = Get.rootController.rootDelegate;
} else {
if (!keys.containsKey(k)) {
throw 'Route id ($k) not found';
... ... @@ -1093,21 +1104,22 @@ you can only use widgets and widget functions here''';
_key = keys[k]!;
}
if (_key.currentContext == null && !testMode) {
throw """You are trying to use contextless navigation without
a GetMaterialApp or Get.key.
If you are testing your app, you can use:
[Get.testMode = true], or if you are running your app on
a physical device or emulator, you must exchange your [MaterialApp]
for a [GetMaterialApp].
""";
}
// if (_key.listenersLength == 0 && !testMode) {
// throw """You are trying to use contextless navigation without
// a GetMaterialApp or Get.key.
// If you are testing your app, you can use:
// [Get.testMode = true], or if you are running your app on
// a physical device or emulator, you must exchange your [MaterialApp]
// for a [GetMaterialApp].
// """;
// }
return _key;
}
/// give current arguments
dynamic get arguments => routing.args;
//dynamic get arguments => routing.args;
dynamic get arguments => _getxController.rootDelegate.arguments();
/// give name from current route
String get currentRoute => routing.current;
... ... @@ -1163,12 +1175,9 @@ you can only use widgets and widget functions here''';
return _theme;
}
///The current [WidgetsBinding]
WidgetsBinding? get engine {
if (WidgetsBinding.instance == null) {
WidgetsFlutterBinding();
}
return WidgetsBinding.instance;
/// The current null safe [WidgetsBinding]
WidgetsBinding get engine {
return WidgetsFlutterBinding.ensureInitialized();
}
/// The window to which this binding is bound.
... ... @@ -1225,7 +1234,7 @@ you can only use widgets and widget functions here''';
GlobalKey<NavigatorState> get key => _getxController.key;
Map<dynamic, GlobalKey<NavigatorState>> get keys => _getxController.keys;
Map<dynamic, GetDelegate> get keys => _getxController.keys;
GetMaterialController get rootController => _getxController;
... ... @@ -1250,7 +1259,8 @@ you can only use widgets and widget functions here''';
Routing get routing => _getxController.routing;
Map<String, String?> get parameters => _getxController.parameters;
Map<String, String?> get parameters =>
_getxController.rootDelegate.parameters;
set parameters(Map<String, String?> newParameters) =>
_getxController.parameters = newParameters;
... ... @@ -1280,10 +1290,15 @@ extension NavTwoExt on GetInterface {
static late final _routeTree = ParseRouteTree(routes: []);
ParseRouteTree get routeTree => _routeTree;
void addPage(GetPage getPage) {
routeTree.addRoute(getPage);
}
void removePage(GetPage getPage) {
routeTree.removeRoute(getPage);
}
/// Casts the stored router delegate to a desired type
TDelegate? delegate<TDelegate extends RouterDelegate<TPage>, TPage>() =>
routerDelegate as TDelegate?;
... ... @@ -1307,15 +1322,15 @@ extension NavTwoExt on GetInterface {
// static GetDelegate? _delegate;
GetDelegate get rootDelegate => createDelegate();
GetDelegate createDelegate({
GetPage<dynamic>? notFoundRoute,
List<GetPage> pages = const [],
List<NavigatorObserver>? navigatorObservers,
TransitionDelegate<dynamic>? transitionDelegate,
PopMode backButtonPopMode = PopMode.History,
PreventDuplicateHandlingMode preventDuplicateHandlingMode =
PreventDuplicateHandlingMode.ReorderRoutes,
GlobalKey<NavigatorState>? navigatorKey,
}) {
if (routerDelegate == null) {
return routerDelegate = GetDelegate(
... ... @@ -1324,6 +1339,8 @@ extension NavTwoExt on GetInterface {
transitionDelegate: transitionDelegate,
backButtonPopMode: backButtonPopMode,
preventDuplicateHandlingMode: preventDuplicateHandlingMode,
pages: pages,
navigatorKey: navigatorKey,
);
} else {
return routerDelegate as GetDelegate;
... ...
import 'package:flutter/widgets.dart';
import '../../../get.dart';
// class GetRouterState extends GetxController {
// GetRouterState({required this.currentTreeBranch});
// final List<GetPage> currentTreeBranch;
// GetPage? get currentPage => currentTreeBranch.last;
// static GetNavConfig? fromRoute(String route) {
// final res = Get.routeTree.matchRoute(route);
// if (res.treeBranch.isEmpty) return null;
// return GetNavConfig(
// currentTreeBranch: res.treeBranch,
// location: route,
// state: null,
// );
// }
// }
/// This config enables us to navigate directly to a sub-url
class GetNavConfig extends RouteInformation {
final List<GetPage> currentTreeBranch;
GetPage? get currentPage => currentTreeBranch.last;
GetNavConfig({
required this.currentTreeBranch,
required String? location,
required Object? state,
}) : super(
location: location,
state: state,
);
GetNavConfig copyWith({
List<GetPage>? currentTreeBranch,
required String? location,
required Object? state,
}) {
return GetNavConfig(
currentTreeBranch: currentTreeBranch ?? this.currentTreeBranch,
location: location ?? this.location,
state: state ?? this.state,
);
}
static GetNavConfig? fromRoute(String route) {
final res = Get.routeTree.matchRoute(route);
if (res.treeBranch.isEmpty) return null;
return GetNavConfig(
currentTreeBranch: res.treeBranch,
location: route,
state: null,
);
}
@override
String toString() => '''
======GetNavConfig=====\ncurrentTreeBranch: $currentTreeBranch\ncurrentPage: $currentPage\n======GetNavConfig=====''';
}
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import '../../../get.dart';
import '../../../get_state_manager/src/simple/list_notifier.dart';
class GetDelegate extends RouterDelegate<GetNavConfig>
with ListenableMixin, ListNotifierMixin {
final List<GetNavConfig> history = <GetNavConfig>[];
final PopMode backButtonPopMode;
final PreventDuplicateHandlingMode preventDuplicateHandlingMode;
final GetPage notFoundRoute;
final List<NavigatorObserver>? navigatorObservers;
final TransitionDelegate<dynamic>? transitionDelegate;
final _allCompleters = <GetPage, Completer>{};
GetDelegate({
GetPage? notFoundRoute,
this.navigatorObservers,
this.transitionDelegate,
this.backButtonPopMode = PopMode.History,
this.preventDuplicateHandlingMode =
PreventDuplicateHandlingMode.ReorderRoutes,
}) : notFoundRoute = notFoundRoute ??
GetPage(
name: '/404',
page: () => Scaffold(
body: Text('Route not found'),
),
) {
Get.log('GetDelegate is created !');
}
@override
GetNavConfig? get currentConfiguration {
if (history.isEmpty) return null;
final route = history.last;
return route;
}
GlobalKey<NavigatorState> get navigatorKey => Get.key;
Map<String, String> get parameters {
return currentConfiguration?.currentPage?.parameters ?? {};
}
T arguments<T>() {
return currentConfiguration?.currentPage?.arguments as T;
}
/// Removes routes according to [PopMode]
/// until it reaches the specifc [fullRoute],
/// DOES NOT remove the [fullRoute]
Future<void> backUntil(
String fullRoute, {
PopMode popMode = PopMode.Page,
}) async {
// remove history or page entries until you meet route
var iterator = currentConfiguration;
while (_canPop(popMode) &&
iterator != null &&
iterator.location != fullRoute) {
await _pop(popMode);
// replace iterator
iterator = currentConfiguration;
}
refresh();
}
@override
Widget build(BuildContext context) {
final pages = getVisualPages();
if (pages.length == 0) return SizedBox.shrink();
final extraObservers = navigatorObservers;
return GetNavigator(
key: navigatorKey,
onPopPage: _onPopVisualRoute,
pages: pages,
observers: [
GetObserver(),
if (extraObservers != null) ...extraObservers,
],
transitionDelegate:
transitionDelegate ?? const DefaultTransitionDelegate<dynamic>(),
);
}
// void _unsafeHistoryClear() {
// history.clear();
// }
Future<bool> canPopHistory() {
return SynchronousFuture(_canPopHistory());
}
Future<bool> canPopPage() {
return SynchronousFuture(_canPopPage());
}
/// gets the visual pages from the current history entry
///
/// visual pages must have [participatesInRootNavigator] set to true
List<GetPage> getVisualPages() {
final currentHistory = currentConfiguration;
if (currentHistory == null) return <GetPage>[];
final res = currentHistory.currentTreeBranch
.where((r) => r.participatesInRootNavigator != null);
if (res.length == 0) {
//default behavoir, all routes participate in root navigator
return history.map((e) => e.currentPage!).toList();
} else {
//user specified at least one participatesInRootNavigator
return res
.where((element) => element.participatesInRootNavigator == true)
.toList();
}
}
// GetPageRoute getPageRoute(RouteSettings? settings) {
// return PageRedirect(settings ?? RouteSettings(name: '/404'), _notFound())
// .page();
// }
Future<bool> handlePopupRoutes({
Object? result,
}) async {
Route? currentRoute;
navigatorKey.currentState!.popUntil((route) {
currentRoute = route;
return true;
});
if (currentRoute is PopupRoute) {
return await navigatorKey.currentState!.maybePop(result);
}
return false;
}
Future<T?>? offAndToNamed<T>(
String page, {
dynamic arguments,
int? id,
dynamic result,
Map<String, String>? parameters,
PopMode popMode = PopMode.History,
}) async {
if (parameters != null) {
final uri = Uri(path: page, queryParameters: parameters);
page = uri.toString();
}
await popRoute(result: result);
return toNamed(page, arguments: arguments, parameters: parameters);
}
Future<T> offNamed<T>(
String page, {
dynamic arguments,
Map<String, String>? parameters,
}) async {
history.removeLast();
return toNamed<T>(page, arguments: arguments, parameters: parameters);
}
Future<GetNavConfig?> popHistory() async {
return await _popHistory();
}
// returns the popped page
@override
Future<bool> popRoute({
Object? result,
PopMode popMode = PopMode.Page,
}) async {
//Returning false will cause the entire app to be popped.
final wasPopup = await handlePopupRoutes(result: result);
if (wasPopup) return true;
final _popped = await _pop(popMode);
refresh();
if (_popped != null) {
//emulate the old pop with result
return true;
}
return false;
}
/// Adds a new history entry and waits for the result
Future<void> pushHistory(
GetNavConfig config, {
bool rebuildStack = true,
}) async {
//this changes the currentConfiguration
await _pushHistory(config);
if (rebuildStack) {
refresh();
}
}
Future<GetNavConfig?> runMiddleware(GetNavConfig config) async {
final middlewares = config.currentTreeBranch.last.middlewares;
if (middlewares == null) {
return config;
}
var iterator = config;
for (var item in middlewares) {
var redirectRes = await item.redirectDelegate(iterator);
if (redirectRes == null) return null;
iterator = redirectRes;
}
return iterator;
}
@override
Future<void> setNewRoutePath(GetNavConfig configuration) async {
await pushHistory(configuration);
}
Future<T> toNamed<T>(
String page, {
dynamic arguments,
Map<String, String>? parameters,
}) async {
if (parameters != null) {
final uri = Uri(path: page, queryParameters: parameters);
page = uri.toString();
}
final decoder = Get.routeTree.matchRoute(page, arguments: arguments);
decoder.replaceArguments(arguments);
final completer = Completer<T>();
if (decoder.route != null) {
_allCompleters[decoder.route!] = completer;
await pushHistory(
GetNavConfig(
currentTreeBranch: decoder.treeBranch,
location: page,
state: null, //TODO: persist state?
),
);
return completer.future;
} else {
///TODO: IMPLEMENT ROUTE NOT FOUND
return Future.value();
}
}
bool _canPop(PopMode mode) {
switch (mode) {
case PopMode.History:
return _canPopHistory();
case PopMode.Page:
default:
return _canPopPage();
}
}
bool _canPopHistory() {
return history.length > 1;
}
bool _canPopPage() {
final currentTreeBranch = currentConfiguration?.currentTreeBranch;
if (currentTreeBranch == null) return false;
return currentTreeBranch.length > 1 ? true : _canPopHistory();
}
Future<GetNavConfig?> _doPopHistory() async {
return await _unsafeHistoryRemoveAt(history.length - 1);
}
// @override
// Future<void> setInitialRoutePath(GetNavConfig configuration) async {
// //no need to clear history with Reorder route strategy
// // _unsafeHistoryClear();
// // _resultCompleter.clear();
// await pushHistory(configuration);
// }
Future<GetNavConfig?> _doPopPage() async {
final currentBranch = currentConfiguration?.currentTreeBranch;
if (currentBranch != null && currentBranch.length > 1) {
//remove last part only
final remaining = currentBranch.take(currentBranch.length - 1);
final prevHistoryEntry =
history.length > 1 ? history[history.length - 2] : null;
//check if current route is the same as the previous route
if (prevHistoryEntry != null) {
//if so, pop the entire history entry
final newLocation = remaining.last.name;
final prevLocation = prevHistoryEntry.location;
if (newLocation == prevLocation) {
//pop the entire history entry
return await _popHistory();
}
}
//create a new route with the remaining tree branch
final res = await _popHistory();
await _pushHistory(
GetNavConfig(
currentTreeBranch: remaining.toList(),
location: remaining.last.name,
state: null, //TOOD: persist state??
),
);
return res;
} else {
//remove entire entry
return await _popHistory();
}
}
bool _onPopVisualRoute(Route<dynamic> route, dynamic result) {
final didPop = route.didPop(result);
if (!didPop) {
return false;
}
final settings = route.settings;
if (settings is GetPage) {
final config = history.cast<GetNavConfig?>().firstWhere(
(element) => element?.currentPage == settings,
orElse: () => null,
);
if (config != null) {
_removeHistoryEntry(config);
}
if (_allCompleters.containsKey(settings)) {
_allCompleters[settings]?.complete(route.popped);
}
}
refresh();
return true;
}
Future<GetNavConfig?> _pop(PopMode mode) async {
switch (mode) {
case PopMode.History:
return await _popHistory();
case PopMode.Page:
return await _popPage();
default:
return null;
}
}
Future<GetNavConfig?> _popHistory() async {
if (!_canPopHistory()) return null;
return await _doPopHistory();
}
Future<GetNavConfig?> _popPage() async {
if (!_canPopPage()) return null;
return await _doPopPage();
}
Future<void> _pushHistory(GetNavConfig config) async {
if (config.currentPage!.preventDuplicates) {
final originalEntryIndex =
history.indexWhere((element) => element.location == config.location);
if (originalEntryIndex >= 0) {
switch (preventDuplicateHandlingMode) {
case PreventDuplicateHandlingMode.PopUntilOriginalRoute:
await backUntil(config.location!, popMode: PopMode.Page);
break;
case PreventDuplicateHandlingMode.ReorderRoutes:
await _unsafeHistoryRemoveAt(originalEntryIndex);
await _unsafeHistoryAdd(config);
break;
case PreventDuplicateHandlingMode.DoNothing:
default:
break;
}
return;
}
}
await _unsafeHistoryAdd(config);
}
Future<void> _removeHistoryEntry(GetNavConfig entry) async {
await _unsafeHistoryRemove(entry);
}
Future<void> _unsafeHistoryAdd(GetNavConfig config) async {
final res = await runMiddleware(config);
if (res == null) return;
history.add(res);
}
Future<void> _unsafeHistoryRemove(GetNavConfig config) async {
var index = history.indexOf(config);
if (index >= 0) await _unsafeHistoryRemoveAt(index);
}
Future<GetNavConfig?> _unsafeHistoryRemoveAt(int index) async {
if (index == history.length - 1 && history.length > 1) {
//removing WILL update the current route
final toCheck = history[history.length - 2];
final resMiddleware = await runMiddleware(toCheck);
if (resMiddleware == null) return null;
history[history.length - 2] = resMiddleware;
}
return history.removeAt(index);
}
}
class GetNavigator extends Navigator {
GetNavigator({
GlobalKey<NavigatorState>? key,
bool Function(Route<dynamic>, dynamic)? onPopPage,
required List<Page> pages,
List<NavigatorObserver>? observers,
bool reportsRouteUpdateToEngine = false,
TransitionDelegate? transitionDelegate,
}) : super(
//keys should be optional
key: key,
onPopPage: onPopPage ??
(route, result) {
final didPop = route.didPop(result);
if (!didPop) {
return false;
}
return true;
},
reportsRouteUpdateToEngine: reportsRouteUpdateToEngine,
pages: pages,
observers: [
// GetObserver(),
if (observers != null) ...observers,
],
transitionDelegate:
transitionDelegate ?? const DefaultTransitionDelegate<dynamic>(),
);
}
/// Enables the user to customize the intended pop behavior
///
/// Goes to either the previous history entry or the previous page entry
///
/// e.g. if the user navigates to these pages
/// 1) /home
/// 2) /home/products/1234
///
/// when popping on [History] mode, it will emulate a browser back button.
///
/// so the new history stack will be:
/// 1) /home
///
/// when popping on [Page] mode, it will only remove the last part of the route
/// so the new history stack will be:
/// 1) /home
/// 2) /home/products
///
/// another pop will change the history stack to:
/// 1) /home
enum PopMode {
History,
Page,
}
/// Enables the user to customize the behavior when pushing multiple routes that
/// shouldn't be duplicates
enum PreventDuplicateHandlingMode {
/// Removes the history entries until it reaches the old route
PopUntilOriginalRoute,
/// Simply don't push the new route
DoNothing,
/// Recommended - Moves the old route entry to the front
///
/// With this mode, you guarantee there will be only one
/// route entry for each location
ReorderRoutes
}
... ... @@ -7,7 +7,6 @@ import '../../../get_instance/get_instance.dart';
import '../../../get_state_manager/get_state_manager.dart';
import '../../../get_utils/get_utils.dart';
import '../../get_navigation.dart';
import 'root_controller.dart';
class GetCupertinoApp extends StatelessWidget {
final GlobalKey<NavigatorState>? navigatorKey;
... ... @@ -52,7 +51,7 @@ class GetCupertinoApp extends StatelessWidget {
final LogWriterCallback? logWriterCallback;
final bool? popGesture;
final SmartManagement smartManagement;
final Bindings? initialBinding;
final BindingsInterface? initialBinding;
final Duration? transitionDuration;
final bool? defaultGlobalState;
final List<GetPage>? getPages;
... ... @@ -63,7 +62,7 @@ class GetCupertinoApp extends StatelessWidget {
final BackButtonDispatcher? backButtonDispatcher;
final CupertinoThemeData? theme;
final bool useInheritedMediaQuery;
const GetCupertinoApp({
GetCupertinoApp({
Key? key,
this.theme,
this.navigatorKey,
... ... @@ -116,17 +115,28 @@ class GetCupertinoApp extends StatelessWidget {
this.highContrastDarkTheme,
this.actions,
}) : routeInformationProvider = null,
backButtonDispatcher = null,
routeInformationParser = null,
routerDelegate = null,
backButtonDispatcher = null,
super(key: key);
static String _cleanRouteName(String name) {
name = name.replaceAll('() => ', '');
/// uncommonent for URL styling.
// name = name.paramCase!;
if (!name.startsWith('/')) {
name = '/$name';
}
return Uri.tryParse(name)?.toString() ?? name;
}
GetCupertinoApp.router({
Key? key,
this.theme,
this.routeInformationProvider,
RouteInformationParser<Object>? routeInformationParser,
RouterDelegate<Object>? routerDelegate,
this.routeInformationParser,
this.routerDelegate,
this.backButtonDispatcher,
this.builder,
this.title = '',
... ... @@ -166,35 +176,30 @@ class GetCupertinoApp extends StatelessWidget {
this.transitionDuration,
this.defaultGlobalState,
this.getPages,
this.navigatorObservers,
this.unknownRoute,
}) : routerDelegate = routerDelegate ??= Get.createDelegate(
notFoundRoute: unknownRoute,
),
routeInformationParser =
routeInformationParser ??= Get.createInformationParser(
initialRoute: getPages?.first.name ?? '/',
),
navigatorObservers = null,
navigatorKey = null,
}) : navigatorKey = null,
onGenerateRoute = null,
home = null,
onGenerateInitialRoutes = null,
onUnknownRoute = null,
routes = null,
initialRoute = null,
super(key: key) {
Get.routerDelegate = routerDelegate;
Get.routeInformationParser = routeInformationParser;
}
super(key: key);
@override
Widget build(BuildContext context) => GetBuilder<GetMaterialController>(
init: Get.rootController,
dispose: (d) {
onDispose?.call();
Get.clearRouteTree();
Get.clearTranslations();
Get.resetRootNavigator();
Get.routerDelegate = null;
Get.routeInformationParser = null;
},
initState: (i) {
Get.engine!.addPostFrameCallback((timeStamp) {
Get.engine.addPostFrameCallback((timeStamp) {
onReady?.call();
});
if (locale != null) Get.locale = locale;
... ... @@ -212,6 +217,13 @@ class GetCupertinoApp extends StatelessWidget {
initialBinding?.dependencies();
if (getPages != null) {
Get.addPages(getPages!);
} else {
Get.addPage(
GetPage(
name: _cleanRouteName("/${home.runtimeType}"),
page: () => home!,
),
);
}
Get.smartManagement = smartManagement;
... ... @@ -227,46 +239,11 @@ class GetCupertinoApp extends StatelessWidget {
transitionDuration ?? Get.defaultTransitionDuration,
);
},
builder: (_) => routerDelegate != null
? CupertinoApp.router(
routerDelegate: routerDelegate!,
routeInformationParser: routeInformationParser!,
backButtonDispatcher: backButtonDispatcher,
routeInformationProvider: routeInformationProvider,
key: _.unikey,
theme: theme,
builder: defaultBuilder,
title: title,
onGenerateTitle: onGenerateTitle,
color: color,
locale: Get.locale ?? locale,
localizationsDelegates: localizationsDelegates,
localeListResolutionCallback: localeListResolutionCallback,
localeResolutionCallback: localeResolutionCallback,
supportedLocales: supportedLocales,
showPerformanceOverlay: showPerformanceOverlay,
checkerboardRasterCacheImages: checkerboardRasterCacheImages,
checkerboardOffscreenLayers: checkerboardOffscreenLayers,
showSemanticsDebugger: showSemanticsDebugger,
debugShowCheckedModeBanner: debugShowCheckedModeBanner,
shortcuts: shortcuts,
useInheritedMediaQuery: useInheritedMediaQuery,
)
: CupertinoApp(
key: _.unikey,
theme: theme,
navigatorKey: (navigatorKey == null
? Get.key
: Get.addKey(navigatorKey!)),
home: home,
routes: routes ?? const <String, WidgetBuilder>{},
initialRoute: initialRoute,
onGenerateRoute:
(getPages != null ? generator : onGenerateRoute),
onGenerateInitialRoutes: (getPages == null || home != null)
? onGenerateInitialRoutes
: initialRoutesGenerate,
onUnknownRoute: onUnknownRoute,
builder: (_) {
final routerDelegate = Get.createDelegate(
pages: getPages ?? [],
notFoundRoute: unknownRoute,
navigatorKey: navigatorKey,
navigatorObservers: (navigatorObservers == null
? <NavigatorObserver>[
GetObserver(routingCallback, Get.routing)
... ... @@ -274,7 +251,20 @@ class GetCupertinoApp extends StatelessWidget {
: <NavigatorObserver>[
GetObserver(routingCallback, Get.routing)
]
..addAll(navigatorObservers!)),
..addAll(navigatorObservers!)));
final routeInformationParser = Get.createInformationParser(
initialRoute: initialRoute ??
getPages?.first.name ??
_cleanRouteName("/${home.runtimeType}"),
);
return CupertinoApp.router(
routerDelegate: routerDelegate,
routeInformationParser: routeInformationParser,
backButtonDispatcher: backButtonDispatcher,
routeInformationProvider: routeInformationProvider,
key: _.unikey,
theme: theme,
builder: defaultBuilder,
title: title,
onGenerateTitle: onGenerateTitle,
... ... @@ -291,8 +281,8 @@ class GetCupertinoApp extends StatelessWidget {
debugShowCheckedModeBanner: debugShowCheckedModeBanner,
shortcuts: shortcuts,
useInheritedMediaQuery: useInheritedMediaQuery,
// actions: actions,
),
);
},
);
Widget defaultBuilder(BuildContext context, Widget? child) {
... ...
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
... ... @@ -7,7 +6,6 @@ import '../../../get_instance/get_instance.dart';
import '../../../get_state_manager/get_state_manager.dart';
import '../../../get_utils/get_utils.dart';
import '../../get_navigation.dart';
import 'root_controller.dart';
class GetMaterialApp extends StatelessWidget {
final GlobalKey<NavigatorState>? navigatorKey;
... ... @@ -58,7 +56,7 @@ class GetMaterialApp extends StatelessWidget {
final LogWriterCallback? logWriterCallback;
final bool? popGesture;
final SmartManagement smartManagement;
final Bindings? initialBinding;
final BindingsInterface? initialBinding;
final Duration? transitionDuration;
final bool? defaultGlobalState;
final List<GetPage>? getPages;
... ... @@ -68,7 +66,7 @@ class GetMaterialApp extends StatelessWidget {
final RouterDelegate<Object>? routerDelegate;
final BackButtonDispatcher? backButtonDispatcher;
final bool useInheritedMediaQuery;
const GetMaterialApp({
GetMaterialApp({
Key? key,
this.navigatorKey,
this.scaffoldMessengerKey,
... ... @@ -126,17 +124,28 @@ class GetMaterialApp extends StatelessWidget {
this.highContrastDarkTheme,
this.actions,
}) : routeInformationProvider = null,
backButtonDispatcher = null,
routeInformationParser = null,
routerDelegate = null,
backButtonDispatcher = null,
super(key: key);
static String _cleanRouteName(String name) {
name = name.replaceAll('() => ', '');
/// uncommonent for URL styling.
// name = name.paramCase!;
if (!name.startsWith('/')) {
name = '/$name';
}
return Uri.tryParse(name)?.toString() ?? name;
}
GetMaterialApp.router({
Key? key,
this.routeInformationProvider,
this.scaffoldMessengerKey,
RouteInformationParser<Object>? routeInformationParser,
RouterDelegate<Object>? routerDelegate,
this.routeInformationParser,
this.routerDelegate,
this.backButtonDispatcher,
this.builder,
this.title = '',
... ... @@ -183,34 +192,30 @@ class GetMaterialApp extends StatelessWidget {
this.getPages,
this.navigatorObservers,
this.unknownRoute,
}) : routerDelegate = routerDelegate ??= Get.createDelegate(
notFoundRoute: unknownRoute,
),
routeInformationParser =
routeInformationParser ??= Get.createInformationParser(
initialRoute: getPages?.first.name ?? '/',
),
//navigatorObservers = null,
navigatorKey = null,
}) : navigatorKey = null,
onGenerateRoute = null,
home = null,
onGenerateInitialRoutes = null,
onUnknownRoute = null,
routes = null,
initialRoute = null,
super(key: key) {
Get.routerDelegate = routerDelegate;
Get.routeInformationParser = routeInformationParser;
}
super(key: key);
@override
Widget build(BuildContext context) => GetBuilder<GetMaterialController>(
init: Get.rootController,
dispose: (d) {
onDispose?.call();
Get.clearRouteTree();
Get.clearTranslations();
Get.resetRootNavigator();
Get.routerDelegate = null;
Get.routeInformationParser = null;
},
initState: (i) {
Get.engine!.addPostFrameCallback((timeStamp) {
// Get.routerDelegate = routerDelegate;
// Get.routeInformationParser = routeInformationParser;
Get.engine.addPostFrameCallback((timeStamp) {
onReady?.call();
});
if (locale != null) Get.locale = locale;
... ... @@ -228,6 +233,13 @@ class GetMaterialApp extends StatelessWidget {
initialBinding?.dependencies();
if (getPages != null) {
Get.addPages(getPages!);
} else {
Get.addPage(
GetPage(
name: _cleanRouteName("/${home.runtimeType}"),
page: () => home!,
),
);
}
//Get.setDefaultDelegate(routerDelegate);
... ... @@ -244,10 +256,26 @@ class GetMaterialApp extends StatelessWidget {
transitionDuration ?? Get.defaultTransitionDuration,
);
},
builder: (_) => routerDelegate != null
? MaterialApp.router(
routerDelegate: routerDelegate!,
routeInformationParser: routeInformationParser!,
builder: (_) {
final routerDelegate = Get.createDelegate(
pages: getPages ?? [],
notFoundRoute: unknownRoute,
navigatorKey: navigatorKey,
navigatorObservers: (navigatorObservers == null
? <NavigatorObserver>[GetObserver(routingCallback, Get.routing)]
: <NavigatorObserver>[
GetObserver(routingCallback, Get.routing),
...navigatorObservers!
]));
final routeInformationParser = Get.createInformationParser(
initialRoute: initialRoute ??
getPages?.first.name ??
_cleanRouteName("/${home.runtimeType}"),
);
return MaterialApp.router(
routerDelegate: routerDelegate,
routeInformationParser: routeInformationParser,
backButtonDispatcher: backButtonDispatcher,
routeInformationProvider: routeInformationProvider,
key: _.unikey,
... ... @@ -256,59 +284,10 @@ class GetMaterialApp extends StatelessWidget {
onGenerateTitle: onGenerateTitle,
color: color,
theme: _.theme ?? theme ?? ThemeData.fallback(),
darkTheme:
_.darkTheme ?? darkTheme ?? theme ?? ThemeData.fallback(),
themeMode: _.themeMode ?? themeMode,
locale: Get.locale ?? locale,
scaffoldMessengerKey:
scaffoldMessengerKey ?? _.scaffoldMessengerKey,
localizationsDelegates: localizationsDelegates,
localeListResolutionCallback: localeListResolutionCallback,
localeResolutionCallback: localeResolutionCallback,
supportedLocales: supportedLocales,
debugShowMaterialGrid: debugShowMaterialGrid,
showPerformanceOverlay: showPerformanceOverlay,
checkerboardRasterCacheImages: checkerboardRasterCacheImages,
checkerboardOffscreenLayers: checkerboardOffscreenLayers,
showSemanticsDebugger: showSemanticsDebugger,
debugShowCheckedModeBanner: debugShowCheckedModeBanner,
shortcuts: shortcuts,
scrollBehavior: scrollBehavior,
useInheritedMediaQuery: useInheritedMediaQuery,
)
: MaterialApp(
key: _.unikey,
navigatorKey: (navigatorKey == null
? Get.key
: Get.addKey(navigatorKey!)),
scaffoldMessengerKey:
scaffoldMessengerKey ?? _.scaffoldMessengerKey,
home: home,
routes: routes ?? const <String, WidgetBuilder>{},
initialRoute: initialRoute,
onGenerateRoute:
(getPages != null ? generator : onGenerateRoute),
onGenerateInitialRoutes: (getPages == null || home != null)
? onGenerateInitialRoutes
: initialRoutesGenerate,
onUnknownRoute: onUnknownRoute,
navigatorObservers: (navigatorObservers == null
? <NavigatorObserver>[
GetObserver(routingCallback, Get.routing)
]
: <NavigatorObserver>[
GetObserver(routingCallback, Get.routing)
]
..addAll(navigatorObservers!)),
builder: defaultBuilder,
title: title,
onGenerateTitle: onGenerateTitle,
color: color,
theme: _.theme ?? theme ?? ThemeData.fallback(),
darkTheme:
_.darkTheme ?? darkTheme ?? theme ?? ThemeData.fallback(),
darkTheme: _.darkTheme ?? darkTheme ?? theme ?? ThemeData.fallback(),
themeMode: _.themeMode ?? themeMode,
locale: Get.locale ?? locale,
scaffoldMessengerKey: scaffoldMessengerKey ?? _.scaffoldMessengerKey,
localizationsDelegates: localizationsDelegates,
localeListResolutionCallback: localeListResolutionCallback,
localeResolutionCallback: localeResolutionCallback,
... ... @@ -322,9 +301,8 @@ class GetMaterialApp extends StatelessWidget {
shortcuts: shortcuts,
scrollBehavior: scrollBehavior,
useInheritedMediaQuery: useInheritedMediaQuery,
// actions: actions,
),
);
});
Widget defaultBuilder(BuildContext context, Widget? child) {
return Directionality(
... ... @@ -342,12 +320,12 @@ class GetMaterialApp extends StatelessWidget {
return PageRedirect(settings: settings, unknownRoute: unknownRoute).page();
}
List<Route<dynamic>> initialRoutesGenerate(String name) {
return [
PageRedirect(
settings: RouteSettings(name: name),
unknownRoute: unknownRoute,
).page()
];
}
// List<Route<dynamic>> initialRoutesGenerate(String name) {
// return [
// PageRedirect(
// settings: RouteSettings(name: name),
// unknownRoute: unknownRoute,
// ).page()
// ];
// }
}
... ...
import 'package:flutter/material.dart';
import '../../../get.dart';
import '../../../get_state_manager/get_state_manager.dart';
import '../../../get_utils/get_utils.dart';
import '../routes/custom_transition.dart';
import '../routes/observers/route_observer.dart';
import '../routes/transitions_type.dart';
class GetMaterialController extends SuperController {
class GetMaterialController extends FullLifeCycleController {
static GetMaterialController get to => Get.find();
bool testMode = false;
Key? unikey;
ThemeData? theme;
... ... @@ -33,14 +30,14 @@ class GetMaterialController extends SuperController {
CustomTransition? customTransition;
var _key = GlobalKey<NavigatorState>(debugLabel: 'Key Created by default');
Map<dynamic, GetDelegate> keys = {};
Map<dynamic, GlobalKey<NavigatorState>> keys = {};
GlobalKey<NavigatorState> get key => rootDelegate.navigatorKey;
GlobalKey<NavigatorState> get key => _key;
GetDelegate get rootDelegate => Get.createDelegate();
GlobalKey<NavigatorState>? addKey(GlobalKey<NavigatorState> newKey) {
_key = newKey;
rootDelegate.navigatorKey = newKey;
return key;
}
... ... @@ -54,18 +51,6 @@ class GetMaterialController extends SuperController {
});
}
@override
void onDetached() {}
@override
void onInactive() {}
@override
void onPaused() {}
@override
void onResumed() {}
void restartApp() {
unikey = UniqueKey();
update();
... ...
... ... @@ -7,28 +7,30 @@ import '../../get.dart';
class RouterReportManager<T> {
/// Holds a reference to `Get.reference` when the Instance was
/// created to manage the memory.
static final Map<Route?, List<String>> _routesKey = {};
final Map<T?, List<String>> _routesKey = {};
/// Stores the onClose() references of instances created with `Get.create()`
/// using the `Get.reference`.
/// Experimental feature to keep the lifecycle and memory management with
/// non-singleton instances.
static final Map<Route?, HashSet<Function>> _routesByCreate = {};
final Map<T?, HashSet<Function>> _routesByCreate = {};
static late final RouterReportManager instance = RouterReportManager();
void printInstanceStack() {
Get.log(_routesKey.toString());
}
static Route? _current;
T? _current;
// ignore: use_setters_to_change_properties
static void reportCurrentRoute(Route newRoute) {
void reportCurrentRoute(T newRoute) {
_current = newRoute;
}
/// Links a Class instance [S] (or [tag]) to the current route.
/// Requires usage of `GetMaterialApp`.
static void reportDependencyLinkedToRoute(String depedencyKey) {
void reportDependencyLinkedToRoute(String depedencyKey) {
if (_current == null) return;
if (_routesKey.containsKey(_current)) {
_routesKey[_current!]!.add(depedencyKey);
... ... @@ -37,26 +39,26 @@ class RouterReportManager<T> {
}
}
static void clearRouteKeys() {
void clearRouteKeys() {
_routesKey.clear();
_routesByCreate.clear();
}
static void appendRouteByCreate(GetLifeCycleBase i) {
void appendRouteByCreate(GetLifeCycleMixin i) {
_routesByCreate[_current] ??= HashSet<Function>();
// _routesByCreate[Get.reference]!.add(i.onDelete as Function);
_routesByCreate[_current]!.add(i.onDelete);
}
static void reportRouteDispose(Route disposed) {
void reportRouteDispose(T disposed) {
if (Get.smartManagement != SmartManagement.onlyBuilder) {
WidgetsBinding.instance!.addPostFrameCallback((_) {
ambiguate(WidgetsBinding.instance)!.addPostFrameCallback((_) {
_removeDependencyByRoute(disposed);
});
}
}
static void reportRouteWillDispose(Route disposed) {
void reportRouteWillDispose(T disposed) {
final keysToRemove = <String>[];
_routesKey[disposed]?.forEach(keysToRemove.add);
... ... @@ -85,7 +87,7 @@ class RouterReportManager<T> {
/// using `Get.smartManagement` as [SmartManagement.full] or
/// [SmartManagement.keepFactory]
/// Meant for internal usage of `GetPageRoute` and `GetDialogRoute`
static void _removeDependencyByRoute(Route routeName) {
void _removeDependencyByRoute(T routeName) {
final keysToRemove = <String>[];
_routesKey[routeName]?.forEach(keysToRemove.add);
... ...
import 'dart:math' show sqrt, max;
import 'dart:ui' show lerpDouble;
import 'package:flutter/material.dart';
class CircularRevealClipper extends CustomClipper<Path> {
final double fraction;
final Alignment? centerAlignment;
final Offset? centerOffset;
final double? minRadius;
final double? maxRadius;
CircularRevealClipper({
required this.fraction,
this.centerAlignment,
this.centerOffset,
this.minRadius,
this.maxRadius,
});
@override
Path getClip(Size size) {
final center = centerAlignment?.alongSize(size) ??
centerOffset ??
Offset(size.width / 2, size.height / 2);
final minRadius = this.minRadius ?? 0;
final maxRadius = this.maxRadius ?? calcMaxRadius(size, center);
return Path()
..addOval(
Rect.fromCircle(
center: center,
radius: lerpDouble(minRadius, maxRadius, fraction)!,
),
);
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) => true;
static double calcMaxRadius(Size size, Offset center) {
final w = max(center.dx, size.width - center.dx);
final h = max(center.dy, size.height - center.dy);
return sqrt(w * w + h * h);
}
}
... ...
... ... @@ -2,22 +2,33 @@ import 'package:flutter/material.dart';
import '../../../get.dart';
import '../router_report.dart';
import 'custom_transition.dart';
import 'get_transition_mixin.dart';
import 'route_middleware.dart';
import 'transitions_type.dart';
@optionalTypeArgs
mixin RouteReportMixin<T extends StatefulWidget> on State<T> {
@override
void initState() {
super.initState();
RouterReportManager.instance.reportCurrentRoute(this);
}
@override
void dispose() {
super.dispose();
RouterReportManager.instance.reportRouteDispose(this);
}
}
mixin PageRouteReportMixin<T> on Route<T> {
@override
void install() {
super.install();
RouterReportManager.reportCurrentRoute(this);
RouterReportManager.instance.reportCurrentRoute(this);
}
@override
void dispose() {
super.dispose();
RouterReportManager.reportRouteDispose(this);
RouterReportManager.instance.reportRouteDispose(this);
}
}
... ... @@ -40,8 +51,8 @@ class GetPageRoute<T> extends PageRoute<T>
this.customTransition,
this.barrierDismissible = false,
this.barrierColor,
this.binding,
this.bindings,
this.bindings = const [],
this.binds,
this.routeName,
this.page,
this.title,
... ... @@ -50,7 +61,11 @@ class GetPageRoute<T> extends PageRoute<T>
this.maintainState = true,
bool fullscreenDialog = false,
this.middlewares,
}) : super(settings: settings, fullscreenDialog: fullscreenDialog);
}) : super(
settings: settings,
fullscreenDialog: fullscreenDialog,
// builder: (context) => Container(),
);
@override
final Duration transitionDuration;
... ... @@ -58,9 +73,9 @@ class GetPageRoute<T> extends PageRoute<T>
final String? routeName;
//final String reference;
final CustomTransition? customTransition;
final Bindings? binding;
final List<BindingsInterface> bindings;
final Map<String, String>? parameter;
final List<Bindings>? bindings;
final List<Bind>? binds;
@override
final bool showCupertinoParallax;
... ... @@ -98,20 +113,33 @@ class GetPageRoute<T> extends PageRoute<T>
if (_child != null) return _child!;
final middlewareRunner = MiddlewareRunner(middlewares);
final localbindings = [
if (bindings != null) ...bindings!,
if (binding != null) ...[binding!]
];
final bindingsToBind = middlewareRunner.runOnBindingsStart(localbindings);
if (bindingsToBind != null) {
for (final binding in bindingsToBind) {
binding.dependencies();
final localbinds = [if (binds != null) ...binds!];
final bindingsToBind = middlewareRunner
.runOnBindingsStart(bindings.isNotEmpty ? bindings : localbinds);
final pageToBuild = middlewareRunner.runOnPageBuildStart(page)!;
if (bindingsToBind != null && bindingsToBind.isNotEmpty) {
if (bindingsToBind is List<BindingsInterface>) {
for (final item in bindingsToBind) {
final dep = item.dependencies();
if (dep is List<Bind>) {
_child = Binds(
child: middlewareRunner.runOnPageBuilt(pageToBuild()),
binds: dep,
);
}
}
} else if (bindingsToBind is List<Bind>) {
_child = Binds(
child: middlewareRunner.runOnPageBuilt(pageToBuild()),
binds: bindingsToBind,
);
}
}
final pageToBuild = middlewareRunner.runOnPageBuildStart(page)!;
_child = middlewareRunner.runOnPageBuilt(pageToBuild());
return _child!;
return _child ??= middlewareRunner.runOnPageBuilt(pageToBuild());
}
@override
... ...
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'circular_reveal_clipper.dart';
class LeftToRightFadeTransition {
Widget buildTransitions(
BuildContext context,
... ... @@ -184,3 +186,24 @@ class SizeTransitions {
);
}
}
class CircularRevealTransition {
Widget buildTransitions(
BuildContext context,
Curve? curve,
Alignment? alignment,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
return ClipPath(
clipper: CircularRevealClipper(
fraction: animation.value,
centerAlignment: Alignment.center,
centerOffset: Offset.zero,
minRadius: 0,
maxRadius: 800,
),
child: child,
);
}
}
... ...
... ... @@ -3,16 +3,16 @@ import 'package:flutter/widgets.dart';
import '../../../get.dart';
class GetInformationParser extends RouteInformationParser<GetNavConfig> {
class GetInformationParser extends RouteInformationParser<RouteDecoder> {
final String initialRoute;
GetInformationParser({
this.initialRoute = '/',
required this.initialRoute,
}) {
Get.log('GetInformationParser is created !');
}
@override
SynchronousFuture<GetNavConfig> parseRouteInformation(
SynchronousFuture<RouteDecoder> parseRouteInformation(
RouteInformation routeInformation,
) {
var location = routeInformation.location;
... ... @@ -26,22 +26,16 @@ class GetInformationParser extends RouteInformationParser<GetNavConfig> {
Get.log('GetInformationParser: route location: $location');
final matchResult = Get.routeTree.matchRoute(location ?? initialRoute);
final routeName = location ?? initialRoute;
return SynchronousFuture(
GetNavConfig(
currentTreeBranch: matchResult.treeBranch,
location: location,
state: routeInformation.state,
),
);
return SynchronousFuture(RouteDecoder.fromRoute(routeName));
}
@override
RouteInformation restoreRouteInformation(GetNavConfig config) {
RouteInformation restoreRouteInformation(RouteDecoder config) {
return RouteInformation(
location: config.location,
state: config.state,
location: config.pageSettings?.name,
state: null,
);
}
}
... ...
import 'package:flutter/widgets.dart';
import '../../../get_instance/src/bindings_interface.dart';
import '../routes/get_route.dart';
import '../routes/transitions_type.dart';
/// Enables the user to customize the intended pop behavior
///
/// Goes to either the previous _activePages entry or the previous page entry
///
/// e.g. if the user navigates to these pages
/// 1) /home
/// 2) /home/products/1234
///
/// when popping on [History] mode, it will emulate a browser back button.
///
/// so the new _activePages stack will be:
/// 1) /home
///
/// when popping on [Page] mode, it will only remove the last part of the route
/// so the new _activePages stack will be:
/// 1) /home
/// 2) /home/products
///
/// another pop will change the _activePages stack to:
/// 1) /home
enum PopMode {
History,
Page,
}
/// Enables the user to customize the behavior when pushing multiple routes that
/// shouldn't be duplicates
enum PreventDuplicateHandlingMode {
/// Removes the _activePages entries until it reaches the old route
PopUntilOriginalRoute,
/// Simply don't push the new route
DoNothing,
/// Recommended - Moves the old route entry to the front
///
/// With this mode, you guarantee there will be only one
/// route entry for each location
ReorderRoutes,
Recreate,
}
mixin IGetNavigation {
Future<T?> to<T>(
Widget Function() page, {
bool? opaque,
Transition? transition,
Curve? curve,
Duration? duration,
int? id,
String? routeName,
bool fullscreenDialog = false,
dynamic arguments,
List<BindingsInterface> bindings = const [],
bool preventDuplicates = true,
bool? popGesture,
bool showCupertinoParallax = true,
double Function(BuildContext context)? gestureWidth,
});
Future<void> popModeUntil(
String fullRoute, {
PopMode popMode = PopMode.History,
});
Future<T?> off<T>(
Widget Function() page, {
bool? opaque,
Transition? transition,
Curve? curve,
Duration? duration,
int? id,
String? routeName,
bool fullscreenDialog = false,
dynamic arguments,
List<BindingsInterface> bindings = const [],
bool preventDuplicates = true,
bool? popGesture,
bool showCupertinoParallax = true,
double Function(BuildContext context)? gestureWidth,
});
Future<T?>? offAll<T>(
Widget Function() page, {
bool Function(GetPage route)? predicate,
bool opaque = true,
bool? popGesture,
int? id,
String? routeName,
dynamic arguments,
List<BindingsInterface> bindings = const [],
bool fullscreenDialog = false,
Transition? transition,
Curve? curve,
Duration? duration,
bool showCupertinoParallax = true,
double Function(BuildContext context)? gestureWidth,
});
Future<T?> toNamed<T>(
String page, {
dynamic arguments,
int? id,
bool preventDuplicates = true,
Map<String, String>? parameters,
});
Future<T?> offNamed<T>(
String page, {
dynamic arguments,
int? id,
Map<String, String>? parameters,
});
Future<T?>? offAllNamed<T>(
String newRouteName, {
// bool Function(GetPage route)? predicate,
dynamic arguments,
int? id,
Map<String, String>? parameters,
});
Future<T?>? offNamedUntil<T>(
String page, {
bool Function(GetPage route)? predicate,
dynamic arguments,
int? id,
Map<String, String>? parameters,
});
Future<T?> toNamedAndOffUntil<T>(
String page,
bool Function(GetPage) predicate, [
Object? data,
]);
Future<T?> offUntil<T>(
Widget Function() page,
bool Function(GetPage) predicate, [
Object? arguments,
]);
void back<T>([T? result]);
Future<R?> backAndtoNamed<T, R>(String page, {T? result, Object? arguments});
void backUntil(bool Function(GetPage) predicate);
void goToUnknownPage([bool clearPages = true]);
}
... ...
import 'package:flutter/widgets.dart';
import '../../../get.dart';
class GetNavigator extends Navigator {
GetNavigator.onGenerateRoute({
GlobalKey<NavigatorState>? key,
bool Function(Route<dynamic>, dynamic)? onPopPage,
required List<GetPage> pages,
List<NavigatorObserver>? observers,
bool reportsRouteUpdateToEngine = false,
TransitionDelegate? transitionDelegate,
String? initialRoute,
String? restorationScopeId,
}) : super(
//keys should be optional
key: key,
initialRoute: initialRoute,
onPopPage: onPopPage ??
(route, result) {
final didPop = route.didPop(result);
if (!didPop) {
return false;
}
return true;
},
onGenerateRoute: (settings) {
final selectedPageList =
pages.where((element) => element.name == settings.name);
if (selectedPageList.isNotEmpty) {
final selectedPage = selectedPageList.first;
return GetPageRoute(
page: selectedPage.page,
settings: settings,
);
}
return null;
},
reportsRouteUpdateToEngine: reportsRouteUpdateToEngine,
restorationScopeId: restorationScopeId,
pages: pages,
observers: [
// GetObserver(),
...?observers,
],
transitionDelegate:
transitionDelegate ?? const DefaultTransitionDelegate<dynamic>(),
);
GetNavigator({
GlobalKey<NavigatorState>? key,
bool Function(Route<dynamic>, dynamic)? onPopPage,
required List<GetPage> pages,
List<NavigatorObserver>? observers,
bool reportsRouteUpdateToEngine = false,
TransitionDelegate? transitionDelegate,
String? initialRoute,
String? restorationScopeId,
}) : super(
//keys should be optional
key: key,
initialRoute: initialRoute,
onPopPage: onPopPage ??
(route, result) {
final didPop = route.didPop(result);
if (!didPop) {
return false;
}
return true;
},
reportsRouteUpdateToEngine: reportsRouteUpdateToEngine,
restorationScopeId: restorationScopeId,
pages: pages,
observers: [
// GetObserver(null, Get.routing),
HeroController(),
...?observers,
],
transitionDelegate:
transitionDelegate ?? const DefaultTransitionDelegate<dynamic>(),
);
}
... ...
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import '../../../get_core/src/get_main.dart';
import '../../../get_instance/get_instance.dart';
import '../../../get_instance/src/bindings_interface.dart';
import '../../../get_state_manager/src/simple/get_state.dart';
import '../../get_navigation.dart';
import 'custom_transition.dart';
import 'transitions_type.dart';
class GetPage<T> extends Page<T> {
final GetPageBuilder page;
... ... @@ -21,12 +20,14 @@ class GetPage<T> extends Page<T> {
final bool maintainState;
final bool opaque;
final double Function(BuildContext context)? gestureWidth;
final Bindings? binding;
final List<Bindings> bindings;
//final BindingsInterface? binding;
final List<BindingsInterface> bindings;
final List<Bind> binds;
final CustomTransition? customTransition;
final Duration? transitionDuration;
final bool fullscreenDialog;
final bool preventDuplicates;
final Completer<T?>? completer;
// @override
// final LocalKey? key;
... ... @@ -45,6 +46,8 @@ class GetPage<T> extends Page<T> {
final GetPage? unknownRoute;
final bool showCupertinoParallax;
final PreventDuplicateHandlingMode preventDuplicateHandlingMode;
GetPage({
required this.name,
required this.page,
... ... @@ -59,8 +62,8 @@ class GetPage<T> extends Page<T> {
this.opaque = true,
this.transitionDuration,
this.popGesture,
this.binding,
this.bindings = const [],
this.binds = const [],
this.transition,
this.customTransition,
this.fullscreenDialog = false,
... ... @@ -70,17 +73,22 @@ class GetPage<T> extends Page<T> {
this.arguments,
this.showCupertinoParallax = true,
this.preventDuplicates = true,
this.preventDuplicateHandlingMode =
PreventDuplicateHandlingMode.ReorderRoutes,
this.completer,
LocalKey? key,
}) : path = _nameToRegex(name),
assert(name.startsWith('/'),
'It is necessary to start route name [$name] with a slash: /$name'),
super(
key: ValueKey(name),
key: key ?? ValueKey(name),
name: name,
arguments: Get.arguments,
// arguments: Get.arguments,
);
// settings = RouteSettings(name: name, arguments: Get.arguments);
GetPage<T> copy({
LocalKey? key,
String? name,
GetPageBuilder? page,
bool? popGesture,
... ... @@ -91,13 +99,14 @@ class GetPage<T> extends Page<T> {
Alignment? alignment,
bool? maintainState,
bool? opaque,
Bindings? binding,
List<Bindings>? bindings,
List<BindingsInterface>? bindings,
// BindingsInterface? binding,
List<Bind>? binds,
CustomTransition? customTransition,
Duration? transitionDuration,
bool? fullscreenDialog,
RouteSettings? settings,
List<GetPage>? children,
List<GetPage<T>>? children,
GetPage? unknownRoute,
List<GetMiddleware>? middlewares,
bool? preventDuplicates,
... ... @@ -105,8 +114,10 @@ class GetPage<T> extends Page<T> {
bool? participatesInRootNavigator,
Object? arguments,
bool? showCupertinoParallax,
Completer<T?>? completer,
}) {
return GetPage(
key: key ?? this.key,
participatesInRootNavigator:
participatesInRootNavigator ?? this.participatesInRootNavigator,
preventDuplicates: preventDuplicates ?? this.preventDuplicates,
... ... @@ -120,8 +131,8 @@ class GetPage<T> extends Page<T> {
alignment: alignment ?? this.alignment,
maintainState: maintainState ?? this.maintainState,
opaque: opaque ?? this.opaque,
binding: binding ?? this.binding,
bindings: bindings ?? this.bindings,
binds: binds ?? this.binds,
customTransition: customTransition ?? this.customTransition,
transitionDuration: transitionDuration ?? this.transitionDuration,
fullscreenDialog: fullscreenDialog ?? this.fullscreenDialog,
... ... @@ -132,6 +143,7 @@ class GetPage<T> extends Page<T> {
arguments: arguments ?? this.arguments,
showCupertinoParallax:
showCupertinoParallax ?? this.showCupertinoParallax,
completer: completer ?? this.completer,
);
}
... ... @@ -167,6 +179,21 @@ class GetPage<T> extends Page<T> {
return PathDecoded(RegExp('^$stringPath\$'), keys);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is GetPage<T> && other.key == key;
}
@override
String toString() =>
'${objectRuntimeType(this, 'Page')}("$name", $key, $arguments)';
@override
int get hashCode {
return key.hashCode;
}
}
@immutable
... ...