所以Flutter使用GetX真的很不错
为什么说什么GetX好用呢?
1、依赖注入
GetX是通过依赖注入的方式,存储相应的XxxGetxController;已经脱离了InheritedWidget那一套玩法,自己手动去管理这些实例,使用场景被大大拓展
2、跨页面交互
这绝对是GetX的一个优点!对于复杂的生产环境,跨页面交互的场景,实在太常见了,GetX的跨页面交互,实现的也较为优雅
3、路由管理
GetX内部实现了路由管理,而且用起来,非常简单。
(bloc没实现路由管理,fluro的路由管理使用非常麻烦)
4、实现了全局BuildContext
5、国际化
6、主题管理
GetX的版本事
4.3.8之后的就是非空安全的版本了。
arduino复制代码# getx 状态管理框架 https://pub.flutter-io.cn/packages/get
get: ^3.26.0
get: ^4.3.8
使用方式引入下:
vbnet复制代码dependencies: get: ^4.3.8
以上,巴拉巴拉。
以下,说事。
一、状态管理
官方的计数器换成GetX怎么实现呢
这是官方的默认计数器。
DART复制代码import ‘package:flutter/material.dart’;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: ‘Flutter Demo’,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State createState() => _MyHomePageState();
}
class _MyHomePageState extends State {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
‘You have pushed the button this many times:’,
),
Text(
‘$_counter’,
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: ‘Increment’,
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
官方的
.
.
换成GetX
dart复制代码import ‘package:flutter/material.dart’;
import ‘package:get/get.dart’;
void main() {
// runApp(const MyApp());
runApp(GetMaterialApp(home: Home()));
}
class Controller extends GetxController{
var count = 0.obs;
increment() => count++;
}
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: RaisedButton(
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}”)));
}
}
Home页面
.
.
Other页面
.
.
上面的代码有一下几个点需要注意下:
1、GetMaterialApp并不是修改后的MaterialApp,它只是一个预先配置的Widget,它的子组件是默认的MaterialApp。你可以手动配置,但绝对没有必要。GetMaterialApp会创建路由,注入它们,注入翻译,注入你需要的一切路由导航。如果你只用Get来进行状态管理或依赖管理,就没有必要使用GetMaterialApp。GetMaterialApp对于路由、snackbar、国际化、bottomSheet、对话框以及与路由相关的高级apis和没有上下文(context)的情况下是必要的。
2、使用GetMaterialApp,你就可以直接使用GetX的路由功能。如果不需要路由也可以不这么做。
3、我们创建了一个Controller业务逻辑类,class Controller extends GetxController。并将所有的变量,方法和控制器放在里面。另外,我们使用一个简单的".obs "使任何变量成为可观察的。这样跨页面的数据交互就方便了很多。
4、你应该也发现了,我们在other里面方式使用home页面的count的数据是如何方便。
(Get不是其他状态管理器的敌人,因为Get是一个微框架,而不仅仅是一个状态管理器,既可以单独使用,也可以与其他状态管理器结合使用。)
5、使用GetX可以忘记StatefulWidget,因为GetX自身都是有用状态的
复制代码
二、GetX的状态管理器
Get有两个不同的状态管理器:简单的状态管理器(GetBuilder)和响应式状态管理器(GetX)
简单状态管理: GetBuilder:这是一个极其轻巧的状态管理器,占用资源极少!数据变化需要手动update
响应式状态管理:当数据源变化时,将自动执行刷新组件的方法 obs。
1、简易模式状态管理 GetBuilder
DART复制代码import ‘package:flutter/material.dart’;
import ‘package:get/get.dart’;
void main() {
runApp(GetMaterialApp(home: CounterEasyPage()));
}
class EasyController extends GetxController{
// 注意这里没加 obs
var count = 0;
void increase() {
++count;
// 手动更新数据
update();
}
}
class CounterEasyPage extends StatelessWidget {
@override
Widget build(context) {
// 使用Get.put()实例化你的类,使其对当下的所有子路由可用。
final EasyController easyC = Get.put(EasyController());
return Scaffold(
appBar: AppBar(title: const Text('计数器-简易模式')),
body: Center(
// 使用GetBuilder 简易模式,省内存,需要手动更新数据
child: GetBuilder(builder: (controller) {
return Text(
'点击了 ${controller.count} 次',
style: TextStyle(fontSize: 30.0),
);
}),
),
floatingActionButton: FloatingActionButton(
// 调用increase方法
onPressed: () => easyC.increase(),
child: Icon(Icons.add),
),
);
}
}
1、runApp使用GetMaterialApp
2、写一个继承自GetxController的类,对数据的变化使用update手动更新数据
3、使用 GetBuilder 方法,实现简易模式的状态管理
关于GetBuilder方法
init:虽然上述代码没用到,但是,这个参数是存在在GetBuilder中的,因为在加载变量的时候就使用Get.put()生成了CounterEasyGetLogic对象,GetBuilder会自动查找该对象,所以,就可以不使用init参数
builder:方法参数,拥有一个入参,类型便是GetBuilder所传入泛型的类型
initState,dispose等:GetBuilder拥有StatefulWidget所有周期回调,可以在相应回调内做一些操作
.
.
.
.
2、响应式状态管理 obx obs
响应式状态管理,当数据源变化时,将自动执行刷新组件的方法
DART复制代码import ‘package:flutter/material.dart’;
import ‘package:get/get.dart’;
void main() {
runApp(GetMaterialApp(home: CounterEasyPage()));
}
class EasyController extends GetxController{
// 注意使用了 obs
var count = 0.obs;
void increase() {
++count;
// 使用了obs就无需手动刷新了
}
}
class CounterEasyPage extends StatelessWidget {
@override
Widget build(context) {
// 使用Get.put()实例化你的类,使其对当下的所有子路由可用。
final EasyController easyC = Get.put(EasyController());
return Scaffold(
appBar: AppBar(title: const Text('计数器-响应式模式')),
body: Center(
// 使用响应式状态管理,自动刷新
child: Obx((){
return Text(
'点击了 ${easyC.count} 次',
style: TextStyle(fontSize: 30.0),
);
}),
),
floatingActionButton: FloatingActionButton(
// 调用increase方法
onPressed: () => easyC.increase(),
child: Icon(Icons.add),
),
);
}
}
.
.
.
.
1、runApp使用GetMaterialApp
2、写一个继承自GetxController的类,变量数值后写.obs操作,是说明定义了该变量为响应式变量,当该变量数值变化时,页面的刷新方法将自动刷新。
3、无需 GetBuilder 方法。
4、基础类型,List,类都可以加.obs,使其变成响应式变量
注意点:
只有当响应式变量的值发生变化时,才会会执行刷新操作,当某个变量初始值为:“test”,再赋值为:“test”,并不会执行刷新操作
当你定义了一个响应式变量,该响应式变量改变时,包裹该响应式变量的Obx()方法才会执行刷新操作,其它的未包裹该响应式变量的Obx()方法并不会执行刷新操作
3、简单模式个相应的对比选择
响应式模式耗内存! 每一个响应式变量,都需要生成对应的GetStream,占用资源大于基本数据类型,会对内存造成一定压力
简易模式省内存,GetBuilder内部实际上是对StatefulWidget的封装,所以占用资源极小。
一般来说,对于大多数场景都是可以使用响应式变量的。
但是,在一个包含了大量对象的List,都使用响应式变量,将生成大量的GetStream,必将对内存造成较大的压力,该情况下,就要考虑使用简单状态管理了
总的来说:推荐GetBuilder和update配合的写法
定义obx变量的3种方式
DART复制代码// 第一种 使用 Rx{Type}
// final name = RxString(‘’);
// final isLogged = RxBool(false);
// final count = RxInt(0);
// final balance = RxDouble(0.0);
// final items = RxList([]);
// final myMap = RxMap
// 第二种是使用 Rx,规定泛型 Rx。
// final name = Rx(‘’);
// final isLogged = Rx(false);
// final count = Rx(0);
// final balance = Rx(0.0);
// final number = Rx(0)
// final items = Rx([]);
// final myMap = Rx
// 第三种更实用、更简单、更可取的方法,只需添加 .obs 作为value的属性。
// final name = ‘’.obs;
// final isLogged = false.obs;
// final count = 0.obs;
// final balance = 0.0.obs;
// final number = 0.obs;
// final items = [].obs;
// final myMap =
// 自定义类 - 可以是任何类
// final user = User().obs;
三、路由管理和跨页面交互
GetX的路由,可以分为 简单路由 和 命名路由。
在GetX的路由世界,完全可以忘记context。
四.1、普通路由
.
普通路由 打开新页面
GetX方式: Get.to(NextScreen());
原生路由方式
DART复制代码Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) {
return NextScreen();
},
));
.
普通路由 返回 / 关闭当前页
GetX方式: Get.Get.back();
原生路由方式
DART复制代码Navigator.pop(context);
.
普通路由 关闭当前页,打开新页面
GetX方式: Get.off(NextScreen());
原生路由方式
DART复制代码 Navigator.pushReplacement(context, MaterialPageRoute(
builder: (BuildContext context) {
return NextScreen();
},
));
普通路由 打开新页面并删除之前的所有路由
GetX方式: Get.offAll(NextScreen());
原生路由方式
DART复制代码 Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (BuildContext context) {
return NextScreen();
},
),
(Route route) => false,
);
普通路由 导航到新页面,在返回时接收返回数据
GetX方式:
打开时:var data = await Get.to(NextScreen());
打开的页面返回时:Get.back(result: ‘success’);
.
原生路由方式
DART复制代码 var data = await Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) {
return NextScreen();
},
));
arduino复制代码 Navigator.pop(context, ‘success’);
.
四.2、别名路由
普通路由的使用比较简单,而别名路由在使用之前,需要先写个路由表。
往往我们都是采用别名路由。
准备工作
声明别名
DART复制代码abstract class Routes {
static const Initial = ‘/’;
static const NextScreen = ‘/NextScreen’;
}
.
注册路由表
php复制代码abstract class AppPages {
static final pages = [
GetPage(
name: Routes.Initial,
page: () => HomePage(),
),
GetPage(
name: Routes.NextScreen,
page: () => NextScreen(),
),
];
}
.
GetMaterialApp配置
less复制代码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);
发送数据到别名路由(非常好用)
GetX在这里接受任何东西,无论是一个字符串,一个Map,一个List,甚至一个类的实例。
Get.toNamed(Routes.NextScreen, arguments: ‘打豆豆’);
动态网页链接
动态网页链接 – 方式1
像web一样携带参数,适合前端开发的风格。
传参
vbnet复制代码Get.offAllNamed(“/NextScreen?device=phone&id=354&name=Enzo”);
.
获取参数
ini复制代码int id = Get.parameters[‘id’];
// out: 354
String name=Get.parameters[‘name’];
.
.
动态网页链接 – 方式2
定义路由别名
less复制代码 GetPage(
name: ‘/profile/:user’,
page: () => UserProfile(),
),
导航
vbnet复制代码Get.toNamed(“/profile/34954”);
说了这么多,还是来点例子比较直接。
例子一、 无参交互
GetX打开页面,关闭页面
DART复制代码import ‘package:flutter/material.dart’;
import ‘package:get/get.dart’;
import ‘app_theme.dart’;
void main() {
runApp(GetMaterialApp(
debugShowCheckedModeBanner: false,
initialRoute: ‘/’,
theme: appThemeData,
defaultTransition: Transition.fade,
getPages: AppPages.pages,
home: HomePage(),
));
}
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(),
),
];
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(‘HomePage’),
),
body: Center(
child: RaisedButton(
child: Text(“Go to NextScreen”),
onPressed: () => Get.toNamed(Routes.NextScreen)),
),
);
}
}
class NextScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(‘NextScreen’),
),
body: Center(
child: RaisedButton(
child: Text(“返回”),
onPressed: () {
Get.back();
},
),
));
}
}
.
.
(备用:appTheme)
DART复制代码import ‘package:flutter/material.dart’;
const kPrimaryColor = Color(0xff171616);
const kPrimaryLightColor = Color(0xff151515);
const primaryDarkColor = Color(0xff1d1d1d);
const secondaryColor = Color(0xff1f1f1f);
const secondaryLightColor = Color(0xff1c1c1c);
const secondaryDarkColor = Color(0xff000000);
// const primaryTextColor = Color(0xFFF1E6FF);
// const secondaryTextColor = Color(0xFFF1E6FF);
ThemeData get appThemeData => ThemeData(
primaryColor: kPrimaryColor,
primaryColorLight: kPrimaryLightColor,
scaffoldBackgroundColor: Colors.white,
accentColor: kPrimaryColor,
appBarTheme: appBarTheme,
textTheme: const TextTheme());
AppBarTheme get appBarTheme => const AppBarTheme();
.
.
例子二、跨页面数据传输
页面1向页面2传递数据
页面2修改页面1的数据
GetX的 onReady 方法
DART复制代码import ‘package:flutter/material.dart’;
import ‘package:get/get.dart’;
import ‘app_theme.dart’;
void main() {
runApp(GetMaterialApp(
debugShowCheckedModeBanner: false,
initialRoute: ‘/’,
theme: appThemeData,
defaultTransition: Transition.fade,
getPages: AppPages.pages,
home: HomePage(),
));
}
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(),
),
];
}
class HomeController extends GetxController {
var count = 0;
///跳转到跨页面
void toJumpNextScreen() {
Get.toNamed(Routes.NextScreen, arguments: {‘msg’: ‘我是上个页面传递过来的数据’});
}
///跳转到跨页面
void increase() {
count = ++count;
update();
}
}
class HomePage extends StatelessWidget {
// 自身的Controller用put来获取
final homeCon = Get.put(HomeController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(‘HomePage’),
),
floatingActionButton: FloatingActionButton(
// 跳转新页面
onPressed: () => homeCon.toJumpNextScreen(),
child: const Icon(Icons.arrow_forward_outlined),
),
body: Center(
child: GetBuilder(
builder: (logic) {
return Text('NextScreen点击了 ${logic.count} 次',
style: TextStyle(fontSize: 30.0));
},
),
),
);
}
}
class NextScreenController extends GetxController {
var count = 0;
var msg = ‘’;
// 如果接收的数据需要刷新到界面上,请在onReady回调里面接收数据操作,
// onReady是在addPostFrameCallback回调中调用,刷新数据的操作在onReady进行
// 能保证界面是初始加载完毕后才进行页面刷新操作的
@override
void onReady() {
var map = Get.arguments;
msg = map[‘msg’];
update();
super.onReady();
}
///跳转到跨页面
void increase() {
count = ++count;
update();
}
}
class NextScreen extends StatelessWidget {
final homeController = Get.find();
final nextScreen = Get.put(NextScreenController());
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(title: Text(‘NextScreen’)),
floatingActionButton: FloatingActionButton(
onPressed: () {
homeController.increase();
nextScreen.increase();
},
child: const Icon(Icons.add),
),
body: Center(
child: Column(mainAxisSize: MainAxisSize.min, children: [
//计数显示
GetBuilder(
builder: (logic) {
return Text(‘NextScreen 点击了 ${nextScreen.count} 次’,
style: TextStyle(fontSize: 30.0));
},
),
//传递数据
GetBuilder(
builder: (logic) {
return Text('传递的数据:${nextScreen.msg}',
style: TextStyle(fontSize: 20.0));
},
),
]),
),
);
}
}
四.3、中间件 routingCallback
在跳转前做些事情,比如判断是否登录,可以使用 routingCallback 来实现:
DART复制代码GetMaterialApp(
routingCallback: (routing) {
if(routing.current == ‘/second’){
// 如果登录。。。
}
}
)
四、GetxController生命周期方法
onInit:组件在内存分配后会被马上调用,可以在这个方法对 controller 做一些初始化工作。
onReady:适合做一些导航进入的事件,例如对话框提示、SnackBar 或异步网络请求。
onClose:在 onDelete 方法前调用、用于销毁 controller 使用的资源,例如关闭事件监听,关闭流对象,或者销毁可能造成内存泄露的对象,例如 TextEditingController,AniamtionController。也适用于将数据进行离线持久化。
DART复制代码@override
void onInit() {
// TODO: implement onInit
print(“初始化”);
super.onInit();
}
@override
void onReady() {
// TODO: implement onReady
print(“加载完成”);
super.onReady();
}
@override
void onClose() {
// TODO: implement onClose
print(“控制器被释放”);
super.onClose();
}
深入分析生命周期,可以查看 zhuanlan.zhihu.com/p/445371503
五、避开全局,单独处理 UniqueID
我们在开发的过程中会碰到一种情况,就是多个地方引用了同一个属性,但我只想单独更新某一个地方,那么就可以用UniqueID来进行区分。
第一步:应用程序入口设置
DART复制代码import ‘package:flutter/material.dart’;
import ‘package:flutter_getx_example/GetXControllerUniqueIDExample/GetXControllerUniqueIDExample.dart’;
import ‘package:get/get.dart’;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: “GetX”,
home: GetXControllerUniqueIDExample(),
);
}
}
第二步、定义控制器继承自GetxController,并且定义uniqueID
DART复制代码import ‘package:get/get.dart’;
class CountController extends GetxController {
var count = 0;
void increment() {
count++;
update([‘jimi_count’]);
}
}
第三步:实例化控制器并使用
DART复制代码
import ‘package:flutter/material.dart’;
import ‘package:flutter_getx_example/GetXControllerUniqueIDExample/CountConroller.dart’;
import ‘package:get/get.dart’;
class GetXControllerUniqueIDExample extends StatelessWidget {
CountController countController = Get.put(CountController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(“GetX Obx—GetXController”),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
GetBuilder(
builder: (controller) {
return Text(
“计数器值为: ${controller.count}”,
style: TextStyle(color: Colors.red, fontSize: 30),
);
},
),
GetBuilder(
id: ‘jimi_count’,
builder: (controller) {
return Text(
“计数器值为: ${controller.count}”,
style: TextStyle(color: Colors.green, fontSize: 30),
);
},
),
SizedBox(height: 20,),
ElevatedButton(
onPressed: () => countController.increment(),
child: Text(“增加”))
],
),
),
);
}
}
六、GetX下的Snackbar、Dialog、BottomSheet
GetX下的 Snackbar
触发某些特定的事件后,需要弹出一则快捷消息,那么使用Snackbar则是最佳的选择
dart复制代码import ‘package:flutter/material.dart’;
import ‘package:get/get.dart’;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: “GetX”,
home: Scaffold(
appBar: AppBar(
title: Text(“GetX Title”),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Get.snackbar(“Snackbar 标题”, “欢迎使用Snackbar”);
},
child: Text(“显示 Snackbar”))
],
),
),
),
);
}
}
GetX下的 Dialog
dart复制代码import ‘package:flutter/material.dart’;
import ‘package:get/get.dart’;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: “GetX”,
home: Scaffold(
appBar: AppBar(
title: Text(“GetX Title”),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Get.defaultDialog(title: ‘标题’,content: Text(‘dialog的内容’));
},
child: Text(“显示 Dialog”))
],
),
),
),
);
}
}
GetX下的 BottomSheet
DART复制代码import ‘package:flutter/material.dart’;
import ‘package:get/get.dart’;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: “GetX”,
home: BottomSheetExample(),
);
}
}
class BottomSheetExample extends StatelessWidget {
GlobalKey _navKey = GlobalKey();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(“GetX Title”),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(onPressed: () {
Get.bottomSheet(
Container(
child: Wrap(
children: [
ListTile(
leading: Icon(Icons.wb_sunny_outlined),
title: Text(“白天模式”),
onTap: () {
Get.changeTheme(ThemeData.light());
},
),
ListTile(
leading: Icon(Icons.wb_sunny),
title: Text(“黑夜模式”),
onTap: () {
Get.changeTheme(ThemeData.dark());
},
)
],
),
)
);
}, child: Text(“Bottom Sheet”))
],
),
),
);
}
}
七、GetX的效率插件
在Android Studio上使用的插件
如果想使用插件,可以好好看下这篇文章。
juejin.cn/post/692410…
作者:J船长
链接:https://juejin.cn/post/7057519485036527623
首发:https://www.kkview.com