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

登录界面

上级 0a5b3ac8
......@@ -5,20 +5,13 @@ enum LoginType{code, password}
class LoginController extends GetxController {
/// 登录方式
final loginType = LoginType.code;
/// 1、验证码登录
/// 账号
final TextEditingController codePhoneInput = TextEditingController();
/// 密码
final TextEditingController codeInput = TextEditingController();
/// 2、密码登录
/// 账号
final TextEditingController phoneInput = TextEditingController();
/// 密码
final TextEditingController passwordInput = TextEditingController();
// 显示密码
final ValueNotifier<bool> showPassword = ValueNotifier(false);
......@@ -26,7 +19,7 @@ class LoginController extends GetxController {
void onInit() {
/// 测试账号
if (kDebugMode) {
phoneInput.text = '18810760819';
phoneInput.text = '13521054068';
passwordInput.text = '123456';
}
super.onInit();
......@@ -36,18 +29,13 @@ class LoginController extends GetxController {
void onClose() {
phoneInput.dispose();
passwordInput.dispose();
showPassword.dispose();
super.onClose();
}
void onShowPassword() => showPassword.value = !showPassword.value;
void submit(BuildContext context) async {
/// 验证码登录
if (loginType == LoginType.code){
}
else if (loginType == LoginType.password){
}
Future<bool> onLogin(GlobalKey<FormState> key) async {
final result = AccountAPI.login(
phone: '',
......@@ -65,13 +53,15 @@ class LoginController extends GetxController {
]);
if (!context.mounted) return;
if (context.canPop()){
context.pop();
}else {
context.goNamed(Routes.main);
}
// if (!context.mounted) return;
// if (context.canPop()){
// context.pop();
// }else {
// context.goNamed(Routes.main);
// }
return true;
......
......@@ -3,8 +3,10 @@ library login;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_book/apis/index.dart';
import 'package:flutter_book/store/index.dart';
import 'package:flutter_book/theme.dart';
import 'package:flutter_book/utils/index.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
......@@ -14,6 +16,7 @@ import 'package:ionicons/ionicons.dart';
import '../../routes/index.dart';
import '../../services/index.dart';
import '../../widgets/index.dart';
......
part of login;
class LoginPage extends StatelessWidget {
class LoginPage extends StatefulWidget {
const LoginPage({Key? key}) : super(key: key);
@override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final _formKey = GlobalKey<FormState>();
bool isChecked = false;
@override
Widget build(BuildContext context) {
return GetBuilder<LoginController>(
......@@ -11,59 +18,119 @@ class LoginPage extends StatelessWidget {
builder:(controller) => GestureDetector(
onTap: () => Tools.unfocus(),
child: Scaffold(
extendBodyBehindAppBar: true,
body: SingleChildScrollView(
child: SafeArea(
minimum: const EdgeInsets.all(0),
child: codeLogin()
appBar: CustomAppBar(
systemOverlayStyle: Theme.of(context).brightness == Brightness.light
? SystemUiOverlayStyle.dark
: SystemUiOverlayStyle.light,
backgroundColor: Colors.transparent,
),
// extendBodyBehindAppBar: true,
body: SafeArea(
top: false,
minimum: const EdgeInsets.all(AppTheme.margin).copyWith(
// top:Screen.statusBar,
left: 25,
right: 25,
bottom: 25
),
),
)
);
}
/// 验证码登录
Widget codeLogin(){
return Container(
margin: EdgeInsets.only(left: 10,right: 10),
child: Form(
key: _formKey,
child: Container(
// color: Colors.green,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
// crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(height: 120,),
Container(
width: 180,
height: 50,
color: Colors.cyan,
),
const SizedBox(height:66),
CustomFormInput(
// label: 'Phone',
// required: true,
hintText: '请输入手机号',
keyboardType: TextInputType.number,
controller: controller.phoneInput,
validator: EmailValidator(),
),
Gaps.vGaps13,
ValueListenableBuilder<bool>(
valueListenable: controller.showPassword,
builder:(context, value, child) => CustomFormInput(
obscureText: !value,
hintText: '请输入密码',
iconData:
value ? Icons.visibility : Icons.visibility_off,
controller: controller.passwordInput,
validator: EmailValidator(),
onIcon: controller.onShowPassword,
),
),
Container(
margin: const EdgeInsets.only(left: 10,right: 10,top: 8.5),
child: const Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('验证码登录',style: TextStyle(fontSize: 13,height: 1.3,color: Colours.c6),),
Text('忘记密码',style: TextStyle(fontSize: 13,height: 1.3,color: Colours.cBlue),),
],
),
),
const SizedBox(height: 30,),
// 登录按钮
Container(
height: 42,
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50),
// color: Colors.red,
border: Border.all(width: 1,color: Colors.red),
gradient: const LinearGradient(
colors: [Color(0xFFDE2E5E),Color(0xFFC31F4C)],
begin: Alignment.centerLeft,
end: Alignment.centerRight
),
child: TextField(
style: TextStyle(
color: Colors.cyan
borderRadius: BorderRadius.circular(21),
),
decoration: InputDecoration(
hintText: '请输入手机号',
prefixIcon: Icon(Ionicons.phone_portrait),
border:InputBorder.none,
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none
// contentPadding: EdgeInsets.symmetric(vertical: 16.0),
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),),
),
),
SizedBox(height: 30.w,),
TextField(
decoration: const InputDecoration(hintText: '获取验证码'),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 10.w,
height: 10.w,
color: AppTheme.primary,
),
Gaps.hGaps10,
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)),
],
)
],
),
),
),
)
),
)
);
}
/// 密码登录
Widget passwordLogin(){
return Container();
}
}
......@@ -38,8 +38,8 @@ abstract class Routes {
static final GoRouter config = GoRouter(
// initialLocation: '/$splash',
initialLocation: '/',
initialLocation: '/$login',
// initialLocation: '/',
observers: [observer],
routes: [
GoRoute(
......
......@@ -80,7 +80,7 @@ abstract class AppTheme {
secondary: const Color(0xFFFFB800),
onSecondary: Colors.white,
tertiary: const Color(0xFFF4F6F9),
outline: const Color(0xFFF4F6F9),
outline: const Color(0xFFD5D5D5),
shadow: const Color(0xFFAB1941).withOpacity(0.08),
error: error,
onError: Colors.white,
......@@ -209,43 +209,43 @@ abstract class AppTheme {
isCollapsed: true,
isDense: true,
filled: true,
fillColor: scheme.surface,
fillColor: scheme.background,
labelStyle: TextStyle(
fontSize: 16.w,
color: scheme.onBackground,
fontWeight: FontWeight.w600,
fontSize: 15.w,
color: Colours.c3,
// fontWeight: FontWeight.w600,
),
helperStyle: TextStyle(
fontSize: 14.w,
fontSize: 15.w,
color: scheme.onBackground.withOpacity(0.7),
),
contentPadding: EdgeInsets.symmetric(
horizontal: 20.w,
vertical: 14.w,
horizontal: 10.w,
vertical: 10.w,
),
border: OutlineInputBorder(
borderSide: BorderSide(color: scheme.outline, width: 2.w),
borderRadius: BorderRadius.all(Radius.circular(25.w)),
borderSide: BorderSide(color: scheme.outline, width: 0.5.w),
borderRadius: BorderRadius.all(Radius.circular(6.w)),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: scheme.outline, width: 2.w),
borderRadius: BorderRadius.all(Radius.circular(25.w)),
borderSide: BorderSide(color: scheme.outline, width: 0.5.w),
borderRadius: BorderRadius.all(Radius.circular(6.w)),
),
disabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: scheme.outline, width: 2.w),
borderRadius: BorderRadius.all(Radius.circular(25.w)),
borderSide: BorderSide(color: scheme.outline, width: 0.5.w),
borderRadius: BorderRadius.all(Radius.circular(6.w)),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: scheme.primary, width: 2.w),
borderRadius: BorderRadius.all(Radius.circular(25.w)),
borderSide: BorderSide(color: scheme.outline, width: 0.5.w),
borderRadius: BorderRadius.all(Radius.circular(6.w)),
),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(color: error, width: 2.w),
borderRadius: BorderRadius.all(Radius.circular(25.w)),
borderSide: BorderSide(color: error, width: 0.5.w),
borderRadius: BorderRadius.all(Radius.circular(6.w)),
),
focusedErrorBorder: OutlineInputBorder(
borderSide: BorderSide(color: error, width: 2.w),
borderRadius: BorderRadius.all(Radius.circular(100.w)),
borderSide: BorderSide(color: error, width: 0.5.w),
borderRadius: BorderRadius.all(Radius.circular(6.w)),
),
),
tabBarTheme: TabBarTheme(
......
......@@ -20,3 +20,4 @@ part 'sign.dart';
part 'encrypt_util.dart';
part 'tools.dart';
part 'styles.dart';
part 'validator.dart';
\ No newline at end of file
......@@ -22,6 +22,7 @@ class Colours {
static const c3 = Color(0xFF333333);
static const c6 = Color(0xFF666666);
static const cLine = Color(0xFFF0F0F0);
static const cBlue = Color(0xFF2A82D9);
}
class Gaps {
......@@ -32,6 +33,7 @@ class Gaps {
static const Widget vGaps5 = SizedBox(height: 5,);
static const Widget vGaps10 = SizedBox(height: 10,);
static const Widget vGaps13 = SizedBox(height: 13,);
static const Widget vGaps15 = SizedBox(height: 15,);
// static const Widget line = Padding(
// padding: EdgeInsets.symmetric(horizontal: 15.0),
......
part of utils;
abstract class Validator<T> {
final String error;
Validator(this.error);
bool isValid(T value);
String? call(T value) => isValid(value) ? null : error;
bool hasMatch(
String pattern,
String value, {
bool caseSensitive = true,
}) =>
RegExp(
pattern,
caseSensitive: caseSensitive,
).hasMatch(value);
}
class RequiredValidator extends Validator<String?> {
RequiredValidator() : super('This field is required');
@override
bool isValid(String? value) => (value ?? '').isNotEmpty;
}
class EmailValidator extends Validator<String?> {
final Pattern _pattern =
r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
EmailValidator() : super('Email is invalid');
@override
bool isValid(String? value) {
if ((value ?? '').isEmpty) return true;
return hasMatch(_pattern.toString(), value!, caseSensitive: false);
}
}
class PasswordValidator extends Validator<String?> {
final Pattern _pattern = r'(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,16}$';
PasswordValidator()
: super('The password oil consists of 6 to 16 digits and letters');
@override
bool isValid(String? value) {
if ((value ?? '').isEmpty) return true;
return hasMatch(_pattern.toString(), value!, caseSensitive: false);
}
}
class EqualValidator extends Validator<String?> {
final TextEditingController input;
EqualValidator(this.input) : super('Does not match the password');
@override
bool isValid(String? value) {
if ((value ?? '').isEmpty) return true;
return input.value.text == value;
}
}
class AmountValidator extends Validator<String?> {
final Pattern _pattern =
r'(^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9])?$)';
AmountValidator() : super('Amount is invalid');
@override
bool isValid(String? value) {
if ((value ?? '').isEmpty) return true;
return hasMatch(_pattern.toString(), value!, caseSensitive: false);
}
}
part of widgets;
class CustomFormInput extends FormField<String> {
final TextEditingController? controller;
final String? label;
final String? helper;
final String? hintText;
final bool required;
final bool readOnly;
final bool obscureText;
final bool autofocus;
final IconData? iconData;
final int? maxLines;
final void Function()? onTap;
final void Function()? onIcon;
final ValueChanged<String>? onChanged;
final TextInputType? keyboardType;
final List<TextInputFormatter>? inputFormatters;
CustomFormInput({
Key? key,
this.controller,
this.label,
this.helper,
this.hintText,
this.required = false,
this.readOnly = false,
this.obscureText = false,
this.autofocus = false,
this.maxLines = 1,
this.onTap,
this.onIcon,
this.iconData,
this.onChanged,
this.keyboardType,
this.inputFormatters,
Validator<String?>? validator,
}) : super(
key: key,
validator: (value) {
if (required) {
final call = RequiredValidator().call(value);
if (validator == null) return call;
return call ?? validator.call(value);
}
return validator?.call(value);
},
builder: (field) {
final state = field as _CustomFormInputState;
final decoration = const InputDecoration().applyDefaults(
Theme.of(field.context).inputDecorationTheme,
);
return UnmanagedRestorationScope(
bucket: field.bucket,
child: CustomInput(
controller: state._effectiveController,
label: label,
hintText: hintText,
required: required,
readOnly: readOnly,
obscureText: obscureText,
autofocus: autofocus,
onTap: onTap,
onIcon: onIcon,
iconData: iconData,
maxLines: maxLines,
error: field.errorText,
decoration: decoration,
inputFormatters: inputFormatters,
keyboardType: keyboardType,
onChanged: (value) {
field.didChange(value);
onChanged?.call(value);
},
),
);
},
);
@override
FormFieldState<String> createState() => _CustomFormInputState();
}
class _CustomFormInputState extends FormFieldState<String> {
RestorableTextEditingController? _controller;
TextEditingController get _effectiveController =>
widget.controller ?? _controller!.value;
@override
CustomFormInput get widget => super.widget as CustomFormInput;
@override
void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
super.restoreState(oldBucket, initialRestore);
if (_controller != null) _registerController();
setValue(_effectiveController.text);
}
void _registerController() {
assert(_controller != null);
registerForRestoration(_controller!, 'controller');
}
void _createLocalController([TextEditingValue? value]) {
assert(_controller == null);
_controller = value == null
? RestorableTextEditingController()
: RestorableTextEditingController.fromValue(value);
if (!restorePending) _registerController();
}
@override
void initState() {
super.initState();
if (widget.controller == null) {
final editingValue = widget.initialValue != null
? TextEditingValue(text: widget.initialValue!)
: null;
_createLocalController(editingValue);
} else {
widget.controller!.addListener(_onControllerChanged);
}
}
@override
void didUpdateWidget(CustomFormInput oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.controller != oldWidget.controller) {
oldWidget.controller?.removeListener(_onControllerChanged);
widget.controller?.addListener(_onControllerChanged);
if (oldWidget.controller != null && widget.controller == null) {
_createLocalController(oldWidget.controller!.value);
}
if (widget.controller != null) {
setValue(widget.controller!.text);
if (oldWidget.controller == null) {
unregisterFromRestoration(_controller!);
_controller!.dispose();
_controller = null;
}
}
}
}
@override
void dispose() {
widget.controller?.removeListener(_onControllerChanged);
_controller?.dispose();
super.dispose();
}
@override
void didChange(String? value) {
super.didChange(value);
if (_effectiveController.text != value) {
_effectiveController.text = value ?? '';
}
}
@override
void reset() {
_effectiveController.text = widget.initialValue ?? '';
super.reset();
}
void _onControllerChanged() {
if (_effectiveController.text != value) {
didChange(_effectiveController.text);
}
}
}
class CustomInput extends StatelessWidget {
final TextEditingController? controller;
final String? label;
final String? helper;
final String? hintText;
final String? error;
final bool required;
final bool readOnly;
final bool obscureText;
final IconData? iconData;
final int? minLines;
final int? maxLines;
final void Function()? onTap;
final void Function()? onIcon;
final ValueChanged<String>? onChanged;
final InputDecoration decoration;
final TextInputType? keyboardType;
final bool autofocus;
final List<TextInputFormatter>? inputFormatters;
const CustomInput({
Key? key,
this.controller,
this.label,
this.helper,
this.hintText,
this.error,
this.required = false,
this.readOnly = false,
this.obscureText = false,
this.onTap,
this.onIcon,
this.iconData,
this.onChanged,
this.decoration = const InputDecoration(),
this.minLines,
this.maxLines = 1,
this.keyboardType,
this.autofocus = false,
this.inputFormatters,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final decorationTheme = theme.inputDecorationTheme;
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (label != null || required)
Padding(
padding: EdgeInsets.symmetric(
horizontal: (decorationTheme.contentPadding?.horizontal ?? 0) / 2,
vertical: 10.w,
).copyWith(top: 0),
child: RichText(
text: TextSpan(
text: label,
style: decorationTheme.labelStyle,
children: [
if (required)
TextSpan(
text: label != null ? ' *' : '*',
style: const TextStyle(color: AppTheme.error),
),
],
),
),
),
DecoratedBox(
decoration: const BoxDecoration(
boxShadow: [
// BoxShadow(
// color: theme.colorScheme.shadow,
// offset: Offset(0, 20.w),
// blurRadius: 10.w,
// spreadRadius: -10.w,
// ),
],
),
child: TextField(
autofocus: autofocus,
controller: controller,
readOnly: readOnly,
onTap: onTap,
obscureText: obscureText,
onChanged: onChanged,
minLines: minLines,
maxLines: maxLines,
inputFormatters: inputFormatters,
keyboardType: keyboardType,
style: TextStyle(
// fontFamily: 'Sans',
fontSize: 15.w,
height: 1.2,
// fontWeight: FontWeight.w600,
),
decoration: decoration.copyWith(
hintText: hintText,
hintStyle: const TextStyle(fontWeight: FontWeight.normal,color: Colours.c9),
suffixIconConstraints: const BoxConstraints(),
suffixIcon: _suffixIcon(decorationTheme),
),
),
),
AnimatedSize(
duration: const Duration(milliseconds: 180),
alignment: Alignment.topCenter,
child: error != null
? Padding(
padding: EdgeInsets.only(top: 10.w),
// child: CustomAlert.error(
// size: CustomAlertSize.mini,
// text: Text(error!),
// ),
)
: const SizedBox.shrink(),
),
if (helper != null)
Padding(
padding: EdgeInsets.symmetric(
horizontal: (decorationTheme.contentPadding?.horizontal ?? 0) / 2,
vertical: 10.w,
).copyWith(bottom: 0),
child: Text(
helper!,
style: decorationTheme.helperStyle,
),
)
],
);
}
Widget? _suffixIcon(InputDecorationTheme decorationTheme) {
if (iconData == null) return null;
return Padding(
padding: EdgeInsetsDirectional.only(
end: (decorationTheme.contentPadding?.horizontal ?? 0) / 2,
),
child: GestureDetector(
onTap: onIcon,
child: Icon(iconData!, size: 24.w),
),
);
}
}
class CustomInputSearch extends StatelessWidget {
final IconData iconData;
final String? hintText;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论