# 数据持久化-GetStorage
有几个常见的flutter侧的数据持久化方案:
- shared_preferences
- localstorage
- get_storage(推荐)
- Fluttter_secure_storage -> 安全性好,但是性能会差一点
其中的get_storage的性能最好:
都是用于存储key-value的形式的数据。
# get_storage介绍
# 安装依赖
dependencies:
get_storage: ^2.0.3
使用flutter pub get
安装依赖。
# 具体使用
调整main.dart
,需要初始化GetStorage
:
main() async {
await GetStorage.init();
runApp(App());
}
创建一个storage.dart
的文件,用于存放常用的比如token、userInfo等方法:
import 'package:get_storage/get_storage.dart';
import 'package:my_app/entity/user_info.dart';
enum StoreKeys { token, refreshToken, userInfo }
class Storage {
static Storage _storage = Storage._internal();
final GetStorage _box = GetStorage();
GetStorage get box => _box;
Storage._internal();
factory Storage() => _storage;
// setToken, getToken
setToken(String token) => _box.write(StoreKeys.token.toString(), token);
String? getToken() => _box.read<String>(StoreKeys.token.toString());
// setRefreshToken, getRefreshToken
setRefreshToken(String refreshToken) =>
_box.write(StoreKeys.refreshToken.toString(), refreshToken);
String? getRefreshToken() =>
_box.read<String>(StoreKeys.refreshToken.toString());
// setUserInfo, getUserInfo
setUserInfo(UserInfo userInfo) =>
_box.write(StoreKeys.userInfo.toString(), userInfo);
UserInfo? getUserInfo() => _box.read<UserInfo>(StoreKeys.userInfo.toString());
}
GetStorage高性能的原因:
As soon as you declare "write" the file is immediately written in memory and can now be accessed immediately with
box.read()
. You can also wait for the callback that it was written to disk usingawait box.write()
.read与write方法不需要使用async/await,它会先记录到内存中,再记录到文件,是自动异步。
在文件中使用,可以见下文。
# 完善登录逻辑
登录部分现在还缺少:
- 登录请求
- 登录成功之后处理
- 存储token等有用信息 -> 持久化
- 登录失败的错误提示
对用户手机号进行正则判断,可以考虑把所有的正则写在一个文件中,方便维护:
class RegParttern {
static const mobileParttern = r'^1[3-9]\d{9}$';
static const codeParttern = r'\d{6}$';
}
修改lib/services/user_service.dart
文件,新增方法login
:
Future login(String mobile, String code) async {
var params = {'mobile': mobile, 'code': code};
var res = await DioHttp().post('/login/loginByPhone', params);
if (res.statusCode == 200) {
// 请求成功
Map<String, dynamic>? data = res.data;
HttpResponse httpResponse = HttpResponse.fromJson(data!);
if (httpResponse.code == 200) {
UserInfo? userInfo = UserInfo.fromJson(httpResponse.data);
Storage().setToken(httpResponse.token!);
Storage().setRefreshToken(httpResponse.refreshToken!);
Storage().setUserInfo(userInfo);
// 返回给前端一个数据 -> 路由导航
httpResponse.data = userInfo;
// _userInfo = httpResponse.data as UserInfo
} else {
// 请求异常
CommonToast.showToast(httpResponse.message ?? '登录失败,请重试');
}
return httpResponse;
}
}
调整login_form.dart
的逻辑:
Padding(
padding: const EdgeInsets.only(top: 10.0),
child: SizedBox(
width: 120,
child: ElevatedButton(
child: Text('登录'),
onPressed: () async {
String mobile = controller.text;
String code = codeController.text;
if (RegExp(RegParttern.mobileParttern).hasMatch(mobile) &&
RegExp(RegParttern.codeParttern).hasMatch(code)) {
await userService.login(mobile, code);
} else {
CommonToast.showToast("手机号或验证码格式不正确");
}
},
),
),
)
# 整个login_form.dart
文件
import 'package:flutter/material.dart';
import 'package:my_app/services/user_service.dart';
import 'package:my_app/setup_get_it.dart';
import 'package:my_app/widgets/base/button/counter_button.dart';
import 'package:my_app/widgets/base/fonts/my_icons_icons.dart';
import 'package:my_app/widgets/base/toast/common_toast.dart';
import 'package:my_app/widgets/const/reg_parttern.dart';
class LoginForm extends StatefulWidget {
LoginForm({Key? key}) : super(key: key);
_LoginFormState createState() => _LoginFormState();
}
class _LoginFormState extends State<LoginForm> {
bool _active = false;
TextEditingController controller = TextEditingController();
TextEditingController codeController = TextEditingController();
FocusNode currentFocusNode = FocusNode();
FocusNode focusNode = FocusNode();
UserService userService = getIt<UserService>();
_changeFocus(FocusNode focusNodeNext) {
// 判断一下,current是否被聚焦,取消聚焦 -> 聚焦新的输入框
FocusNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
focusNodeNext.requestFocus();
}
Widget build(BuildContext context) {
return Container(
height: 300,
margin: const EdgeInsets.symmetric(
vertical: 20.0,
horizontal: 15.0,
), // padding(20px, 15px)
padding: const EdgeInsets.only(
top: 30.0,
right: 30.0,
bottom: 20.0,
left: 30.0,
),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black26,
blurRadius: 5.0,
),
],
// BorderRadius.circular(10),
borderRadius: BorderRadius.all(Radius.circular(10)),
),
// 1. padding
// padding: const EdgeInsets.only(top: 110),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
// crossAxisAlignment: CrossAxisAlignment.center,
children: [
TextField(
focusNode: currentFocusNode,
controller: controller,
// keyboardAppearance: Brightness.light,
decoration: InputDecoration(
counterText: "",
prefixIcon: Icon(
MyIcons.person,
color: Colors.black54,
size: 26,
),
),
maxLength: 11,
keyboardType: TextInputType.phone,
onChanged: (val) {
RegExp exp = RegExp(r'^1[3-9]\d{9}$');
if (exp.hasMatch(val)) {
setState(() {
_active = true;
});
} else {
setState(() {
_active = false;
});
}
// print('val is 👉 $val');
}),
// Container, SizedBox, Padding, Divider
Padding(
padding: const EdgeInsets.all(15.0),
),
// stack
Stack(
children: [
TextField(
controller: codeController,
focusNode: focusNode,
decoration: InputDecoration(
counterText: "",
prefixIcon: Icon(
Icons.lock,
color: Colors.black54,
),
),
maxLength: 6,
keyboardType: TextInputType.number,
),
// Positioned
// 1.页面按钮状态 -> 有状态组件如何设置页面状态
// 2.倒计时逻辑 -> stream
Positioned(
right: 10.0,
child: CounterButton(
active: _active,
onPressed: () async {
_changeFocus(focusNode);
userService.sendCode(controller.text);
},
),
)
],
),
// method1: Padding -> child
// methods2: Padding
Padding(
padding: const EdgeInsets.only(top: 10.0),
child: SizedBox(
width: 120,
child: ElevatedButton(
child: Text('登录'),
onPressed: () async {
String mobile = controller.text;
String code = codeController.text;
if (RegExp(RegParttern.mobileParttern).hasMatch(mobile) &&
RegExp(RegParttern.codeParttern).hasMatch(code)) {
await userService.login(mobile, code);
} else {
CommonToast.showToast("手机号或验证码格式不正确");
}
},
),
),
)
],
),
);
}
}