关于Get
GetX 是 Flutter 上的一个轻量且强大的解决方案:高性能的状态管理、智能的依赖注入和便捷的路由管理。
GetX 有3个基本原则:
性能: GetX 专注于性能和最小资源消耗。GetX 打包后的apk占用大小和运行时的内存占用与其他状态管理插件不相上下。如果你感兴趣,这里有一个性能测试。
效率: GetX 的语法非常简捷,并保持了极高的性能,能极大缩短你的开发时长。
结构: GetX 可以将界面、逻辑、依赖和路由完全解耦,用起来更清爽,逻辑更清晰,代码更容易维护。
GetX 并不臃肿,却很轻量。如果你只使用状态管理,只有状态管理模块会被编译,其他没用到的东西都不会被编译到你的代码中。它拥有众多的功能,但这些功能都在独立的容器中,只有在使用后才会启动。
Getx有一个庞大的生态系统,能够在Android、iOS、Web、Mac、Linux、Windows和你的服务器上用同样的代码运行。 通过Get Server 可以在你的后端完全重用你在前端写的代码。
依赖注入
//controller.dart
class Controller extends GetxController{
var count = 0.obs;
increment() => count++;
}
//view.dart
class Home extends StatelessWidget {
@override
Widget build(context) {
// 使用Get.put()实例化你的类,使其对当下的所有子路由可用。
final Controller c = Get.put(Controller());
return Scaffold(
// 使用Obx(()=>每当改变计数时,就更新Text()。
appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))),
// 用一个简单的Get.to()即可代替Navigator.push那8行,无需上下文!
body: Center(child: ElevatedButton(
child: Text("Go to Other"), onPressed: () => Get.to(Other()))),
floatingActionButton:
FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment));
}
}
class Other extends StatelessWidget {
// 你可以让Get找到一个正在被其他页面使用的Controller,并将它返回给你。
final Controller c = Get.find();
@override
Widget build(context){
// 访问更新后的计数变量
return Scaffold(body: Center(child: Text("${c.count}")));
}
}
GET组件
Get.snackbar 通知提示框
RaisedVutton(
child:Text('snackbar'),
onPressed:(){
Get.snackbar("标题","网络错误!",
colorText:Colors.white,backgroundColor:Colors.black54);
}
)
Get.defaultDialog弹出提示框
RaisedVutton(
child:Text('defaultDialog'),
onPressed:(){
Get.defaultDialog(
title:'标题'
titleStyle:TextStyle(color:Colors.red),
content:Column(
children:[
Text('内容')
Text('内容')
Text('内容')
Text('内容')
Text('内容')
]
),
cancel:RaisedButton(
child:Text('取消'),
onPressed:(){
Get.back();
}
),
confirm:RaisedButton(
color:Colors.green
child:Text('确定',
style:TextStyle(Colors.white)),
onPressed:(){
Get.back();
Get.snackbar("提示","消息已确认!")
}
),
barrierDismissible:false //点击空白处diglog不关闭
);
}
)
Get.bottomSheet底部弹出框
RaisedButton(
child:Text('bottomSheet'),
onPressed:(){
Get.bottomSheet(
Container(
height:150,
color:Colors.white,
child:ListView(
children:[
ListTitle(
title:Text('修改密码')
leading:Icon(Icons.add)//左边的图标
trailing:Icon(Icons.add)//右边的图标
),
ListTitle(
title:Text('注销')
)
]
)
)
enableDrag:false// 禁止拖拽改变大小
isDismissible:false//点击空白处diglog不关闭
);
}
)
GET路由
如果你想免上下文(context)使用路由/snackbars/dialogs/bottomsheets,GetX对你来说也是极好的,来吧展示:
在你的MaterialApp前加上 "Get",把它变成GetMaterialApp。
GetMaterialApp( // Before: MaterialApp(
home: MyHome(),
)
导航到新页面
Get.to(NextScreen());
Get.to(NextScreen(),
transition:native,//动画
duration:Duration(milliseconds:1000),//延时
arguments:{'name':'jack','age':'20'},//传参
)
//接收
Get.arguments['name']
Get.arguments['age']
用别名导航到新页面。
Get.toNamed('/details');
//定义路由
void main() {
runApp(
GetMaterialApp(
initialRoute: '/',
defaultTransition:Transition.zoom
getPages: [
GetPage(name: '/', page: () => MyHomePage()),
GetPage(name: '/second', page: () => Second()),
GetPage(
name: '/third',
page: () => Third(),
transition: Transition.zoom //动画
),
],
routingCallback: (routing) { //中间件 可用作路由拦截
if(routing.current == '/second')
{
openAds();
}
}
)
);
}
//动态网页链接
Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");
print(Get.parameters['id']);
// out: 354
print(Get.parameters['name']);
// out: Enzo
//NamedParameters。
void main() {
runApp(
GetMaterialApp(
initialRoute: '/',
getPages: [
GetPage(
name: '/profile/',
page: () => MyProfile(),
),
//你可以为有参数的路由定义一个不同的页面,也可以为没有参数的路由定义一个不同的页面,但是你必须在不接收参数的路由上使用斜杠"/",就像上面说的那样。
GetPage(
name: '/profile/:user',
page: () => UserProfile(),
),
],
)
);
}
//发送别名路由数据
Get.toNamed("/second/34954");
//在第二个页面上,通过参数获取数据
print(Get.parameters['user']);
// out: 34954
//发送多个参数
Get.toNamed("/profile/34954?flag=true");
//参数获取数据
print(Get.parameters['user']);
print(Get.parameters['flag']);
要关闭snackbars, dialogs, bottomsheets或任何你通常会用Navigator.pop(context)关闭的东西。
Get.back();
进入下一个页面,但没有返回上一个页面的选项(用于闪屏页,登录页面等)。
Get.off(NextScreen());
进入下一个页面并取消之前的所有路由(在购物车、投票和测试中很有用)。
Get.off(NextScreen());
状态管理器
// controller
final count1 = 0.obs;
final count2 = 0.obs;
int get sum => count1.value + count2.value;
// 视图
GetX<Controller>(
builder: (controller) {
print("count 1 rebuild");
return Text('${controller.count1.value}');
},
),
GetX<Controller>(
builder: (controller) {
print("count 2 rebuild");
return Text('${controller.count2.value}');
},
),
GetX<Controller>(
builder: (controller) {
print("count 3 rebuild");
return Text('${controller.sum}');
},
),
Obx(() => Text("Clicks: ${c.count}"))
如果我们把count1.value++递增,就会打印出来:
count 1 rebuild
count 3 rebuild
如果我们改变count2.value++,就会打印出来。
count 2 rebuild
count 3 rebuild
类实例化观察者
//可以将你的类值转换为 obs
class RxUser {
final name = "Camila".obs;
final age = 18.obs;
}
//或者可以将整个类转换为一个可观察的类。
class User {
User({String name, int age});
var name;
var age;
}
//实例化时。
final user = User(name: "Camila", age: 18).obs;
// 我们将使整个类成为可观察的,而不是每个属性。
class User{
User({this.name = '', this.age = 0});
String name;
int age;
}
// controller
final user = User().obs;
//当你需要更新user变量时。
user.update( (user) { // 这个参数是你要更新的类本身。
user.name = 'Jonny';
user.age = 18;
});
// 更新user变量的另一种方式。
user(User(name: 'João', age: 35));
// view
Obx(
()=> Text("Name ${user.value.name}: Age: ${user.value.age}")
);
// 你也可以不使用.value来访问模型值。
user().name; // 注意是user变量,而不是类变量(首字母是小写的)。
Workers
//控制器初始化
// controller
class Controller extends GetxController{
final count1 = 0.obs;
final count2 = 0.obs;
int get sum => count1.value + count2.value;
void onInit(){
super.onInit();
ever(count1,(callback){
//每次count1变化时触发。例如处理购物车价格
})
once(count1, (callback) {
//只有在变量count1第一次被改变时才会被调用
//例如用户登录信息
print("count1 was changed once"));
})
debounce(count1, (callback) {
//防抖 - 每当用户停止输入1秒时调用,例如搜索
print("count1 debouce"));
time: Duration(seconds: 1));
})
interval(count1, (callback) {
//节流 - 每隔一秒再调用,例如滑动再刷新
print("count1 interval"));
time: Duration(seconds: 1));
})
}
}
简单状态处理器
controller
class Controller extends GetxController{
var count1 = 0;
final count1 = 0.obsl
int get sum => count1.value + count2.value;
static Controller get to => Get.find(); // 注册到to对象
void increment(){
count1++;
update();
}
}
View
FloatingActionButton(
onPressed: () {
Controller.to.increment(),
} //
child: Text("${Controller.to.counter}"),
),
//或
GetBuilder<Controller>(
init: Controller(),
builder: (value) => Text(
'${value.counter}', //here
),
),
使用ID更新
//controller
class Controller extends GetxController{
var count1 = 0;
final count1 = 0.obsl
int get sum => count1.value + count2.value;
static Controller get to => Get.find(); // 注册到to对象
void increment(){
count1++;
update(['text1']);
}
}
//view
GetBuilder<Controller>(
init: Controller(),
id:'text1',
builder: (value) => Text(
'${value.counter}', //here
),
),
依赖管理
Get.put()
Get.put<SomeClass>(SomeClass());
Get.put<LoginController>(LoginController(), permanent: true);
Get.put<ListItemController>(ListItemController, tag: "some unique string");
Get.put<S>(
// 必备:你想得到保存的类,比如控制器或其他东西。
// 注:"S "意味着它可以是任何类型的类。
S dependency
// 可选:当你想要多个相同类型的类时,可以用这个方法。
// 因为你通常使用Get.find<Controller>()来获取一个类。
// 你需要使用标签来告诉你需要哪个实例。
// 必须是唯一的字符串
String tag,
// 可选:默认情况下,get会在实例不再使用后进行销毁
// (例如:一个已经销毁的视图的Controller)
// 但你可能需要这个实例在整个应用生命周期中保留在那里,就像一个sharedPreferences的实例或其他东西。
//所以你设置这个选项
// 默认值为false
bool permanent = false,
// 可选:允许你在测试中使用一个抽象类后,用另一个抽象类代替它,然后再进行测试。
// 默认为false
bool overrideAbstract = false,
// 可选:允许你使用函数而不是依赖(dependency)本身来创建依赖。
// 这个不常用
InstanceBuilderCallback<S> builder,
)
Get.lazyPut
可以懒加载一个依赖,这样它只有在使用时才会被实例化。这对于计算代价高的类来说非常有用,或者如果你想在一个地方实例化几个类(比如在Bindings类中),而且你知道你不会在那个时候使用这个类。
///只有当第一次使用Get.find<ApiMock>时,ApiMock才会被调用。
Get.lazyPut<ApiMock>(() => ApiMock());
Get.lazyPut<FirebaseAuth>(
() {
// ... some logic if needed
return FirebaseAuth();
},
tag: Math.random().toString(),
fenix: true
)
Get.lazyPut<Controller>( () => Controller() )
Get.putAsync
Get.putAsync<SharedPreferences>(() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('counter', 12345);
return prefs;
});
Get.putAsync<YourAsyncClass>( () async => await YourAsyncClass() )
//参数
Get.putAsync<S>(
// 必备:一个将被执行的异步方法,用于实例化你的类。
AsyncInstanceBuilderCallback<S> builder,
// 可选:和Get.put()一样,当你想让同一个类有多个不同的实例时,就会用到它。
// 必须是唯一的
String tag,
// 可选:与Get.put()相同,当你需要在整个应用程序中保持该实例的生命时使用。
// 默认值为false
bool permanent = false
)
Get_Cli
flutter pub global activate get_cli
get create project:getxdemo
E:\flutter\getx>get create project:getxdemo
1) Flutter Project
2) Get Server
1
? What is your company's domain? Example: com.yourcompany com.yourcompany
what language do you want to use on ios?
1) Swift
2) Objective-C
1
what language do you want to use on android?
1) Kotlin
2) Java
1
Do you want to use null safe?
1) Yes!
2) No
1
do you want to use some linter?
1) no
2) Pedantic [Deprecated]
3) Effective Dart [Deprecated]
4) Dart Recommended
4
Running `flutter create E:\flutter\getx\getxdemo` …
$ flutter create --no-pub -i swift -a kotlin --org com.yourcompany E:\flutter\getx\getxdemo
Creating project ....
lib\main.dart (created)
pubspec.yaml (created)
README.md (created)
test\widget_test.dart (created)
.gitignore (created)
.idea\libraries\Dart_SDK.xml (created)
.idea\libraries\KotlinJavaRuntime.xml (created)
.idea\modules.xml (created)
.idea\runConfigurations\main_dart.xml (created)
.idea\workspace.xml (created)
.metadata (created)
analysis_options.yaml (created)
android\app\build.gradle (created)
android\app\src\main\kotlin\com\yourcompany\getxdemo\MainActivity.kt (created)
android\build.gradle (created)
android\getxdemo_android.iml (created)
android\.gitignore (created)
android\app\src\debug\AndroidManifest.xml (created)
android\app\src\main\AndroidManifest.xml (created)
android\app\src\main\res\drawable\launch_background.xml (created)
android\app\src\main\res\drawable-v21\launch_background.xml (created)
android\app\src\main\res\mipmap-hdpi\ic_launcher.png (created)
android\app\src\main\res\mipmap-mdpi\ic_launcher.png (created)
android\app\src\main\res\mipmap-xhdpi\ic_launcher.png (created)
android\app\src\main\res\mipmap-xxhdpi\ic_launcher.png (created)
android\app\src\main\res\mipmap-xxxhdpi\ic_launcher.png (created)
android\app\src\main\res\values\styles.xml (created)
android\app\src\main\res\values-night\styles.xml (created)
android\app\src\profile\AndroidManifest.xml (created)
android\gradle\wrapper\gradle-wrapper.properties (created)
android\gradle.properties (created)
android\settings.gradle (created)
ios\Runner\AppDelegate.swift (created)
ios\Runner\Runner-Bridging-Header.h (created)
ios\Runner.xcodeproj\project.pbxproj (created)
ios\Runner.xcodeproj\xcshareddata\xcschemes\Runner.xcscheme (created)
ios\.gitignore (created)
ios\Flutter\AppFrameworkInfo.plist (created)
ios\Flutter\Debug.xcconfig (created)
ios\Flutter\Release.xcconfig (created)
ios\Runner\Assets.xcassets\AppIcon.appiconset\Contents.json (created)
ios\Runner\Assets.xcassets\AppIcon.appiconset\Icon-App-1024x1024@1x.png (created)
ios\Runner\Assets.xcassets\AppIcon.appiconset\Icon-App-20x20@1x.png (created)
ios\Runner\Assets.xcassets\AppIcon.appiconset\Icon-App-20x20@2x.png (created)
ios\Runner\Assets.xcassets\AppIcon.appiconset\Icon-App-20x20@3x.png (created)
ios\Runner\Assets.xcassets\AppIcon.appiconset\Icon-App-29x29@1x.png (created)
ios\Runner\Assets.xcassets\AppIcon.appiconset\Icon-App-29x29@2x.png (created)
ios\Runner\Assets.xcassets\AppIcon.appiconset\Icon-App-29x29@3x.png (created)
ios\Runner\Assets.xcassets\AppIcon.appiconset\Icon-App-40x40@1x.png (created)
ios\Runner\Assets.xcassets\AppIcon.appiconset\Icon-App-40x40@2x.png (created)
ios\Runner\Assets.xcassets\AppIcon.appiconset\Icon-App-40x40@3x.png (created)
ios\Runner\Assets.xcassets\AppIcon.appiconset\Icon-App-60x60@2x.png (created)
ios\Runner\Assets.xcassets\AppIcon.appiconset\Icon-App-60x60@3x.png (created)
ios\Runner\Assets.xcassets\AppIcon.appiconset\Icon-App-76x76@1x.png (created)
ios\Runner\Assets.xcassets\AppIcon.appiconset\Icon-App-76x76@2x.png (created)
ios\Runner\Assets.xcassets\AppIcon.appiconset\Icon-App-83.5x83.5@2x.png (created)
ios\Runner\Assets.xcassets\LaunchImage.imageset\Contents.json (created)
ios\Runner\Assets.xcassets\LaunchImage.imageset\LaunchImage.png (created)
ios\Runner\Assets.xcassets\LaunchImage.imageset\LaunchImage@2x.png (created)
ios\Runner\Assets.xcassets\LaunchImage.imageset\LaunchImage@3x.png (created)
ios\Runner\Assets.xcassets\LaunchImage.imageset\README.md (created)
ios\Runner\Base.lproj\LaunchScreen.storyboard (created)
ios\Runner\Base.lproj\Main.storyboard (created)
ios\Runner\Info.plist (created)
ios\Runner.xcodeproj\project.xcworkspace\contents.xcworkspacedata (created)
ios\Runner.xcodeproj\project.xcworkspace\xcshareddata\IDEWorkspaceChecks.plist (created)
ios\Runner.xcodeproj\project.xcworkspace\xcshareddata\WorkspaceSettings.xcsettings (created)
ios\Runner.xcworkspace\contents.xcworkspacedata (created)
ios\Runner.xcworkspace\xcshareddata\IDEWorkspaceChecks.plist (created)
ios\Runner.xcworkspace\xcshareddata\WorkspaceSettings.xcsettings (created)
getxdemo.iml (created)
web\favicon.png (created)
web\icons\Icon-192.png (created)
web\icons\Icon-512.png (created)
web\icons\Icon-maskable-192.png (created)
web\icons\Icon-maskable-512.png (created)
web\index.html (created)
web\manifest.json (created)
Wrote 81 files.
All done!
In order to run your application, type:
$ cd .
$ flutter run
Your application code is in .\lib\main.dart.
Running `flutter pub get` …
$ flutter pub get
Running "flutter pub get" in getxdemo...
Got socket error trying to find package cupertino_icons at https://pub.dartlang.org.
pub get failed (server unavailable) -- attempting retry 1 in 1 second...
Got socket error trying to find package flutter_lints at https://pub.dartlang.org.
pub get failed (server unavailable) -- attempting retry 2 in 2 seconds...
Running "flutter pub get" in getxdemo... 612.9s
$ dart migrate --apply-changes --skip-import-check
Migrating E:\flutter\getx\getxdemo
See https://dart.dev/go/null-safety-migration for a migration guide.
Analyzing project...
All sources appear to be already migrated. Nothing to do.
✖ + HandshakeException: Connection terminated during handshake
✓ File: analysis_options.yaml created successfully at path: analysis_options.yaml
1) GetX Pattern (by Kauê)
2) CLEAN (by Arktekko)
1
Your lib folder is not empty. Are you sure you want to overwrite your application?
WARNING: This action is irreversible
1) Yes!
2) No
1
✖ + HandshakeException: Connection terminated during handshake
✓ File: main.dart created successfully at path: lib\\main.dart
✓ File: home_controller.dart created successfully at path: ./lib\app\modules\home\\controllers\\home_controller.dart
✓ File: home_view.dart created successfully at path: ./lib\app\modules\home\\views\\home_view.dart
✓ File: home_binding.dart created successfully at path: ./lib\app\modules\home\\bindings\\home_binding.dart
✓ File: app_routes.dart created successfully at path: lib\\app\\routes\\app_routes.dart
✖ + Package: get is not installed in this application
Time: 6636020 Milliseconds
No active package get_cli.
创建一个页面
get create page:login
更新数据
//view
GetBuilder<LoginController>(builder:(login){
id:'userName'
return Text('当前登录用户:${login.name}')
})
ElevatedButton(
onPressed:(){
controller.changeUser();
},
child:Text('click')
)
//Controller
class HomeController extends GetxController {
//TODO: Implement HomeController
var name = 'miles'
final count = 0.obs;
@override
void onInit() {
super.onInit();
}
@override
void onReady() {
super.onReady();
}
changeUser(){
name = 'admin';
update('userName')
}
@override
void onClose() {}
void increment() => count.value++;
}