所以Flutter使用GetX真的很不错

1,595 阅读6分钟

简单说,Bloc,Provider等这类依赖InheritedWidget实现的状态管理,相对麻烦不好用。而GetX相对方便好用一些。

为什么说什么GetX好用呢?

1、依赖注入

GetX是通过依赖注入的方式,存储相应的XxxGetxController;已经脱离了InheritedWidget那一套玩法,自己手动去管理这些实例,使用场景被大大拓展

2、跨页面交互

这绝对是GetX的一个优点!对于复杂的生产环境,跨页面交互的场景,实在太常见了,GetX的跨页面交互,实现的也较为优雅

3、路由管理

GetX内部实现了路由管理,而且用起来,非常简单。
(bloc没实现路由管理,fluro的路由管理使用非常麻烦)

4、实现了全局BuildContext

5、国际化

6、主题管理

GetX的版本事

4.3.8之后的就是非空安全的版本了。

# getx 状态管理框架 https://pub.flutter-io.cn/packages/get
# 非空安全最后一个版本(flutter 2.0之前版本)
get: ^3.26.0
    
# 空安全版本 最新版本请查看  https://pub.flutter-io.cn/packages/get
get: ^4.3.8

使用方式引入下:

dependencies: get: ^4.3.8

以上,巴拉巴拉。 以下,说事。

一、状态管理

官方的计数器换成GetX怎么实现呢

这是官方的默认计数器。

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<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  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: <Widget>[
            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.
    );
  }
}

官方的 image.png

.
.


换成GetX

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页面 image.png

.
.

Other页面 image.png .
.

上面的代码有一下几个点需要注意下:

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

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<EasyController>(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所有周期回调,可以在相应回调内做一些操作

.
.

image.png

.
.

2、响应式状态管理 obx obs

响应式状态管理,当数据源变化时,将自动执行刷新组件的方法

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),
      ),
    );
  }
}

.
.

image.png

.
.

  • 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种方式

// 第一种 使用 Rx{Type}
// final name = RxString('');
// final isLogged = RxBool(false);
// final count = RxInt(0);
// final balance = RxDouble(0.0);
// final items = RxList<String>([]);
// final myMap = RxMap<String, int>({});

// 第二种是使用 Rx,规定泛型 Rx<Type>。
// final name = Rx<String>('');
// final isLogged = Rx<Bool>(false);
// final count = Rx<Int>(0);
// final balance = Rx<Double>(0.0);
// final number = Rx<Num>(0)
// final items = Rx<List<String>>([]);
// final myMap = Rx<Map<String, int>>({});
// 自定义类 - 可以是任何类
// final user = Rx<User>();

// 第三种更实用、更简单、更可取的方法,只需添加 .obs 作为value的属性。
// final name = ''.obs;
// final isLogged = false.obs;
// final count = 0.obs;
// final balance = 0.0.obs;
// final number = 0.obs;
// final items = <String>[].obs;
// final myMap = <String, int>{}.obs;
// 自定义类 - 可以是任何类
// final user = User().obs;

三、路由管理和跨页面交互

GetX的路由,可以分为 简单路由命名路由

在GetX的路由世界,完全可以忘记context。

四.1、普通路由

.

普通路由 打开新页面

GetX方式: Get.to(NextScreen());

原生路由方式

Navigator.push(context, MaterialPageRoute<void>(
  builder: (BuildContext context) {
    return NextScreen();
  },
));

.

普通路由 返回 / 关闭当前页

GetX方式: Get.Get.back();

原生路由方式

Navigator.pop(context);

.

普通路由 关闭当前页,打开新页面

GetX方式: Get.off(NextScreen());

原生路由方式

 Navigator.pushReplacement(context, MaterialPageRoute<void>(
      builder: (BuildContext context) {
        return NextScreen();
      },
    ));

普通路由 打开新页面并删除之前的所有路由

GetX方式: Get.offAll(NextScreen());

