Flutter状态管理--GetX的简单使用

一、前言
Flutter开发,就需要对各种状态的管理,就是在请求数据的时候需要实时变化,各种交互变化等,在没有使用GetX之前使用Provider,用Provider的时候觉得真香,挺方便的,需要刷新的时候直接 notifyListeners(); 用了GetX之后觉得Provider太繁琐了。这边介绍下GetX的使用以及常用的方法。

二、 GetX
GetX 是 Flutter 上的一个轻量且强大的解决方案:高性能的状态管理、智能的依赖注入和便捷的路由管理。
1、相关优势:

1、轻量,可以模块单独编译,没有用到的功能不会编译进我们的代码
2、刷新简单,
第一种自动刷新 Obx(() => Text())
第二中手动刷新 update()
3、跨页面交互
4、路由管理
    getx内部实现了路由管理,这个是非常重要的,这样我们就不需要使用其他第三插件,之前都是使用fluro,现在直接不用了,而且getx的路由管理真的真的非常简单。代码也简洁。
6、国际化、主题的适配
7、获取全局的BuildContext  这个也是比较喜欢的地方,很多时候弹窗或者其他地方,需要拿到上下文,使用getx,直接获取。方便至极
8、依然注入

三、使用
1、第一步 引入get

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^1.0.2
  get: ^4.1.4

2、第二步
修改入口、配置路由

@override
  Widget build(BuildContext context) {
    // 只需要将MaterialApp改成GetMaterialAp
    return GetMaterialApp(
      title: 'GetX使用',
      debugShowCheckedModeBanner: false,
      enableLog: true,
      initialRoute: '/', //根路由
      getPages: Pages.routes, // 配置路由
      defaultTransition:
          Utils.isIOS() ? Transition.native : Transition.rightToLeft, // 转场动画
      themeMode: ThemeMode.system, // 主题
      darkTheme: ThemeData.dark(),
      theme: ThemeConfig.lightTheme,
      home: SplashPage(),
      builder: (context, child) {
        return Scaffold(
            body: GestureDetector(
          onTap: () {
            hideKeyboard(context);
          },
          child: FlutterEasyLoading(child: child),
        ));
      },
    );
  }

3、路由

/// 跳转新页面
/// 第一种方式 进入新页面 直接页面
Get.to(ProjectCloudVisiblePage());
/// 第二种方式 进入新页面 配置路由名称  建议这种统一配置
Get.toNamed(Routes.PROJECT_CLOUD_SELECT_MEMBERS);
///弹出当前页,并将一个新的[page]推入堆栈,就是删除就页面,使用新页面
Get.off(ProjectCloudVisiblePage());
/// Push a [page]和弹出几个页面在堆栈中,就是进入新页面,删除之前进栈的页面。比如场景(注册-手机号-其他注册信息-注册完了直接到主页,之前页面全部删掉。)
Get.offAll(ProjectCloudVisiblePage());
/// 同上,就是传路由名称
Get.offAllNamed(FXRoutes.PROJECT_CLOUD_SELECT_MEMBERS);
返回上一面 就一句
Get.back()

Routes类

abstract class Routes {
  static const INITIAL = '/';
  static const GUIDE = '/guide';
  static const LOGIN = '/login';
  static const CODE_LOGIN = '/code_login';
  static const INPUT_CODE = '/input_code';
  static const FORGET_PASSWORD = '/forget_password';
  static const REGISTER = '/register';
  static const PROJECT_CLOUD_VISIBLE = 'project_cloud_visible';
}

Pages类

class Pages {
  static const INITIAL = FXRoutes.INITIAL;
  static final routes = [
    GetPage(
        name: Routes.GUIDE,
        page: () => SplashPage(),
        transition: Transition.fadeIn),
    GetPage(
        name: Routes.INITIAL,
        page: () => LoginPage(),
        transition: Transition.fadeIn),
    GetPage(
        name: Routes.LOGIN,
        page: () => LoginPage(),
        transition: Transition.fadeIn),
    GetPage(
        name: Routes.CODE_LOGIN,
        page: () => CodeLoginPage(),
        transition: Transition.fadeIn),
    GetPage(name: Routes.INPUT_CODE, page: () => InputCodePage()),
    /// 这边使用依赖注入
    GetPage(
        name: FXRoutes.PROJECT_CLOUD_VISIBLE,
        page: () => ProjectCloudVisiblePage(),
        binding: BindingsBuilder(() => {
              Get.lazyPut<ProjectCloudVisibleController>(
                  () => ProjectCloudVisibleController())
            })),
}

4、状态管理
我一般一个page对应一个controller, controller来处理逻辑,控制page.
简单使用

/// 
controller 要继承 GetxController
class ProjectCloudListSearchController extends GetxController {
  List<String> searchRecords = [];
  TextEditingController editingController;
  String searchText;
  List<String> dataList = [];

  @override
  void onInit() {
    // TODO: implement onInit
    super.onInit();
    editingController = TextEditingController();

    /// 获取历史记录
    ProjectCloudSearchCache.getProjectCloudSearchList().then((value) {
      searchRecords.addAll(value);
      update();
    });
  }

  @override
  void onClose() {
    // TODO: implement onClose
    super.onClose();

    /// 添加缓存
    ProjectCloudSearchCache.setProjectCloudSearchList(searchRecords);
  }

