初始化
This commit is contained in:
154
lib/pages/common/auth/login_page.dart
Normal file
154
lib/pages/common/auth/login_page.dart
Normal file
@@ -0,0 +1,154 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:app/router/route_paths.dart';
|
||||
import 'package:app/widgets/base/button/index.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:remixicon/remixicon.dart';
|
||||
|
||||
import 'widgets/login_agree.dart';
|
||||
import 'widgets/login_input.dart';
|
||||
|
||||
class LoginPage extends StatefulWidget {
|
||||
const LoginPage({super.key});
|
||||
|
||||
@override
|
||||
State<LoginPage> createState() => _LoginPageState();
|
||||
}
|
||||
|
||||
class _LoginPageState extends State<LoginPage> {
|
||||
///协议
|
||||
bool _agree = false;
|
||||
|
||||
///输入框
|
||||
final TextEditingController _telController = TextEditingController();
|
||||
final TextEditingController _codeController = TextEditingController();
|
||||
|
||||
///验证码倒计时
|
||||
var _countDown = 0;
|
||||
Timer? _timer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
///点击发送验证码
|
||||
void _sendCode() async {
|
||||
RegExp regExp = RegExp(r'^1\d{10}$');
|
||||
if (!regExp.hasMatch(_telController.text)) {
|
||||
EasyLoading.showToast("请填写正确的手机号");
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_countDown = 60;
|
||||
});
|
||||
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
|
||||
setState(() {
|
||||
_countDown--;
|
||||
});
|
||||
if (_countDown <= 0) {
|
||||
_timer?.cancel();
|
||||
_timer = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
///提交登录
|
||||
void _handSubmit() async {
|
||||
RegExp regExp = RegExp(r'^1\d{10}$');
|
||||
if (!regExp.hasMatch(_telController.text) || _codeController.text.isEmpty) {
|
||||
EasyLoading.showToast("请填写完整手机号或验证码");
|
||||
return;
|
||||
}
|
||||
context.go(RoutePaths.sHome);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_timer?.cancel();
|
||||
_timer = null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
//是否已发送验证码
|
||||
bool codeOk = _countDown == 0;
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: SafeArea(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 20, right: 20, top: 0.08.sh, bottom: 40),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
margin: EdgeInsets.only(bottom: 40),
|
||||
child: Text("登陆学光自习室", style: Theme.of(context).textTheme.titleLarge),
|
||||
),
|
||||
LoginInput(
|
||||
hintText: "请输入手机号",
|
||||
controller: _telController,
|
||||
suffix: Visibility(
|
||||
visible: _telController.text.isNotEmpty,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
_telController.clear();
|
||||
},
|
||||
child: Icon(RemixIcons.close_circle_fill, size: 20),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 20),
|
||||
child: Row(
|
||||
spacing: 20,
|
||||
children: [
|
||||
Expanded(
|
||||
child: LoginInput(
|
||||
hintText: "输入验证码",
|
||||
controller: _codeController,
|
||||
maxLength: 4,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 45,
|
||||
child: Button(
|
||||
text: codeOk ? "发送验证码" : "$_countDown 秒后发送",
|
||||
radius: BorderRadius.circular(10),
|
||||
disabled: !codeOk,
|
||||
onPressed: _sendCode,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 40),
|
||||
height: 50,
|
||||
child: Button(text: "登 录", onPressed: _handSubmit),
|
||||
),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
margin: EdgeInsets.only(top: 20),
|
||||
alignment: Alignment.center,
|
||||
child: LoginAgree(
|
||||
value: _agree,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_agree = value!;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
71
lib/pages/common/auth/widgets/login_agree.dart
Normal file
71
lib/pages/common/auth/widgets/login_agree.dart
Normal file
@@ -0,0 +1,71 @@
|
||||
import 'package:app/router/route_paths.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
|
||||
class LoginAgree extends StatelessWidget {
|
||||
final ValueChanged<bool?>? onChanged;
|
||||
final bool value;
|
||||
|
||||
const LoginAgree({
|
||||
super.key,
|
||||
this.onChanged,
|
||||
required this.value,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 25,
|
||||
child: Transform.scale(
|
||||
scale: 0.8,
|
||||
child: Checkbox(
|
||||
value: value,
|
||||
shape: CircleBorder(),
|
||||
onChanged: onChanged,
|
||||
),
|
||||
),
|
||||
),
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
style: Theme.of(context).textTheme.labelMedium,
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "注册或登录即表示您了解并同意",
|
||||
),
|
||||
TextSpan(
|
||||
text: "服务条款",
|
||||
style: TextStyle(color: Theme.of(context).primaryColor),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => context.push(
|
||||
RoutePaths.agreement,
|
||||
extra: {
|
||||
"title": "Terms of Service",
|
||||
"url": "https://support.curain.ai/privacy/foodcura/terms_service.html",
|
||||
},
|
||||
),
|
||||
),
|
||||
TextSpan(text: " 和 "),
|
||||
TextSpan(
|
||||
text: "隐私协议",
|
||||
style: TextStyle(color: Theme.of(context).primaryColor),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => context.push(
|
||||
RoutePaths.agreement,
|
||||
extra: {
|
||||
"title": "Privacy",
|
||||
"url": "https://support.curain.ai/privacy/foodcura/privacy_policy.html",
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
49
lib/pages/common/auth/widgets/login_input.dart
Normal file
49
lib/pages/common/auth/widgets/login_input.dart
Normal file
@@ -0,0 +1,49 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class LoginInput extends StatelessWidget {
|
||||
final String hintText;
|
||||
final int maxLength;
|
||||
final TextEditingController controller;
|
||||
final Widget? suffix;
|
||||
|
||||
const LoginInput({
|
||||
super.key,
|
||||
required this.hintText,
|
||||
this.maxLength = 11,
|
||||
required this.controller,
|
||||
this.suffix,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextField(
|
||||
controller: controller,
|
||||
maxLength: maxLength,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly, // 只允许 0-9
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
hintText: hintText,
|
||||
hintStyle: Theme.of(context).textTheme.labelLarge?.copyWith(fontSize: 16),
|
||||
counterText: '',
|
||||
filled: true,
|
||||
fillColor: Theme.of(context).colorScheme.surfaceContainer,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(60),
|
||||
borderSide: BorderSide.none, // 去掉边框
|
||||
),
|
||||
isCollapsed: true,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 14, horizontal: 20),
|
||||
suffixIconConstraints: BoxConstraints(minWidth: 0, minHeight: 0),
|
||||
suffixIcon: suffix != null
|
||||
? Container(
|
||||
padding: EdgeInsets.only(right: 20),
|
||||
child: suffix,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user