getx 基础教程

449 阅读4分钟

安装

flutter pub add get
//或者 pubspec.yaml
dependencies:
  get: ^4.3.1

//Getx初始化项目
get init

//在文件中引用
import 'package:get/get.dart';

// 运行
flutter run

应用程序入口

当我们导入依赖后,在应用程序顶层把GetMaterialApp 作为顶层

import 'package:get/get.dart';

void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'Flutter GetX',
      home: HomeScreen(),
    );
  }
}

get.snackbar

我们可以通过Get.snackbar() 来显示 snackbar

ElevatedButton(
  onPressed: () => Get.snackbar(
    'My Title',
    'My Message',
  ), 
  child: Text('Show Snackbar'),
)
//其它属性参考官方文档

get.defaultDialog

Dialog 底层其实是对AlertDialog进行了封装, 一般用于二次确认的弹出框,比如当点击某个按钮提交资料时,需要用户二次确认,以防止误操作。

ElevatedButton(
  onPressed: (){
    Get.defaultDialog(
      title: 'My Title',
      middleText: 'My Message',
      onConfirm: (){
        //点击确定按钮的回调
      },
      onCancel: (){
        //点击取消按钮的回调
      },
      confirmTextColor: Colors.white,
      cancelTextColor: Colors.black,
      buttonColor: Colors.blue,
      barrierDismissible: false,
    );
  }, 
  child: Text('Show dialog'),
)
//其它属性参考官方文档

get.bottomSheet

BottomSheet 是底部弹出的一个组件,常用于单选、验证码二次校验弹窗等,GetX的BottomSheet底部弹出是自定义通过路由push的方法实现底部弹窗的一个效果。

ElevatedButton(onPressed: (){
  Get.bottomSheet(
    Container(
      height: 200,
      color: Colors.white,
      child: Center(
        child: Text('Bottom Sheet'),
      ),
    ),
  );
});
//底部弹出,其它属性参考官方文档

get.navigation路由

使用GetX 进行路由跳转非常的简单,只需要调用Get.to()即可进行路由跳转,而系统的路由跳转需要写八行代码

Get.to(
  SecondScreen(),
);

//基本使用
get.to(
  SecondScreen(),
  fullscreenDialog: true,
  transition: Transition.rightToLeft,
  duration: Duration(milliseconds: 500),
  curve: Curves.easeInOut,
)

//传参
get.to(
  ()=>SecondScreen(),
  arguments: {'key': 'value'},
)

//返回数据
Get.back(result: 'Hello World');

var data = await Get.to(Home(),arguments: {'key': 'value'});
print(data);

//命名式路由
GetMaterialApp(
  title: 'Flutter Demo',
  initialRoute: '/',
  getPages: [
    GetPage(name: '/', page: ()=>Home()),
    GetPage(name: '/second', page: ()=>SecondScreen()),
  ],
  home: Home(),
)

Get.toNamed('/second',arguments: {'key': 'value'});
Get.toNamed('/');

Obx 响应式状态管理

响应式编程可能会让很多人感到陌生,因为它很复杂,但是GetX将响应式编程变得非常简单。

//三种方式
final name = RxString('');
final isLogged = RxBool(false);
final count = RxInt(0);
final balance = RxDouble(0.0);
final items = RxList<String>([]);
final user = RxMap<String, dynamic>({});

final name = Rx<String>('');
final user = Rx<User>();

final name = "".obs;
final user = User().obs;

//应用
RxInt count = RxInt(0);
//var count = 0.obs;
//var count = Rx<double>(0.0)

void increment() {
  count++;
}

children: [
  Obx(() => Text("Count: ${count.value}")),
  ElevatedButton(
    onPressed: increment,
    child: Text("Increment"),
  )
]

GetxController 控制器

UI 代码与业务逻辑代码分离

class Teacher {
  RxString name = "".obs;
  RxInt age = 0.obs;

  //构造函数
  /*
  var name = "";
  var age = 0;
  Teacher({this.name, this.age});
  */
}
class CounterController extends GetxController {

  //第一种
  var teacher = Teacher();
  void convertToUpperCase() {
    teacher.name.value = teacher.name.value.toUpperCase();
  }

  //第二种
  /*
  var teacher = Teacher(name: "Jimi", age: 18).obs;
  void convertToUpperCase() {
    teacher.update((val) {
      teacher.value.name = teacher.value.name.toUpperCase();
    });
  }
  */

  //第三种
  /*
  var teacher = Teacher();
  void convertToUpperCase() {
    teacher.name.value = teacher.name.value.toUpperCase();
    update();
  }
  */

}


//界面
CounterController controller = Get.put(CounterController());

//第一种
Obx(() => Text("我的名字是:${controller.teacher.name}"))
//第二种
Getx<CounterController>(
  init: CounterController(),
  builder:(controller){
    return Text("我的名字是:${controller.teacher.name}");
  }
)
//第三种
GetBuilder<CounterController>(
  init: controller,
  builder: (controller) {
    return Text("我的名字是:${controller.teacher.name}");
  }
)

//修改
onPressed: () {
  //第一种
  controller.convertToUpperCase();

  //第二种
  Get.find<CounterController>().convertToUpperCase();
}

