/// 首页模板 library; import 'package:card_swiper/card_swiper.dart'; import 'package:easy_refresh/easy_refresh.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:get/get.dart'; import 'package:loopin/components/backtop.dart'; import 'package:loopin/components/loading.dart'; import 'package:loopin/controller/shop_index_controller.dart'; import 'package:loopin/utils/index.dart'; class IndexPage extends StatefulWidget { const IndexPage({super.key}); @override State createState() => _IndexPageState(); } class _IndexPageState extends State with SingleTickerProviderStateMixin { // 分类列表 // List cateList = [ // { // 'id': 1, // 'list': [ // { // 'icon': 'order.svg', // 'label': '我的订单', // }, // { // 'icon': 'chongzhi.svg', // 'label': '充值中心', // }, // {'icon': 'qianbao.svg', 'label': '余额'}, // {'icon': 'comment.svg', 'label': '评价中心'} // ] // } // ]; final ScrollController pageScrollController = ScrollController(); final ShopIndexController controller = Get.put(ShopIndexController()); // 瀑布流卡片 Widget cardList(item) { return GestureDetector( child: Container( clipBehavior: Clip.antiAlias, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(15.0), boxShadow: [ BoxShadow( color: Colors.black.withAlpha(5), offset: Offset(0.0, 1.0), blurRadius: 1.0, spreadRadius: 0.0, ), ]), child: Column( children: [ Image.network('${item['pic']}'), Container( padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, spacing: 5.0, children: [ Text( '${item['name']}', style: TextStyle(fontSize: 14.0, height: 1.2), maxLines: 2, overflow: TextOverflow.ellipsis, ), Row( spacing: 5.0, children: [ Text.rich( TextSpan(style: TextStyle(color: Colors.red, fontSize: 12.0, fontWeight: FontWeight.w700, fontFamily: 'Arial'), children: [ TextSpan(text: '¥'), TextSpan( text: '${item['price']}', style: TextStyle( fontSize: 16.0, )), ]), ), Text( '已售${Utils().graceNumber(int.parse(item['sales'] ?? '0'))}件', style: TextStyle(color: Colors.grey, fontSize: 10.0), ), ], ), Text( '${item['storeName']}', style: TextStyle(color: Colors.grey, fontSize: 12.0), ), ], ), ) ], ), ), onTap: () { Get.toNamed('/goods', arguments: item['id']); }, ); } @override void initState() { super.initState(); controller.initTabs(); } @override Widget build(BuildContext context) { return Obx(() { final tabIndex = controller.currentTabIndex.value; final currentTab = controller.tabs[tabIndex]; return Scaffold( backgroundColor: Colors.grey[50], body: Column( children: [ // 顶部固定区域(轮播图 + TabBar) _buildTopSection(), // 内容区域 Expanded( child: TabBarView( controller: controller.tabController, children: controller.tabList.asMap().entries.map((entry) { final index = entry.key; return _buildTabContent(index); }).toList(), ), ), ], ), floatingActionButton: currentTab != null ? Backtop( controller: currentTab.scrollController, offset: currentTab.scrollOffset.value, ) : null, ); }); } // 构建顶部固定区域 Widget _buildTopSection() { double screenWidth = MediaQuery.of(context).size.width; int tabCount = controller.tabList.length; // 每个 Tab 的最小宽度 double minTabWidth = 80; // 是否可滚动 bool isScrollable = tabCount * minTabWidth > screenWidth; return Column( children: [ // 轮播图 Container( width: double.infinity, height: 240, decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Color(0xFFFF5000), Color(0xFFfcaec4)], ), ), child: controller.swiperData.length <= 1 ? (controller.swiperData.isNotEmpty ? Image.network( controller.swiperData.first['images'] ?? '', fit: BoxFit.fill, ) : const SizedBox.shrink()) : Swiper( itemCount: controller.swiperData.length, autoplay: true, loop: true, pagination: SwiperPagination( builder: DotSwiperPaginationBuilder( color: Colors.white70, activeColor: Colors.white, ), ), itemBuilder: (context, index) { final imageUrl = controller.swiperData[index]['images'] ?? ''; return imageUrl.isNotEmpty ? Image.network(imageUrl, fit: BoxFit.fill) : const SizedBox.shrink(); }, ), ), // TabBar Container( color: Colors.white, child: TabBar( controller: controller.tabController, tabs: controller.tabList.map((item) { return Tab( child: Text(item['name'], style: const TextStyle(fontWeight: FontWeight.bold)), ); }).toList(), isScrollable: isScrollable, overlayColor: WidgetStateProperty.all(Colors.transparent), unselectedLabelColor: Colors.black87, labelColor: Color.fromARGB(255, 236, 108, 49), indicator: const UnderlineTabIndicator(borderSide: BorderSide(color: Color.fromARGB(255, 236, 108, 49), width: 2.0)), unselectedLabelStyle: const TextStyle(fontSize: 16.0, fontFamily: 'Microsoft YaHei'), labelStyle: const TextStyle(fontSize: 18.0, fontFamily: 'Microsoft YaHei', fontWeight: FontWeight.bold), dividerHeight: 0, ), ), ], ); } // 构建标签页内容 Widget _buildTabContent(int index) { final tabState = controller.tabs[index]!; return Obx(() { if (tabState.dataList.isEmpty && tabState.isLoading.value) { return Center( child: RefreshProgressIndicator( backgroundColor: Colors.white, color: Color(0xFFFF5000), ), ); } // 添加 下拉刷新 return EasyRefresh( onRefresh: () async { await controller.refreshData(index); }, header: ClassicHeader( dragText: '下拉刷新', armedText: '释放刷新', readyText: '刷新中...', processingText: '刷新完成', messageText: '最后更新于 %T', ), child: CustomScrollView( controller: tabState.scrollController, key: PageStorageKey('tab_$index'), physics: const AlwaysScrollableScrollPhysics(), // 确保可下拉 slivers: [ SliverPadding( padding: EdgeInsets.fromLTRB(10, 10, 10, 10), sliver: tabState.dataList.isEmpty ? SliverToBoxAdapter( child: SizedBox( height: MediaQuery.of(context).size.height - 500, // 给个足够高度让下拉触发 child: Center(child: _emptyTip('暂无数据')), ), ) : SliverMasonryGrid.count( crossAxisCount: 2, mainAxisSpacing: 10.0, crossAxisSpacing: 10.0, childCount: tabState.dataList.length, itemBuilder: (context, idx) => cardList(tabState.dataList[idx]), ), ), SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.symmetric(vertical: 20.0), child: Center( child: tabState.isLoading.value ? const Loading(title: 'loading...') : (tabState.dataList.isNotEmpty ? const Text('没有更多数据了') : const SizedBox.shrink()), ), ), ), ], ), ); }); } // 空状态提示 Widget _emptyTip(String text) { return Center( child: Padding( padding: const EdgeInsets.only(top: 50), child: Column( mainAxisSize: MainAxisSize.min, children: [ Image.asset('assets/images/empty.png', width: 100), const SizedBox(height: 8), Text( text, style: const TextStyle(color: Colors.grey, fontSize: 13), ), ], ), ), ); } }