import 'package:flutter/material.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:food_hub_app/api/session.dart'; import 'package:food_hub_app/models/session.dart'; import 'package:food_hub_app/utils/sp_util.dart'; import 'package:food_hub_app/widgets/common/index.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart'; class LoginPage extends StatefulWidget { const LoginPage({super.key}); @override State createState() => _LoginPage(); } class _LoginPage extends State { final GlobalKey _formKey = GlobalKey(); String _username = "", _password = ""; bool _isRemember = false; bool _isObscure = true; Color _eyeColor = Colors.grey; final List _loginMethod = [ {"title": "phone", "icon": Icons.phone_android}, {"title": "email", "icon": Icons.email}, {"title": "wechat", "icon": Icons.wechat}, ]; @override void initState() { super.initState(); _loadRememberState(); } Future _loadRememberState() async { bool? isRemember = await SPUtil.getBool('isRemember'); if (isRemember) { setState(() { _isRemember = true; _username = SPUtil.getString('username'); _password = SPUtil.getString('password'); }); } } void handleRememberState() { if (_isRemember) { SPUtil.set('isRemember', true); SPUtil.set('username', _username); SPUtil.set('password', _password); } else { SPUtil.set('isRemember', false); SPUtil.remove('username'); SPUtil.remove('password'); } } void handleTokenState(String token) { SPUtil.set('token', token); } void loginClick(BuildContext context) async { // 表单校验通过才会继续执行 if ((_formKey.currentState as FormState).validate()) { (_formKey.currentState as FormState).save(); Session session = await loginApi(_username, _password); showSuccessToast('登录成功'); handleRememberState(); handleTokenState(session.saToken.tokenValue); Navigator.pushNamed(context, '/home'); } else { showErrorToast('请先输入信息'); } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.grey[100], body: Form( key: _formKey, // autovalidateMode: AutovalidateMode.onUserInteraction, child: Column( children: [ // 可滚动的主要内容区域 Expanded(child: buildLoginForm()), // 固定在底部的其他登录选项区域 Padding( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 50), child: Column( children: [ buildOtherLoginText(), const SizedBox(height: 15), buildOtherMethod(context), const SizedBox(height: 20), buildRegisterText(context), ], ), ), ], ), ), ); } Widget buildTitle() { return const Text( '食光集', textAlign: TextAlign.center, style: TextStyle(fontSize: 42), ); } Widget buildLoginForm() { return ListView( padding: const EdgeInsets.symmetric(horizontal: 20), children: [ const SizedBox(height: kToolbarHeight), buildTitle(), const SizedBox(height: 60), buildUsernameTextField(), const SizedBox(height: 20), buildPasswordTextField(context), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ buildRememberPasswordCheckbox(context), // 记住密码(左对齐) buildForgetPasswordText(context), ], ), const SizedBox(height: 20), // 调整与登录按钮的间距 buildLoginButton(context), ], ); } Widget buildUsernameTextField() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ formLabelText(labelText: "账号:"), const SizedBox(height: 10), FormBuilderTextField( name: 'username', initialValue: _username, keyboardType: TextInputType.text, decoration: formInputDecoration( hintText: "请输入账号", prefixIcon: Icons.person, ), validator: FormBuilderValidators.required(), onSaved: (v) => _username = v!, ), ], ); } Widget buildPasswordTextField(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ formLabelText(labelText: "密码:"), const SizedBox(height: 10), FormBuilderTextField( name: 'password', initialValue: _password, keyboardType: TextInputType.visiblePassword, obscureText: _isObscure, onSaved: (v) => _password = v!, validator: FormBuilderValidators.required(), decoration: InputDecoration( prefixIcon: Icon(Icons.lock), hintText: "请输入密码", hintStyle: TextStyle(color: Colors.grey), border: OutlineInputBorder(), filled: true, fillColor: Colors.white, suffixIcon: IconButton( icon: Icon(Icons.remove_red_eye, color: _eyeColor), onPressed: () { setState(() { _isObscure = !_isObscure; _eyeColor = (_isObscure ? Colors.grey : Theme.of(context).iconTheme.color)!; }); }, ), ), ), ], ); } Widget buildRememberPasswordCheckbox(BuildContext context) { return Row( children: [ Checkbox( value: _isRemember, onChanged: (bool? value) { setState(() { _isRemember = value ?? false; }); }, ), const Text('记住密码', style: TextStyle(color: Colors.grey, fontSize: 14)), ], ); } Widget buildForgetPasswordText(BuildContext context) { return Align( alignment: Alignment.centerRight, child: TextButton( onPressed: () { print("忘记密码"); }, child: const Text( "忘记密码?", style: TextStyle(fontSize: 14, color: Colors.grey), ), ), ); } Widget buildLoginButton(BuildContext context) { return Align( child: SizedBox( height: 45, width: double.infinity, child: TDButton( text: '登录', size: TDButtonSize.large, type: TDButtonType.fill, shape: TDButtonShape.rectangle, theme: TDButtonTheme.primary, onTap: () => loginClick(context), ), ), ); } Widget buildOtherLoginText() { return TDDivider(text: '其他方式登录', alignment: TextAlignment.center); } Widget buildOtherMethod(context) { return OverflowBar( alignment: MainAxisAlignment.center, children: _loginMethod .map( (item) => Builder( builder: (context) { return IconButton( icon: Icon( item['icon'], color: Theme.of(context).iconTheme.color, ), onPressed: () { //TODO: 第三方登录方法 ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('${item['title']}登录'), action: SnackBarAction( label: '取消', onPressed: () {}, ), ), ); }, ); }, ), ) .toList(), ); } Widget buildRegisterText(context) { return Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('没有账号?', style: TextStyle(fontSize: 18)), GestureDetector( child: const Text( '点击注册', style: TextStyle(fontSize: 18, color: Colors.green), ), onTap: () { print("点击注册"); }, ), ], ), ); } }