Files
food_hub_app/lib/views/login.dart
2026-06-27 17:12:38 +08:00

315 lines
9.0 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_common/utils/sp_utils.dart';
import 'package:flutter_common/utils/toast_util.dart';
import 'package:flutter_common/widget/loading_widget.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:food_hub_app/apis/session.dart';
import 'package:food_hub_app/models/session.dart';
import 'package:food_hub_app/widgets/common/form.dart';
import 'package:food_hub_app/widgets/common/index.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@override
State<StatefulWidget> createState() => _LoginPage();
}
class _LoginPage extends State<LoginPage> {
final GlobalKey _formKey = GlobalKey<FormState>();
String _username = "Cxx0822", _password = "19940822Cxx";
bool _isRemember = false;
bool _isObscure = true;
bool _isLoading = false;
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<void> _loadRememberState() async {
bool? isRemember = 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 handleUserInfo(User user) {
SPUtil.set('userId', user.id);
}
void loginClick() async {
setState(() {
_isLoading = true;
});
// 表单校验通过才会继续执行
if ((_formKey.currentState as FormState).validate()) {
(_formKey.currentState as FormState).save();
try {
LoadingDialog.show(context, message: '登录中');
Session session = await loginApi(_username, _password);
ToastUtil.success('登录成功');
handleRememberState();
handleTokenState(session.saToken.tokenValue);
handleUserInfo(session.userInfo);
LoadingDialog.hide(context);
Navigator.pushNamed(context, '/home');
} catch (e) {
LoadingDialog.hide(context);
ToastUtil.error(e.toString());
}
}
setState(() {
_isLoading = false;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Form(
key: _formKey,
child: Column(
children: [
Expanded(child: buildLoginForm(context)),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 50),
child: Column(
children: [
buildOtherLoginText(context),
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(BuildContext context) {
return ListView(
padding: const EdgeInsets.symmetric(horizontal: 20),
children: [
const SizedBox(height: kToolbarHeight),
buildTitle(),
const SizedBox(height: 60),
buildUsernameTextField(context),
const SizedBox(height: 20),
buildPasswordTextField(context),
const SizedBox(height: 10),
buildRememberPasswordCheckbox(context),
const SizedBox(height: 20),
buildLoginButton(context),
],
);
}
Widget buildUsernameTextField(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("账号:", style: Theme.of(context).textTheme.bodyLarge),
const SizedBox(height: 10),
FormBuilderTextField(
name: 'username',
initialValue: _username,
keyboardType: TextInputType.text,
decoration: buildInputDecoration(
context: context,
hintText: "请输入账号",
prefixIcon: Icons.person,
),
validator: FormBuilderValidators.required(),
onSaved: (v) => _username = v!,
),
],
);
}
Widget buildPasswordTextField(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("密码:", style: Theme.of(context).textTheme.bodyLarge),
const SizedBox(height: 10),
FormBuilderTextField(
name: 'password',
initialValue: _password,
keyboardType: TextInputType.visiblePassword,
obscureText: _isObscure,
onSaved: (v) => _password = v!,
validator: FormBuilderValidators.required(),
decoration: buildInputDecoration(
context: context,
hintText: "请输入密码",
prefixIcon: Icons.lock,
).copyWith(
suffixIcon: IconButton(
icon: Icon(Icons.remove_red_eye, color: _eyeColor),
onPressed: () {
final colors = Theme.of(context).colorScheme;
setState(() {
_isObscure = !_isObscure;
_eyeColor = (_isObscure ? Colors.grey : colors.surface);
});
},
),
),
),
],
);
}
Widget buildRememberPasswordCheckbox(BuildContext context) {
return Row(
children: [
Checkbox(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: VisualDensity.compact,
value: _isRemember,
onChanged: (bool? value) {
setState(() {
_isRemember = value ?? false;
});
},
),
Text('记住密码', style: Theme.of(context).textTheme.bodyMedium),
],
);
}
Widget buildLoginButton(BuildContext context) {
return SizedBox(
height: 45,
width: double.infinity,
child: ElevatedButton(
onPressed: _isLoading ? null : loginClick,
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
foregroundColor: Colors.white,
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
child: const Text(
'登录',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
),
);
}
Widget buildOtherLoginText(BuildContext context) {
return Row(
children: [
Expanded(child: Divider(color: Colors.grey[300], thickness: 1)),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text('其他方式登录', style: Theme.of(context).textTheme.bodyMedium),
),
Expanded(child: Divider(color: Colors.grey[300], thickness: 1)),
],
);
}
Widget buildOtherMethod(BuildContext 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(BuildContext context) {
return Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('没有账号?', style: Theme.of(context).textTheme.bodyMedium),
GestureDetector(
child: Text(
'点击注册',
style: TextStyle(
fontSize: 14,
color: Theme.of(context).colorScheme.primary,
),
),
onTap: () {
print("点击注册");
},
),
],
),
);
}
}