feat:增加菜单栏模块

This commit is contained in:
2025-11-14 17:29:54 +08:00
parent 3055f99aab
commit 94849be69c
17 changed files with 1376 additions and 252 deletions

62
lib/pages/about_page.dart Normal file
View File

@@ -0,0 +1,62 @@
import 'package:flutter/material.dart';
class AboutPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FlutterLogo(size: 100),
SizedBox(height: 24),
Text(
'我的Flutter应用',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
),
SizedBox(height: 8),
Text(
'版本 2.1.0',
style: TextStyle(fontSize: 16, color: Colors.grey),
),
SizedBox(height: 32),
Container(
width: 200,
child: Column(
children: [
_buildAboutItem('编译版本', '2.1.0 (20240115)'),
_buildAboutItem('更新时间', '2024年1月15日'),
_buildAboutItem('开发者', 'Flutter开发团队'),
],
),
),
SizedBox(height: 40),
ElevatedButton.icon(
onPressed: () {},
icon: Icon(Icons.star),
label: Text('给我们评分'),
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
),
),
],
),
);
}
Widget _buildAboutItem(String label, String value) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label, style: TextStyle(color: Colors.grey)),
Text(value, style: TextStyle(fontWeight: FontWeight.bold)),
],
),
);
}
}

328
lib/pages/home_page.dart Normal file
View File

