diff --git a/lib/api/moment.dart b/lib/api/moment.dart index d8d2127..af3f0a5 100644 --- a/lib/api/moment.dart +++ b/lib/api/moment.dart @@ -21,6 +21,14 @@ Future> queryMomentListApi() { ); } +Future queryMomentByPageApi(int currentPage, int pageSize) { + return HttpUtil().get( + "/moment/page", + queryParameters: {"currentPage": currentPage, "pageSize": pageSize}, + converter: (data) => PageMoment.fromJson(data), + ); +} + Future addMomentCommentApi(int id, String content) { return HttpUtil().post("/moment/$id/comment", data: content); } diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index 570bf25..71dcad3 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -4,4 +4,5 @@ class AppConfig { // http://192.168.1.3:8100 // 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://172.29.101.108:8100"; } \ No newline at end of file diff --git a/lib/layout/index.dart b/lib/layout/index.dart index ae7dc8f..5d3fab4 100644 --- a/lib/layout/index.dart +++ b/lib/layout/index.dart @@ -93,7 +93,7 @@ class SettingsDrawer extends StatelessWidget { } } -List homeActions() { +List homeActions(BuildContext context) { return [ IconButton( icon: Icon(Icons.search, color: Colors.white), @@ -101,15 +101,21 @@ List homeActions() { // 搜索功能 }, ), - Builder( - builder: (BuildContext context) { - return IconButton( - icon: Icon(Icons.settings, color: Colors.white), - onPressed: () { - Scaffold.of(context).openEndDrawer(); - }, - ); + IconButton( + icon: Icon(Icons.add, color: Colors.white), + onPressed: () { + Navigator.pushNamed(context, '/recordForm'); }, ), + // Builder( + // builder: (BuildContext context) { + // return IconButton( + // icon: Icon(Icons.settings, color: Colors.white), + // onPressed: () { + // Scaffold.of(context).openEndDrawer(); + // }, + // ); + // }, + // ), ]; } diff --git a/lib/models/moment.dart b/lib/models/moment.dart index 427e386..f914d00 100644 --- a/lib/models/moment.dart +++ b/lib/models/moment.dart @@ -51,3 +51,24 @@ class Moment { Map toJson() => _$MomentToJson(this); } + +@JsonSerializable() +class PageMoment { + final List records; + final int total; + final int size; + final int current; + final int pages; + + PageMoment({ + required this.records, + required this.total, + required this.size, + required this.current, + required this.pages, + }); + + factory PageMoment.fromJson(Map json) => _$PageMomentFromJson(json); + + Map toJson() => _$PageMomentToJson(this); +} diff --git a/lib/models/moment.g.dart b/lib/models/moment.g.dart index cd6554c..94a323c 100644 --- a/lib/models/moment.g.dart +++ b/lib/models/moment.g.dart @@ -52,3 +52,23 @@ Map _$MomentToJson(Moment instance) => { 'likeList': instance.likeList, 'commentList': instance.commentList, }; + +PageMoment _$PageMomentFromJson(Map json) => PageMoment( + records: + (json['records'] as List) + .map((e) => Moment.fromJson(e as Map)) + .toList(), + total: (json['total'] as num).toInt(), + size: (json['size'] as num).toInt(), + current: (json['current'] as num).toInt(), + pages: (json['pages'] as num).toInt(), +); + +Map _$PageMomentToJson(PageMoment instance) => + { + 'records': instance.records, + 'total': instance.total, + 'size': instance.size, + 'current': instance.current, + 'pages': instance.pages, + }; diff --git a/lib/views/home.dart b/lib/views/home.dart index 7e481a4..1796fb1 100644 --- a/lib/views/home.dart +++ b/lib/views/home.dart @@ -40,22 +40,21 @@ class _HomePage extends State { icon: Icons.account_circle_outlined, activeIcon: Icons.account_circle, page: ProfilePage(), - ) + ), ]; List get bottomNavItems => - navItems.map((item) => BottomNavigationBarItem( - icon: Icon(item.icon), - activeIcon: Icon(item.activeIcon), - label: item.label, - )).toList(); + navItems + .map( + (item) => BottomNavigationBarItem( + icon: Icon(item.icon), + activeIcon: Icon(item.activeIcon), + label: item.label, + ), + ) + .toList(); - List get tabPages => - navItems.map((item) => item.page).toList(); - - bool isShow(int index) { - return index == 0 || index == 2; - } + List get tabPages => navItems.map((item) => item.page).toList(); @override Widget build(BuildContext context) { @@ -64,19 +63,20 @@ class _HomePage extends State { centerTitle: true, title: Text('Food Hub', style: TextStyle(color: Colors.white)), backgroundColor: Theme.of(context).primaryColor, - leading: IconButton( - icon: Icon(Icons.menu, color: Colors.white), - onPressed: () { - // 打开侧边栏或菜单 + leading: Builder( + builder: (context) { + return IconButton( + icon: const Icon(Icons.menu), + color: Colors.white, + onPressed: () => Scaffold.of(context).openDrawer(), + ); }, ), - actions: homeActions(), + actions: homeActions(context), ), backgroundColor: Color(0xFFF5F5F5), - endDrawer: const SettingsDrawer(), + drawer: const SettingsDrawer(), body: tabPages[_currentIndex], - floatingActionButton: - isShow(_currentIndex) ? addItemButton(context) : null, bottomNavigationBar: NavBar( navItems: bottomNavItems, currentIndex: _currentIndex, @@ -89,17 +89,3 @@ class _HomePage extends State { ); } } - -Widget addItemButton(BuildContext context) { - void onAddItemClick(BuildContext context) { - Navigator.pushNamed(context, '/recordForm'); - } - - return FloatingActionButton( - mini: true, - onPressed: () => onAddItemClick(context), - backgroundColor: Theme.of(context).primaryColor, - shape: const CircleBorder(), - child: Icon(Icons.add, color: Colors.white), - ); -} diff --git a/lib/views/moment.dart b/lib/views/moment.dart index e442f40..ef314a8 100644 --- a/lib/views/moment.dart +++ b/lib/views/moment.dart @@ -1,3 +1,4 @@ +import 'package:easy_refresh/easy_refresh.dart'; import 'package:flutter/material.dart'; import 'package:food_hub_app/api/moment.dart'; import 'package:food_hub_app/models/moment.dart'; @@ -8,40 +9,188 @@ class MomentPage extends StatefulWidget { const MomentPage({super.key}); @override - State createState() => _MomentPage(); + State createState() => _MomentPageState(); } -class _MomentPage extends State { +class _MomentPageState extends State { List momentList = []; + // 分页相关变量 + int _page = 1; + final int _pageSize = 3; + bool _hasMore = true; + // 初始化EasyRefresh控制器 + final EasyRefreshController _freshController = EasyRefreshController( + controlFinishRefresh: true, + controlFinishLoad: true, + ); + + bool _showScrollToTop = false; + final ScrollController _scrollController = ScrollController(); + @override void initState() { super.initState(); - refreshRecipeList(); + // 初始加载数据 + _loadData(isRefresh: true); + _scrollController.addListener(_onScroll); } - Future refreshRecipeList() async { - final result = await queryMomentListApi(); + @override + void dispose() { + _scrollController.removeListener(_onScroll); + _freshController.dispose(); + _scrollController.dispose(); + super.dispose(); + } - setState(() { - momentList = result; - }); + // 统一的数据加载方法 + Future _loadData({required bool isRefresh}) async { + try { + // 如果是刷新,重置页码 + if (isRefresh) { + _page = 1; + } + + // 调用API获取数据,传入页码和页大小 + final result = await queryMomentByPageApi(_page, _pageSize); + + setState(() { + if (isRefresh) { + // 刷新时直接替换数据 + momentList = result.records; + } else { + // 加载更多时追加数据 + momentList.addAll(result.records); + } + + // 判断是否还有更多数据 + _hasMore = result.current < result.pages; + // 如果有更多数据,准备加载下一页 + if (_hasMore) { + _page++; + } + }); + } catch (e) { + // 处理错误 + debugPrint('加载数据失败: $e'); + } finally { + _freshController.finishRefresh(); + _freshController.resetFooter(); + } + } + + // 下拉刷新 + Future _onRefresh() async { + await _loadData(isRefresh: true); + } + + // 上拉加载 + Future _onLoad() async { + if (_hasMore) { + await _loadData(isRefresh: false); + _freshController.finishLoad(IndicatorResult.success); + } else { + _freshController.finishLoad(IndicatorResult.noMore); + } + } + + void _onScroll() { + // 当滚动距离超过300时显示返回顶部按钮 + if (_scrollController.offset > 300) { + if (!_showScrollToTop) { + setState(() { + _showScrollToTop = true; + }); + } + } else { + if (_showScrollToTop) { + setState(() { + _showScrollToTop = false; + }); + } + } + } + + // 滚动到顶部 + void _scrollToTop() { + _scrollController.animateTo( + 0, + duration: const Duration(milliseconds: 500), // 滚动动画时长 + curve: Curves.easeInOut, // 滚动动画曲线 + ); } @override Widget build(BuildContext context) { + // 空状态显示 if (momentList.isEmpty) { - return const TDEmpty(type: TDEmptyType.plain, emptyText: '暂无数据'); - } else { - return Padding( - padding: EdgeInsets.all(5), - child: ListView.builder( - itemCount: momentList.length, - itemBuilder: (context, index) { - return MomentCard(moment: momentList[index]); - }, - ), + return EasyRefresh( + controller: _freshController, + onRefresh: _onRefresh, + child: const TDEmpty(type: TDEmptyType.plain, emptyText: '暂无数据'), ); } + + // 有数据时显示列表 + return Stack( + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: 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: ListView.builder( + controller: _scrollController, + itemCount: momentList.length, + itemBuilder: (context, index) { + return MomentCard(moment: momentList[index]); + }, + ), + ), + ), + + // 返回顶部按钮 + if (_showScrollToTop) + Positioned( + right: 10, + bottom: 20, + child: FloatingActionButton( + onPressed: _scrollToTop, + backgroundColor: Colors.white, + elevation: 5, + mini: true, + child: const Icon( + Icons.arrow_upward + ), + ), + ), + ], + ); } } diff --git a/pubspec.lock b/pubspec.lock index 87717d9..259d93f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -210,7 +210,7 @@ packages: source: hosted version: "2.1.1" easy_refresh: - dependency: transitive + dependency: "direct main" description: name: easy_refresh sha256: "486e30abfcaae66c0f2c2798a10de2298eb9dc5e0bb7e1dba9328308968cae0c" diff --git a/pubspec.yaml b/pubspec.yaml index fcd36ee..c946b40 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -49,6 +49,7 @@ dependencies: logger: ^2.6.0 photo_view: ^0.15.0 carousel_slider: ^5.1.1 + easy_refresh: ^3.4.0 dependency_overrides: tdesign_flutter_adaptation: 3.16.0