国际化、依赖注入、状态管理、路由导航(不再显式传入BuildContext,任意位置可导航)、网络请求库
首先
flutter pub add get #安装依赖
将MaterialApp换成GetMaterialApp,GetMaterialApp是GetX预先封装配置好的一个组件,MaterialApp是它的子组件,并在此之上添加了一些属性。
全局状态/依赖注入
声明控制器class
class Controller extends GetxController {
late SharedPreferences sharePreInstance;
@override
void onInit() async {
super.onInit();
await initGlobalState(); //初始化时进行数据的加载
}
var isLogin = false.obs; //添加.obs设为可观察对象,其类型变成RxBool
void setIsLogin(bool v){
token.value = v; //不可将bool直接赋值给RxBool,要用.value
}
Future<void> initGlobalState() async {
sharePreInstance = await SharedPreferences.getInstance();
setIsLogin(sharePreInstance.getBool("login") ?? false);
}
}
注入控制器:
void main() async {
Get.put(Controller());
runApp(
const MyApp()
);
}
声明控制器变量方式1:
仅限StatelessWidget
class MyApp extends GetView<Controller> { // StatelessWidget改为用GetView<Controller>,Controller是上面声明的类,如果有多个控制器改为对应控制器
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return Text("${controller.isLogin}");
}
}
声明控制器变量方式2:
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
final Controller c = Get.find();
return /*Widget*/;
}
}
声明控制器变量方式3:
class Test extends StatefulWidget {
const Test({super.key});
@override
State<Test> createState() => _TestState();
}
class _TestState extends State<Test> {
final Controller c = Get.find(); //StatefulWidget中可以声明在类成员属性
@override
Widget build(BuildContext context) {
return /*Widget*/
}
}
变量变化时自动刷新UI
需用Obx包裹,如果一个大区块内用到了多次控制器中的变量,则将整个容器放入Obx
Obx(()=>Text("count:${controller.count}"))
多语言
声明class
class MyTranslation extends Translations {
@override
Map<String, Map<String, String>> get keys => {
'zh_CN': { //languageCode_countryCode,countryCode可省略,如:zh
'hello': "你好",
"nickname": "欢迎回来,用户:@user" //加入变量
},
'en_US': {
'hello': "hello"
}
};
}
程序中注入
GetMaterialApp(
//i18n
translations: MyTranslation(),
locale: Locale('zh', 'CN'),
//...
);
切换语言、获取当前语言
Get.updateLocale(Locale('en', 'US'));
Get.locale?.countryCode
Get.locale?.languageCode
UI中使用
Text("hello".tr); //加上.tr
Text("nickname".trParams({
'user': username.value
})); //加上.tr
网络请求库
编写class
import 'package:flutter_scan/main.dart';
import 'package:get/get.dart';
class ApiRequest extends GetConnect {
final Controller c = Get.find(); //使用控制器中数据
@override
void onInit() {
httpClient.baseUrl = "https://xxxx.cn";
httpClient.timeout = Duration(seconds: 30);
httpClient.addRequestModifier<void>((request){ //请求发送前的统一修改器
if(c.isLogin.value) {
request.headers["Authorization"] = "Bearer ${c.token}";
}
return request;
});
httpClient.addResponseModifier((request, response){ //响应收到时先进入修改器
if(response.bodyString!.contains("登录信息无效")) {
c.logout(); //控制器中的方法,做的事情就是清除本地存储、until一直返回到首页、清除控制器中存储的登录状态
Get.snackbar("", "登录已经失效");
throw Exception("登录失效"); //抛出错误,发起请求的地方会接收到null
}
return response; //没有登录失效则正常返回响应的内容
});
super.onInit();
}
}
注入
void main() async {
Get.put(Controller()); //如果api类中使用了控制器,就要先put控制器
Get.put(ApiRequest());
runApp(
const MyApp()
);
}
use
class _LoginPageState extends State<LoginPage> {
@override
Widget build(BuildContext context) {
final api = Get.find<ApiRequest>();
return TextButton(onPress:() async {
await api.post("/api/v1/login", LoginRequest(username: username, password: password, role: role).toString());
}, child: Text("登录"));
}
}
路由导航
Get.back(result: '返回给上一页的数据'); //返回到上一页
//在上层打开一个页面
Get.to(LoginPage());
Get.to("/login");
Get.toNamed("/login");
Get.until((route)=>route.settings.name == "/"); //一直返回,直到首页
Get.off(LoginPage()); //关闭当前页面,并打开对应页面
Get.off("/login"); //关闭当前页面,并打开对应页面
Get.offNamed("/login"); //关闭当前页面,然后打开login页面
Get.offAndToNamed("/login"); //关闭当前页面,然后打开login页面
Get.offAllNamed("/login"); //关闭所有页面,打开login页面(完成后页面栈只有login)
Get.offAll(LoginPage()); //关闭所有页面,打开指定页面(完成后页面栈只有login)
Get.offNamedUntil("/login", (route)=>route.settings.name=="/"); //一直退回到首页后打开login页面(完成后只有首页和login页面)
Get.offUntil(MaterialPageRoute(builder: (BuildContext context) { return LoginPage(); }), (route)=>route.settings.name=="/"); //退回到首页后通过Route<T>打开一个新页面