diff --git a/lib/layout/app_actions.dart b/lib/layout/app_actions.dart new file mode 100644 index 0000000..3efa033 --- /dev/null +++ b/lib/layout/app_actions.dart @@ -0,0 +1,96 @@ +import 'package:flutter/material.dart'; +import 'package:food_hub_app/provider/food_provider.dart'; +import 'package:provider/provider.dart'; + +class AppActions extends StatefulWidget { + const AppActions({super.key}); + + @override + State createState() => AppActionsState(); +} + +class AppActionsState extends State { + void _buildBottomSheet(FoodProvider provider) { + showModalBottomSheet( + context: context, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(16)), + ), + builder: (context) => _buildBottomSheetBody(provider), + ); + } + + Widget _buildBottomSheetBody(FoodProvider provider) { + final colors = Theme.of(context).colorScheme; + final provider = context.watch(); + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 8), + const Text( + '请选择操作', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 10), + ListTile( + leading: Icon(Icons.book, color: colors.primary), + title: const Text('新增菜谱'), + onTap: () => _handleAddRecipe(provider), + ), + ListTile( + leading: Icon(Icons.note_add, color: colors.primary), + title: const Text('新增记录'), + onTap: () => _handleAddRecord(provider), + ), + ListTile( + leading: Icon(Icons.group, color: colors.primary), + title: const Text('发布朋友圈'), + onTap: () => _handleAddMoment(provider), + ), + ], + ); + } + + void _handleAddRecipe(FoodProvider provider) { + // provider.resetRecordForm(); + // Navigator.pop(context); + // Navigator.pushNamed(context, "/recordForm"); + } + + void _handleAddRecord(FoodProvider provider) { + provider.resetRecordForm(); + provider.isEditing = false; + Navigator.pop(context); + Navigator.pushNamed(context, "/recordForm"); + } + + void _handleAddMoment(FoodProvider provider) { + provider.resetMomentForm(); + provider.isEditing = false; + Navigator.pop(context); + Navigator.pushNamed(context, "/momentForm"); + } + + @override + Widget build(BuildContext context) { + final provider = context.watch(); + + return Row( + children: [ + IconButton( + icon: Icon(Icons.search, color: Colors.white), + onPressed: () { + // 搜索功能 + }, + ), + IconButton( + icon: Icon(Icons.add, color: Colors.white), + onPressed: () { + _buildBottomSheet(provider); + }, + ), + ], + ); + } +} diff --git a/lib/layout/app_drawer.dart b/lib/layout/app_drawer.dart new file mode 100644 index 0000000..6add289 --- /dev/null +++ b/lib/layout/app_drawer.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:food_hub_app/layout/theme_layout.dart'; + +class AppDrawer extends StatefulWidget { + const AppDrawer({super.key}); + + @override + State createState() => AppDrawerState(); +} + +class AppDrawerState extends State { + @override + Widget build(BuildContext context) { + // final themeProvider = Provider.of(context); + + return Drawer( + child: ListView( + padding: EdgeInsets.zero, + children: [ + ConstrainedBox( + constraints: const BoxConstraints(maxHeight: 100), + child: DrawerHeader( + decoration: BoxDecoration(color: Theme.of(context).primaryColor), + child: const Text( + '设置', + style: TextStyle(color: Colors.white, fontSize: 24), + ), + ), + ), + ThemeLayout(), + const Divider(), + ListTile( + leading: const Icon(Icons.info), + title: const Text('关于我们'), + onTap: () { + Navigator.pop(context); // 关闭抽屉 + // 跳转到关于页面 + }, + ), + ], + ), + ); + } +} diff --git a/lib/layout/app_navbar.dart b/lib/layout/app_navbar.dart new file mode 100644 index 0000000..e6727ff --- /dev/null +++ b/lib/layout/app_navbar.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; +import 'menu.dart'; + +class AppNavbar extends StatefulWidget { + final int currentPageIndex; + final Function(int) onTapNavbarItem; + + const AppNavbar({ + super.key, + required this.currentPageIndex, + required this.onTapNavbarItem, + }); + + @override + State createState() => AppNavbarState(); +} + +class AppNavbarState extends State { + + @override + Widget build(BuildContext context) { + final colors = Theme.of(context).colorScheme; + + return BottomNavigationBar( + currentIndex: widget.currentPageIndex, + onTap: widget.onTapNavbarItem, + items: bottomNavItems, + type: BottomNavigationBarType.fixed, + selectedItemColor: colors.primary, + unselectedItemColor: colors.onSurface.withAlpha(150), + showSelectedLabels: true, + showUnselectedLabels: true, + backgroundColor: colors.surface, + elevation: 8, + ); + } +} diff --git a/lib/layout/drawer.dart b/lib/layout/drawer.dart deleted file mode 100644 index 497323c..0000000 --- a/lib/layout/drawer.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:flutter/material.dart'; - -class SettingsDrawer extends StatelessWidget { - const SettingsDrawer({super.key}); - - @override - Widget build(BuildContext context) { - // final themeProvider = Provider.of(context); - - return Drawer( - child: ListView( - padding: EdgeInsets.zero, - children: [ - ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 100), - child: DrawerHeader( - decoration: BoxDecoration(color: Theme.of(context).primaryColor), - child: const Text( - '设置', - style: TextStyle(color: Colors.white, fontSize: 24), - ), - ), - ), - ListTile( - leading: const Icon(Icons.brightness_4), - title: const Text('夜间模式'), - // trailing: Switch( - // value: themeProvider.isDarkMode, - // onChanged: (bool value) { - // themeProvider.toggleTheme(); - // }, - // ), - // onTap: () => themeProvider.toggleTheme(), - ), - - // 其他设置选项 - ListTile( - leading: const Icon(Icons.notifications), - title: const Text('通知设置'), - onTap: () { - Navigator.pop(context); // 关闭抽屉 - // 跳转到通知设置页面 - }, - ), - - ListTile( - leading: const Icon(Icons.language), - title: const Text('语言'), - onTap: () { - Navigator.pop(context); // 关闭抽屉 - // 跳转到语言设置页面 - }, - ), - - // 底部关于 - const Divider(), - ListTile( - leading: const Icon(Icons.info), - title: const Text('关于我们'), - onTap: () { - Navigator.pop(context); // 关闭抽屉 - // 跳转到关于页面 - }, - ), - ], - ), - ); - } -} diff --git a/lib/layout/menu.dart b/lib/layout/menu.dart new file mode 100644 index 0000000..9890044 --- /dev/null +++ b/lib/layout/menu.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:food_hub_app/models/layout.dart'; +import 'package:food_hub_app/views/moment.dart'; +import 'package:food_hub_app/views/profile.dart'; +import 'package:food_hub_app/views/record.dart'; +import 'package:food_hub_app/views/stats.dart'; + +final List pages = [ + PageInfo( + label: "记录", + icon: Icons.home_outlined, + activeIcon: Icons.home, + page: RecordPage(), + ), + PageInfo( + label: "统计", + icon: Icons.pie_chart_outline, + activeIcon: Icons.pie_chart, + page: StatsPage(), + ), + PageInfo( + label: "朋友圈", + icon: Icons.group_outlined, + activeIcon: Icons.group, + page: MomentPage(), + ), + PageInfo( + label: "我的", + icon: Icons.account_circle_outlined, + activeIcon: Icons.account_circle, + page: ProfilePage(), + ), +]; + +final List bottomNavItems = + pages.map((page) { + return BottomNavigationBarItem(icon: Icon(page.icon), label: page.label); + }).toList(); + +final List tabPages = pages.map((item) => item.page).toList(); diff --git a/lib/layout/nav_bar.dart b/lib/layout/nav_bar.dart deleted file mode 100644 index a6e58ab..0000000 --- a/lib/layout/nav_bar.dart +++ /dev/null @@ -1,124 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:food_hub_app/models/layout.dart'; -import 'package:food_hub_app/models/recipe.dart'; -import 'package:food_hub_app/provider/food_provider.dart'; -import 'package:food_hub_app/views/moment.dart'; -import 'package:food_hub_app/views/profile.dart'; -import 'package:food_hub_app/views/record.dart'; -import 'package:food_hub_app/views/stats.dart'; - -import 'package:provider/provider.dart'; - -final List pageInfos = [ - PageInfo( - label: "记录", - icon: Icons.home_outlined, - activeIcon: Icons.home, - page: RecordPage(), - ), - PageInfo( - label: "统计", - icon: Icons.pie_chart_outline, - activeIcon: Icons.pie_chart, - page: StatsPage(), - ), - PageInfo( - label: "朋友圈", - icon: Icons.group_outlined, - activeIcon: Icons.group, - page: MomentPage(), - ), - PageInfo( - label: "我的", - icon: Icons.account_circle_outlined, - activeIcon: Icons.account_circle, - page: ProfilePage(), - ), -]; - -Widget buildBottomNavBar({ - required int currentIndex, - required Function(int) onTap, -}) { - final List items = - pageInfos - .map( - (item) => BottomNavigationBarItem( - icon: Icon(item.icon), - activeIcon: Icon(item.activeIcon), - label: item.label, - ), - ) - .toList(); - - return BottomNavigationBar( - currentIndex: currentIndex, - iconSize: 25, - type: BottomNavigationBarType.fixed, - backgroundColor: Colors.white, - items: items, - onTap: onTap, - ); -} - -void buildBottomSheet(BuildContext context) { - showModalBottomSheet( - context: context, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(16)), - ), - builder: (context) => buildBottomSheetBody(context), - ); -} - -Widget buildBottomSheetBody(BuildContext context) { - final colors = Theme.of(context).colorScheme; - final provider = context.watch(); - - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(height: 8), - const Text( - '请选择操作', - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 10), - ListTile( - leading: Icon(Icons.book, color: colors.primary), - title: const Text('新增菜谱'), - onTap: () => _handleAddRecipe(context, provider), - ), - ListTile( - leading: Icon(Icons.note_add, color: colors.primary), - title: const Text('新增记录'), - onTap: () => _handleAddRecord(context, provider), - ), - ListTile( - leading: Icon(Icons.group, color: colors.primary), - title: const Text('发布朋友圈'), - onTap: () => _handleAddMoment(context, provider), - ), - ], - ); -} - -void _handleAddRecipe(BuildContext context, FoodProvider provider) { - // provider.resetRecordForm(); - // Navigator.pop(context); - // Navigator.pushNamed(context, "/recordForm"); -} - -void _handleAddRecord(BuildContext context, FoodProvider provider) { - provider.resetRecordForm(); - provider.isEditing = false; - Navigator.pop(context); - Navigator.pushNamed(context, "/recordForm"); -} - -void _handleAddMoment(BuildContext context, FoodProvider provider) { - provider.resetMomentForm(); - provider.isEditing = false; - Navigator.pop(context); - Navigator.pushNamed(context, "/momentForm"); -} diff --git a/lib/layout/theme_layout.dart b/lib/layout/theme_layout.dart new file mode 100644 index 0000000..0d7f53b --- /dev/null +++ b/lib/layout/theme_layout.dart @@ -0,0 +1,128 @@ +import 'package:flutter/material.dart'; +import 'package:food_hub_app/provider/theme_provider.dart'; +import 'package:food_hub_app/utils/theme_utils.dart'; +import 'package:provider/provider.dart'; + +class ThemeLayout extends StatelessWidget { + final Widget? child; + + const ThemeLayout({super.key, this.child}); + + Widget _buildThemeColorList(BuildContext context, ThemeProvider provider) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), + child: GridView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 4, + crossAxisSpacing: 8, + mainAxisSpacing: 8, + childAspectRatio: 1.2, + ), + itemCount: provider.availableThemes.length, + itemBuilder: (context, index) { + final themeColor = provider.availableThemes[index]; + final isSelected = provider.currentTheme == themeColor; + + return _buildThemeColorItem( + themeColor: themeColor, + isSelected: isSelected, + onTap: () => provider.changeTheme(themeColor), + ); + }, + ), + ); + } + + Widget _buildThemeColorItem({ + required ThemeColor themeColor, + required bool isSelected, + required VoidCallback onTap, + }) { + return GestureDetector( + onTap: onTap, + child: Container( + decoration: BoxDecoration( + color: + isSelected + ? themeColor.primaryColor.withAlpha(50) + : Colors.transparent, + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: isSelected ? themeColor.primaryColor : Colors.grey.shade300, + width: isSelected ? 2 : 1, + ), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // 颜色圆点 + Container( + width: 20, + height: 20, + decoration: BoxDecoration( + color: themeColor.primaryColor, + shape: BoxShape.circle, + border: Border.all(color: Colors.white, width: 2), + boxShadow: [ + BoxShadow( + color: Colors.black.withAlpha(10), + blurRadius: 2, + offset: const Offset(0, 1), + ), + ], + ), + ), + const SizedBox(height: 4), + Text( + themeColor.name, + style: TextStyle( + fontSize: 10, + fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, + color: + isSelected ? themeColor.primaryColor : Colors.grey.shade600, + ), + ), + ], + ), + ), + ); + } + + Widget _buildDarkModeSection(BuildContext context, ThemeProvider provider) { + return ListTile( + leading: Icon( + provider.isDarkMode ? Icons.dark_mode : Icons.light_mode, + color: Theme.of(context).colorScheme.primary, + ), + title: const Text('暗黑模式'), + trailing: Switch( + value: provider.isDarkMode, + onChanged: (value) { + provider.toggleDarkMode(value); + }, + ), + onTap: () { + provider.toggleDarkMode(!provider.isDarkMode); + }, + ); + } + + @override + Widget build(BuildContext context) { + final provider = context.watch(); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(16, 16, 16, 8), + child: Text('主题颜色', style: Theme.of(context).textTheme.titleMedium), + ), + _buildDarkModeSection(context, provider), + _buildThemeColorList(context, provider), + ], + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index 427e085..2a09123 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:food_hub_app/provider/food_provider.dart'; +import 'package:food_hub_app/provider/theme_provider.dart'; import 'package:food_hub_app/utils/sp_util.dart'; import 'package:food_hub_app/views/home.dart'; import 'package:food_hub_app/views/login.dart'; @@ -18,6 +19,7 @@ void main() async { MultiProvider( providers: [ ChangeNotifierProvider(create: (context) => FoodProvider()), + ChangeNotifierProvider(create: (context) => ThemeProvider()), ], child: MyApp(), ), @@ -30,9 +32,11 @@ class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { + final themeProvider = context.watch(); + return MaterialApp( debugShowCheckedModeBanner: false, - theme: ThemeData(fontFamily: 'CustomFont'), + theme: themeProvider.currentThemeData, supportedLocales: const [ Locale('en', 'US'), // 英语 Locale('zh', 'CN'), // 中文 diff --git a/lib/provider/theme_provider.dart b/lib/provider/theme_provider.dart new file mode 100644 index 0000000..8d09789 --- /dev/null +++ b/lib/provider/theme_provider.dart @@ -0,0 +1,76 @@ +import 'package:flutter/material.dart'; +import 'package:food_hub_app/utils/theme_utils.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class ThemeProvider with ChangeNotifier { + static const String _themeKey = 'selected_theme'; + static const String _darkModeKey = 'is_dark_mode'; + + bool isDarkMode = false; + ThemeColor currentTheme = defaultThemes[0]; + + late SharedPreferences _prefs; + bool isInitialized = false; + + ThemeProvider() { + _initPreferences(); + } + + // 初始化 SharedPreferences + Future _initPreferences() async { + _prefs = await SharedPreferences.getInstance(); + _loadPreferences(); + + isInitialized = true; + notifyListeners(); + } + + // 加载存储的设置 + void _loadPreferences() { + isDarkMode = _prefs.getBool(_darkModeKey) ?? false; + + // 加载主题色 + final themeName = _prefs.getString(_themeKey); + if (themeName != null) { + currentTheme = getThemeColor(themeName); + } + } + + // 保存主题色 + Future _saveTheme() async { + await _prefs.setString(_themeKey, currentTheme.name); + } + + // 保存暗黑模式 + Future _saveDarkMode() async { + await _prefs.setBool(_darkModeKey, isDarkMode); + } + + List get availableThemes => defaultThemes; + + // 切换明暗模式 + void toggleDarkMode(bool value) { + isDarkMode = value; + _saveDarkMode(); + notifyListeners(); + } + + // 更改主题色 + void changeTheme(ThemeColor theme) { + currentTheme = theme; + _saveTheme(); + notifyListeners(); + } + + ThemeData get currentThemeData { + return ThemeData( + primarySwatch: currentTheme.materialColor, + colorScheme: ColorScheme.fromSeed( + seedColor: currentTheme.primaryColor, + brightness: isDarkMode ? Brightness.dark : Brightness.light, + ), + useMaterial3: true, + fontFamily: 'CustomFont', + ); + } +} diff --git a/lib/utils/theme_utils.dart b/lib/utils/theme_utils.dart new file mode 100644 index 0000000..1998144 --- /dev/null +++ b/lib/utils/theme_utils.dart @@ -0,0 +1,87 @@ +import 'package:flutter/material.dart'; + +class ThemeColor { + final String name; + final Color primaryColor; + final MaterialColor materialColor; + + ThemeColor({ + required this.name, + required this.primaryColor, + required this.materialColor, + }); +} + +final List defaultThemes = [ + ThemeColor( + name: '梦幻紫', + primaryColor: Color(0xFF8B5CF6), + materialColor: MaterialColor(0xFF8B5CF6, { + 50: Color(0xFFF5F3FF), + 100: Color(0xFFEDE9FE), + 200: Color(0xFFDDD6FE), + 300: Color(0xFFC4B5FD), + 400: Color(0xFFA78BFA), + 500: Color(0xFF8B5CF6), + 600: Color(0xFF7C3AED), + 700: Color(0xFF6D28D9), + 800: Color(0xFF5B21B6), + 900: Color(0xFF4C1D95), + }), + ), + ThemeColor( + name: '活力橙', + primaryColor: Color(0xFFF59E0B), + materialColor: MaterialColor(0xFFF59E0B, { + 50: Color(0xFFFFFBEB), + 100: Color(0xFFFEF3C7), + 200: Color(0xFFFDE68A), + 300: Color(0xFFFCD34D), + 400: Color(0xFFFBBF24), + 500: Color(0xFFF59E0B), + 600: Color(0xFFD97706), + 700: Color(0xFFB45309), + 800: Color(0xFF92400E), + 900: Color(0xFF78350F), + }), + ), + ThemeColor( + name: '浪漫粉', + primaryColor: Color(0xFFEC4899), + materialColor: MaterialColor(0xFFEC4899, { + 50: Color(0xFFFDF2F8), + 100: Color(0xFFFCE7F3), + 200: Color(0xFFFBCFE8), + 300: Color(0xFFF9A8D4), + 400: Color(0xFFF472B6), + 500: Color(0xFFEC4899), + 600: Color(0xFFDB2777), + 700: Color(0xFFBE185D), + 800: Color(0xFF9D174D), + 900: Color(0xFF831843), + }), + ), + ThemeColor( + name: '清新青', + primaryColor: Color(0xFF06B6D4), + materialColor: MaterialColor(0xFF06B6D4, { + 50: Color(0xFFF0FDFA), + 100: Color(0xFFCCFBF1), + 200: Color(0xFF99F6E4), + 300: Color(0xFF5EEAD4), + 400: Color(0xFF2DD4BF), + 500: Color(0xFF06B6D4), + 600: Color(0xFF0891B2), + 700: Color(0xFF0E7490), + 800: Color(0xFF155E75), + 900: Color(0xFF164E63), + }), + ), +]; + +ThemeColor getThemeColor(String themeName) { + return defaultThemes.firstWhere( + (theme) => theme.name == themeName, + orElse: () => defaultThemes[0], + ); +} diff --git a/lib/views/home.dart b/lib/views/home.dart index 1129838..a2f7bd9 100644 --- a/lib/views/home.dart +++ b/lib/views/home.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:food_hub_app/layout/drawer.dart'; -import 'package:food_hub_app/layout/nav_bar.dart'; +import 'package:food_hub_app/layout/app_actions.dart'; +import 'package:food_hub_app/layout/app_drawer.dart'; +import 'package:food_hub_app/layout/app_navbar.dart'; +import 'package:food_hub_app/layout/menu.dart'; class HomePage extends StatefulWidget { const HomePage({super.key}); @@ -12,8 +14,6 @@ class HomePage extends StatefulWidget { class _HomePage extends State { int _currentIndex = 0; - List get tabPages => pageInfos.map((item) => item.page).toList(); - @override Widget build(BuildContext context) { return Scaffold( @@ -30,34 +30,17 @@ class _HomePage extends State { ); }, ), - actions: [ - Row( - children: [ - IconButton( - icon: Icon(Icons.search, color: Colors.white), - onPressed: () { - // 搜索功能 - }, - ), - IconButton( - icon: Icon(Icons.add, color: Colors.white), - onPressed: () { - buildBottomSheet(context); - }, - ), - ], - ), - ], + actions: [AppActions()], ), // backgroundColor: Color(0xFFF5F5F5), - drawer: SettingsDrawer(), + drawer: AppDrawer(), body: Padding( padding: EdgeInsets.all(10), child: tabPages[_currentIndex], ), - bottomNavigationBar: buildBottomNavBar( - currentIndex: _currentIndex, - onTap: (index) { + bottomNavigationBar: AppNavbar( + currentPageIndex: _currentIndex, + onTapNavbarItem: (index) { setState(() { _currentIndex = index; }); diff --git a/lib/widgets/common/index.dart b/lib/widgets/common/index.dart index f22200b..8eb8380 100644 --- a/lib/widgets/common/index.dart +++ b/lib/widgets/common/index.dart @@ -43,7 +43,7 @@ class BuildCard extends StatelessWidget { borderRadius: BorderRadius.circular(12), // border: Border.all(color: colors.outline.withAlpha(50), width: 1), ), - padding: const EdgeInsets.all(16), + padding: const EdgeInsets.all(10), child: child, ); } @@ -62,16 +62,18 @@ Widget buildToggleSwitch({ required List labels, required ValueChanged onTabChanged, }) { + final colors = Theme.of(context).colorScheme; + return ToggleSwitch( minWidth: 90.0, minHeight: 40.0, initialLabelIndex: tabValues.indexOf(currentTab), totalSwitches: tabValues.length, labels: labels, - activeBgColor: [Theme.of(context).colorScheme.primary], + activeBgColor: [colors.primary], activeFgColor: Colors.white, - inactiveBgColor: Colors.grey.shade200, - inactiveFgColor: Colors.grey.shade700, + inactiveBgColor: colors.surfaceContainer, + inactiveFgColor: colors.onSurface, cornerRadius: 12.0, customTextStyles: [TextStyle(fontSize: 12, fontWeight: FontWeight.w500)], onToggle: (index) { @@ -111,7 +113,7 @@ Widget buildTag(BuildContext context, String title) { return Container( padding: EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( - color: Color.lerp(colors.primary, Colors.white, 0.8), + color: colors.primary.withAlpha(60), borderRadius: BorderRadius.circular(10), ), child: Text(title, style: TextStyle(color: colors.primary)), diff --git a/lib/widgets/recipe/card.dart b/lib/widgets/recipe/card.dart index 4f03364..4771bef 100644 --- a/lib/widgets/recipe/card.dart +++ b/lib/widgets/recipe/card.dart @@ -17,85 +17,60 @@ class RecipeCard extends StatelessWidget { final List imageUrls = recipe.recordList.reversed.map((record) => record.imageUrl).toList(); - Widget buildRecipeContent(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Text( - recipe.name, - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - SizedBox(height: 5), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Row( - children: [ - _buildIconText( - icon: Icons.food_bank, - text: recipe.category, - color: Color(0xFF6A5ACD), - ), - const SizedBox(width: 12), - _buildIconText( - icon: Icons.thumb_up_alt_outlined, - text: recipe.likeCount.toString(), - color: Color(0xFFDC143C), - ), - const SizedBox(width: 12), - _buildIconText( - icon: Icons.star_outline, - text: recipe.favouriteCount.toString(), - color: Color(0xFF20B2AA), - ), - const SizedBox(width: 12), - _buildIconText( - icon: Icons.comment_outlined, - text: recipe.commentCount.toString(), - color: Color(0xFFDAC570), - ), - ], - ), - Row( - children: [ - _buildIconText( - icon: Icons.date_range, - text: recipe.recordList[0].date, - color: Color(0xFF32CD32), - ), - ], - ), - ], - ), - ], - ); - } - return BuildCard( child: Column( + mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ buildNetworkImage(context, imageUrls, 0), SizedBox(height: 8), GestureDetector( onTap: () => navigatorToRecipeDetail(context), - child: buildRecipeContent(context), + child: _buildRecipeContent(), ), ], ), ); } + Widget _buildRecipeContent() { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + recipe.name, + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + SizedBox(height: 5), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _buildIconText( + icon: Icons.food_bank, + text: recipe.category, + color: Color(0xFF6A5ACD), + ), + const SizedBox(width: 12), + _buildIconText( + icon: Icons.thumb_up_alt_outlined, + text: recipe.likeCount.toString(), + color: Color(0xFFDC143C), + ), + const SizedBox(width: 12), + _buildIconText( + icon: Icons.star_outline, + text: recipe.favouriteCount.toString(), + color: Color(0xFF20B2AA), + ), + ], + ) + ], + ); + } + // 通用图标文本组件 Widget _buildIconText({ required IconData icon, diff --git a/lib/widgets/recipe/list.dart b/lib/widgets/recipe/list.dart index 5cdddd8..f59c728 100644 --- a/lib/widgets/recipe/list.dart +++ b/lib/widgets/recipe/list.dart @@ -82,9 +82,14 @@ class _RecipeListState extends State { _buildSelectCategory(provider), SizedBox(height: 5), Expanded( - child: ListView.separated( + child: GridView.builder( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, // 每行2个 + crossAxisSpacing: 8, // 水平间距 + mainAxisSpacing: 8, // 垂直间距 + childAspectRatio: 0.9, + ), itemCount: provider.recipeSummaryList.length, - separatorBuilder: (context, index) => SizedBox(height: 8), itemBuilder: (context, index) { return RecipeCard(recipe: provider.recipeSummaryList[index]); }, diff --git a/lib/widgets/recipe/timeline.dart b/lib/widgets/recipe/timeline.dart index 40aa361..e0aba3c 100644 --- a/lib/widgets/recipe/timeline.dart +++ b/lib/widgets/recipe/timeline.dart @@ -16,35 +16,39 @@ class RecipeTimeline extends StatefulWidget { class _RecipeTimeline extends State { Widget buildTimelineCard(BuildContext context, FoodRecord record) { - return BuildCard( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Icon( - Icons.date_range, - color: Theme.of(context).colorScheme.primary, - ), - SizedBox(width: 5), - Text( - record.date, - style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - ), - ], + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + Icons.date_range, + color: Theme.of(context).colorScheme.primary, + ), + SizedBox(width: 5), + Text( + record.date, + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + ], + ), + SizedBox(height: 6), + Padding( + padding: EdgeInsets.symmetric(vertical: 8, horizontal: 0), + child: BuildCard( + child: Column( + children: [ + Text( + record.name, + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 5), + buildNetworkImage(context, [record.imageUrl], 0), + ], + ), ), - Column( - children: [ - Text( - record.name, - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 5), - buildNetworkImage(context, [record.imageUrl], 0), - ], - ), - ], - ), + ), + ], ); } @@ -69,7 +73,7 @@ class _RecipeTimeline extends State { }, contentsBuilder: (context, index) { return Padding( - padding: const EdgeInsets.all(4), + padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 8), child: buildTimelineCard(context, recordList[index]), ); },