@@ -0,0 +1,328 @@
import 'package:blog_app/pages/about_page.dart';
import 'package:blog_app/pages/message_page.dart';
import 'package:blog_app/pages/profile_page.dart';
import 'package:blog_app/pages/settings_page.dart';
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _currentPageIndex = 0;
late PageController _pageController;
// 页面标题列表
final List<String> _pageTitles = [
'个人资料',
'消息中心',
'设置',
'关于我们',
];
// 页面图标列表
final List<IconData> _pageIcons = [
Icons.home,
Icons.person,
Icons.message,
Icons.settings,
Icons.info,
];
@override
void initState() {
super.initState();
_pageController = PageController(initialPage: _currentPageIndex);
}
@override
void dispose() {
_pageController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_pageTitles[_currentPageIndex]),
backgroundColor: Colors.blue,
elevation: 0,
leading: Builder(
builder: (context) => IconButton(
icon: Icon(Icons.menu, color: Colors.white),
onPressed: () => Scaffold.of(context).openDrawer(),
),
),
actions: [
IconButton(
icon: Icon(Icons.search, color: Colors.white),
onPressed: () {},
),
],
),
drawer: _buildDrawer(),
body: PageView(
controller: _pageController,
onPageChanged: (index) {
setState(() {
_currentPageIndex = index;
});
},
children: [
ProfilePage(),
MessagePage(),
SettingsPage(),
AboutPage(),
],
),
);
}
Widget _buildDrawer() {
return Drawer(
child: Container(
color: Colors.white,
child: Column(
children: [
// 抽屉头部
_buildDrawerHeader(),
// 菜单项列表
Expanded(
child: ListView(
padding: EdgeInsets.zero,
children: [
...List.generate(_pageTitles.length, (index) =>
_buildDrawerItem(
icon: _pageIcons[index],
title: _pageTitles[index],
index: index,
)
),
],
),
),
],
),
),
);
}
Widget _buildDrawerHeader() {
return Container(
width: double.infinity,
height: 200,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Colors.blue, Colors.lightBlue],
),
),
child: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircleAvatar(
radius: 40,
backgroundColor: Colors.white.withOpacity(0.3),
child: Icon(
Icons.person,
size: 50,
color: Colors.white,
),
),
SizedBox(height: 16),
Text(
'用户名',
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 4),
Text(
'user@example.com',
style: TextStyle(
color: Colors.white70,
fontSize: 14,
),
),
],
),
),
);
}
Widget _buildDrawerItem({
required IconData icon,
required String title,
required int index,
}) {
final bool isSelected = index == _currentPageIndex;
final bool isSpecialItem = index == -1; // 特殊菜单项标识
return Container(
margin: EdgeInsets.symmetric(horizontal: 12, vertical: 4),
decoration: BoxDecoration(
color: isSelected ? Colors.blue.withOpacity(0.1) : Colors.transparent,
borderRadius: BorderRadius.circular(12),
),
child: ListTile(
leading: Icon(
icon,
color: isSelected ? Colors.blue :
isSpecialItem ? Colors.grey[600] : Colors.grey[700],
size: 24,
),
title: Text(
title,
style: TextStyle(
color: isSelected ? Colors.blue :
isSpecialItem ? Colors.grey[600] : Colors.grey[800],
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
fontSize: 16,
),
),
trailing: isSelected ? Icon(
Icons.arrow_forward_ios,
size: 16,
color: Colors.blue,
) : null,
onTap: () {
if (!isSpecialItem) {
// 正常页面切换
setState(() {
_currentPageIndex = index;
});
_pageController.animateToPage(
index,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
} else {
// 特殊菜单项处理
_handleSpecialItemTap(title);
}
Navigator.pop(context); // 关闭抽屉
},
),
);
}
void _handleSpecialItemTap(String title) {
// 处理特殊菜单项的点击事件
switch (title) {
case '帮助中心':
print('打开帮助中心');
break;
case '意见反馈':
print('打开意见反馈');
break;
case '退出登录':
print('执行退出登录');
break;
}
}
}
// 各个页面组件
class HomeContentPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 欢迎卡片
Card(
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Padding(
padding: EdgeInsets.all(20),
child: Row(
children: [
Icon(Icons.waving_hand, size: 40, color: Colors.amber),
SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'欢迎回来!',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 4),
Text(
'今天也是美好的一天',
style: TextStyle(color: Colors.grey),
),
],
),
),
],
),
),
),
SizedBox(height: 20),
// 功能网格
GridView.count(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
crossAxisCount: 2,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
children: [
_buildFeatureCard('数据统计', Icons.analytics, Colors.blue),
_buildFeatureCard('文件管理', Icons.folder, Colors.green),
_buildFeatureCard('消息通知', Icons.notifications, Colors.orange),
_buildFeatureCard('系统设置', Icons.settings, Colors.purple),
],
),
],
),
);
}
Widget _buildFeatureCard(String title, IconData icon, Color color) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: InkWell(
borderRadius: BorderRadius.circular(12),
onTap: () {},
child: Container(
padding: EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, size: 40, color: color),
SizedBox(height: 8),
Text(
title,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
),
textAlign: TextAlign.center,
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,122 @@
import 'package:blog_app/utils/http_util.dart';
import 'package:flutter/material.dart';
import 'package:markdown_widget/config/toc.dart';
import 'package:markdown_widget/widget/markdown.dart';
class MarkdownPage extends StatefulWidget {
const MarkdownPage({super.key});
@override
State<MarkdownPage> createState() => _MarkdownPageState();
}
class _MarkdownPageState extends State<MarkdownPage> {
final tocController = TocController();
bool _showToc = false;
late String markdownData = '';
Widget _buildTocPanel() => AnimatedOpacity(
opacity: _showToc ? 1.0 : 0.0,
duration: const Duration(milliseconds: 300),
child: Visibility(
visible: _showToc,
child: Align(
alignment: Alignment.bottomRight,
child: Container(
width: 250,
height: 400,
margin: const EdgeInsets.only(bottom: 60, right: 60),
// 调整位置在按钮左侧
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
border: Border.all(color: Colors.grey[300]!),
),
child: Column(
children: [
// 标题栏
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'目录',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
IconButton(
icon: const Icon(Icons.close, size: 20),
onPressed: () => setState(() => _showToc = false),
),
],
),
),
Expanded(child: TocWidget(controller: tocController)),
],
),
),
),
),
);
Widget buildMarkdown() =>
MarkdownWidget(data: markdownData, tocController: tocController);
@override
void initState() {
super.initState();
// 初始加载数据
_loadData();
}
Future<void> _loadData() async {
final result = await HttpUtil().get("/condition");
setState(() {
markdownData = result[0]['content'];
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('文档')),
body: Padding(
padding: EdgeInsets.all(16),
child: Stack(
children: [
// 主内容
buildMarkdown(),
// 悬浮TOC面板
_buildTocPanel(),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => setState(() => _showToc = !_showToc),
child: Icon(_showToc ? Icons.close : Icons.list),
mini: true,
backgroundColor:
_showToc ? Colors.grey : Theme.of(context).primaryColor,
),
);
}
}

View File

@@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
class MessagePage extends StatelessWidget {
final List<Map<String, dynamic>> messages = [
{'title': '系统通知', 'content': '您的账号安全等级已提升', 'time': '10:30', 'unread': false},
{'title': '活动提醒', 'content': '新活动即将开始,敬请期待', 'time': '昨天', 'unread': true},
{'title': '版本更新', 'content': '新版本v2.1.0已发布', 'time': '2024-01-20', 'unread': false},
];
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: messages.length,
itemBuilder: (context, index) {
final message = messages[index];
return Card(
margin: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: ListTile(
leading: CircleAvatar(
backgroundColor: message['unread'] ? Colors.blue : Colors.grey,
child: Icon(
Icons.notifications,
color: Colors.white,
size: 20,
),
),
title: Text(
message['title'],
style: TextStyle(
fontWeight: message['unread'] ? FontWeight.bold : FontWeight.normal,
),
),
subtitle: Text(message['content']),
trailing: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
message['time'],
style: TextStyle(fontSize: 12, color: Colors.grey),
),
if (message['unread'])
Container(
margin: EdgeInsets.only(top: 4),
width: 8,
height: 8,
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
),
],
),
onTap: () {},
),
);
},
);
}
}

