diff --git a/lib/main.dart b/lib/main.dart index ce00f88..3de3482 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -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 createState() => _mainPage(); + State createState() => _MainPage(); } -class _mainPage extends State { +class _MainPage extends State { int _currentIndex = 0; final List _tabPages = const [RecordPage(), ProfilePage()]; @@ -67,17 +69,7 @@ class _mainPage extends State { 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 { } } -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), + ); } diff --git a/lib/views/login.dart b/lib/views/login.dart new file mode 100644 index 0000000..652838c --- /dev/null +++ b/lib/views/login.dart @@ -0,0 +1,267 @@ +import 'package:flutter/material.dart'; + +class LoginPage extends StatefulWidget { + const LoginPage({super.key}); + + @override + State createState() => _LoginPage(); +} + +class _LoginPage extends State { + final GlobalKey _formKey = GlobalKey(); + 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("点击注册"); + }, + ), + ], + ), + ); + } +} diff --git a/lib/views/recordForm.dart b/lib/views/recordForm.dart new file mode 100644 index 0000000..c63b7c3 --- /dev/null +++ b/lib/views/recordForm.dart @@ -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('返回'), + ), + ], + ), + ), + ); + } +}