  /// 搜索请求 处理缓存
  void searchRequest(String text,
      {bool isRequest = true, bool clickRecords = false}) {
    Utils.logs(text);
    searchText = text;

    if (clickRecords) {
      editingController.text = text;
    }

    /// 请求接口
    if (isRequest) {
      dataList.add('1');
      searchRecordsHandle();
    }
    update();
  }

  /// 删除记录
  void deleteRecord(int index) {
    searchRecords.removeAt(index);
    update();
  }

  /// 清空记录
  void clearRecords() {
    searchRecords.clear();
    /// 手动刷新 调用update
    update();
  }

  /// 搜索记录处理
  void searchRecordsHandle() {
    if (searchText.isNotEmpty) {
      if (!searchRecords.contains(searchText)) {
        if (searchRecords.length >= 10) {
          searchRecords.removeLast();
        }
        searchRecords.insert(0, searchText);
      }
    }
  }
}
/// page页面
class ProjectCloudListSearchPage extends StatefulWidget {
  @override
  _ProjectCloudListSearchPageState createState() =>
      _ProjectCloudListSearchPageState();
}

class _ProjectCloudListSearchPageState
    extends State<ProjectCloudListSearchPage> {
  FocusNode _focusNode = FocusNode();
  /// 注入依赖 使用Get.put
  final _searchC = Get.put(ProjectCloudListSearchController());
  @override
  void initState() {
    // TODO: implement init State
    super.initState();
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    _focusNode.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: FXColor.color_text_66,
      appBar: SearchWidget(
        _searchC.editingController,
        focusNode: _focusNode,
        searchHintText: '输入作品标题',
        searchCallBack: (searchText) {
          _focusNode.unfocus();
          _searchC.searchRequest(searchText);
        },
        onChanged: (searchText) {
          _searchC.searchRequest(searchText, isRequest: false);
        },
      ),
      /// 使用GetBuilder包裹起来 调用update的时候 这边就会自动刷新数据。
      body: GetBuilder<ProjectCloudListSearchController>(builder: (_) {
        return EmptyUtils.isEmptyString(_searchC.searchText) &&
                _searchC.searchRecords.isNotEmpty
            ? ProjectCloudSearchRecordsWidget(
                _searchC.searchRecords,
                valueCallBack: (searchText) {
                  _focusNode.unfocus();
                  _searchC.searchRequest(searchText, clickRecords: true);
                },
                deleteRecordCallBack: (index) {
                  _searchC.deleteRecord(index);
                },
                clearRecordCallBack: () {
                  _searchC.clearRecords();
                },
              )
            : _searchC.dataList.isEmpty
                ? NoDataWidget()
                : ListView.builder(
                    padding: const EdgeInsets.only(top: 10),
                    itemCount: 5,
                    itemBuilder: (context, index) {
                      return ProjectCloudListWidget();
                    });
      }),
    );
  }
}

5、依赖注入
依赖注入也是我喜欢的,可以减少很多工作。
第一步

GetPage(
        name: FXRoutes.PROJECT_CLOUD_VISIBLE,
        page: () => ProjectCloudVisiblePage(),
        /// 主要代码是这个 绑定
        binding: BindingsBuilder(() => {
              Get.lazyPut<ProjectCloudVisibleController>(
                  () => ProjectCloudVisibleController())
            })),

第二步

/// 页面继承GetView<> 传依赖注入的控制器 这样就可以直接使用了,会发现这边没有 Get.put,或者Git.find, 使用的时候直接controller。 看源码可以知道GetView内部已经帮我们实现了。
class ProjectCloudVisiblePage extends GetView<ProjectCloudVisibleController> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBarWidget(
        '谁可见',
        context: context,
        actions: [
          Container(
            margin: const EdgeInsets.only(right: 15),
            child: TextWidget(
              '保存',
              style: TextStyles.setTextStyle14(color: FXColor.blue_color),
              clickCallBack: () {
              /// 直接使用 controller
                controller.saveVisibleSetting();
              },
            ),
          )
        ],
      ),
      body: Column(
        children: [
          ProjectCloudSettingItemWidget(
            '公开',
            isSelect: false,
            clickCallBack: () {
              controller.allMemberVisitingSetting();
            },
          ),
          ProjectCloudSettingItemWidget(
            '部分可见',
            isSelect: true,
            subText: '从组织架构中选择 >',
            isShowDivider: false,
            clickCallBack: () {
              controller.pushToSelectMembers();
            },
          ),
          GetBuilder<ProjectCloudVisibleController>(builder: (_) {
            return Container(
              color: Colors.white,
              padding: const EdgeInsets.only(bottom: 20, right: 15),
              alignment: Alignment.centerRight,
              /// 使用controller来获取数据
              child: TextWidget(controller.selectMemberStr),
            );
          })
        ],
      ),
    );
  }
}

6、跨页面交互

在A界面处理数据,需要再B界面显示的时候,或者C界面,在或者D界面。只要注入了控制器。在其他界面就能拿到A界面的数据。
A界面注入
final _settingC = Get.put(ProjectCloudSettingController());
此刻A界面跳到B
Get.to(BPage);
B在跳到C
Get.to(CPage);
C在跳到D
Get.to(DPage);

D页面需要A界面的数据 注:这边要保证A一直在栈中。 
直接 Get.find(); 就可以拿到数据。比一级一级传值简单方便舒适吧。
final ProjectCloudSettingController settingC = Get.find();

7、黑暗模式
可以参考前期写的博客。黑暗模式的适配

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容