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:
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user