原生路由方式

   Navigator.pushAndRemoveUntil(
      context,
      MaterialPageRoute<void>(
        builder: (BuildContext context) {
          return NextScreen();
        },
      ),
      (Route<dynamic> route) => false,
    );

普通路由 导航到新页面,在返回时接收返回数据

GetX方式:

  • 打开时:var data = await Get.to(NextScreen());
  • 打开的页面返回时:Get.back(result: 'success');

.

原生路由方式

    var data = await  Navigator.push(context, MaterialPageRoute<void>(
      builder: (BuildContext context) {
        return NextScreen();
      },
    ));
 Navigator.pop(context, 'success');

.

四.2、别名路由

普通路由的使用比较简单,而别名路由在使用之前,需要先写个路由表。

往往我们都是采用别名路由。

准备工作

  • 声明别名
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(),
    ),
  ];
}

.

  • 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);

发送数据到别名路由(非常好用)

GetX在这里接受任何东西,无论是一个字符串,一个Map,一个List,甚至一个类的实例。

Get.toNamed(Routes.NextScreen, arguments: '打豆豆');

动态网页链接

  • 动态网页链接 -- 方式1

像web一样携带参数,适合前端开发的风格。

传参

Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");

.

获取参数

int id = Get.parameters['id'];
// out: 354
String name=Get.parameters['name'];

.
.

  • 动态网页链接 -- 方式2

定义路由别名

       GetPage(
        name: '/profile/:user',
        page: () => UserProfile(),
      ),

导航

Get.toNamed("/profile/34954");

说了这么多,还是来点例子比较直接。

例子一、 无参交互

GetX打开页面,关闭页面

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)

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 方法
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<HomeController>(
          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<HomeController>();
  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<NextScreenController>(
            builder: (logic) {
              return Text('NextScreen 点击了 ${nextScreen.count} 次',
                  style: TextStyle(fontSize: 30.0));
            },
          ),

          //传递数据
          GetBuilder<NextScreenController>(
            builder: (logic) {
              return Text('传递的数据:${nextScreen.msg}',
                  style: TextStyle(fontSize: 20.0));
            },
          ),
        ]),
      ),
    );
  }
}

image.png

image.png

四.3、中间件 routingCallback

在跳转前做些事情,比如判断是否登录,可以使用 routingCallback 来实现:

GetMaterialApp(
  routingCallback: (routing) {
    if(routing.current == '/second'){
     // 如果登录。。。
    }
  }
)


四、GetxController生命周期方法

  • onInit:组件在内存分配后会被马上调用,可以在这个方法对 controller 做一些初始化工作。
  • onReady:适合做一些导航进入的事件,例如对话框提示、SnackBar 或异步网络请求
  • onClose:在 onDelete 方法前调用、用于销毁 controller 使用的资源,例如关闭事件监听,关闭流对象,或者销毁可能造成内存泄露的对象,例如 TextEditingControllerAniamtionController。也适用于将数据进行离线持久化。
@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来进行区分。

第一步:应用程序入口设置

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

import 'package:get/get.dart';

class CountController extends GetxController {
  var count = 0;

  void increment() {
    count++;
    update(['jimi_count']);
  }
}

第三步:实例化控制器并使用


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<CountController>(
              builder: (controller) {
                return Text(
                  "计数器值为: ${controller.count}",
                  style: TextStyle(color: Colors.red, fontSize: 30),
                );
              },
            ),
            GetBuilder<CountController>(
              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则是最佳的选择

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"))
            ],
          ),
        ),
      ),
    );
  }
}

image.png

GetX下的 Dialog

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"))
            ],
          ),
        ),
      ),
    );
  }
}

image.png

GetX下的 BottomSheet

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<NavigatorState> _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"))
          ],
        ),
      ),
    );
  }
}

image.png

image.png

七、GetX的效率插件

在Android Studio上使用的插件

如果想使用插件,可以好好看下这篇文章。

juejin.cn/post/692410…

参考:
Flutter GetX使用---简洁的魅力!juejin.cn/post/700501…

flutter 一文带你了解GetX www.jianshu.com/p/ed83a0a32…