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 'forget_password_logic.dart';
class ForgetPasswordBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => ForgetPasswordLogic());
}
}
@@ -0,0 +1,80 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:openim/routes/app_navigator.dart';
import 'package:openim_common/openim_common.dart';
import '../login/login_logic.dart';
class ForgetPasswordLogic extends GetxController {
final phoneCtrl = TextEditingController();
final verificationCodeCtrl = TextEditingController();
final areaCode = "+86".obs;
final enabled = false.obs;
final loginController = Get.find<LoginLogic>();
String? get email => loginController.operateType == LoginType.email ? phoneCtrl.text.trim() : null;
String? get phone => loginController.operateType == LoginType.phone ? phoneCtrl.text.trim() : null;
@override
void onClose() {
phoneCtrl.dispose();
verificationCodeCtrl.dispose();
super.onClose();
}
@override
void onInit() {
phoneCtrl.addListener(_onChanged);
verificationCodeCtrl.addListener(_onChanged);
super.onInit();
}
_onChanged() {
enabled.value = phoneCtrl.text.trim().isNotEmpty && verificationCodeCtrl.text.trim().isNotEmpty;
}
void openCountryCodePicker() async {
String? code = await IMViews.showCountryCodePicker();
if (null != code) areaCode.value = code;
}
Future<bool> getVerificationCode() async {
if (phone?.isNotEmpty == true && !IMUtils.isMobile(areaCode.value, phoneCtrl.text)) {
IMViews.showToast(StrRes.plsEnterRightPhone);
return false;
}
if (email?.isNotEmpty == true && !phoneCtrl.text.isEmail) {
IMViews.showToast(StrRes.plsEnterRightEmail);
return false;
}
final success = await sendVerificationCode();
return success;
}
Future<bool> sendVerificationCode() => LoadingView.singleton.wrap(
asyncFunction: () => Apis.requestVerificationCode(
areaCode: areaCode.value,
phoneNumber: phone,
email: email,
usedFor: 2,
));
checkVerificationCode() => LoadingView.singleton.wrap(
asyncFunction: () => Apis.checkVerificationCode(
areaCode: areaCode.value,
phoneNumber: phone,
email: email,
verificationCode: verificationCodeCtrl.text,
usedFor: 2,
));
void nextStep() async {
await checkVerificationCode();
AppNavigator.startResetPassword(
areaCode: areaCode.value,
phoneNumber: phone,
email: email,
verificationCode: verificationCodeCtrl.text,
);
}
}
@@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:openim/pages/login/login_logic.dart';
import 'package:openim/widgets/register_page_bg.dart';
import 'package:openim_common/openim_common.dart';
import 'forget_password_logic.dart';
class ForgetPasswordPage extends StatelessWidget {
final logic = Get.find<ForgetPasswordLogic>();
ForgetPasswordPage({super.key});
@override
Widget build(BuildContext context) => RegisterBgView(
child: Obx(() => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
StrRes.forgetPassword.toText..style = Styles.ts_0089FF_22sp_semibold,
29.verticalSpace,
InputBox.account(
label: logic.loginController.operateType.name,
hintText: logic.loginController.operateType.hintText,
code: logic.areaCode.value,
onAreaCode: logic.loginController.operateType == LoginType.phone ? logic.openCountryCodePicker : null,
controller: logic.phoneCtrl,
),
16.verticalSpace,
InputBox.verificationCode(
label: StrRes.verificationCode,
hintText: StrRes.plsEnterVerificationCode,
controller: logic.verificationCodeCtrl,
onSendVerificationCode: logic.getVerificationCode,
),
130.verticalSpace,
Button(
text: StrRes.nextStep,
enabled: logic.enabled.value,
onTap: logic.nextStep,
),
],
)),
);
}
@@ -0,0 +1,10 @@
import 'package:get/get.dart';
import 'reset_password_logic.dart';
class ResetPasswordBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => ResetPasswordLogic());
}
}
@@ -0,0 +1,66 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:openim/routes/app_navigator.dart';
import 'package:openim_common/openim_common.dart';
class ResetPasswordLogic extends GetxController {
final pwdCtrl = TextEditingController();
final pwdAgainCtrl = TextEditingController();
final enabled = false.obs;
String? phoneNumber;
String? email;
late String areaCode;
late int usedFor;
late String verificationCode;
String? invitationCode;
@override
void onClose() {
pwdCtrl.dispose();
pwdAgainCtrl.dispose();
super.onClose();
}
@override
void onInit() {
phoneNumber = Get.arguments['phoneNumber'];
email = Get.arguments['email'];
areaCode = Get.arguments['areaCode'];
verificationCode = Get.arguments['verificationCode'];
pwdCtrl.addListener(_onChanged);
pwdAgainCtrl.addListener(_onChanged);
super.onInit();
}
_onChanged() {
enabled.value = pwdCtrl.text.trim().isNotEmpty && pwdAgainCtrl.text.trim().isNotEmpty;
}
bool _checkingInput() {
if (!IMUtils.isValidPassword(pwdCtrl.text)) {
IMViews.showToast(StrRes.wrongPasswordFormat);
return false;
} else if (pwdCtrl.text != pwdAgainCtrl.text) {
IMViews.showToast(StrRes.twicePwdNoSame);
return false;
}
return true;
}
resetPassword() => LoadingView.singleton.wrap(
asyncFunction: () => Apis.resetPassword(
areaCode: areaCode,
phoneNumber: phoneNumber,
email: email,
password: pwdCtrl.text,
verificationCode: verificationCode,
));
confirmTheChanges() async {
if (_checkingInput()) {
await resetPassword();
IMViews.showToast(StrRes.changedSuccessfully);
AppNavigator.startBackLogin();
}
}
}
@@ -0,0 +1,45 @@
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 '../../../widgets/register_page_bg.dart';
import 'reset_password_logic.dart';
class ResetPasswordPage extends StatelessWidget {
final logic = Get.find<ResetPasswordLogic>();
ResetPasswordPage({super.key});
@override
Widget build(BuildContext context) => RegisterBgView(
child: Obx(() => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
StrRes.forgetPassword.toText
..style = Styles.ts_0089FF_22sp_semibold,
29.verticalSpace,
InputBox.password(
label: StrRes.password,
hintText: StrRes.plsEnterPassword,
controller: logic.pwdCtrl,
formatHintText: StrRes.loginPwdFormat,
inputFormatters: [IMUtils.getPasswordFormatter()],
),
17.verticalSpace,
InputBox.password(
label: StrRes.confirmPassword,
hintText: StrRes.plsConfirmPasswordAgain,
controller: logic.pwdAgainCtrl,
inputFormatters: [IMUtils.getPasswordFormatter()],
),
129.verticalSpace,
Button(
text: StrRes.confirmTheChanges,
enabled: logic.enabled.value,
onTap: logic.confirmTheChanges,
),
],
)),
);
}