feat: 验证码登录、密码登录页UI开发
This commit is contained in:
parent
d858805563
commit
422e6a0291
20
lib/base/base_api_provider.dart
Normal file
20
lib/base/base_api_provider.dart
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:starwork_flutter/flavors.dart';
|
||||||
|
|
||||||
|
class BaseApiProvider extends GetConnect {
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
httpClient.baseUrl = F.apiHost;
|
||||||
|
|
||||||
|
// 请求拦截
|
||||||
|
httpClient.addRequestModifier<void>((request) {
|
||||||
|
request.headers['Authorization'] = '12345678';
|
||||||
|
return request;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 响应拦截
|
||||||
|
httpClient.addResponseModifier((request, response) {
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
5
lib/common/enums/login_type.dart
Normal file
5
lib/common/enums/login_type.dart
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
enum LoginType {
|
||||||
|
phoneCode,
|
||||||
|
phonePassword;
|
||||||
|
|
||||||
|
}
|
||||||
@ -29,4 +29,21 @@ class F {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String get apiHost {
|
||||||
|
switch (appFlavor) {
|
||||||
|
case Flavor.skyDev:
|
||||||
|
return 'https://loacl.work.star-lock.cn/api';
|
||||||
|
case Flavor.skyPre:
|
||||||
|
return 'https://loacl.work.star-lock.cn/api';
|
||||||
|
case Flavor.skyRelease:
|
||||||
|
return 'https://loacl.work.star-lock.cn/api';
|
||||||
|
case Flavor.xhjDev:
|
||||||
|
return 'https://loacl.work.star-lock.cn/api';
|
||||||
|
case Flavor.xhjPre:
|
||||||
|
return 'https://loacl.work.star-lock.cn/api';
|
||||||
|
case Flavor.xhjRelease:
|
||||||
|
return 'https://loacl.work.star-lock.cn/api';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import '../flavors.dart';
|
|
||||||
|
|
||||||
class MyHomePage extends StatelessWidget {
|
|
||||||
const MyHomePage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(title: Text(F.title)),
|
|
||||||
body: Center(child: Text('Hello ${F.title}')),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -2,6 +2,12 @@ import 'package:get/get.dart';
|
|||||||
import 'package:starwork_flutter/routes/app_routes.dart';
|
import 'package:starwork_flutter/routes/app_routes.dart';
|
||||||
import 'package:starwork_flutter/views/home/home_binding.dart';
|
import 'package:starwork_flutter/views/home/home_binding.dart';
|
||||||
import 'package:starwork_flutter/views/home/home_view.dart';
|
import 'package:starwork_flutter/views/home/home_view.dart';
|
||||||
|
import 'package:starwork_flutter/views/login/forgotPassword/forgot_password_binding.dart';
|
||||||
|
import 'package:starwork_flutter/views/login/forgotPassword/forgot_password_view.dart';
|
||||||
|
import 'package:starwork_flutter/views/login/forgotPassword/setNewPassword/set_new_password_binding.dart';
|
||||||
|
import 'package:starwork_flutter/views/login/forgotPassword/setNewPassword/set_new_password_view.dart';
|
||||||
|
import 'package:starwork_flutter/views/login/inputVerificationCode/input_verification_code_binding.dart';
|
||||||
|
import 'package:starwork_flutter/views/login/inputVerificationCode/input_verification_code_view.dart';
|
||||||
import 'package:starwork_flutter/views/login/login_binding.dart';
|
import 'package:starwork_flutter/views/login/login_binding.dart';
|
||||||
import 'package:starwork_flutter/views/login/login_view.dart';
|
import 'package:starwork_flutter/views/login/login_view.dart';
|
||||||
import 'package:starwork_flutter/views/main/main_binding.dart';
|
import 'package:starwork_flutter/views/main/main_binding.dart';
|
||||||
@ -39,5 +45,20 @@ class AppPages {
|
|||||||
page: () => const MineView(),
|
page: () => const MineView(),
|
||||||
binding: MineBinding(),
|
binding: MineBinding(),
|
||||||
),
|
),
|
||||||
|
GetPage(
|
||||||
|
name: AppRoutes.inputVerificationCode,
|
||||||
|
page: () => const InputVerificationCodeView(),
|
||||||
|
binding: InputVerificationCodeBinding(),
|
||||||
|
),
|
||||||
|
GetPage(
|
||||||
|
name: AppRoutes.forgotPassword,
|
||||||
|
page: () => const ForgotPasswordView(),
|
||||||
|
binding: ForgotPasswordBinding(),
|
||||||
|
),
|
||||||
|
GetPage(
|
||||||
|
name: AppRoutes.setNewPassword,
|
||||||
|
page: () => const SetNewPasswordView(),
|
||||||
|
binding: SetNewPasswordBinding(),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,4 +5,7 @@ class AppRoutes{
|
|||||||
static const String main = '/main';
|
static const String main = '/main';
|
||||||
static const String messages = '/messages';
|
static const String messages = '/messages';
|
||||||
static const String mine = '/mine';
|
static const String mine = '/mine';
|
||||||
|
static const String inputVerificationCode = '/inputVerificationCode';
|
||||||
|
static const String forgotPassword = '/forgotPassword';
|
||||||
|
static const String setNewPassword = '/setNewPassword';
|
||||||
}
|
}
|
||||||
11
lib/views/login/forgotPassword/forgot_password_binding.dart
Normal file
11
lib/views/login/forgotPassword/forgot_password_binding.dart
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:starwork_flutter/views/login/forgotPassword/forgot_password_controller.dart';
|
||||||
|
|
||||||
|
class ForgotPasswordBinding extends Bindings {
|
||||||
|
@override
|
||||||
|
void dependencies() {
|
||||||
|
Get.lazyPut<ForgotPasswordController>(
|
||||||
|
() => ForgotPasswordController(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:starwork_flutter/base/base_controller.dart';
|
||||||
|
import 'package:starwork_flutter/routes/app_routes.dart';
|
||||||
|
import 'package:starwork_flutter/views/login/login_controller.dart';
|
||||||
|
|
||||||
|
class ForgotPasswordController extends BaseController {
|
||||||
|
// 在任意位置获取已注入的实例
|
||||||
|
final loginController = Get.find<LoginController>();
|
||||||
|
|
||||||
|
// 获取手机验证码
|
||||||
|
void requestPhoneCode() {
|
||||||
|
Get.toNamed(
|
||||||
|
AppRoutes.inputVerificationCode,
|
||||||
|
parameters: {
|
||||||
|
'phone': loginController.phoneController.text.trim(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
102
lib/views/login/forgotPassword/forgot_password_view.dart
Normal file
102
lib/views/login/forgotPassword/forgot_password_view.dart
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/src/widgets/framework.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:starwork_flutter/views/login/forgotPassword/forgot_password_controller.dart';
|
||||||
|
|
||||||
|
class ForgotPasswordView extends GetView<ForgotPasswordController> {
|
||||||
|
const ForgotPasswordView({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(),
|
||||||
|
body: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 40.w),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'请输入手机号'.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22.sp,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20.h,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: controller.loginController.phoneController,
|
||||||
|
keyboardType: TextInputType.phone,
|
||||||
|
textInputAction: TextInputAction.done,
|
||||||
|
maxLength: 11,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
counterText: '',
|
||||||
|
hintText: '请输入手机号码'.tr,
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
suffixIcon: controller.loginController.phoneController.text.isNotEmpty
|
||||||
|
? IconButton(
|
||||||
|
icon: const Icon(Icons.clear, color: Colors.grey),
|
||||||
|
onPressed: () {
|
||||||
|
controller.loginController.phoneController.clear(); // 清空输入
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
// 如果没有内容,就不显示清除按钮,
|
||||||
|
// 获取焦点时的边框
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: controller.loginController.isFormValid.value
|
||||||
|
? Colors.blue
|
||||||
|
: Colors.blue.withOpacity(0.5),
|
||||||
|
), //
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 48.h,
|
||||||
|
),
|
||||||
|
Obx(
|
||||||
|
() => ElevatedButton(
|
||||||
|
onPressed: controller.loginController.isFormValid.value
|
||||||
|
? controller.requestPhoneCode
|
||||||
|
: null,
|
||||||
|
style: ButtonStyle(
|
||||||
|
minimumSize:
|
||||||
|
MaterialStateProperty.all(Size(double.infinity, 44.h)),
|
||||||
|
shape: MaterialStateProperty.all(
|
||||||
|
RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8.r),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
backgroundColor: MaterialStateProperty.all(
|
||||||
|
controller.loginController.isFormValid.value
|
||||||
|
? Colors.blue
|
||||||
|
: Colors.blue.withOpacity(0.5),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'获取验证码'.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16.sp,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:starwork_flutter/views/login/forgotPassword/setNewPassword/set_new_password_controller.dart';
|
||||||
|
|
||||||
|
class SetNewPasswordBinding extends Bindings {
|
||||||
|
@override
|
||||||
|
void dependencies() {
|
||||||
|
Get.lazyPut<SetNewPasswordController>(
|
||||||
|
() => SetNewPasswordController(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:starwork_flutter/base/base_controller.dart';
|
||||||
|
|
||||||
|
class SetNewPasswordController extends BaseController {
|
||||||
|
TextEditingController passwordController = TextEditingController();
|
||||||
|
TextEditingController newPasswordController = TextEditingController();
|
||||||
|
final isPasswordVisible = true.obs;
|
||||||
|
final isNewPasswordVisible = true.obs;
|
||||||
|
final isFormValid = false.obs;
|
||||||
|
final isNewFormValid = false.obs;
|
||||||
|
final isAllFormValid = false.obs;
|
||||||
|
|
||||||
|
void setNewPassword(){
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,239 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/src/widgets/framework.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:starwork_flutter/views/login/forgotPassword/setNewPassword/set_new_password_controller.dart';
|
||||||
|
|
||||||
|
class SetNewPasswordView extends GetView<SetNewPasswordController> {
|
||||||
|
const SetNewPasswordView({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 40.w),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'设置新密码'.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22.sp,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20.h,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: controller.passwordController,
|
||||||
|
keyboardType: TextInputType.visiblePassword,
|
||||||
|
textInputAction: TextInputAction.done,
|
||||||
|
obscureText: controller.isPasswordVisible.value,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
counterText: '',
|
||||||
|
hintText: '请输入新密码'.tr,
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
controller.isPasswordVisible.value
|
||||||
|
? Icons.visibility
|
||||||
|
: Icons.visibility_off,
|
||||||
|
),
|
||||||
|
onPressed: () => controller.isPasswordVisible.value =
|
||||||
|
!controller.isPasswordVisible.value,
|
||||||
|
),
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: controller.isFormValid.value
|
||||||
|
? Colors.blue
|
||||||
|
: Colors.blue.withOpacity(0.5),
|
||||||
|
), //
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20.h,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: controller.newPasswordController,
|
||||||
|
keyboardType: TextInputType.visiblePassword,
|
||||||
|
textInputAction: TextInputAction.done,
|
||||||
|
obscureText: controller.isNewPasswordVisible.value,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
counterText: '',
|
||||||
|
hintText: '再次输入新密码'.tr,
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
controller.isNewPasswordVisible.value
|
||||||
|
? Icons.visibility
|
||||||
|
: Icons.visibility_off,
|
||||||
|
),
|
||||||
|
onPressed: () => controller.isNewPasswordVisible.value =
|
||||||
|
!controller.isNewPasswordVisible.value,
|
||||||
|
),
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: controller.isNewFormValid.value
|
||||||
|
? Colors.blue
|
||||||
|
: Colors.blue.withOpacity(0.5),
|
||||||
|
), //
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20.h,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 4.w, vertical: 8.h),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade200,
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(
|
||||||
|
14.r,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Checkbox(
|
||||||
|
value: true,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4.r),
|
||||||
|
),
|
||||||
|
activeColor: Colors.blue,
|
||||||
|
onChanged: (value) {},
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
'8-16位,由大写字母、小写字母、数字、特殊字符任意两种及以上组成',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12.sp,
|
||||||
|
color: Colors.black45,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Checkbox(
|
||||||
|
value: true,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4.r),
|
||||||
|
),
|
||||||
|
activeColor: Colors.blue,
|
||||||
|
onChanged: (value) {},
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
'连续相同的字符不能超过4个',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12.sp,
|
||||||
|
color: Colors.black45,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Checkbox(
|
||||||
|
value: true,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4.r),
|
||||||
|
),
|
||||||
|
activeColor: Colors.blue,
|
||||||
|
onChanged: (value) {},
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
'连续的数字不能超过4个',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12.sp,
|
||||||
|
color: Colors.black45,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Checkbox(
|
||||||
|
value: true,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4.r),
|
||||||
|
),
|
||||||
|
activeColor: Colors.blue,
|
||||||
|
onChanged: (value) {},
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
'不能使用安全性低的密码',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12.sp,
|
||||||
|
color: Colors.black45,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 48.h,
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: controller.isAllFormValid.value
|
||||||
|
? controller.setNewPassword
|
||||||
|
: null,
|
||||||
|
style: ButtonStyle(
|
||||||
|
minimumSize:
|
||||||
|
MaterialStateProperty.all(Size(double.infinity, 44.h)),
|
||||||
|
shape: MaterialStateProperty.all(
|
||||||
|
RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8.r),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
backgroundColor: MaterialStateProperty.all(
|
||||||
|
controller.isFormValid.value
|
||||||
|
? Colors.blue
|
||||||
|
: Colors.blue.withOpacity(0.5),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'提交'.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16.sp,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:starwork_flutter/views/login/inputVerificationCode/input_verification_code_controller.dart';
|
||||||
|
|
||||||
|
class InputVerificationCodeBinding extends Bindings {
|
||||||
|
@override
|
||||||
|
void dependencies() {
|
||||||
|
Get.lazyPut<InputVerificationCodeController>(
|
||||||
|
() => InputVerificationCodeController(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:starwork_flutter/base/base_controller.dart';
|
||||||
|
import 'package:starwork_flutter/routes/app_routes.dart';
|
||||||
|
|
||||||
|
class InputVerificationCodeController extends BaseController {
|
||||||
|
var phone = ''.obs;
|
||||||
|
var previousRoute = ''.obs;
|
||||||
|
|
||||||
|
// 是否正在倒计时
|
||||||
|
var isCountingDown = false.obs;
|
||||||
|
|
||||||
|
// 倒计时剩余秒数
|
||||||
|
var countdownValue = 60.obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
|
||||||
|
final params = Get.parameters;
|
||||||
|
String? phoneParam = params['phone'];
|
||||||
|
// 在目标页面获取来源路由
|
||||||
|
previousRoute.value = Get.previousRoute; // 如 "/login"
|
||||||
|
|
||||||
|
if (phoneParam != null && phoneParam.isNotEmpty) {
|
||||||
|
phone.value = maskMiddleFive(phoneParam);
|
||||||
|
// 开始倒计时(比如60秒)
|
||||||
|
startCountdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String maskMiddleFive(String phone) {
|
||||||
|
if (phone.length < 8) return phone; // 如果手机号少于8位,不处理(或根据需求返回原值/报错)
|
||||||
|
|
||||||
|
// 假设手机号至少有 8 位,我们保留前 3 位,中间 5 位替换为 ****,显示后 3 位
|
||||||
|
// 13812345678 → 138****678
|
||||||
|
String firstPart = phone.substring(0, 3); // 前3位:138
|
||||||
|
String middleReplaced = '*****'; // 中间5位替换为 ****
|
||||||
|
String lastPart = phone.substring(8); // 后3位:678
|
||||||
|
|
||||||
|
return '$firstPart$middleReplaced$lastPart'; // 拼接:138****678
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 开始倒计时(比如60秒)
|
||||||
|
void startCountdown() {
|
||||||
|
if (isCountingDown.value) return; // 避免重复启动
|
||||||
|
|
||||||
|
isCountingDown.value = true;
|
||||||
|
countdownValue.value = 60;
|
||||||
|
|
||||||
|
// 使用 Timer 实现倒计时
|
||||||
|
Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||||
|
if (countdownValue.value > 1) {
|
||||||
|
countdownValue.value--;
|
||||||
|
} else {
|
||||||
|
timer.cancel(); // 停止计时器
|
||||||
|
isCountingDown.value = false;
|
||||||
|
countdownValue.value = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void requestPhoneCodeLogin(String code) {
|
||||||
|
if (previousRoute.value.contains(AppRoutes.login)) {
|
||||||
|
_handleLogin();
|
||||||
|
} else if (previousRoute.value.contains(AppRoutes.forgotPassword)) {
|
||||||
|
_handleForgotPassword();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleLogin() {
|
||||||
|
Get.offAndToNamed(AppRoutes.main);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleForgotPassword() {
|
||||||
|
Get.toNamed(AppRoutes.setNewPassword);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,121 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:pinput/pinput.dart';
|
||||||
|
import 'package:starwork_flutter/views/login/inputVerificationCode/input_verification_code_controller.dart';
|
||||||
|
|
||||||
|
class InputVerificationCodeView
|
||||||
|
extends GetView<InputVerificationCodeController> {
|
||||||
|
const InputVerificationCodeView({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(),
|
||||||
|
body: SafeArea(
|
||||||
|
child: Container(
|
||||||
|
width: 1.sw,
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 40.w,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"请输入验证码",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22.sp,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'${'已发送验证码至'.tr}${controller.phone.value}',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14.sp,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Obx(
|
||||||
|
() => _buildRequestCodeButton(),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 32.h,
|
||||||
|
),
|
||||||
|
// Pinput 验证码输入框
|
||||||
|
Pinput(
|
||||||
|
length: 6,
|
||||||
|
obscureText: false,
|
||||||
|
defaultPinTheme: PinTheme(
|
||||||
|
width: 50.w,
|
||||||
|
height: 50.w,
|
||||||
|
textStyle: TextStyle(fontSize: 20.sp, color: Colors.black),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
border: Border.all(color: Colors.grey),
|
||||||
|
borderRadius: BorderRadius.circular(8.r),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
validator: (value) {
|
||||||
|
if (value!.isEmpty) return '请输入验证码';
|
||||||
|
if (value.length != 6) return '验证码需6位';
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
onCompleted: (pin) {
|
||||||
|
controller.requestPhoneCodeLogin(pin);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_buildRequestCodeButton() {
|
||||||
|
if (controller.isCountingDown.value) {
|
||||||
|
return RichText(
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
text: TextSpan(
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: '如未收到,请'.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14.sp,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: '${controller.countdownValue.value}s'.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14.sp,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: '后重新获取'.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14.sp,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
controller.startCountdown();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
'重新获取验证码'.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14.sp,
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,20 +1,31 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:starwork_flutter/base/base_controller.dart';
|
import 'package:starwork_flutter/base/base_controller.dart';
|
||||||
|
import 'package:starwork_flutter/common/enums/login_type.dart';
|
||||||
|
import 'package:starwork_flutter/routes/app_pages.dart';
|
||||||
|
import 'package:starwork_flutter/routes/app_routes.dart';
|
||||||
|
|
||||||
class LoginController extends BaseController {
|
class LoginController extends BaseController {
|
||||||
int phoneNumberSize = 11;
|
int phoneNumberSize = 11;
|
||||||
|
|
||||||
TextEditingController phoneController = TextEditingController();
|
TextEditingController phoneController = TextEditingController();
|
||||||
|
TextEditingController passwordController = TextEditingController();
|
||||||
|
|
||||||
final isFormValid = false.obs;
|
final isFormValid = false.obs;
|
||||||
|
final isPasswordVisible = true.obs;
|
||||||
|
final isPrivacyAgreementValid = 0.obs;
|
||||||
|
final loginType = LoginType.phoneCode.obs;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
// 监听输入变化
|
// 监听输入变化
|
||||||
phoneController.addListener(_validateForm);
|
phoneController.addListener(_validateForm);
|
||||||
|
|
||||||
|
phoneController.text = '18269109817';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -31,7 +42,158 @@ class LoginController extends BaseController {
|
|||||||
|
|
||||||
// 获取手机验证码
|
// 获取手机验证码
|
||||||
void requestPhoneCode() {
|
void requestPhoneCode() {
|
||||||
debugPrint("获取手机验证码");
|
if (isPrivacyAgreementValid.value != 1) {
|
||||||
showToast("获取手机验证码");
|
_showCustomDialog(
|
||||||
|
title: '欢迎使用星勤'.tr,
|
||||||
|
content: '1',
|
||||||
|
onConfirm: () {
|
||||||
|
isPrivacyAgreementValid.value = 1;
|
||||||
|
Get.toNamed(
|
||||||
|
AppRoutes.inputVerificationCode,
|
||||||
|
parameters: {
|
||||||
|
'phone': phoneController.text.trim(),
|
||||||
|
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Get.toNamed(
|
||||||
|
AppRoutes.inputVerificationCode,
|
||||||
|
parameters: {
|
||||||
|
'phone': phoneController.text.trim(),
|
||||||
|
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showCustomDialog({
|
||||||
|
required String title,
|
||||||
|
required String content,
|
||||||
|
required VoidCallback onConfirm,
|
||||||
|
}) {
|
||||||
|
Get.dialog(
|
||||||
|
Dialog(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(14.r), // 圆角
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 22.h),
|
||||||
|
child: Text(
|
||||||
|
title,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18.sp,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.all(22.w),
|
||||||
|
child: RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: '请你阅读并同意'.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12.sp,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: '《用户协议》'.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12.sp,
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: '和'.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12.sp,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: '《隐私政策》'.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12.sp,
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20.h),
|
||||||
|
Container(
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
top: BorderSide(
|
||||||
|
color: Colors.grey, // 右侧边框颜色
|
||||||
|
width: 0.5, // 右侧边框宽度
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
// 左侧文本占一半
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Get.back();
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
height: 42.h,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
right: BorderSide(
|
||||||
|
color: Colors.grey, // 右侧边框颜色
|
||||||
|
width: 0.5, // 右侧边框宽度
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'取消'.tr,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(fontSize: 14.sp, color: Colors.grey),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
// 右侧文本占一半
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Get.back();
|
||||||
|
onConfirm();
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
height: 42.h,
|
||||||
|
child: Text(
|
||||||
|
'同意并继续'.tr,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14.sp,
|
||||||
|
color: Colors.blue,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:starwork_flutter/common/enums/login_type.dart';
|
||||||
|
import 'package:starwork_flutter/routes/app_routes.dart';
|
||||||
|
|
||||||
import 'login_controller.dart';
|
import 'login_controller.dart';
|
||||||
|
|
||||||
@ -15,6 +19,7 @@ class LoginView extends GetView<LoginController> {
|
|||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
},
|
},
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
|
resizeToAvoidBottomInset: true,
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: _buildBody(),
|
child: _buildBody(),
|
||||||
@ -25,21 +30,28 @@ class LoginView extends GetView<LoginController> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBody() {
|
Widget _buildBody() {
|
||||||
|
return SizedBox(
|
||||||
|
height: 0.9.sh,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
_buildTop(),
|
||||||
|
_buildPrivacyPolicy(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_buildTop() {
|
||||||
return Container(
|
return Container(
|
||||||
height: 1.sh,
|
margin: EdgeInsets.symmetric(horizontal: 40.w, vertical: 40.h),
|
||||||
margin: EdgeInsets.symmetric(vertical: 48.h),
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 32.w),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
_buildTitle(),
|
_buildTitle(),
|
||||||
SizedBox(
|
SizedBox(height: 20.h),
|
||||||
height: 32.h,
|
|
||||||
),
|
|
||||||
_buildPhoneInputAndLoginButton(),
|
_buildPhoneInputAndLoginButton(),
|
||||||
SizedBox(
|
SizedBox(height: 20.h),
|
||||||
height: 32.h,
|
|
||||||
),
|
|
||||||
_buildPrivacyPolicy(),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -58,9 +70,6 @@ class LoginView extends GetView<LoginController> {
|
|||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(
|
|
||||||
height: 4.h,
|
|
||||||
),
|
|
||||||
Text(
|
Text(
|
||||||
'未注册手机号验证后将自动创建账号'.tr,
|
'未注册手机号验证后将自动创建账号'.tr,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@ -87,6 +96,15 @@ class LoginView extends GetView<LoginController> {
|
|||||||
counterText: '',
|
counterText: '',
|
||||||
hintText: '请输入手机号码'.tr,
|
hintText: '请输入手机号码'.tr,
|
||||||
border: const UnderlineInputBorder(),
|
border: const UnderlineInputBorder(),
|
||||||
|
suffixIcon: controller.phoneController.text.isNotEmpty
|
||||||
|
? IconButton(
|
||||||
|
icon: const Icon(Icons.clear, color: Colors.grey),
|
||||||
|
onPressed: () {
|
||||||
|
controller.phoneController.clear(); // 清空输入
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
// 如果没有内容,就不显示清除按钮,
|
||||||
// 获取焦点时的边框
|
// 获取焦点时的边框
|
||||||
focusedBorder: UnderlineInputBorder(
|
focusedBorder: UnderlineInputBorder(
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
@ -94,12 +112,47 @@ class LoginView extends GetView<LoginController> {
|
|||||||
? Colors.blue
|
? Colors.blue
|
||||||
: Colors.blue.withOpacity(0.5),
|
: Colors.blue.withOpacity(0.5),
|
||||||
), //
|
), //
|
||||||
// 🔥 你想要的颜色
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Visibility(
|
||||||
|
visible: controller.loginType.value == LoginType.phonePassword,
|
||||||
|
child: SizedBox(
|
||||||
|
height: 18.h,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Visibility(
|
||||||
|
visible: controller.loginType.value == LoginType.phonePassword,
|
||||||
|
child: TextField(
|
||||||
|
controller: controller.passwordController,
|
||||||
|
keyboardType: TextInputType.visiblePassword,
|
||||||
|
textInputAction: TextInputAction.done,
|
||||||
|
obscureText: controller.isPasswordVisible.value,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
counterText: '',
|
||||||
|
hintText: '请输入密码'.tr,
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
controller.isPasswordVisible.value
|
||||||
|
? Icons.visibility
|
||||||
|
: Icons.visibility_off,
|
||||||
|
),
|
||||||
|
onPressed: () => controller.isPasswordVisible.value =
|
||||||
|
!controller.isPasswordVisible.value,
|
||||||
|
),
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: controller.isFormValid.value
|
||||||
|
? Colors.blue
|
||||||
|
: Colors.blue.withOpacity(0.5),
|
||||||
|
), //
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 24.h,
|
height: 48.h,
|
||||||
),
|
),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: controller.isFormValid.value
|
onPressed: controller.isFormValid.value
|
||||||
@ -120,7 +173,9 @@ class LoginView extends GetView<LoginController> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'获取验证码'.tr,
|
controller.loginType.value == LoginType.phoneCode
|
||||||
|
? '获取验证码'.tr
|
||||||
|
: '登录'.tr,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16.sp,
|
fontSize: 16.sp,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
@ -128,35 +183,157 @@ class LoginView extends GetView<LoginController> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(height: 20.h),
|
||||||
height: 22.h,
|
Row(
|
||||||
),
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
TextButton(
|
children: [
|
||||||
onPressed: () {},
|
Visibility(
|
||||||
child: Text(
|
visible: LoginType.phoneCode == controller.loginType.value,
|
||||||
'密码登录'.tr,
|
child: TextButton(
|
||||||
style: TextStyle(
|
onPressed: () {
|
||||||
fontSize: 14.sp,
|
controller.loginType.value = LoginType.phonePassword;
|
||||||
fontWeight: FontWeight.w500,
|
},
|
||||||
color: Colors.grey,
|
child: Text(
|
||||||
|
'密码登录'.tr,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16.sp,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
Visibility(
|
||||||
|
visible: LoginType.phonePassword == controller.loginType.value,
|
||||||
|
child: TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
controller.loginType.value = LoginType.phoneCode;
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
'验证码登录'.tr,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16.sp,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Visibility(
|
||||||
|
visible: LoginType.phonePassword == controller.loginType.value,
|
||||||
|
child: Text(
|
||||||
|
'|',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16.sp,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Visibility(
|
||||||
|
visible: LoginType.phonePassword == controller.loginType.value,
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
overlayColor: MaterialStateProperty.all(Colors.transparent), // 墨水覆盖
|
||||||
|
side: MaterialStateProperty.all(null), // 取消边框
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Get.toNamed(
|
||||||
|
AppRoutes.forgotPassword,
|
||||||
|
parameters: {
|
||||||
|
'phone': controller.phoneController.text,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
'忘记密码'.tr,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16.sp,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_handleLoginButtonText() {
|
||||||
|
if (controller.loginType.value == LoginType.phoneCode) {
|
||||||
|
return;
|
||||||
|
} else if (controller.loginType.value == LoginType.phonePassword) {
|
||||||
|
return '验证码登录'.tr + ' | '.tr + '忘记密码'.tr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_buildPrivacyPolicy() {
|
_buildPrivacyPolicy() {
|
||||||
return Row(
|
return GestureDetector(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
onTap: () {
|
||||||
children: [
|
controller.isPrivacyAgreementValid.value =
|
||||||
Radio(
|
controller.isPrivacyAgreementValid.value == 1 ? 0 : 1;
|
||||||
value: '1',
|
},
|
||||||
groupValue: 1,
|
child: Row(
|
||||||
onChanged: (value) {},
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
),
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
],
|
children: [
|
||||||
|
Obx(
|
||||||
|
() => Checkbox(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4.r),
|
||||||
|
),
|
||||||
|
activeColor: Colors.blue,
|
||||||
|
value: controller.isPrivacyAgreementValid.value == 1,
|
||||||
|
// 转换为 bool
|
||||||
|
onChanged: (bool? value) {
|
||||||
|
controller.isPrivacyAgreementValid.value =
|
||||||
|
value == true ? 1 : 0;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
RichText(
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
text: TextSpan(
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: '我已阅读并同意'.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13.sp,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: '《用户协议》'.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13.sp,
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: '和'.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13.sp,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: '《隐私政策》'.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13.sp,
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
pubspec.lock
16
pubspec.lock
@ -341,6 +341,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.2"
|
version: "6.0.2"
|
||||||
|
pinput:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: pinput
|
||||||
|
sha256: "8a73be426a91fefec90a7f130763ca39772d547e92f19a827cf4aa02e323d35a"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.1"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -490,6 +498,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.2"
|
version: "1.3.2"
|
||||||
|
universal_platform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: universal_platform
|
||||||
|
sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@ -27,6 +27,8 @@ dependencies:
|
|||||||
flutter_screenutil: ^5.9.3
|
flutter_screenutil: ^5.9.3
|
||||||
# 提示
|
# 提示
|
||||||
fluttertoast: ^8.2.8
|
fluttertoast: ^8.2.8
|
||||||
|
# 验证码输入框
|
||||||
|
pinput: ^5.0.1
|
||||||
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user