feat:增加登录模块

This commit is contained in:
2025-07-06 22:59:35 +08:00
parent ab00d6726f
commit 570a141133
3 changed files with 310 additions and 33 deletions

View File

@@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:food_hub_app/profile.dart';
import 'package:food_hub_app/record.dart';
import 'package:food_hub_app/views/login.dart';
import 'package:food_hub_app/views/recordForm.dart';
void main() {
runApp(const MyApp());
@@ -15,10 +17,10 @@ class MyApp extends StatelessWidget {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(fontFamily: 'CustomFont', primaryColor: Colors.green),
home: MainPage(),
home: LoginPage(),
routes: {
'/home': (context) => MainPage(),
'/new': (context) => NewPage(),
'/recordForm': (context) => RecordFormPage(),
},
);
}
@@ -28,10 +30,10 @@ class MainPage extends StatefulWidget {
const MainPage({super.key});
@override
State<StatefulWidget> createState() => _mainPage();
State<StatefulWidget> createState() => _MainPage();
}
class _mainPage extends State<MainPage> {
class _MainPage extends State<MainPage> {
int _currentIndex = 0;
final List<Widget> _tabPages = const [RecordPage(), ProfilePage()];
@@ -67,17 +69,7 @@ class _mainPage extends State<MainPage> {
backgroundColor: Color(0xFFF5F5F5),
body: _tabPages[_currentIndex],
floatingActionButton:
_currentIndex == _tabPages.length - 1
? null
: FloatingActionButton(
mini: true,
onPressed: () {
Navigator.pushNamed(context, '/new');
},
backgroundColor: Colors.green,
shape: const CircleBorder(),
child: Icon(Icons.add, color: Colors.white),
),
_currentIndex == _tabPages.length - 1 ? null : addItemButton(context),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
iconSize: 25,
@@ -99,23 +91,16 @@ class _mainPage extends State<MainPage> {
}
}
class NewPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('新页面')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('这是新页面'),
ElevatedButton(
onPressed: () => Navigator.pop(context),
child: Text('返回'),
),
],
),
),
);
Widget addItemButton(BuildContext context) {
void onAddItemClick(BuildContext context) {
Navigator.pushNamed(context, '/recordForm');
}
return FloatingActionButton(
mini: true,
onPressed: () => onAddItemClick(context),
backgroundColor: Colors.green,
shape: const CircleBorder(),
child: Icon(Icons.add, color: Colors.white),
);
}

267
lib/views/login.dart Normal file
View File

@@ -0,0 +1,267 @@
import 'package:flutter/material.dart';
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@override
State<StatefulWidget> createState() => _LoginPage();
}
class _LoginPage extends State<LoginPage> {
final GlobalKey _formKey = GlobalKey<FormState>();
late String _username, _password;
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
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Form(
key: _formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Column(
children: [
// 可滚动的主要内容区域
Expanded(
child: ListView(
padding: const EdgeInsets.symmetric(horizontal: 20),
children: [
const SizedBox(height: kToolbarHeight),
buildTitle(),
const SizedBox(height: 60),
buildUsernameTextField(),
const SizedBox(height: 20),
buildPasswordTextField(context),
// 紧凑排列:记住密码在上,忘记密码在下,间距缩小
const SizedBox(height: 8), // 缩小密码框与记住密码的间距
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
buildRememberPasswordCheckbox(context), // 记住密码(左对齐)
buildForgetPasswordText(context),
],
),
const SizedBox(height: 20), // 调整与登录按钮的间距
buildLoginButton(context),
],
),
),
// 固定在底部的其他登录选项区域
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 buildUsernameTextField() {
return TextFormField(
decoration: InputDecoration(
labelText: '账号',
prefixIcon: Icon(Icons.person),
hintText: "请输入账号",
border: OutlineInputBorder(),
filled: true,
fillColor: Colors.grey[100],
),
validator: (v) {
if (v!.isEmpty) {
return '请输入账号';
}
},
onSaved: (v) => _username = v!,
);
}
Widget buildPasswordTextField(BuildContext context) {
return TextFormField(
obscureText: _isObscure, // 是否显示文字
onSaved: (v) => _password = v!,
validator: (v) {
if (v!.isEmpty) {
return '请输入密码';
}
},
decoration: InputDecoration(
labelText: "密码",
prefixIcon: Icon(Icons.lock),
hintText: "请输入密码",
border: OutlineInputBorder(),
filled: true,
fillColor: Colors.grey[100],
suffixIcon: IconButton(
icon: Icon(Icons.remove_red_eye, color: _eyeColor),
onPressed: () {
// 修改 state 内部变量, 且需要界面内容更新, 需要使用 setState()
setState(() {
_isObscure = !_isObscure;
_eyeColor =
(_isObscure
? Colors.grey
: Theme.of(context).iconTheme.color)!;
});
},
),
),
);
}
Widget buildRememberPasswordCheckbox(BuildContext context) {
return Row(
children: [
Checkbox(
value: false,
onChanged: (bool? value) {
print("记住密码: $value");
},
),
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: ElevatedButton(
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(Colors.green),
shape: WidgetStateProperty.all(
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
),
child: Text(
'登录',
style: TextStyle(fontSize: 24, color: Colors.white),
),
onPressed: () {
// 表单校验通过才会继续执行
if ((_formKey.currentState as FormState).validate()) {
(_formKey.currentState as FormState).save();
Navigator.pushNamed(context, '/home');
}
},
),
),
);
}
Widget buildOtherLoginText() {
return SizedBox(
width: double.infinity,
child: Stack(
alignment: Alignment.center,
children: [
const Divider(color: Colors.grey, thickness: 0.5),
Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
color: Colors.white, // 与背景色一致,覆盖横线
child: const Text(
'其他方式登录',
style: TextStyle(color: Colors.grey, fontSize: 14),
),
),
],
),
);
}
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("点击注册");
},
),
],
),
);
}
}

25
lib/views/recordForm.dart Normal file
View File

@@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
class RecordFormPage extends StatelessWidget {
const RecordFormPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('新页面'), backgroundColor: Colors.white),
backgroundColor: Color(0xFFF5F5F5),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('这是新页面'),
ElevatedButton(
onPressed: () => Navigator.pop(context),
child: Text('返回'),
),
],
),
),
);
}
}