feat(mobile-next): 导入官方 openim-flutter-demo 固定基线 3.8.3-patch.3 并完成双构建

- 上游: github.com/OpenIMSDK/openim-flutter-demo tag 3.8.3-patch.3
  commit b3dfdb1e8aaeaaf6f0793e10cadd20d5c184a31f
- 并行目录 mobile-next/ 原样导入(含 LICENSE),不覆盖现网 mobile/
- 锁定 Flutter 3.24.5 / Dart 3.5.4 / JDK 17 / Gradle 7.6.3 / AGP 7.3.1
- 实测 Android debug 与 release 构建均成功(见 docs/mobile-next-baseline-import.md)
- 本提交可单独回退:git revert 1db1229(重写前)

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
编码工程师
2026-08-19 15:24:33 +08:00
co-authored by multica-agent
parent fa8584a73f
commit 8046ec7483
767 changed files with 57658 additions and 0 deletions
@@ -0,0 +1,10 @@
import 'package:get/get.dart';
import 'about_us_logic.dart';
class AboutUsBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => AboutUsLogic());
}
}
@@ -0,0 +1,60 @@
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import 'package:package_info_plus/package_info_plus.dart';
import '../../../core/controller/app_controller.dart';
import '../../../core/controller/im_controller.dart';
class AboutUsLogic extends GetxController {
final appLogic = Get.find<AppController>();
final imLogic = Get.find<IMController>();
final lineTextController = TextEditingController(text: '1000');
final displayVersion = ''.obs;
void getPackageInfo() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
final version = packageInfo.version;
final appName = packageInfo.appName;
final buildNumber = packageInfo.buildNumber;
displayVersion.value = '$appName $version+$buildNumber SDK: ${OpenIM.version}';
}
void checkUpdate() {
appLogic.checkUpdate();
}
void copyVersion() {
IMViews.showToast(StrRes.copySuccessfully);
Clipboard.setData(ClipboardData(text: displayVersion.value));
}
void uploadLogs([int line = 0]) async {
EasyLoading.showProgress(0);
await OpenIM.iMManager.uploadLogs(line: line);
EasyLoading.dismiss();
}
@override
void onReady() {
getPackageInfo();
imLogic.onUploadProgress = (current, size) {
final p = current / size;
final pStr = '${(p * 100.0).truncate()}%';
EasyLoading.showProgress(p, status: pStr);
};
super.onReady();
}
@override
void onClose() {
super.onClose();
}
}
@@ -0,0 +1,132 @@
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import 'about_us_logic.dart';
class AboutUsPage extends StatelessWidget {
final logic = Get.find<AboutUsLogic>();
AboutUsPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: TitleBar.back(title: StrRes.aboutUs),
backgroundColor: Styles.c_F8F9FA,
body: Column(
children: [
Container(
decoration: BoxDecoration(
color: Styles.c_FFFFFF,
borderRadius: BorderRadius.circular(6.r),
),
margin: EdgeInsets.symmetric(
horizontal: 10.w,
vertical: 10.h,
),
child: Column(
children: [
23.verticalSpace,
ImageRes.splashLogo.toImage
..width = 55.w
..height = 78.h,
10.verticalSpace,
Obx(() => '${logic.displayVersion}'.toText
..style = Styles.ts_0C1C33_14sp
..onTap = logic.copyVersion),
16.verticalSpace,
Container(
margin: EdgeInsets.symmetric(horizontal: 10.w),
color: Styles.c_E8EAEF,
height: .5,
),
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: logic.checkUpdate,
child: Container(
height: 57.h,
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Row(
children: [
StrRes.checkNewVersion.toText..style = Styles.ts_0C1C33_17sp,
const Spacer(),
ImageRes.rightArrow.toImage
..width = 24.w
..height = 24.h,
],
),
),
),
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: logic.uploadLogs,
child: Container(
height: 57.h,
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Row(
children: [
StrRes.uploadErrorLog.toText..style = Styles.ts_0C1C33_17sp,
const Spacer(),
ImageRes.rightArrow.toImage
..width = 24.w
..height = 24.h,
],
),
),
),
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: _showInputDialog,
child: Container(
height: 57.h,
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Row(
children: [
StrRes.uploadLogWithLine.toText..style = Styles.ts_0C1C33_17sp,
const Spacer(),
ImageRes.rightArrow.toImage
..width = 24.w
..height = 24.h,
],
),
),
),
],
),
),
],
),
);
}
void _showInputDialog() {
showDialog(
context: Get.context!,
builder: (ctx) {
return CupertinoAlertDialog(
title: StrRes.setLines.toText..style = Styles.ts_0C1C33_17sp,
content: CupertinoTextField(
controller: logic.lineTextController,
placeholder: logic.lineTextController.text,
keyboardType: TextInputType.number,
),
actions: [
CupertinoButton(
child: StrRes.confirm.toText..style = Styles.ts_0C1C33_17sp,
onPressed: () {
navigator?.pop();
final lineStr = logic.lineTextController.text.trim();
logic.uploadLogs(int.parse(lineStr));
},
)
],
);
});
}
}
@@ -0,0 +1,10 @@
import 'package:get/get.dart';
import 'account_setup_logic.dart';
class AccountSetupBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => AccountSetupLogic());
}
}
@@ -0,0 +1,135 @@
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
import 'package:get/get.dart';
import 'package:openim/routes/app_navigator.dart';
import 'package:openim_common/openim_common.dart';
import '../../../core/controller/im_controller.dart';
class AccountSetupLogic extends GetxController {
final imLogic = Get.find<IMController>();
final curLanguage = "".obs;
@override
void onReady() {
_updateLanguage();
super.onReady();
}
@override
void onClose() {
super.onClose();
}
@override
void onInit() {
_queryMyFullInfo();
super.onInit();
}
bool get isGlobalNotDisturb => imLogic.userInfo.value.globalRecvMsgOpt == 2;
bool get isAllowAddFriend => imLogic.userInfo.value.allowAddFriend == 1;
bool get isAllowBeep => imLogic.userInfo.value.allowBeep == 1;
bool get isAllowVibration => imLogic.userInfo.value.allowVibration == 1;
void _queryMyFullInfo() async {
final data = await LoadingView.singleton.wrap(
asyncFunction: () => Apis.queryMyFullInfo(),
);
if (data is UserFullInfo) {
final userInfo = UserFullInfo.fromJson(data.toJson());
imLogic.userInfo.update((val) {
val?.allowAddFriend = userInfo.allowAddFriend;
val?.allowBeep = userInfo.allowBeep;
val?.allowVibration = userInfo.allowVibration;
});
}
}
void toggleNotDisturbMode() async {
var status = isGlobalNotDisturb ? 0 : 2;
await LoadingView.singleton
.wrap(asyncFunction: () => OpenIM.iMManager.userManager.setGlobalRecvMessageOpt(status: status));
imLogic.userInfo.update((val) {
val?.globalRecvMsgOpt = status;
});
}
void toggleBeep() async {
final allowBeep = !isAllowBeep ? 1 : 2;
await LoadingView.singleton.wrap(
asyncFunction: () => Apis.updateUserInfo(
allowBeep: allowBeep,
userID: OpenIM.iMManager.userID,
),
);
imLogic.userInfo.update((val) {
val?.allowBeep = allowBeep;
});
}
void toggleVibration() async {
final allowVibration = !isAllowVibration ? 1 : 2;
await LoadingView.singleton.wrap(
asyncFunction: () => Apis.updateUserInfo(
allowVibration: allowVibration,
userID: OpenIM.iMManager.userID,
),
);
imLogic.userInfo.update((val) {
val?.allowVibration = allowVibration;
});
}
void toggleForbidAddMeToFriend() async {
final allowAddFriend = !isAllowAddFriend ? 1 : 2;
final data = await LoadingView.singleton.wrap(
asyncFunction: () => Apis.updateUserInfo(
allowAddFriend: allowAddFriend,
userID: OpenIM.iMManager.userID,
),
);
imLogic.userInfo.update((val) {
val?.allowAddFriend = allowAddFriend;
});
}
void blacklist() => AppNavigator.startBlacklist();
void clearChatHistory() async {
var confirm = await Get.dialog(CustomDialog(
title: StrRes.confirmClearChatHistory,
));
if (confirm == true) {
LoadingView.singleton.wrap(asyncFunction: () async {
await OpenIM.iMManager.messageManager.deleteAllMsgFromLocalAndSvr();
});
}
}
void languageSetting() => AppNavigator.startLanguageSetup();
void _updateLanguage() {
var index = DataSp.getLanguage() ?? 0;
switch (index) {
case 1:
curLanguage.value = StrRes.chinese;
break;
case 2:
curLanguage.value = StrRes.english;
break;
default:
curLanguage.value = StrRes.followSystem;
break;
}
}
void unlockSetup() => AppNavigator.startUnlockSetup();
void changePwd() => AppNavigator.startChangePassword();
}
@@ -0,0 +1,139 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import 'account_setup_logic.dart';
class AccountSetupPage extends StatelessWidget {
final logic = Get.find<AccountSetupLogic>();
AccountSetupPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: TitleBar.back(
title: StrRes.accountSetup,
),
backgroundColor: Styles.c_F8F9FA,
body: Obx(() => SingleChildScrollView(
child: Column(
children: [
10.verticalSpace,
_buildItemView(
label: StrRes.notDisturbMode,
switchOn: logic.isGlobalNotDisturb,
onChanged: (_) => logic.toggleNotDisturbMode(),
showSwitchButton: true,
isTopRadius: true,
),
_buildItemView(
label: StrRes.allowRing,
switchOn: logic.isAllowBeep,
onChanged: (_) => logic.toggleBeep(),
showSwitchButton: true,
),
_buildItemView(
label: StrRes.allowVibrate,
switchOn: logic.isAllowVibration,
onChanged: (_) => logic.toggleVibration(),
showSwitchButton: true,
isBottomRadius: true,
),
10.verticalSpace,
_buildItemView(
label: StrRes.forbidAddMeToFriend,
switchOn: !logic.isAllowAddFriend,
onChanged: (_) => logic.toggleForbidAddMeToFriend(),
showSwitchButton: true,
isTopRadius: true,
),
_buildItemView(
label: StrRes.blacklist,
onTap: logic.blacklist,
showRightArrow: true,
),
_buildItemView(
label: StrRes.languageSetup,
value: logic.curLanguage.value,
onTap: logic.languageSetting,
showRightArrow: true,
isBottomRadius: true,
),
10.verticalSpace,
_buildItemView(
label: StrRes.unlockSettings,
onTap: logic.unlockSetup,
showRightArrow: true,
isTopRadius: true,
),
_buildItemView(
label: StrRes.changePassword,
showRightArrow: true,
onTap: logic.changePwd,
),
_buildItemView(
label: StrRes.clearChatHistory,
textStyle: Styles.ts_FF381F_17sp,
onTap: logic.clearChatHistory,
showRightArrow: true,
isBottomRadius: true,
),
],
),
)),
);
}
Widget _buildItemView({
required String label,
TextStyle? textStyle,
String? value,
bool switchOn = false,
bool isTopRadius = false,
bool isBottomRadius = false,
bool showRightArrow = false,
bool showSwitchButton = false,
ValueChanged<bool>? onChanged,
Function()? onTap,
}) =>
Container(
margin: EdgeInsets.symmetric(horizontal: 10.w),
child: Ink(
decoration: BoxDecoration(
color: Styles.c_FFFFFF,
borderRadius: BorderRadius.only(
topRight: Radius.circular(isTopRadius ? 6.r : 0),
topLeft: Radius.circular(isTopRadius ? 6.r : 0),
bottomLeft: Radius.circular(isBottomRadius ? 6.r : 0),
bottomRight: Radius.circular(isBottomRadius ? 6.r : 0),
),
),
child: InkWell(
onTap: onTap,
child: Container(
height: 46.h,
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Row(
children: [
label.toText..style = textStyle ?? Styles.ts_0C1C33_17sp,
const Spacer(),
if (showSwitchButton)
CupertinoSwitch(
value: switchOn,
activeColor: Styles.c_0089FF,
onChanged: onChanged,
),
if (showRightArrow)
ImageRes.rightArrow.toImage
..width = 24.w
..height = 24.h,
],
),
),
),
),
);
}
@@ -0,0 +1,10 @@
import 'package:get/get.dart';
import 'blacklist_logic.dart';
class BlacklistBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => BlacklistLogic());
}
}
@@ -0,0 +1,29 @@
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
import 'package:get/get.dart';
class BlacklistLogic extends GetxController {
final blacklist = <BlacklistInfo>[].obs;
void _getBlacklist() async {
var list = await OpenIM.iMManager.friendshipManager.getBlacklist();
blacklist.addAll(list);
}
remove(BlacklistInfo info) async {
await OpenIM.iMManager.friendshipManager.removeBlacklist(
userID: info.userID!,
);
blacklist.remove(info);
}
@override
void onReady() {
_getBlacklist();
super.onReady();
}
@override
void onClose() {
super.onClose();
}
}
@@ -0,0 +1,86 @@
import 'package:flutter/material.dart';
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import 'blacklist_logic.dart';
class BlacklistPage extends StatelessWidget {
final logic = Get.find<BlacklistLogic>();
BlacklistPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: TitleBar.back(title: StrRes.blacklist),
backgroundColor: Styles.c_F8F9FA,
body: Obx(() => logic.blacklist.isEmpty
? _emptyListView
: ListView.builder(
padding: EdgeInsets.only(top: 10.h),
itemCount: logic.blacklist.length,
itemBuilder: (_, index) => _buildItemView(
logic.blacklist[index],
underline: index != logic.blacklist.length - 1,
),
)),
);
}
Widget _buildItemView(
BlacklistInfo info, {
bool underline = true,
}) =>
Ink(
height: 62.h,
decoration: BoxDecoration(
color: Styles.c_FFFFFF,
border: underline
? BorderDirectional(
bottom: BorderSide(
color: Styles.c_E8EAEF,
width: 1,
),
)
: null,
),
child: InkWell(
onTap: () => logic.remove(info),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Row(
children: [
AvatarView(
width: 44.w,
height: 44.h,
text: info.nickname,
url: info.faceURL,
),
12.horizontalSpace,
Expanded(
child: (info.nickname ?? '').toText..style = Styles.ts_0C1C33_17sp,
),
StrRes.remove.toText..style = Styles.ts_0089FF_17sp,
],
),
),
),
);
Widget get _emptyListView => SizedBox(
width: 1.sw,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
157.verticalSpace,
ImageRes.blacklistEmpty.toImage
..width = 120.w
..height = 120.h,
22.verticalSpace,
StrRes.blacklistEmpty.toText..style = Styles.ts_8E9AB0_16sp,
],
),
);
}
@@ -0,0 +1,10 @@
import 'package:get/get.dart';
import 'change_pwd_logic.dart';
class ChangePwdBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => ChangePwdLogic());
}
}
@@ -0,0 +1,60 @@
import 'package:flutter/material.dart';
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import '../../../routes/app_navigator.dart';
class ChangePwdLogic extends GetxController {
final oldPwdCtrl = TextEditingController();
final newPwdCtrl = TextEditingController();
final againPwdCtrl = TextEditingController();
@override
void onClose() {
oldPwdCtrl.dispose();
newPwdCtrl.dispose();
againPwdCtrl.dispose();
super.onClose();
}
void confirm() async {
if (oldPwdCtrl.text.isEmpty) {
IMViews.showToast(StrRes.plsEnterOldPwd);
return;
}
if (!IMUtils.isValidPassword(newPwdCtrl.text)) {
IMViews.showToast(StrRes.wrongPasswordFormat);
return;
}
if (newPwdCtrl.text.isEmpty) {
IMViews.showToast(StrRes.plsEnterNewPwd);
return;
}
if (againPwdCtrl.text.isEmpty) {
IMViews.showToast(StrRes.plsEnterConfirmPwd);
return;
}
if (newPwdCtrl.text != againPwdCtrl.text) {
IMViews.showToast(StrRes.twicePwdNoSame);
return;
}
final result = await LoadingView.singleton.wrap(
asyncFunction: () => Apis.changePassword(
userID: OpenIM.iMManager.userID,
newPassword: newPwdCtrl.text,
currentPassword: oldPwdCtrl.text,
),
);
if (result) {
IMViews.showToast(StrRes.changedSuccessfully);
await LoadingView.singleton.wrap(asyncFunction: () async {
await OpenIM.iMManager.logout();
await DataSp.removeLoginCertificate();
PushController.logout();
});
AppNavigator.startLogin();
}
}
}
@@ -0,0 +1,99 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import 'change_pwd_logic.dart';
class ChangePwdPage extends StatelessWidget {
final logic = Get.find<ChangePwdLogic>();
ChangePwdPage({super.key});
@override
Widget build(BuildContext context) {
return TouchCloseSoftKeyboard(
child: Scaffold(
appBar: TitleBar.back(
title: StrRes.changePassword,
right: StrRes.determine.toText
..style = Styles.ts_0C1C33_17sp
..onTap = logic.confirm,
),
backgroundColor: Styles.c_F8F9FA,
body: SingleChildScrollView(
child: Column(
children: [
12.verticalSpace,
_buildItemView(
label: StrRes.oldPwd,
controller: logic.oldPwdCtrl,
autofocus: true,
isTopRadius: true,
),
Container(
margin: EdgeInsets.only(left: 26.w, right: 10.w),
color: Styles.c_E8EAEF,
height: .5,
),
_buildItemView(
label: StrRes.newPwd,
controller: logic.newPwdCtrl,
),
Container(
margin: EdgeInsets.only(left: 26.w, right: 10.w),
color: Styles.c_E8EAEF,
height: .5,
),
_buildItemView(
label: StrRes.confirmNewPwd,
controller: logic.againPwdCtrl,
isBottomRadius: true,
),
],
),
),
),
);
}
Widget _buildItemView({
required String label,
TextEditingController? controller,
bool autofocus = false,
bool isTopRadius = false,
bool isBottomRadius = false,
}) =>
Container(
height: 60.h,
padding: EdgeInsets.symmetric(horizontal: 16.w),
margin: EdgeInsets.symmetric(horizontal: 10.w),
decoration: BoxDecoration(
color: Styles.c_FFFFFF,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(isTopRadius ? 6.r : 0),
topRight: Radius.circular(isTopRadius ? 6.r : 0),
bottomRight: Radius.circular(isBottomRadius ? 6.r : 0),
bottomLeft: Radius.circular(isBottomRadius ? 6.r : 0),
),
),
child: Row(
children: [
label.toText..style = Styles.ts_0C1C33_17sp,
Expanded(
child: TextField(
controller: controller,
autofocus: autofocus,
textInputAction: TextInputAction.next,
textAlign: TextAlign.end,
style: Styles.ts_0C1C33_17sp,
decoration: const InputDecoration(
isDense: true,
border: InputBorder.none,
),
),
),
],
),
);
}
@@ -0,0 +1,10 @@
import 'package:get/get.dart';
import 'edit_my_info_logic.dart';
class EditMyInfoBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => EditMyInfoLogic());
}
}
@@ -0,0 +1,97 @@
import 'package:flutter/material.dart';
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import '../../../core/controller/im_controller.dart';
enum EditAttr {
nickname,
englishName,
telephone,
mobile,
email,
}
class EditMyInfoLogic extends GetxController {
final imLogic = Get.find<IMController>();
late TextEditingController inputCtrl;
late EditAttr editAttr;
late int maxLength;
String? title;
String? defaultValue;
TextInputType? keyboardType;
@override
void onInit() {
editAttr = Get.arguments['editAttr'];
maxLength = Get.arguments['maxLength'] ?? 16;
_initAttr();
inputCtrl = TextEditingController(text: defaultValue);
super.onInit();
}
_initAttr() {
switch (editAttr) {
case EditAttr.nickname:
title = StrRes.name;
defaultValue = imLogic.userInfo.value.nickname;
keyboardType = TextInputType.text;
break;
case EditAttr.englishName:
break;
case EditAttr.telephone:
break;
case EditAttr.mobile:
title = StrRes.mobile;
defaultValue = imLogic.userInfo.value.phoneNumber;
keyboardType = TextInputType.phone;
break;
case EditAttr.email:
title = StrRes.email;
defaultValue = imLogic.userInfo.value.email;
keyboardType = TextInputType.emailAddress;
break;
}
}
void save() async {
final value = inputCtrl.text.trim();
if (editAttr == EditAttr.nickname) {
await LoadingView.singleton.wrap(
asyncFunction: () => Apis.updateUserInfo(
userID: OpenIM.iMManager.userID,
nickname: value,
),
);
imLogic.userInfo.update((val) {
val?.nickname = value;
});
} else if (editAttr == EditAttr.mobile) {
await LoadingView.singleton.wrap(
asyncFunction: () => Apis.updateUserInfo(
userID: OpenIM.iMManager.userID,
phoneNumber: value,
),
);
imLogic.userInfo.update((val) {
val?.phoneNumber = value;
});
} else if (editAttr == EditAttr.email) {
if (defaultValue?.isNotEmpty == true && value.isEmpty) {
IMViews.showToast(StrRes.plsEnterEmail);
return;
}
await LoadingView.singleton.wrap(
asyncFunction: () => Apis.updateUserInfo(
userID: OpenIM.iMManager.userID,
email: value,
),
);
imLogic.userInfo.update((val) {
val?.email = value;
});
}
Get.back();
}
}
@@ -0,0 +1,52 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import 'edit_my_info_logic.dart';
class EditMyInfoPage extends StatelessWidget {
final logic = Get.find<EditMyInfoLogic>();
EditMyInfoPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: TitleBar.back(
title: logic.title,
right: StrRes.save.toText
..style = Styles.ts_0C1C33_17sp
..onTap = logic.save,
),
backgroundColor: Styles.c_FFFFFF,
body: Column(
children: [
22.verticalSpace,
Container(
margin: EdgeInsets.symmetric(horizontal: 10.w),
decoration: BoxDecoration(
color: Styles.c_E8EAEF,
borderRadius: BorderRadius.circular(4.r),
),
child: TextField(
controller: logic.inputCtrl,
style: Styles.ts_0C1C33_17sp,
autofocus: true,
keyboardType: logic.keyboardType,
inputFormatters: [LengthLimitingTextInputFormatter(logic.maxLength)],
decoration: InputDecoration(
border: InputBorder.none,
isDense: true,
contentPadding: EdgeInsets.symmetric(
vertical: 10.h,
horizontal: 12.w,
),
),
),
),
],
),
);
}
}
@@ -0,0 +1,10 @@
import 'package:get/get.dart';
import 'language_setup_logic.dart';
class LanguageSetupBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => LanguageSetupLogic());
}
}
@@ -0,0 +1,61 @@
import 'dart:ui';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
class LanguageSetupLogic extends GetxController {
final isFollowSystem = false.obs;
final isChinese = false.obs;
final isEnglish = false.obs;
@override
void onInit() {
_initLanguageSetting();
super.onInit();
}
void _initLanguageSetting() {
var language = DataSp.getLanguage() ?? 0;
switch (language) {
case 1:
isFollowSystem.value = false;
isChinese.value = true;
isEnglish.value = false;
break;
case 2:
isFollowSystem.value = false;
isChinese.value = false;
isEnglish.value = true;
break;
default:
isFollowSystem.value = true;
isChinese.value = false;
isEnglish.value = false;
break;
}
}
void switchLanguage(index) async {
await DataSp.putLanguage(index);
switch (index) {
case 1:
isFollowSystem.value = false;
isChinese.value = true;
isEnglish.value = false;
Get.updateLocale(const Locale('zh', 'CN'));
break;
case 2:
isFollowSystem.value = false;
isChinese.value = false;
isEnglish.value = true;
Get.updateLocale(const Locale('en', 'US'));
break;
default:
isFollowSystem.value = true;
isChinese.value = false;
isEnglish.value = false;
Get.updateLocale(window.locale);
break;
}
}
}
@@ -0,0 +1,91 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import 'language_setup_logic.dart';
class LanguageSetupPage extends StatelessWidget {
final logic = Get.find<LanguageSetupLogic>();
LanguageSetupPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: TitleBar.back(title: StrRes.languageSetup),
backgroundColor: Styles.c_F8F9FA,
body: Obx(() => Column(
children: [
12.verticalSpace,
_buildItemView(
label: StrRes.followSystem,
isChecked: logic.isFollowSystem.value,
onTap: () => logic.switchLanguage(0),
isTopRadius: true,
),
Container(
margin: EdgeInsets.only(left: 26.w, right: 10.w),
color: Styles.c_E8EAEF,
height: .5,
),
_buildItemView(
label: StrRes.chinese,
isChecked: logic.isChinese.value,
onTap: () => logic.switchLanguage(1),
),
Container(
margin: EdgeInsets.only(left: 26.w, right: 10.w),
color: Styles.c_E8EAEF,
height: .5,
),
_buildItemView(
label: StrRes.english,
isChecked: logic.isEnglish.value,
onTap: () => logic.switchLanguage(2),
isBottomRadius: true,
),
],
)),
);
}
Widget _buildItemView({
required String label,
bool isChecked = false,
bool isTopRadius = false,
bool isBottomRadius = false,
Function()? onTap,
}) =>
Container(
margin: EdgeInsets.symmetric(horizontal: 10.w),
child: Ink(
height: 60.h,
decoration: BoxDecoration(
color: Styles.c_FFFFFF,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(isTopRadius ? 6.r : 0),
topRight: Radius.circular(isTopRadius ? 6.r : 0),
bottomRight: Radius.circular(isBottomRadius ? 6.r : 0),
bottomLeft: Radius.circular(isBottomRadius ? 6.r : 0),
),
),
child: InkWell(
onTap: onTap,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Row(
children: [
label.toText..style = Styles.ts_0C1C33_17sp,
const Spacer(),
if (isChecked)
ImageRes.checked.toImage
..width = 24.w
..height = 24.h,
],
),
),
),
),
);
}
@@ -0,0 +1,10 @@
import 'package:get/get.dart';
import 'mine_logic.dart';
class MineBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => MineLogic());
}
}
@@ -0,0 +1,73 @@
import 'dart:async';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/get.dart';
import 'package:openim/core/im_callback.dart';
import 'package:openim/pages/home/home_logic.dart';
import 'package:openim_common/openim_common.dart';
import '../../core/controller/im_controller.dart';
import '../../routes/app_navigator.dart';
class MineLogic extends GetxController {
final imLogic = Get.find<IMController>();
late StreamSubscription kickedOfflineSub;
void viewMyQrcode() => AppNavigator.startMyQrcode();
void viewMyInfo() => AppNavigator.startMyInfo();
void copyID() {
IMUtils.copy(text: imLogic.userInfo.value.userID!);
}
void accountSetup() => AppNavigator.startAccountSetup();
void aboutUs() => AppNavigator.startAboutUs();
void logout() async {
var confirm = await Get.dialog(CustomDialog(title: StrRes.logoutHint));
if (confirm == true) {
try {
await LoadingView.singleton.wrap(asyncFunction: () async {
await imLogic.logout();
await DataSp.removeLoginCertificate();
PushController.logout();
Get.find<HomeLogic>().conversationsAtFirstPage.clear();
});
AppNavigator.startLogin();
} catch (e) {
IMViews.showToast('e:$e');
}
}
}
void kickedOffline({String? tips}) async {
if (EasyLoading.isShow) {
EasyLoading.dismiss();
}
Get.snackbar(StrRes.accountWarn, tips ?? StrRes.accountException);
await DataSp.removeLoginCertificate();
PushController.logout();
AppNavigator.startLogin();
}
@override
void onInit() {
kickedOfflineSub = imLogic.onKickedOfflineSubject.listen((value) {
if (value == KickoffType.userTokenInvalid) {
kickedOffline(tips: StrRes.tokenInvalid);
} else {
kickedOffline();
}
});
super.onInit();
}
@override
void onClose() {
kickedOfflineSub.cancel();
super.onClose();
}
}
+161
View File
@@ -0,0 +1,161 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import 'mine_logic.dart';
class MinePage extends StatelessWidget {
final logic = Get.find<MineLogic>();
MinePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Styles.c_F8F9FA,
body: SingleChildScrollView(
child: Column(
children: [
Stack(
children: [
Container(
height: 138.h,
width: 1.sw,
color: Styles.c_0089FF,
child: ImageRes.mineHeaderBg.toImage,
),
Obx(() => _buildMyInfoView()),
],
),
10.verticalSpace,
_buildItemView(
icon: ImageRes.myInfo,
label: StrRes.myInfo,
onTap: logic.viewMyInfo,
isTopRadius: true,
),
_buildItemView(
icon: ImageRes.accountSetup,
label: StrRes.accountSetup,
onTap: logic.accountSetup,
),
_buildItemView(
icon: ImageRes.aboutUs,
label: StrRes.aboutUs,
onTap: logic.aboutUs,
),
_buildItemView(
icon: ImageRes.logout,
label: StrRes.logout,
onTap: logic.logout,
isBottomRadius: true,
),
],
),
),
);
}
Widget _buildMyInfoView() => Container(
height: 98.h,
margin: EdgeInsets.only(left: 16.w, right: 16.w, top: 90.h),
padding: EdgeInsets.symmetric(horizontal: 16.w),
decoration: BoxDecoration(
color: Styles.c_FFFFFF,
borderRadius: BorderRadius.circular(6.r),
),
child: Row(
children: [
AvatarView(
url: logic.imLogic.userInfo.value.faceURL,
text: logic.imLogic.userInfo.value.nickname,
width: 48.w,
height: 48.h,
textStyle: Styles.ts_FFFFFF_14sp,
),
10.horizontalSpace,
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
(logic.imLogic.userInfo.value.nickname ?? '').toText..style = Styles.ts_0C1C33_17sp_medium,
4.verticalSpace,
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: logic.copyID,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
(logic.imLogic.userInfo.value.userID ?? '').toText..style = Styles.ts_8E9AB0_14sp,
ImageRes.mineCopy.toImage
..width = 16.w
..height = 16.h,
],
),
),
],
),
),
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: logic.viewMyQrcode,
child: Row(
children: [
ImageRes.mineQr.toImage
..width = 18.w
..height = 18.h,
8.horizontalSpace,
ImageRes.rightArrow.toImage
..width = 24.w
..height = 24.h,
],
),
)
],
),
);
Widget _buildItemView({
required String icon,
required String label,
bool isTopRadius = false,
bool isBottomRadius = false,
Function()? onTap,
}) =>
Container(
margin: EdgeInsets.symmetric(horizontal: 16.w),
child: Ink(
decoration: BoxDecoration(
color: Styles.c_FFFFFF,
borderRadius: BorderRadius.only(
topRight: Radius.circular(isTopRadius ? 6.r : 0),
topLeft: Radius.circular(isTopRadius ? 6.r : 0),
bottomLeft: Radius.circular(isBottomRadius ? 6.r : 0),
bottomRight: Radius.circular(isBottomRadius ? 6.r : 0),
),
),
child: InkWell(
onTap: onTap,
child: Container(
height: 56.h,
padding: EdgeInsets.only(left: 12.w, right: 16.w),
child: Row(
children: [
icon.toImage
..width = 24.w
..height = 24.h,
11.horizontalSpace,
label.toText..style = Styles.ts_0C1C33_17sp,
const Spacer(),
ImageRes.rightArrow.toImage
..width = 24.w
..height = 24.h,
],
),
),
),
),
);
}
@@ -0,0 +1,10 @@
import 'package:get/get.dart';
import 'my_info_logic.dart';
class MyInfoBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => MyInfoLogic());
}
}
@@ -0,0 +1,130 @@
import 'package:flutter_datetime_picker_plus/flutter_datetime_picker_plus.dart';
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
import 'package:get/get.dart';
import 'package:openim/pages/login/login_logic.dart';
import 'package:openim/pages/mine/edit_my_info/edit_my_info_logic.dart';
import 'package:openim/routes/app_navigator.dart';
import 'package:openim_common/openim_common.dart';
import '../../../core/controller/im_controller.dart';
class MyInfoLogic extends GetxController {
final imLogic = Get.find<IMController>();
final loginType = LoginType.fromRawValue(DataSp.getLoginType());
void editMyName() => AppNavigator.startEditMyInfo();
void editEnglishName() => AppNavigator.startEditMyInfo(
attr: EditAttr.englishName,
);
void editTel() => AppNavigator.startEditMyInfo(
attr: EditAttr.telephone,
);
void editMobile() => AppNavigator.startEditMyInfo(
attr: EditAttr.mobile,
);
void editEmail() => AppNavigator.startEditMyInfo(attr: EditAttr.email, maxLength: 30);
void openPhotoSheet() {
IMViews.openPhotoSheet(
onData: (path, url) async {
if (url != null) {
LoadingView.singleton.wrap(
asyncFunction: () => Apis.updateUserInfo(userID: OpenIM.iMManager.userID, faceURL: url)
.then((value) => imLogic.userInfo.update((val) {
val?.faceURL = url;
})),
);
}
},
quality: 15);
}
void openDatePicker() {
var appLocale = Get.locale;
var isZh = appLocale!.languageCode.toLowerCase().contains("zh");
DatePicker.showDatePicker(
Get.context!,
locale: isZh ? LocaleType.zh : LocaleType.en,
maxTime: DateTime.now(),
currentTime: DateTime.fromMillisecondsSinceEpoch(imLogic.userInfo.value.birth ?? 0),
theme: DatePickerTheme(
cancelStyle: Styles.ts_0C1C33_17sp,
doneStyle: Styles.ts_0089FF_17sp,
itemStyle: Styles.ts_0C1C33_17sp,
),
onConfirm: (dateTime) {
_updateBirthday(dateTime.millisecondsSinceEpoch ~/ 1000);
},
);
}
void selectGender() {
Get.bottomSheet(
BottomSheetView(
items: [
SheetItem(
label: StrRes.man,
onTap: () => _updateGender(1),
),
SheetItem(
label: StrRes.woman,
onTap: () => _updateGender(2),
),
],
),
);
}
void _updateGender(int gender) {
LoadingView.singleton.wrap(
asyncFunction: () => Apis.updateUserInfo(userID: OpenIM.iMManager.userID, gender: gender)
.then((value) => imLogic.userInfo.update((val) {
val?.gender = gender;
})),
);
}
void _updateBirthday(int birthday) {
LoadingView.singleton.wrap(
asyncFunction: () => Apis.updateUserInfo(
userID: OpenIM.iMManager.userID,
birth: birthday * 1000,
).then((value) => imLogic.userInfo.update((val) {
val?.birth = birthday * 1000;
})),
);
}
@override
void onReady() {
_queryMyFullIno();
super.onReady();
}
@override
void onClose() {
super.onClose();
}
void _queryMyFullIno() async {
final info = await LoadingView.singleton.wrap(
asyncFunction: () => Apis.queryMyFullInfo(),
);
if (null != info) {
imLogic.userInfo.update((val) {
val?.nickname = info.nickname;
val?.faceURL = info.faceURL;
val?.gender = info.gender;
val?.phoneNumber = info.phoneNumber;
val?.birth = info.birth;
val?.email = info.email;
});
}
}
static _trimNullStr(String? value) => IMUtils.emptyStrToNull(value);
}
@@ -0,0 +1,133 @@
import 'package:common_utils/common_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import '../../../core/controller/im_controller.dart';
import 'my_info_logic.dart';
class MyInfoPage extends StatelessWidget {
final logic = Get.find<MyInfoLogic>();
final imLogic = Get.find<IMController>();
MyInfoPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: TitleBar.back(
title: StrRes.myInfo,
),
backgroundColor: Styles.c_F8F9FA,
body: Obx(() => SingleChildScrollView(
child: Column(
children: [
10.verticalSpace,
_buildCornerBgView(
children: [
_buildItemView(
label: StrRes.avatar,
isAvatar: true,
value: imLogic.userInfo.value.nickname,
url: imLogic.userInfo.value.faceURL,
onTap: logic.openPhotoSheet,
),
_buildItemView(
label: StrRes.name,
value: imLogic.userInfo.value.nickname,
onTap: logic.editMyName,
),
_buildItemView(
label: StrRes.gender,
value: imLogic.userInfo.value.isMale ? StrRes.man : StrRes.woman,
onTap: logic.selectGender,
),
_buildItemView(
label: StrRes.birthDay,
value: DateUtil.formatDateMs(
imLogic.userInfo.value.birth ?? 0,
format: IMUtils.getTimeFormat1(),
),
onTap: logic.openDatePicker,
),
],
),
10.verticalSpace,
_buildCornerBgView(
children: [
_buildItemView(
label: StrRes.mobile,
value: imLogic.userInfo.value.phoneNumber,
showRightArrow: false,
),
_buildItemView(
label: StrRes.email,
value: imLogic.userInfo.value.email,
onTap: logic.editEmail,
),
],
),
],
),
)),
);
}
Widget _buildCornerBgView({required List<Widget> children}) => Container(
padding: EdgeInsets.symmetric(horizontal: 16.w),
margin: EdgeInsets.symmetric(horizontal: 10.w),
decoration: BoxDecoration(
color: Styles.c_FFFFFF,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(6.r),
topRight: Radius.circular(6.r),
bottomLeft: Radius.circular(6.r),
bottomRight: Radius.circular(6.r),
),
),
child: Column(children: children),
);
Widget _buildItemView({
required String label,
String? value,
String? url,
bool isAvatar = false,
bool showRightArrow = true,
Function()? onTap,
}) =>
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: showRightArrow ? onTap : null,
child: SizedBox(
height: 46.h,
child: Row(
children: [
label.toText..style = Styles.ts_0C1C33_17sp,
const Spacer(),
if (isAvatar)
AvatarView(
width: 32.w,
height: 32.h,
url: url,
text: value,
textStyle: Styles.ts_FFFFFF_10sp,
)
else
Expanded(
flex: 3,
child: (IMUtils.emptyStrToNull(value) ?? '').toText
..style = Styles.ts_0C1C33_17sp
..maxLines = 1
..overflow = TextOverflow.ellipsis
..textAlign = TextAlign.right),
if (showRightArrow)
ImageRes.rightArrow.toImage
..width = 24.w
..height = 24.h,
],
),
),
);
}
@@ -0,0 +1,10 @@
import 'package:get/get.dart';
import 'my_qrcode_logic.dart';
class MyQrcodeBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => MyQrcodeLogic());
}
}
@@ -0,0 +1,12 @@
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import '../../../core/controller/im_controller.dart';
class MyQrcodeLogic extends GetxController {
final imLogic = Get.find<IMController>();
String buildQRContent() {
return '${Config.friendScheme}${imLogic.userInfo.value.userID}';
}
}
@@ -0,0 +1,95 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'my_qrcode_logic.dart';
class MyQrcodePage extends StatelessWidget {
final logic = Get.find<MyQrcodeLogic>();
MyQrcodePage({super.key});
@override
Widget build(BuildContext context) {
return Obx(
() => Scaffold(
appBar: TitleBar.back(
title: StrRes.qrcode,
),
backgroundColor: Styles.c_F8F9FA,
body: Container(
alignment: Alignment.topCenter,
child: Container(
margin: EdgeInsets.only(top: 22.h),
padding: EdgeInsets.symmetric(horizontal: 30.w),
width: 331.w,
height: 460.h,
decoration: BoxDecoration(
color: Styles.c_FFFFFF,
borderRadius: BorderRadius.circular(10.r),
boxShadow: [
BoxShadow(
blurRadius: 7.r,
spreadRadius: 1.r,
color: Styles.c_000000.withOpacity(.08),
),
],
),
child: Stack(
children: [
Positioned(
top: 30.h,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
AvatarView(
width: 48.w,
height: 48.h,
url: logic.imLogic.userInfo.value.faceURL,
text: logic.imLogic.userInfo.value.nickname,
textStyle: Styles.ts_FFFFFF_14sp,
),
12.horizontalSpace,
(logic.imLogic.userInfo.value.nickname ?? '').toText..style = Styles.ts_0C1C33_20sp,
],
),
),
Positioned(
top: 140.h,
width: 272.w,
child: Container(
alignment: Alignment.center,
child: Column(
children: [
StrRes.qrcodeHint.toText
..style = Styles.ts_8E9AB0_15sp
..textAlign = TextAlign.center,
20.verticalSpace,
Container(
width: 180.w,
height: 180.w,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Styles.c_FFFFFF,
border: Border.all(color: Styles.c_E8EAEF, width: 4.w),
),
child: QrImageView(
data: logic.buildQRContent(),
size: 140.w,
backgroundColor: Styles.c_FFFFFF,
),
),
],
),
),
)
],
),
),
),
),
);
}
}
@@ -0,0 +1,10 @@
import 'package:get/get.dart';
import 'unlock_setup_logic.dart';
class UnlockSetupBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => UnlockSetupLogic());
}
}
@@ -0,0 +1,95 @@
import 'package:flutter/material.dart';
import 'package:flutter_screen_lock/flutter_screen_lock.dart';
import 'package:get/get.dart';
import 'package:local_auth/local_auth.dart';
import 'package:openim_common/openim_common.dart';
class UnlockSetupLogic extends GetxController {
final passwordEnabled = false.obs;
final fingerprintEnabled = false.obs;
final gestureEnabled = false.obs;
final biometricsEnabled = false.obs;
final isSupportedBiometric = false.obs;
final canCheckBiometrics = false.obs;
String? lockScreenPwd;
final auth = LocalAuthentication();
late List<BiometricType> availableBiometrics;
@override
void onInit() {
checkingSupported();
lockScreenPwd = DataSp.getLockScreenPassword();
biometricsEnabled.value = DataSp.isEnabledBiometric() == true;
passwordEnabled.value = lockScreenPwd != null;
super.onInit();
}
void checkingSupported() async {
isSupportedBiometric.value = await auth.isDeviceSupported();
canCheckBiometrics.value = await auth.canCheckBiometrics;
availableBiometrics = await auth.getAvailableBiometrics();
if (availableBiometrics.isNotEmpty) {}
if (availableBiometrics.contains(BiometricType.strong) || availableBiometrics.contains(BiometricType.weak)) {}
}
void toggleBiometricLock() async {
if (biometricsEnabled.value) {
await DataSp.closeBiometric();
biometricsEnabled.value = false;
} else {
final didAuthenticate = await IMUtils.checkingBiometric(auth);
if (didAuthenticate) {
await DataSp.openBiometric();
biometricsEnabled.value = true;
}
}
}
void togglePwdLock() {
if (passwordEnabled.value) {
closePwdLock();
} else {
openPwdLock();
}
}
void closePwdLock() {
screenLock(
context: Get.context!,
correctString: lockScreenPwd!,
title: StrRes.plsEnterPwd.toText..style = Styles.ts_FFFFFF_17sp,
onUnlocked: () async {
await DataSp.clearLockScreenPassword();
await DataSp.closeBiometric();
passwordEnabled.value = false;
biometricsEnabled.value = false;
Get.back();
},
);
}
void openPwdLock() {
final controller = InputController();
screenLockCreate(
context: Get.context!,
inputController: controller,
title: StrRes.plsEnterNewPwd.toText..style = Styles.ts_FFFFFF_17sp,
confirmTitle: StrRes.plsConfirmNewPwd.toText..style = Styles.ts_FFFFFF_17sp,
cancelButton: StrRes.cancel.toText..style = Styles.ts_FFFFFF_17sp,
onConfirmed: (matchedText) async {
lockScreenPwd = matchedText;
await DataSp.putLockScreenPassword(matchedText);
passwordEnabled.value = true;
Get.back();
},
footer: TextButton(
onPressed: () {
controller.unsetConfirmed();
},
child: StrRes.reset.toText..style = Styles.ts_0089FF_17sp,
),
);
}
}
@@ -0,0 +1,82 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import 'unlock_setup_logic.dart';
class UnlockSetupPage extends StatelessWidget {
final logic = Get.find<UnlockSetupLogic>();
UnlockSetupPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: TitleBar.back(title: StrRes.unlockSettings),
backgroundColor: Styles.c_F8F9FA,
body: Obx(() => Column(
children: [
12.verticalSpace,
_buildItemView(
label: StrRes.password,
switchOn: logic.passwordEnabled.value,
onChanged: (_) => logic.togglePwdLock(),
isTopRadius: true,
),
if (logic.passwordEnabled.value &&
(logic.isSupportedBiometric.value &&
logic.canCheckBiometrics.value))
Container(
margin: EdgeInsets.only(left: 26.w, right: 10.w),
color: Styles.c_E8EAEF,
height: .5,
),
if (logic.passwordEnabled.value &&
(logic.isSupportedBiometric.value &&
logic.canCheckBiometrics.value))
_buildItemView(
label: StrRes.biometrics,
switchOn: logic.biometricsEnabled.value,
onChanged: (_) => logic.toggleBiometricLock(),
isBottomRadius: true,
),
],
)),
);
}
Widget _buildItemView({
required String label,
bool isTopRadius = false,
bool isBottomRadius = false,
bool switchOn = false,
ValueChanged<bool>? onChanged,
}) =>
Container(
height: 60.h,
padding: EdgeInsets.symmetric(horizontal: 16.w),
margin: EdgeInsets.symmetric(horizontal: 10.w),
decoration: BoxDecoration(
color: Styles.c_FFFFFF,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(isTopRadius ? 6.r : 0),
topRight: Radius.circular(isTopRadius ? 6.r : 0),
bottomRight: Radius.circular(isBottomRadius ? 6.r : 0),
bottomLeft: Radius.circular(isBottomRadius ? 6.r : 0),
),
),
child: Row(
children: [
label.toText..style = Styles.ts_0C1C33_17sp,
const Spacer(),
CupertinoSwitch(
value: switchOn,
activeColor: Styles.c_0089FF,
onChanged: onChanged,
),
],
),
);
}