feat:增加登录模块
This commit is contained in:
@@ -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
267
lib/views/login.dart
Normal 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
25
lib/views/recordForm.dart
Normal 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('返回'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user