如何优雅地进行Flutter开发,GetX值得一试!
Posted 郭霖
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何优雅地进行Flutter开发,GetX值得一试!相关的知识,希望对你有一定的参考价值。
https://juejin.cn/user/2840793776393847
getx:Obx(() => Text())
这是我非常非常在意的一个方面,因为bloc的build刷新组件方法要传俩个泛型,加上build方法里面的俩个参数,导致一个build方法如果不使用箭头方法简写,几乎占四五行,用起来实在蛋筒,导致我平时开发直接把BlocBuilder方法直接写在页面顶层(不提倡写顶层),一个页面只用写一次了,不用定点到处写BlocBuilder了,手动滑稽.jpg
引入
首先导入GetX的插件
# getx 状态管理框架 https://pub.flutter-io.cn/packages/get
get: ^3.24.0
https://github.com/jonataslaw/getx
https://pub.dev/packages/get
主入口配置
只需要将MaterialApp改成GetMaterialApp即可。
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
home: CounterGetPage(),
);
}
}
import 'package:get/get.dart';
说明
插件效果
看下插件使用的效果图吧,样式参考了fish_redux插件样式
有一些可选择的功能,所以做成多按钮的样式,大家可以按照自己的需求进行操作
说下插件的功能含义
Model:生成GetX的模式,
Default:默认模式,生成三个文件:state,logic,view
Easy:简单模式,生成俩个文件:logic,view
Function:功能选择
useFolder:使用文件,选择后会生成文件夹,大驼峰命名自动转换为:小写+下划线
usePrefix:使用前缀,生成的文件前加上前缀,前缀为:大驼峰命名自动转换为:小写+下划线
Module Name:模块的名称,请使用大驼峰命名
安装
-
在设置里面选择:Plugins ---> 输入“getx”搜索 ---> 选择名字为:“GeX” ---> 然后安装 ---> 最后记得点击下“Apply” -
如果在使用该插件的过程中有什么问题,请在该项目的github上给我提issue,我看到后,会尽快处理
效果图
实现
模式选择:Easy
功能选择:useFolder
import 'package:get/get.dart';
class CounterGetLogic extends GetxController {
}
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'logic.dart';
class CounterGetPage extends StatelessWidget {
final CounterGetLogic logic = Get.put(CounterGetLogic());
@override
Widget build(BuildContext context) {
return Container();
}
}
响应式状态管理
class CounterGetLogic extends GetxController {
var count = 0.obs;
///自增
void increase() => ++count;
}
class CounterGetPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
CounterGetLogic logic = Get.put(CounterGetLogic());
return Scaffold(
appBar: AppBar(title: const Text('GetX计数器')),
body: Center(
child: Obx(
() => Text('点击了 ${logic.count.value} 次',
style: TextStyle(fontSize: 30.0)),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => logic.increase(),
child: const Icon(Icons.add),
),
);
}
}
class CounterGetPage extends StatelessWidget {
final CounterGetLogic logic = Get.put(CounterGetLogic());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text( 'GetX计数器')),
body: Center(
child: Obx(
() => Text( '点击了 ${logic.count.value} 次',
style: TextStyle(fontSize: 30.0)),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => logic.increase(),
child: const Icon(Icons.add),
),
);
}
}
// model
// 我们将使整个类成为可观察的,而不是每个属性。
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变量,而不是类变量(首字母是小写的)。
简单状态管理
class CounterEasyGetLogic extends GetxController {
var count = 0;
void increase() {
++count;
update();
}
}
class CounterEasyGetPage extends StatelessWidget {
final CounterEasyGetLogic logic = Get.put(CounterEasyGetLogic());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('计数器-简单式')),
body: Center(
child: GetBuilder<CounterEasyGetLogic>(
builder: (logicGet) => Text(
'点击了 ${logicGet.count} 次',
style: TextStyle(fontSize: 30.0),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => logic.increase(),
child: const Icon(Icons.add),
),
);
}
}
init:虽然上述代码没用到,但是,这个参数是存在在GetBuilder中的,因为在加载变量的时候就使用Get.put()生成了CounterEasyGetLogic对象,GetBuilder会自动查找该对象,所以,就可以不使用init参数
builder:方法参数,拥有一个入参,类型便是GetBuilder所传入泛型的类型
initState,dispose等:GetBuilder拥有StatefulWidget所有周期回调,可以在相应回调内做一些操作
总结
GetBuilder内部实际上是对StatefulWidget的封装,所以占用资源极小
响应式变量,因为使用的是StreamBuilder,会消耗一定资源
一般来说,对于大多数场景都是可以使用响应式变量的
但是,在一个包含了大量对象的List,都使用响应式变量,将生成大量的StreamBuilder,必将对内存造成较大的压力,该情况下,就要考虑使用简单状态管理了
实现
页面一,常规代码。
logic
这里的自增事件,是供其它页面调用的,该页面本身没使用。
class JumpOneLogic extends GetxController {
var count = 0.obs;
///跳转到跨页面
void toJumpTwo() {
Get.toNamed(RouteConfig.jumpTwo, arguments: {'msg': '我是上个页面传递过来的数据'});
}
///跳转到跨页面
void increase() => count++;
}
class JumpOnePage extends StatelessWidget {
/// 使用Get.put()实例化你的类,使其对当下的所有子路由可用。
final JumpOneLogic logic = Get.put(JumpOneLogic());
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(title: Text('跨页面-One')),
floatingActionButton: FloatingActionButton(
onPressed: () => logic.toJumpTwo(),
child: const Icon(Icons.arrow_forward_outlined),
),
body: Center(
child: Obx(
() => Text('跨页面-Two点击了 ${logic.count.value} 次',
style: TextStyle(fontSize: 30.0)),
),
),
);
}
}
页面二,这个页面就是重点了。
将演示怎么调用前一个页面的事件
怎么接收上个页面数据
请注意,GetxController包含比较完整的生命周期回调,可以在onInit()接受传递的数据;如果接收的数据需要刷新到界面上,请在onReady回调里面接收数据操作,onReady是在addPostFrameCallback回调中调用,刷新数据的操作在onReady进行,能保证界面是初始加载完毕后才进行页面刷新操作的
class JumpTwoLogic extends GetxController {
var count = 0.obs;
var msg = ''.obs;
@override
void onReady() {
var map = Get.arguments;
msg.value = map['msg'];
super.onReady();
}
///跳转到跨页面
void increase() => count++;
}
加号的点击事件,点击时,能实现俩个页面数据的变换
重点来了,这里通过Get.find(),获取到了之前实例化GetXController,获取某个模块的GetXController后就很好做了,可以通过这个GetXController去调用相应的事件,也可以通过它,拿到该模块的数据!
class JumpTwoPage extends StatelessWidget {
final JumpOneLogic oneLogic = Get.find();
final JumpTwoLogic twoLogic = Get.put(JumpTwoLogic());
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(title: Text('跨页面-Two')),
floatingActionButton: FloatingActionButton(
onPressed: () {
oneLogic.increase();
twoLogic.increase();
},
child: const Icon(Icons.add),
),
body: Center(
child: Column(mainAxisSize: MainAxisSize.min, children: [
//计数显示
Obx(
() => Text('跨页面-Two点击了 ${twoLogic.count.value} 次',
style: TextStyle(fontSize: 30.0)),
),
//传递数据
Obx(
() => Text('传递的数据:${twoLogic.msg.value}',
style: TextStyle(fontSize: 30.0)),
),
]),
),
);
}
}
总结
/ 进阶吧!计数器 /
实现
Model:选择Default(默认)
-
Function:useFolder(默认中)
class CounterHighGetState {
CounterHighGetState() {
///Initialize variables
}
}
import 'package:get/get.dart';
import 'state.dart';
class CounterHighGetLogic extends GetxController {
final state = CounterHighGetState();
}
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'logic.dart';
import 'state.dart';
class CounterHighGetPage extends StatelessWidget {
final CounterHighGetLogic logic = Get.put(CounterHighGetLogic());
final CounterHighGetState state = Get.find<CounterHighGetLogic>().state;
@override
Widget build(BuildContext context) {
return Container();
}
}
class CounterHighGetState {
RxInt count;
CounterHighGetState() {
count = 0.obs;
}
}
class CounterHighGetLogic extends GetxController {
final state = CounterHighGetState();
///自增
void increase() => ++state.count;
}
class CounterHighGetPage extends StatelessWidget {
final CounterHighGetLogic logic = Get.put(CounterHighGetLogic());
final CounterHighGetState state = Get.find<CounterHighGetLogic>().state;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('计数器-响应式')),
body: Center(
child: Obx(
() => Text('点击了 ${state.count.value} 次',
style: TextStyle(fontSize: 30.0)),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => logic.increase(),
child: const Icon(Icons.add),
),
);
}
}
https://github.com/CNAD666/flutter_use
state
class MainState {
///选择index - 响应式
RxInt selectedIndex;
///控制是否展开 - 响应式
RxBool isUnfold;
///分类按钮数据源
List<BtnInfo> list;
///Navigation的item信息
List<BtnInfo> itemList;
///PageView页面
List<Widget> pageList;
PageController pageController;
MainState() {
//初始化index
selectedIndex = 0.obs;
//默认不展开
isUnfold = false.obs;
//PageView页面
pageList = [
keepAliveWrapper(FunctionPage()),
keepAliveWrapper(ExamplePage()),
keepAliveWrapper(Center(child: Container())),
];
//item栏目
itemList = [
BtnInfo(
title: "功能",
icon: Icon(Icons.bubble_chart),
),
BtnInfo(
title: "范例",
icon: Icon(Icons.opacity),
),
BtnInfo(
title: "设置",
icon: Icon(Icons.settings),
),
];
//页面控制器
pageController = PageController();
}
}
class MainLogic extends GetxController {
final state = MainState();
///切换tab
void switchTap(int index) {
state.selectedIndex.value = index;
}
///是否展开侧边栏
void onUnfold(bool unfold) {
state.isUnfold.value = !state.isUnfold.value;
}
}
class MainPage extends StatelessWidget {
final MainLogic logic = Get.put(MainLogic());
final MainState state = Get.find<MainLogic>().state;
@override
Widget build(BuildContext context) {
return BaseScaffold(
backgroundColor: Colors.white,
body: Row(children: [
///侧边栏区域
Obx(
() => SideNavigation(
selectedIndex: state.selectedIndex.value,
sideItems: state.itemList,
onItem: (index) {
logic.switchTap(index);
state.pageController.jumpToPage(index);
},
isUnfold: state.isUnfold.value,
onUnfold: (unfold) {
logic.onUnfold(unfold);
},
),
),
///Expanded占满剩下的空间
Expanded(
child: PageView.builder(
physics: NeverScrollableScrollPhysics(),
itemCount: state.pageList.length,
itemBuilder: (context, index) => state.pageList[index],
controller: state.pageController,
),
)
]),
);
}
}
最后
/ 路由管理 /
简单路由
主入口配置
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
home: MainPage(),
);
}
}
//跳转新页面
Get.to(SomePage());
命名路由导航
统一管理起了所有页面
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
initialRoute: RouteConfig.main,
getPages: RouteConfig.getPages,
);
}
}
class RouteConfig{
///主页面
static final String main = "/";
///dialog页面
static final String dialog = "/dialog";
///bloc计数器模块
static final String counter = "/counter";
///测试布局页面
static final String testLayout = "/testLayout";
///演示SmartDialog控件
static final String smartDialog = "/smartDialog";
///Bloc跨页面传递事件
static final String spanOne = "/spanOne";
static final String spanTwo = "/spanOne/spanTwo";
///GetX 计数器 跨页面交互
static final String counterGet = "/counterGet";
static final String jumpOne = "/jumpOne";
static final String jumpTwo = "/jumpOne/jumpTwo";
///别名映射页面
static final List<GetPage> getPages = [
GetPage(name: main, page: () => MainPage()),
GetPage(name: dialog, page: () => Dialog()),
GetPage(name: counter, page: () => CounterPage()),
GetPage(name: testLayout, page: () => TestLayoutPage()),
GetPage(name: smartDialog, page: () => SmartDialogPage()),
GetPage(name: spanOne, page: () => SpanOnePage()),
GetPage(name: spanTwo, page: () => SpanTwoPage()),
GetPage(name: counterGet, page: () => CounterGetPage()),
GetPage(name: jumpOne, page: () => JumpOnePage()),
GetPage(name: jumpTwo, page: () => JumpTwoPage()),
];
}
路由API
-
默认:Get.to(SomePage()); -
命名路由:Get.toNamed(“/somePage”);
Get.to(NextScreen());
Get.toNamed("/NextScreen");
Get.back();
Get.off(NextScreen());
Get.offNamed("/NextScreen");
Get.offAll(NextScreen());
Get.offAllNamed("/NextScreen");
Get.to(NextScreen(), arguments: 'Get is the best');
Get.toNamed("/NextScreen", arguments: 'Get is the best');
print(Get.arguments);
//print out: Get is the best
var data = await Get.to(Payment());
var data = await Get.toNamed("/payment");
Get.back(result: 'success');
// 并使用它,例:
if(data == 'success') madeAnything();
// 默认的Flutter导航
Navigator.of(context).push(
context,
MaterialPageRoute(
builder: (BuildContext context) {
return HomePage();
},
),
);
// 使用Flutter语法获得,而不需要context。
navigator.push(
MaterialPageRoute(
builder: (_) {
return HomePage();
},
),
);
// get语法
Get.to(HomePage());
Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");
print(Get.parameters['id']);
// out: 354
print(Get.parameters['name']);
// out: Enzo
void main() {
runApp(
GetMaterialApp(
initialRoute: '/',
getPages: [
GetPage(
name: '/',
page: () => MyHomePage(),
),
GetPage(
name: '/profile/',
page: () => MyProfile(),
),
//你可以为有参数的路由定义一个不同的页面,也可以为没有参数的路由定义一个不同的页面,但是你必须在不接收参数的路由上使用斜杠"/",就像上面说的那样。
GetPage(
name: '/profile/:user',
page: () => UserProfile(),
),
GetPage(
name: '/third',
page: () => Third(),
transition: Transition.cupertino
),
],
)
);
}
Get.toNamed("/profile/34954");
print(Get.parameters['user']);
// out: 34954
/ 最后 /
https://github.com/CNAD666/flutter_use
https://github.com/CNAD666/getx_template
以上是关于如何优雅地进行Flutter开发,GetX值得一试!的主要内容,如果未能解决你的问题,请参考以下文章