我用 GetX写了一个使用demo,和一个app,demo中导航的演示如下:
代码示例
前言
GetX 是 Flutter 上的一个轻量且强大的解决方案:高性能的状态管理、智能的依赖注入和便捷的路由管理。
为什么是 GetX,而不是 BLoC、MobX、Provider?
BLoC 非常安全和高效,但是对于初学者来说非常复杂,即使学会,样板代码也很多。
MobX 比 BLoC 更容易,而且是响应式的,但是需要使用一个代码生成器,需要等很久,这降低了生产力。
GetX我喜欢的地方:
- 轻量。模块单独编译,没用到的功能不会编译进我们的代码。
- 语法简洁。个人非常喜欢,显而易见且实用,比如路由摆脱了 context 的依赖,
Get.to(SomePage())
就能导航到新路由。 - 性能。Provider、BLoC 等只能在父子组件保存状态,同层级模块状态管理需要全局处理,存活在整个应用生命周期。而 GetX 可以随时添加控制器和删除控制器,并且会自动释放使用完的控制器。
- 依赖注入。提供依赖注入功能,代码层级可以完全分离,甚至依赖注入的代码也是分离的。
- 丰富的api。许多复杂的操作,使用 GetX 就会有简单的实现。
有的同学看过我写的Flutter状态管理provider的使用和封装,讲解了 Provider 的使用,其实在使用过程中发现了许多痛点,最致命的是 Provider 使用InheritedWidget
来传递相同的监听器,这意味着对其 ChangeNotifier 类的任何访问都必须在父子widget树内。非父子组件的状态管理问题,需要借助别的手段(eventbus,全局,单例),十分痛苦,在改用GetX后,越来越舒服了。
路由
普通路由导航
打开到新的页面:
Get.to(NextScreen());
对应原生路由:
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) {
return NextScreen();
},
));
返回:
Get.back();
对应原生路由:
Navigator.pop(context);
打开新页面,并且用新页面替换旧页面(删除旧页面):
Get.off(NextScreen());
对应原生路由:
Navigator.pushReplacement(context, MaterialPageRoute(
builder: (BuildContext context) {
return NextScreen();
},
));
打开新页面并删除之前的所有路由:
Get.offAll(NextScreen());
对应原生路由:
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (BuildContext context) {
return NextScreen();
},
),
(Route route) => false,
);
导航到新页面,在返回时接收返回数据:
var data = await Get.to(NextScreen());
对应原生路由:
var data = await Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) {
return NextScreen();
},
));
带返回值返回前一个路由,配合上面使用:
Get.back(result: 'success');
对应原生路由:
Navigator.pop(context, 'success');
别名路由导航
- 声明别名:
abstract class Routes {
static const Initial = '/';
static const NextScreen = '/NextScreen';
}
- 注册路由表:
abstract class AppPages {
static final pages = [
GetPage(
name: Routes.Initial,
page: () => HomePage(),
),
GetPage(
name: Routes.NextScreen,
page: () => NextScreen(),
),
];
}
- 替换MaterialApp为GetMaterialApp:
void main() {
runApp(GetMaterialApp(
debugShowCheckedModeBanner: false,
initialRoute: '/',
theme: appThemeData,
defaultTransition: Transition.fade,
getPages: AppPages.pages,
home: HomePage(),
));
}
使用
导航到下一个页面:
Get.toNamed(Routes.NextScreen);
导航到下一个页面并删除前一个页面:
Get.offNamed(Routes.NextScreen);
导航到下一个页面并删除以前所有的页面:
Get.offAllNamed(Routes.NextScreen);
发送数据到别名路由:
Get在这里接受任何东西,无论是一个字符串,一个Map,一个List,甚至一个类的实例。
Get.toNamed(Routes.NextScreen, arguments: '新垣结衣');
获取参数:
String name=Get.arguments;
动态网页链接:
像web一样携带参数,适合前端开发的风格。
Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");
获取参数:
int id = Get.parameters['id'];
// out: 354
String name=Get.parameters['name'];
还可以这样定义路由别名:
GetPage(
name: '/profile/:user',
page: () => UserProfile(),
),
导航:
Get.toNamed("/profile/34954");
在第二个页面上,通过参数获取数据
print(Get.parameters['user']);
// out: 34954
中间件
在跳转前做些事情,比如判断是否登录,可以使用routingCallback来实现:
GetMaterialApp(
routingCallback: (routing) {
if(routing.current == '/second'){
// 如果登录。。。
}
}
)
小部件导航
SnackBars
弹出:
Get.snackbar('Hi', 'i am a modern snackbar');
对应原生写法:
final snackBar = SnackBar(
content: Text('Hi!'),
action: SnackBarAction(
label: 'I am a old and ugly snackbar',
onPressed: (){}
),
);
//用Flutter创建一个简单的SnackBar,你必须获得Scaffold的context,或者你必须使用一个GlobalKey附加到你的Scaffold上。
Scaffold.of(context).showSnackBar(snackBar);
Dialogs
打开一个默认的Dialog:
Get.defaultDialog(
onConfirm: () => print("Ok"),
middleText: "Dialog made in 3 lines of code"
);
打开自定义的Dialog:
Get.dialog(YourDialogWidget());
BottomSheets
Get.bottomSheet类似于showModalBottomSheet,但不需要context:
Get.bottomSheet(
Container(
child: Wrap(
children: [
ListTile(
leading: Icon(Icons.music_note),
title: Text('Music'),
onTap: () => {}
),
ListTile(
leading: Icon(Icons.videocam),
title: Text('Video'),
onTap: () => {},
),
],
),
)
);