View File

@@ -0,0 +1,78 @@
import 'package:flutter/material.dart';
class ProfilePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
padding: EdgeInsets.all(16),
child: Column(
children: [
// 个人信息卡片
Card(
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Padding(
padding: EdgeInsets.all(20),
child: Row(
children: [
CircleAvatar(
radius: 40,
backgroundColor: Colors.blue,
child: Icon(Icons.person, size: 40, color: Colors.white),
),
SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'张小明',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 4),
Text('高级用户', style: TextStyle(color: Colors.grey)),
SizedBox(height: 8),
Row(
children: [
Icon(Icons.star, size: 16, color: Colors.amber),
SizedBox(width: 4),
Text('会员等级: VIP3'),
],
),
],
),
),
],
),
),
),
SizedBox(height: 20),
// 详细信息
_buildInfoItem('手机号码', '138****1234', Icons.phone),
_buildInfoItem('邮箱地址', 'zhang@example.com', Icons.email),
_buildInfoItem('注册时间', '2024年1月15日', Icons.calendar_today),
_buildInfoItem('所在地区', '北京市朝阳区', Icons.location_on),
],
),
);
}
Widget _buildInfoItem(String title, String value, IconData icon) {
return Card(
margin: EdgeInsets.only(bottom: 12),
child: ListTile(
leading: Icon(icon, color: Colors.blue),
title: Text(title),
subtitle: Text(value),
trailing: Icon(Icons.edit, size: 20),
),
);
}
}

View File

@@ -0,0 +1,71 @@
import 'package:flutter/material.dart';
class SettingsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
padding: EdgeInsets.all(16),
child: Column(
children: [
_buildSettingsSection('账户设置', [
_buildSettingsItem('隐私设置', Icons.privacy_tip),
_buildSettingsItem('安全设置', Icons.security),
_buildSettingsItem('账号绑定', Icons.link),
]),
SizedBox(height: 20),
_buildSettingsSection('通知设置', [
_buildSettingsItem('推送通知', Icons.notifications_active, hasSwitch: true),
_buildSettingsItem('声音提醒', Icons.volume_up, hasSwitch: true),
_buildSettingsItem('震动提醒', Icons.vibration, hasSwitch: true),
]),
SizedBox(height: 20),
_buildSettingsSection('其他设置', [
_buildSettingsItem('清理缓存', Icons.cleaning_services),
_buildSettingsItem('语言设置', Icons.language),
_buildSettingsItem('主题设置', Icons.color_lens),
_buildSettingsItem('关于应用', Icons.info_outline),
]),
],
),
);
}
Widget _buildSettingsSection(String title, List<Widget> children) {
return Card(
elevation: 2,
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.grey[700],
),
),
SizedBox(height: 8),
...children,
],
),
),
);
}
Widget _buildSettingsItem(String title, IconData icon, {bool hasSwitch = false}) {
return ListTile(
leading: Icon(icon, color: Colors.blue),
title: Text(title),
trailing: hasSwitch
? Switch(value: true, onChanged: (value) {})
: Icon(Icons.arrow_forward_ios, size: 16),
onTap: () {},
);
}
}