GetxController 事件监听

class CounterController extends GetxController {
  var count = 0.obs;
  void increment() {
    count++;
  }
  @override
  void onInit() {
    // 监听 count 的变化
    ever(count, (callback) => print('Count changed__$count'));
    /*
    // 监听多个值
    ever([count], (callback) => print('Count or count changed__$count'));

    // 监听 count 的变化只执行一次
    once(count, (callback) => print('Count changed once__$count'));

    //用户停止打字1秒后执行,主要是防止DDos
    debounce(count, (callback) => print('Count changed after 1 second__$count'));

    //忽略3秒内的所有变动
    interval(count, (callback) => print('Count changed every 1 second__$count'), time: Duration(seconds: 3));
    */
    super.onInit();
  }
}

GetxController 生命周期

class CounterController extends GetxController {
  var count = 0;
  void increment() async {
    await Future.delayed(Duration(seconds: 3));
    count++;
    update();
  }
  void cleanTask(){
    print("清除了任务");
  }

  @override
  void onInit() {
    print("初始化");
    super.onInit();
  }

  @override
  void onReady() {
    print("加载完毕");
    super.onReady();
  }

  @override
  void onClose() {
    print("控制器被释放");
    super.onClose();  
  }
}

// 调用
CounterController counterController = Get.put(CounterController());

GetBuilder<CounterController>(
  initState: (data) => counterController.increment(),
  dispose:(_)=>counterController.cleanTask(),
  builder: (controller) => Text("${controller.count}"),
)

GetxController UniqueKey

我们在开发的过程中会碰到一种情况,就是多个地方引用了同一个属性,但我只想单独更新某一个地方,那么就可以用UniqueID来进行区分。

class CounterController extends GetxController {
  var count = 0;
  void increment() {
    count++;
    update("leo_count");
  }
}
//界面
GetBuilder<CounterController>(
  id: "leo_count",
  builder: (controller) => Text("${controller.count}"),
)

国际化配置

Flutter 项目需要支持国际化,采用getx自带的国际化,简单快捷又方便。

GetMaterialApp(
  translations:Messages(),//国际化配置文件
  locale: Locale('zh', 'CN'),//设置默认语言
  fallbackLocale: Locale('zh', 'CN'),//配置错误使用的语言
  // ...
)

//国际化配置文件
class Messages extends Translations{
  @override
  Map<String, Map<String, String>> get keys => {
    'zh_CN':zh_CN,
    'en_US':en_US
  };
}

Map<String, Map<String, String>> zh_CN = {
  "hello": "你好",
  "world": "世界"
};

Map<String, Map<String, String>> en_US = {
  "hello": "hello",
  "world": "world"
}

//控制器
class MessageController extends GetxController{
  void changeLanguage(String locale, String countryCode){
    var locale = Locale(locale, countryCode);
    Get.updateLocale(locale);
  }
}

//界面
MessageController messageController = Get.put(MessageController());

children: [
  Text('hello'.tr,)
  ElevatedButton(onPressed: (){
      messageController.changeLanguage('zh', 'CN');
    }
    child: Text('切回中文')),
  ElevatedButton(onPressed: (){
      messageController.changeLanguage('en', 'US');
    }
    child: Text('切回英文'));

]

Get Service

如果你需要在你的应用程序的生命周期内对一个类实例进行绝对的持久化,那么就可以使用GetxService。

class Service extends GetxService {
  Future<void> getCounter() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    int counter = prefs.getInt('counter') ?? 0;
    counter ++;
    await prefs.setInt('counter', counter);
  }
}

//界面
//初始化服务
Future<void> main() async {
  await initServices();
  runApp(MyApp());
}
Future<void> initServices() async {
  await Get.putAsync(() async => await Service());
}

//修改更新
onPressed:(){
  Get.find<Service>().getCounter();
}

GetX Binding

Bindings 主要配合 GetX 路由和依赖一起使用,作用是在路由跳转页面加载时注入当前页面所需的依赖关系。Bindings 的好处是能统一管理页面的依赖关系,当业务复杂时可能一个页面需要注入大量的依赖,此时使用 Bindings 能更方便的维护页面的依赖关系。

GetMaterialApp(
  initialBinding: MyBinding(),//初始化所有binding
)

class MyBinding implements Bindings{
  @override
  void dependencies() {
    Get.lazyPut<MyController>(() => MyController());
    Get.lazyPut<MyService>(() => MyService());
  }
}

//界面
Obx(() => Text('${Get.find<MyController>().count}'),)
onPressed: () => Get.find<MyController>().increment(),

//路由初始化 Bindings
GetPage(
  name:"/",
  page: () => MyPage(),
  //binding: MyBinding(),
  binding:BindingBuilder(()=>{
    Get.lazyPut<MyController>(() => MyController());
    Get.lazyPut<MyService>(() => MyService());
  })
)

GetUtils

GetUtils 是getx为我们提供一些常用的工具类库,包括值是否为空、是否是数字、是否是视频、图片、音频、PPT、Word、APK、邮箱、手机号码、日期、MD5、SHA1等等。

