提交 de62e122 authored 作者: yueweilu's avatar yueweilu

登录界面

上级 e695958e
...@@ -5,18 +5,75 @@ enum LoginType{code, password} ...@@ -5,18 +5,75 @@ enum LoginType{code, password}
class LoginController extends GetxController { class LoginController extends GetxController {
/// 2、密码登录 // 0:密码登录 1:验证码登录
/// 账号 late int loginType = 0;
// 2、密码登录
// 账号
final TextEditingController phoneInput = TextEditingController(); final TextEditingController phoneInput = TextEditingController();
/// 密码 // 密码
final TextEditingController passwordInput = TextEditingController(); final TextEditingController passwordInput = TextEditingController();
// 验证码
final TextEditingController codeInput = TextEditingController();
// 显示密码 // 显示密码
final ValueNotifier<bool> showPassword = ValueNotifier(false); final ValueNotifier<bool> showPassword = ValueNotifier(false);
// 定时器
late Timer _timer;
// 按钮是否可用
bool _enable = false;
bool get enable => _enable;
// 倒计时60秒
int _countDown = 60;
int get countDown => _countDown;
bool _isCounting = false;
bool get isCounting => _isCounting;
// 同意协议
bool _agree = false;
bool get agree => _agree;
void setAgree() {
_agree = !_agree;
}
// 开启定时器
void start() {
_isCounting = true;
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (_countDown > 1) {
_countDown--;
} else {
stop();
}
update();
});
}
// 停止计时器
void stop() {
if (_timer != null){
_timer.cancel();
_isCounting = false;
}
_countDown = 60;
}
void setCanClick(){
if (phoneInput.text.length == 11 && passwordInput.text.length == 4){
_enable = true;
}
else{
_enable = false;
}
update();
}
@override @override
void onInit() { void onInit() {
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
// 定时器回调
});
/// 测试账号 /// 测试账号
if (kDebugMode) { if (kDebugMode) {
phoneInput.text = '13521054068'; phoneInput.text = '13521054068';
...@@ -30,11 +87,20 @@ class LoginController extends GetxController { ...@@ -30,11 +87,20 @@ class LoginController extends GetxController {
phoneInput.dispose(); phoneInput.dispose();
passwordInput.dispose(); passwordInput.dispose();
showPassword.dispose(); showPassword.dispose();
codeInput.dispose();
_timer.cancel();
_isCounting = false;
super.onClose(); super.onClose();
} }
void onShowPassword() => showPassword.value = !showPassword.value; void onShowPassword() => showPassword.value = !showPassword.value;
void updateLoginType(int value){
stop();
loginType = value;
update();
}
Future<bool> onLogin(GlobalKey<FormState> key) async { Future<bool> onLogin(GlobalKey<FormState> key) async {
final result = AccountAPI.login( final result = AccountAPI.login(
......
library login; library login;
import 'dart:async';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
......
...@@ -11,6 +11,8 @@ class LoginPage extends StatefulWidget { ...@@ -11,6 +11,8 @@ class LoginPage extends StatefulWidget {
class _LoginPageState extends State<LoginPage> { class _LoginPageState extends State<LoginPage> {
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
bool isChecked = false; bool isChecked = false;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GetBuilder<LoginController>( return GetBuilder<LoginController>(
...@@ -33,10 +35,18 @@ class _LoginPageState extends State<LoginPage> { ...@@ -33,10 +35,18 @@ class _LoginPageState extends State<LoginPage> {
right: 25, right: 25,
bottom: 25 bottom: 25
), ),
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Form( child: Form(
autovalidateMode: AutovalidateMode.always,
onChanged: (){
setState(() {
print('++++++++++++++++');
});
},
key: _formKey, key: _formKey,
child: Container(
// color: Colors.green,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
...@@ -51,15 +61,19 @@ class _LoginPageState extends State<LoginPage> { ...@@ -51,15 +61,19 @@ class _LoginPageState extends State<LoginPage> {
color: Colors.cyan, color: Colors.cyan,
), ),
const SizedBox(height:66), const SizedBox(height:66),
//
CustomFormInput( CustomFormInput(
// label: 'Phone', // label: 'Phone',
// required: true, // required: true,
hintText: '请输入手机号', hintText: '请输入手机号',
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
controller: controller.phoneInput, controller: controller.phoneInput,
validator: EmailValidator(), onChanged: (text){
controller.setCanClick();
},
), ),
Gaps.vGaps13, Gaps.vGaps13,
(controller.loginType == 0)?
ValueListenableBuilder<bool>( ValueListenableBuilder<bool>(
valueListenable: controller.showPassword, valueListenable: controller.showPassword,
builder:(context, value, child) => CustomFormInput( builder:(context, value, child) => CustomFormInput(
...@@ -68,49 +82,86 @@ class _LoginPageState extends State<LoginPage> { ...@@ -68,49 +82,86 @@ class _LoginPageState extends State<LoginPage> {
iconData: iconData:
value ? Icons.visibility : Icons.visibility_off, value ? Icons.visibility : Icons.visibility_off,
controller: controller.passwordInput, controller: controller.passwordInput,
validator: EmailValidator(), onChanged: (text){
controller.setCanClick();
},
onIcon: controller.onShowPassword, onIcon: controller.onShowPassword,
), ),
):Stack(
alignment: Alignment.centerRight,
children: [
CustomFormInput(
// label: 'Phone',
// required: true,
hintText: '请输入验证码',
keyboardType: TextInputType.number,
controller: controller.codeInput,
),
Positioned(
right: 10,
child: Row(
children: [
Container(height: 20,width: 1,color: const Color(0xFFEBEBEB),),
Gaps.hGaps10,
GestureDetector(
child: Container(
padding: const EdgeInsets.symmetric(vertical: 10),
color: Colors.yellow,
child: Text(controller.isCounting?'${controller.countDown}':'获取验证码',style: const TextStyle(fontSize: 11,color: AppTheme.primary,height: 1.4),)),
onTap: (){
controller.start();
},
),
],
)
)
],
), ),
Container( Container(
margin: const EdgeInsets.only(left: 10,right: 10,top: 8.5), margin: const EdgeInsets.only(left: 10,right: 10,top: 8.5),
child: const Row( alignment: Alignment.centerLeft,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text('验证码登录',style: TextStyle(fontSize: 13,height: 1.3,color: Colours.c6),), controller.loginType == 0? GestureDetector(
Text('忘记密码',style: TextStyle(fontSize: 13,height: 1.3,color: Colours.cBlue),), child: const Text('验证码登录',style: TextStyle(fontSize: 13,height: 1.3,color: Colours.c6),),
], onTap: (){
controller.updateLoginType(1);
},
):GestureDetector(
child: const Text('密码登录',style: TextStyle(fontSize: 13,height: 1.3,color: Colours.c6),),
onTap: (){
controller.updateLoginType(0);
},
), ),
controller.loginType == 0? const Text('忘记密码',style: TextStyle(fontSize: 13,height: 1.3,color: Colours.cBlue),):const Text('*登录后将自动完成注册',style: TextStyle(fontSize: 13,height: 1.3,color: Colours.c6),),
],
)
), ),
const SizedBox(height: 30,), const SizedBox(height: 30,),
// 登录按钮 CustomGradientButton(
Container( text: '立即登录',
height: 42, isEnabled: !controller.enable,
width: double.infinity, onPressed: () {
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFFDE2E5E),Color(0xFFC31F4C)],
begin: Alignment.centerLeft,
end: Alignment.centerRight
),
borderRadius: BorderRadius.circular(21),
),
child: TextButton(
onPressed: () async {
FocusScope.of(context).requestFocus(FocusNode());
final result = await controller.onLogin(_formKey);
}, },
style: TextButton.styleFrom( )
padding: const EdgeInsets.symmetric(vertical: 10.5), ],
// textStyle: TextStyle(fontSize: 15.0,color: Colors.white), ),
],
), ),
child: Text('立即登录',style: TextStyle(color: Colors.white,fontSize: 15.0.w),),
), ),
), ),
],
), ),
Row( Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: (){
///TODO: 同意协议
},
child: Row(
children: [ children: [
Container( Container(
width: 10.w, width: 10.w,
...@@ -119,13 +170,24 @@ class _LoginPageState extends State<LoginPage> { ...@@ -119,13 +170,24 @@ class _LoginPageState extends State<LoginPage> {
), ),
Gaps.hGaps10, Gaps.hGaps10,
const Text('我已阅读并同意',style: TextStyle(color: Colours.c9,fontSize:10)), const Text('我已阅读并同意',style: TextStyle(color: Colours.c9,fontSize:10)),
const Text('《用户协议》',style: TextStyle(color: Colours.cBlue,fontSize:10)),
const Text('《隐私政策》',style: TextStyle(color: Colours.cBlue,fontSize:10)),
],
)
], ],
), ),
), ),
GestureDetector(
child: const Text('《用户协议》',style: TextStyle(color: Colours.cBlue,fontSize:10)),
onTap: (){
context.pushNamed(Routes.terms);
},
),
GestureDetector(
child: const Text('《隐私政策》',style: TextStyle(color: Colours.cBlue,fontSize:10)),
onTap: (){
context.pushNamed(Routes.terms);
},
),
],
)
],
), ),
) )
), ),
......
...@@ -79,3 +79,21 @@ class AmountValidator extends Validator<String?> { ...@@ -79,3 +79,21 @@ class AmountValidator extends Validator<String?> {
return hasMatch(_pattern.toString(), value!, caseSensitive: false); return hasMatch(_pattern.toString(), value!, caseSensitive: false);
} }
} }
class ValidatorTool {
// 验证手机号
static bool isValidPhoneNumber(String value) {
RegExp phonePattern = RegExp(r'^1[3-9]\d{9}$');
return phonePattern.hasMatch(value);
}
// 验证密码
static bool isValidPassword(String value) {
RegExp passwordPattern = RegExp(r'^[A-Za-z0-9!@#\$%^&*()_+{}\[\]:;<>,.?~\\/-]{6,20}$');;
return passwordPattern.hasMatch(value);
}
}
...@@ -239,3 +239,52 @@ class _ButtonWithIconChild extends StatelessWidget { ...@@ -239,3 +239,52 @@ class _ButtonWithIconChild extends StatelessWidget {
); );
} }
} }
class CustomGradientButton extends StatelessWidget {
final String text;
final bool isEnabled;
final VoidCallback onPressed;
const CustomGradientButton({
Key? key,
required this.text,
required this.isEnabled,
required this.onPressed,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
height: 42,
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(21),
gradient: LinearGradient(
colors: isEnabled
? [const Color(0xFFDE2E5E).withOpacity(0.7),const Color(0xFFC31F4C).withOpacity(0.7)] // 可点击时的颜色
: [const Color(0xFFDE2E5E),const Color(0xFFC31F4C)], // 不可点击时的颜色,透明度为0.7
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
),
child: TextButton(
onPressed: isEnabled ? onPressed : null,
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 10.5),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(21),
),
backgroundColor: Colors.transparent,
),
child: Text(
text,
style: const TextStyle(color: Colors.white, fontSize: 15.0),
),
),
);
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论