diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index 10cf5e5..cd79309 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -5,10 +5,11 @@ class AppConfig { // http://14.103.235.151:81/food-service // static const String baseApiUrl = "http://14.103.235.151:81/food-service"; // static const String baseApiUrl = "http://192.168.1.3:8100"; - // static const String baseApiUrl = "https://cxx0822.iepose.cn/food-api"; - static const String baseApiUrl = "http://127.0.0.1:8083"; + static const String baseApiUrl = "https://cxx0822.iepose.cn/food-api"; + // static const String baseApiUrl = "http://192.168.1.103:8083"; static const String rustfsIp = '14.103.235.151'; static const String rustfsFileUrl = 'http://14.103.235.151:9100'; static const String bucketName = 'food'; - static const String imageBaseUrl = '$rustfsFileUrl/$bucketName/'; + // static const String imageBaseUrl = '$rustfsFileUrl/$bucketName/'; + static const String imageBaseUrl = '$baseApiUrl/'; } diff --git a/lib/provider/food_provider.dart b/lib/provider/food_provider.dart index 3dc2c76..655e3d2 100644 --- a/lib/provider/food_provider.dart +++ b/lib/provider/food_provider.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:food_hub_app/apis/moment.dart'; import 'package:food_hub_app/apis/recipe.dart'; import 'package:food_hub_app/apis/stats.dart'; +import 'package:food_hub_app/models/moment.dart'; import 'package:food_hub_app/models/recipe.dart'; import 'package:food_hub_app/models/stats.dart'; import 'package:food_hub_app/utils/date_util.dart'; @@ -13,6 +15,8 @@ class FoodProvider with ChangeNotifier { bool isLoading = false; String? error; + String queryCategory = '全部菜系'; + late List categoryList = []; late List recipeSummaryList = []; DateTime selectedDay = DateTime.now(); @@ -31,6 +35,11 @@ class FoodProvider with ChangeNotifier { late List categoryStats = []; late List rankStats = []; + List momentList = []; + int currentPage = 1; + final int pageSize = 3; + bool hasMore = true; + void resetRecordForm() { recordFormItem = FoodRecord.getEmpty(); notifyListeners(); @@ -41,6 +50,23 @@ class FoodProvider with ChangeNotifier { notifyListeners(); } + Future queryCategoryList() async { + try { + error = null; + notifyListeners(); + + final result = await queryCategoryApi(); + categoryList.clear(); + categoryList.add('全部菜系'); + categoryList.addAll(result); + } catch (e) { + error = '加载数据失败: $e'; + debugPrint('加载数据失败: $e'); + } finally { + notifyListeners(); + } + } + Future refreshRecipeList() async { if (isLoading) return; @@ -49,7 +75,8 @@ class FoodProvider with ChangeNotifier { error = null; notifyListeners(); - final result = await queryRecipeApi(RecipeQuery(category: "")); + final category = queryCategory == '全部菜系' ? '' : queryCategory; + final result = await queryRecipeApi(RecipeQuery(category: category)); recipeSummaryList = result; } catch (e) { error = '加载数据失败: $e'; @@ -131,4 +158,50 @@ class FoodProvider with ChangeNotifier { notifyListeners(); } } + + Future queryMomentByPage({bool isRefresh = true}) async { + if (isLoading) return; + + try { + isLoading = true; + error = null; + notifyListeners(); + + // 如果是刷新,重置页码 + if (isRefresh) { + currentPage = 1; + } + + final result = await queryMomentByPageApi(currentPage, pageSize); + + // 更新数据 + if (isRefresh) { + momentList = result.records; + } else { + momentList.addAll(result.records); + } + + // 判断是否还有更多数据 + hasMore = result.current < result.pages; + if (hasMore) { + currentPage++; + } + } catch (e) { + error = '加载数据失败: $e'; + debugPrint('加载数据失败: $e'); + } finally { + isLoading = false; + notifyListeners(); + } + } + + Future loadMoreMomentList() async { + if (hasMore && !isLoading) { + await queryMomentByPage(isRefresh: false); + } + } + + Future refreshMomentList() async { + await queryMomentByPage(isRefresh: true); + } } diff --git a/lib/views/home.dart b/lib/views/home.dart index eebb220..eeb927b 100644 --- a/lib/views/home.dart +++ b/lib/views/home.dart @@ -31,25 +31,35 @@ class _HomePage extends State { }, ), actions: [ - IconButton( - icon: Icon(Icons.search, color: Colors.white), - onPressed: () { - // 搜索功能 - }, - ), + Row( + children: [ + IconButton( + icon: Icon(Icons.search, color: Colors.white), + onPressed: () { + // 搜索功能 + }, + ), + IconButton( + icon: Icon(Icons.add, color: Colors.white), + onPressed: () { + buildBottomSheet(context); + }, + ) + ], + ) ], ), backgroundColor: Color(0xFFF5F5F5), drawer: SettingsDrawer(), body: Padding(padding: EdgeInsets.all(4), child: tabPages[_currentIndex]), - floatingActionButton: FloatingActionButton( - backgroundColor: Theme.of(context).colorScheme.primary, - onPressed: () => buildBottomSheet(context), - shape: const CircleBorder(), - mini: true, - child: const Icon(Icons.add, color: Colors.white, size: 30), - ), - floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, + // floatingActionButton: FloatingActionButton( + // backgroundColor: Theme.of(context).colorScheme.primary, + // onPressed: () => buildBottomSheet(context), + // shape: const CircleBorder(), + // mini: true, + // child: const Icon(Icons.add, color: Colors.white, size: 30), + // ), + // floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, bottomNavigationBar: buildBottomNavBar( currentIndex: _currentIndex, onTap: (index) { diff --git a/lib/views/moment.dart b/lib/views/moment.dart index c6d6d9f..dcf4571 100644 --- a/lib/views/moment.dart +++ b/lib/views/moment.dart @@ -1,9 +1,10 @@ import 'package:easy_refresh/easy_refresh.dart'; import 'package:flutter/material.dart'; -import 'package:food_hub_app/apis/moment.dart'; -import 'package:food_hub_app/models/moment.dart'; +import 'package:food_hub_app/provider/food_provider.dart'; +import 'package:food_hub_app/widgets/common/easy_refresh.dart'; import 'package:food_hub_app/widgets/common/index.dart'; import 'package:food_hub_app/widgets/moment/card.dart'; +import 'package:provider/provider.dart'; class MomentPage extends StatefulWidget { const MomentPage({super.key}); @@ -13,26 +14,23 @@ class MomentPage extends StatefulWidget { } class _MomentPageState extends State { - List momentList = []; - int _currentPage = 1; - final int _pageSize = 3; - bool _hasMore = true; - // 初始化EasyRefresh控制器 final EasyRefreshController _freshController = EasyRefreshController( controlFinishRefresh: true, controlFinishLoad: true, ); - - bool _showScrollToTop = false; final ScrollController _scrollController = ScrollController(); + bool _showScrollToTop = false; @override void initState() { super.initState(); - // 初始加载数据 - _loadData(isRefresh: true); _scrollController.addListener(_onScroll); + + // 初始化加载数据 + WidgetsBinding.instance.addPostFrameCallback((_) { + context.read().refreshMomentList(); + }); } @override @@ -43,50 +41,18 @@ class _MomentPageState extends State { super.dispose(); } - // 统一的数据加载方法 - Future _loadData({required bool isRefresh}) async { - try { - // 如果是刷新,重置页码 - if (isRefresh) { - _currentPage = 1; - } - - final result = await queryMomentByPageApi(_currentPage, _pageSize); - - setState(() { - if (isRefresh) { - // 刷新时直接替换数据 - momentList = result.records; - } else { - // 加载更多时追加数据 - momentList.addAll(result.records); - } - - // 判断是否还有更多数据 - _hasMore = result.current < result.pages; - // 如果有更多数据,准备加载下一页 - if (_hasMore) { - _currentPage++; - } - }); - } catch (e) { - // 处理错误 - debugPrint('加载数据失败: $e'); - } finally { - _freshController.finishRefresh(); - _freshController.resetFooter(); - } - } - // 下拉刷新 Future _onRefresh() async { - await _loadData(isRefresh: true); + await context.read().refreshMomentList(); + _freshController.finishRefresh(); } // 上拉加载 Future _onLoad() async { - if (_hasMore) { - await _loadData(isRefresh: false); + final provider = context.read(); + + if (provider.hasMore) { + await provider.loadMoreMomentList(); _freshController.finishLoad(IndicatorResult.success); } else { _freshController.finishLoad(IndicatorResult.noMore); @@ -111,78 +77,47 @@ class _MomentPageState extends State { } // 滚动到顶部 - void _scrollToTop() { - _scrollController.animateTo( - 0, - duration: const Duration(milliseconds: 500), // 滚动动画时长 - curve: Curves.easeInOut, // 滚动动画曲线 + void _scrollToTop() => scrollToTopAnimateTo(_scrollController); + + Widget _buildMomentList(FoodProvider provider) { + return ListView.builder( + controller: _scrollController, + itemCount: provider.momentList.length, + itemBuilder: (context, index) { + return MomentCard(moment: provider.momentList[index]); + }, ); } @override Widget build(BuildContext context) { + final provider = context.watch(); + // 空状态显示 - if (momentList.isEmpty) { - return EasyRefresh( - controller: _freshController, - onRefresh: _onRefresh, - child: buildEmptyData(), - ); + if (provider.momentList.isEmpty) { + return buildEmptyData(); } // 有数据时显示列表 return Stack( children: [ - EasyRefresh( - controller: _freshController, - header: ClassicHeader( - dragText: '下拉刷新', - armedText: '释放刷新', - readyText: '准备刷新', - processingText: '刷新中...', - processedText: '刷新完成', - failedText: '刷新失败', - noMoreText: '没有更多数据', - showText: true, - messageText: '更新于 %T', - showMessage: true, - ), - footer: ClassicFooter( - dragText: '上拉加载', - armedText: '释放加载', - readyText: '准备加载', - processingText: '加载中...', - processedText: '加载完成', - failedText: '加载失败', - noMoreText: '没有更多数据', - showText: true, - messageText: '更新于 %T', - showMessage: true, - ), + buildEasyRefresh( + freshController: _freshController, onRefresh: _onRefresh, onLoad: _onLoad, - child: ListView.builder( - controller: _scrollController, - itemCount: momentList.length, - itemBuilder: (context, index) { - return MomentCard(moment: momentList[index]); - }, + body: Stack( + children: [ + if (provider.isLoading) + buildLoadingIndicator(context) + else + _buildMomentList(provider), + ], ), ), // 返回顶部按钮 if (_showScrollToTop) - Positioned( - right: 0, - bottom: 0, - child: FloatingActionButton( - onPressed: _scrollToTop, - backgroundColor: Theme.of(context).colorScheme.primary, - elevation: 5, - mini: true, - child: const Icon(Icons.arrow_upward, color: Colors.white), - ), - ), + buildScrollToTop(context: context, scrollToTop: _scrollToTop), ], ); } diff --git a/lib/views/record_form.dart b/lib/views/record_form.dart index 8cc1f76..1318eef 100644 --- a/lib/views/record_form.dart +++ b/lib/views/record_form.dart @@ -86,7 +86,8 @@ class _RecordFormPageState extends State { else buildImagePreviewItem( context: context, - imageUrl: provider.recordFormItem.imageUrl, + imageUrls: [provider.recordFormItem.imageUrl], + index: 0, onRemoveImage: () => _removeImage(provider), ), ], @@ -110,7 +111,7 @@ class _RecordFormPageState extends State { return const Iterable.empty(); } return _foodNameList.where( - (option) => option.toLowerCase().contains( + (option) => option.toLowerCase().contains( textEditingValue.text.toLowerCase(), ), ); @@ -123,10 +124,10 @@ class _RecordFormPageState extends State { }, optionsViewBuilder: ( - BuildContext context, - AutocompleteOnSelected onSelected, - Iterable options, - ) { + BuildContext context, + AutocompleteOnSelected onSelected, + Iterable options, + ) { Widget buildOptionItem(String option) { return InkWell( onTap: () => onSelected(option), @@ -164,11 +165,11 @@ class _RecordFormPageState extends State { }, fieldViewBuilder: ( - BuildContext context, - TextEditingController controller, - FocusNode focusNode, - VoidCallback onFieldSubmitted, - ) { + BuildContext context, + TextEditingController controller, + FocusNode focusNode, + VoidCallback onFieldSubmitted, + ) { WidgetsBinding.instance.addPostFrameCallback((_) { if (provider.recordFormItem.name.isNotEmpty && controller.text.isEmpty) { @@ -184,11 +185,13 @@ class _RecordFormPageState extends State { } Widget? buildSuffixIcon() { - return controller.text.isNotEmpty + final isShow = controller.text.isNotEmpty && !provider.isEditing; + + return isShow ? IconButton( - icon: Icon(Icons.clear, size: 18), - onPressed: onClearRecipeName, - ) + icon: Icon(Icons.clear, size: 18), + onPressed: onClearRecipeName, + ) : null; } diff --git a/lib/widgets/common/easy_refresh.dart b/lib/widgets/common/easy_refresh.dart new file mode 100644 index 0000000..fc42b24 --- /dev/null +++ b/lib/widgets/common/easy_refresh.dart @@ -0,0 +1,65 @@ +import 'package:easy_refresh/easy_refresh.dart'; +import 'package:flutter/material.dart'; + +Widget buildEasyRefresh({ + required EasyRefreshController freshController, + required Future Function()? onRefresh, + required Future Function()? onLoad, + required Widget body, +}) { + return EasyRefresh( + controller: freshController, + header: ClassicHeader( + dragText: '下拉刷新', + armedText: '释放刷新', + readyText: '准备刷新', + processingText: '刷新中...', + processedText: '刷新完成', + failedText: '刷新失败', + noMoreText: '没有更多数据', + showText: true, + messageText: '更新于 %T', + showMessage: true, + ), + footer: ClassicFooter( + dragText: '上拉加载', + armedText: '释放加载', + readyText: '准备加载', + processingText: '加载中...', + processedText: '加载完成', + failedText: '加载失败', + noMoreText: '没有更多数据', + showText: true, + messageText: '更新于 %T', + showMessage: true, + ), + onRefresh: onRefresh, + onLoad: onLoad, + child: body, + ); +} + +Widget buildScrollToTop({ + required BuildContext context, + required VoidCallback scrollToTop, +}) { + return Positioned( + right: 0, + bottom: 20, + child: FloatingActionButton( + onPressed: scrollToTop, + backgroundColor: Theme.of(context).colorScheme.primary, + elevation: 2, + mini: true, + child: const Icon(Icons.arrow_upward, color: Colors.white), + ), + ); +} + +void scrollToTopAnimateTo(ScrollController controller) { + controller.animateTo( + 0, + duration: const Duration(milliseconds: 500), // 滚动动画时长 + curve: Curves.easeInOut, // 滚动动画曲线 + ); +} diff --git a/lib/widgets/common/image.dart b/lib/widgets/common/image.dart index d5272ff..374a3c3 100644 --- a/lib/widgets/common/image.dart +++ b/lib/widgets/common/image.dart @@ -75,7 +75,7 @@ class _ImagePreviewPageState extends State { pageOptions: widget.images.map((url) { return PhotoViewGalleryPageOptions( - imageProvider: NetworkImage(url), + imageProvider: NetworkImage('${AppConfig.imageBaseUrl}$url'), minScale: PhotoViewComputedScale.contained, maxScale: PhotoViewComputedScale.covered * 2, // 点击空白处关闭预览 @@ -91,42 +91,27 @@ class _ImagePreviewPageState extends State { } } -Widget buildFileImage(BuildContext context, File imageFile) { - return GestureDetector( - onTap: () => showFullScreenImage(context, FileImage(imageFile)), - child: Image( - image: FileImage(imageFile), - width: 120, - height: 120, - fit: BoxFit.cover, - loadingBuilder: (context, child, loadingProgress) { - if (loadingProgress == null) return child; - return buildImageLoadingIndicator(loadingProgress); - }, - ), - ); -} - -Widget buildNetworkImage(BuildContext context, String url) { +Widget buildNetworkImage( + BuildContext context, + List imageUrls, + int index, +) { return AspectRatio( - aspectRatio: 1.5, + aspectRatio: 4 / 3, child: ClipRRect( borderRadius: BorderRadius.circular(8), child: GestureDetector( onTap: () { - showFullScreenImage( - context, - NetworkImage('${AppConfig.imageBaseUrl}$url'), - ); + showFullScreenImage(context, imageUrls, index); }, child: Image.network( - '${AppConfig.imageBaseUrl}$url', + '${AppConfig.imageBaseUrl}${imageUrls[index]}', fit: BoxFit.cover, loadingBuilder: (context, child, loadingProgress) { if (loadingProgress == null) return child; return buildImageLoadingIndicator(loadingProgress); }, - errorBuilder: (context, error, stackTrace) => _buildErrorImage(), + errorBuilder: (context, error, stackTrace) => buildErrorImage(), ), ), ), @@ -135,7 +120,8 @@ Widget buildNetworkImage(BuildContext context, String url) { Widget buildImagePreviewItem({ required BuildContext context, - required String imageUrl, + required List imageUrls, + required int index, required VoidCallback onRemoveImage, }) { return Container( @@ -144,7 +130,7 @@ Widget buildImagePreviewItem({ borderRadius: BorderRadius.circular(8), child: Stack( children: [ - buildNetworkImage(context, imageUrl), + buildNetworkImage(context, imageUrls, index), buildDeleteImage(onRemoveImage: onRemoveImage), ], ), @@ -168,7 +154,7 @@ Widget buildImageLoadingIndicator(ImageChunkEvent? loadingProgress) { ); } -Widget _buildErrorImage() { +Widget buildErrorImage() { return Container( color: Colors.grey[200], child: const Icon(Icons.image, color: Colors.grey, size: 30), @@ -183,7 +169,7 @@ Widget _buildPhotoView(ImageProvider imageProvider) { maxScale: PhotoViewComputedScale.covered * 2, initialScale: PhotoViewComputedScale.contained, loadingBuilder: (context, event) => buildImageLoadingIndicator(event), - errorBuilder: (context, error, stackTrace) => _buildErrorImage(), + errorBuilder: (context, error, stackTrace) => buildErrorImage(), ); } @@ -198,29 +184,40 @@ Widget _buildCloseImage(BuildContext context) { ); } -void showFullScreenImage(BuildContext context, ImageProvider imageProvider) { - Navigator.of(context).push( - PageRouteBuilder( - opaque: false, - pageBuilder: ( - BuildContext context, - Animation animation, - Animation secondaryAnimation, - ) { - return Scaffold( - backgroundColor: Colors.black.withAlpha(200), - body: Stack( - children: [ - // 可缩放图片 - Positioned.fill(child: _buildPhotoView(imageProvider)), - // 关闭按钮 - _buildCloseImage(context), - ], - ), - ); - }, +void showFullScreenImage( + BuildContext context, + List imageUrls, + int index, +) { + Navigator.push( + context, + MaterialPageRoute( + builder: + (context) => ImagePreviewPage(images: imageUrls, initialIndex: index), ), ); + // Navigator.of(context).push( + // PageRouteBuilder( + // opaque: false, + // pageBuilder: ( + // BuildContext context, + // Animation animation, + // Animation secondaryAnimation, + // ) { + // return Scaffold( + // backgroundColor: Colors.black.withAlpha(200), + // body: Stack( + // children: [ + // // 可缩放图片 + // Positioned.fill(child: _buildPhotoView(imageProvider)), + // // 关闭按钮 + // _buildCloseImage(context), + // ], + // ), + // ); + // }, + // ), + // ); } Widget buildImageUploadButton({required VoidCallback onPickImage}) { diff --git a/lib/widgets/common/index.dart b/lib/widgets/common/index.dart index e77fc83..5e98fbf 100644 --- a/lib/widgets/common/index.dart +++ b/lib/widgets/common/index.dart @@ -29,20 +29,18 @@ InputDecoration formInputDecoration({ } Widget buildCard({required BuildContext context, required Widget child}) { - final colors = Theme - .of(context) - .colorScheme; + final colors = Theme.of(context).colorScheme; return Card( elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: Container( width: double.infinity, decoration: BoxDecoration( - color: colors.surface, + color: Colors.white, borderRadius: BorderRadius.circular(12), - border: Border.all(color: colors.outline.withAlpha(50), width: 1), + border: Border.all(color: colors.outline.withAlpha(50)), ), - child: Padding(padding: EdgeInsets.all(8), child: child), + child: Padding(padding: EdgeInsets.all(10 ), child: child), ), ); } @@ -66,11 +64,7 @@ Widget buildToggleSwitch({ initialLabelIndex: tabValues.indexOf(currentTab), totalSwitches: tabValues.length, labels: labels, - activeBgColor: [Theme - .of(context) - .colorScheme - .primary - ], + activeBgColor: [Theme.of(context).colorScheme.primary], activeFgColor: Colors.white, inactiveBgColor: Colors.grey.shade200, inactiveFgColor: Colors.grey.shade700, @@ -99,10 +93,7 @@ Widget circleIconButton({ fixedSize: Size(size, size), shape: CircleBorder(), elevation: 0, - backgroundColor: Theme - .of(context) - .colorScheme - .primary, + backgroundColor: Theme.of(context).colorScheme.primary, padding: EdgeInsets.zero, minimumSize: const Size(0, 0), ), @@ -111,9 +102,7 @@ Widget circleIconButton({ } Widget buildTag(BuildContext context, String title) { - final colors = Theme - .of(context) - .colorScheme; + final colors = Theme.of(context).colorScheme; return Container( padding: EdgeInsets.symmetric(horizontal: 10, vertical: 4), @@ -195,11 +184,7 @@ Widget buildLoadingIndicator(BuildContext context) { color: colors.surface, borderRadius: BorderRadius.circular(12), boxShadow: [ - BoxShadow( - color: Colors.black12, - blurRadius: 8, - offset: Offset(0, 2), - ), + BoxShadow(color: Colors.black12, blurRadius: 8, offset: Offset(0, 2)), ], ), child: Column( diff --git a/lib/widgets/moment/card.dart b/lib/widgets/moment/card.dart index 0df9ac1..c175a40 100644 --- a/lib/widgets/moment/card.dart +++ b/lib/widgets/moment/card.dart @@ -102,17 +102,6 @@ class MomentCard extends StatelessWidget { final List imageUrls = moment.imageList.map((path) => path).toList(); - void imageTapClick(int index) { - Navigator.push( - context, - MaterialPageRoute( - builder: - (context) => - ImagePreviewPage(images: imageUrls, initialIndex: index), - ), - ); - } - return GridView.count( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), @@ -121,13 +110,7 @@ class MomentCard extends StatelessWidget { mainAxisSpacing: 4, childAspectRatio: itemAspectRatio, children: List.generate(imageCount, (index) { - // 单个图片项:添加点击事件 - return GestureDetector( - // 点击图片时,跳转到预览页面 - onTap: () => imageTapClick(index), - // 原图片组件 - child: buildNetworkImage(context, imageUrls[index]), - ); + return buildNetworkImage(context, imageUrls, index); }), ); } diff --git a/lib/widgets/recipe/calendar.dart b/lib/widgets/recipe/calendar.dart index f7a7b60..1b60930 100644 --- a/lib/widgets/recipe/calendar.dart +++ b/lib/widgets/recipe/calendar.dart @@ -116,7 +116,7 @@ class _RecipeCalendarState extends State { return Card( elevation: 0, - color: colors.onSecondary, + color: colors.primary.withAlpha(50), child: Padding( padding: EdgeInsets.all(10), child: Row( diff --git a/lib/widgets/recipe/card.dart b/lib/widgets/recipe/card.dart index e1c1c3f..b0b94ec 100644 --- a/lib/widgets/recipe/card.dart +++ b/lib/widgets/recipe/card.dart @@ -14,7 +14,8 @@ class RecipeCard extends StatelessWidget { @override Widget build(BuildContext context) { - final String firstImageUrl = recipe.recordList.first.imageUrl; + final List imageUrls = + recipe.recordList.reversed.map((record) => record.imageUrl).toList(); Widget buildRecipeContent(BuildContext context) { return Column( @@ -33,6 +34,7 @@ class RecipeCard extends StatelessWidget { ), ], ), + SizedBox(height: 5), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, @@ -84,7 +86,7 @@ class RecipeCard extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - buildNetworkImage(context, firstImageUrl), + buildNetworkImage(context, imageUrls, 0), SizedBox(height: 8), GestureDetector( onTap: () => navigatorToRecipeDetail(context), diff --git a/lib/widgets/recipe/list.dart b/lib/widgets/recipe/list.dart index 44f2128..7b6db3a 100644 --- a/lib/widgets/recipe/list.dart +++ b/lib/widgets/recipe/list.dart @@ -19,18 +19,82 @@ class _RecipeListState extends State { // 初始化加载数据 WidgetsBinding.instance.addPostFrameCallback((_) { context.read().refreshRecipeList(); + context.read().queryCategoryList(); }); } + Widget _buildSelectCategory(FoodProvider provider) { + final colors = Theme.of(context).colorScheme; + + return Container( + width: 150, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.grey[200]!), + ), + padding: EdgeInsets.symmetric(horizontal: 16), + child: DropdownButton( + value: provider.queryCategory, + hint: Text( + '请选择菜谱类别', + style: TextStyle(color: Colors.grey[500]), + ), + isExpanded: true, + borderRadius: BorderRadius.circular(12), + dropdownColor: Colors.white, + elevation: 6, + underline: Container(), + items: provider.categoryList.map((String value) { + return DropdownMenuItem( + value: value, + child: Row( + children: [ + Container( + width: 8, + height: 8, + decoration: BoxDecoration( + color: colors.primary, + shape: BoxShape.circle, + ), + ), + SizedBox(width: 8), + Expanded( + child: Text( + value, + style: TextStyle(fontSize: 14), + ), + ) + ], + ), + ); + }).toList(), + onChanged: (value) { + setState(() { + provider.queryCategory = value!; + provider.refreshRecipeList(); + }); + }, + ), + ); + } + Widget _buildContent(FoodProvider provider) { if (provider.recipeSummaryList.isEmpty) { return buildEmptyData(); } else { - return ListView.builder( - itemCount: provider.recipeSummaryList.length, - itemBuilder: (context, index) { - return RecipeCard(recipe: provider.recipeSummaryList[index]); - }, + return Column( + children: [ + _buildSelectCategory(provider), + Expanded( + child: ListView.builder( + itemCount: provider.recipeSummaryList.length, + itemBuilder: (context, index) { + return RecipeCard(recipe: provider.recipeSummaryList[index]); + }, + ), + ), + ], ); } } diff --git a/lib/widgets/recipe/timeline.dart b/lib/widgets/recipe/timeline.dart index 278bdd8..91fd989 100644 --- a/lib/widgets/recipe/timeline.dart +++ b/lib/widgets/recipe/timeline.dart @@ -41,7 +41,7 @@ class _RecipeTimeline extends State { style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 5), - buildNetworkImage(context, record.imageUrl), + buildNetworkImage(context, [record.imageUrl], 0), ], ), ], @@ -88,7 +88,12 @@ class _RecipeTimeline extends State { (year) => provider.refreshRecordList("$year-01-01", "$year-12-31"), ), - Expanded(child: buildTimeline(context, provider.recordList)), + Expanded( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 0, horizontal: 10), + child: buildTimeline(context, provider.recordList), + ), + ), ], ); }