GetUtils.isEmail('example@email.com')
GetUtils.isURL('https://www.baidu.com')
GetUtils.isDateTime(DateTime.now())
GetUtils.isPhoneNumber('13816002089')
GetUtils.isIPv4('192.168.1.1')

GetView,GetWidget

GetView只是对已注册的Controller有一个名为controller的getter的const Stateless的Widget,如果我们只有单个控制器作为依赖项,那我们就可以使用GetView,而不是使用StatelessWidget,并且避免了写Get.Find()。 GetWidget "缓存 "了一个Controller,由于cache,不能成为一个 "const Stateless"(因为cache,所以不能成为一个const Stateless)。当我们使用Get.create(()=>Controller())会在每次调用时生成一个新的ControllerGet.find()`

//getView
class GetViewAndGetWidget extends GetView<GetViewCountController> {

  Get.put(GetViewCountController());//有 controller内置属性

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Obx(()=>Text('${controller.count}')),
        ElevatedButton(onPressed: (){
          controller.increment();
        },
        child: Text('点击')),
      ]
    )

  }
}

//GetWidget
class GetViewAndGetWidget extends GetWidget<GetViewCountController> {

  Get.creade(()=>GetViewCountController());//有 controller内置属性

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Obx(()=>Text('${controller.count}')),
        ElevatedButton(onPressed: (){
          controller.increment();
        },
        child: Text('点击')),
      ]
    )

  }
}

GetX Cli

//安装
flutter pub global activate get_cli

//设置环境变量 .bash_profile
#getx
export PATH="$PATH:$HOME/.pub-cache/bin"

//生效环境变量
source .bash_profile

//判断是否安装成功
get

//创建工程//////////////////////////////
get create project

//创建页面
get create page:login

//创建控制器
get create controller:another on home

//创建view
get create view:another on home

//创建provider
get create provider:another on home

//创建国际化
get generate locales assets/locales

//安装包
get install dio
get install path dio

//卸载包
get remove dio


//更新脚手架
get update
get -v

GetConnect

GetConnect是一种可以使用http或websockets实现和服务器通信的方法,可以使用它实现GET/POST/PUT/DELETE/SOCKET网络请求。

class UserProvider extends GetConnect {
  // Get request
  Future<Response> getUser(int id) => get('http://youapi/users/$id');
  // Post request
  Future<Response> postUser(Map data) => post('http://youapi/users', body: data);
  // Post request with File
  Future<Response<CasesModel>> postCases(List<int> image) {
    final form = FormData({
      'file': MultipartFile(image, filename: 'avatar.png'),
      'otherFile': MultipartFile(image, filename: 'cover.png'),
    });
    return post('http://youapi/users/upload', form);
  }

  GetSocket userMessages() {
    return socket('https://yourapi/users/socket');
  }
}

//和其他的网络请求库一样,GetConnect也支持自定义基Url、请求头、自定义请求修饰符,以及请求重试,以及解码器和将请求结果转换为model等。

class HomeProvider extends GetConnect {
  @override
  void onInit() {
    // All request will pass to jsonEncode so CasesModel.fromJson()
    httpClient.defaultDecoder = CasesModel.fromJson;
    httpClient.baseUrl = 'https://api.covid19api.com';
    // baseUrl = 'https://api.covid19api.com'; // It define baseUrl to
    // Http and websockets if used with no [httpClient] instance
    // It's will attach 'apikey' property on header from all requests
    httpClient.addRequestModifier((request) {
      request.headers['apikey'] = '12345678';
      return request;
    });
    // Even if the server sends data from the country "Brazil",
    // it will never be displayed to users, because you remove
    // that data from the response, even before the response is delivered
    httpClient.addResponseModifier<CasesModel>((request, response) {
      CasesModel model = response.body;
      if (model.countries.contains('Brazil')) {
        model.countries.remove('Brazilll');
      }
    });
    httpClient.addAuthenticator((request) async {
      final response = await get("http://yourapi/token");
      final token = response.body['token'];
      // Set the header
      request.headers['Authorization'] = "$token";
      return request;
    });
    //Autenticator will be called 3 times if HttpStatus is
    //HttpStatus.unauthorized
    httpClient.maxAuthRetries = 3;
  }
  
  @override
  Future<Response<CasesModel>> getCases(String path) => get(path);
}


StateMixin

处理UI状态的另一种方法是使用StateMixin,使用前需要使用with将StateMixin添加到T模型的控制器中。

class Controller extends GetController with StateMixin<User>{}

//mixin是Dart中一个非常重要的概念,是一种在多个类层次结构中复用类代码的方法。而change()方法是一种可以随时更改状态的方法。
change(data, status: RxStatus.success());

//RxStatus支持的状态有如下一些:
RxStatus.loading();
RxStatus.success();
RxStatus.empty();
RxStatus.error('message');


//然后,我们使用obx根据状态来加载不同的视图。
class OtherClass extends GetView<Controller> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: controller.obx(
        (state)=>Text(state.name),
        onLoading: CustomLoadingIndicator(),
        onEmpty: Text('No data found'),
        onError: (error)=>Text(error),
      ),
    );
  }
}