flutter/lib/pages/index/index.dart

332 lines
12 KiB
Dart
Raw Permalink Normal View History

2025-07-21 15:46:30 +08:00
/// 首页模板
library;
import 'package:card_swiper/card_swiper.dart';
2025-09-09 10:57:52 +08:00
import 'package:dynamic_tabbar/dynamic_tabbar.dart';
2025-08-21 10:50:38 +08:00
import 'package:easy_refresh/easy_refresh.dart';
2025-07-21 15:46:30 +08:00
import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:get/get.dart';
2025-09-09 10:57:52 +08:00
import 'package:loopin/IM/im_friend_listeners.dart';
2025-08-21 10:50:38 +08:00
import 'package:loopin/components/backtop.dart';
import 'package:loopin/components/loading.dart';
import 'package:loopin/components/network_or_asset_image.dart';
2025-08-21 10:50:38 +08:00
import 'package:loopin/controller/shop_index_controller.dart';
2025-09-09 10:57:52 +08:00
import 'package:loopin/styles/index.dart';
2025-08-21 10:50:38 +08:00
import 'package:loopin/utils/index.dart';
2025-07-21 15:46:30 +08:00
class IndexPage extends StatefulWidget {
const IndexPage({super.key});
@override
State<IndexPage> createState() => _IndexPageState();
}
class _IndexPageState extends State<IndexPage> with SingleTickerProviderStateMixin {
// 分类列表
2025-08-21 10:50:38 +08:00
// 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();
late ShopIndexController controller;
2025-07-21 15:46:30 +08:00
// 瀑布流卡片
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(),
NetworkOrAssetImage(
imageUrl: '${item['pic']}',
width: double.infinity,
placeholderAsset: 'assets/images/bk.jpg',
),
2025-07-21 15:46:30 +08:00
Container(
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 5.0,
children: [
Text(
2025-08-21 10:50:38 +08:00
'${item['name']}',
2025-07-21 15:46:30 +08:00
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(
2025-09-03 11:25:31 +08:00
'已售${Utils.graceNumber(int.parse(item['sales'] ?? '0'))}',
2025-07-21 15:46:30 +08:00
style: TextStyle(color: Colors.grey, fontSize: 10.0),
),
],
),
Text(
2025-08-21 10:50:38 +08:00
'${item['storeName']}',
2025-07-21 15:46:30 +08:00
style: TextStyle(color: Colors.grey, fontSize: 12.0),
),
],
),
)
],
),
),
onTap: () {
// Get.toNamed('/goods', arguments: item['id']);
Get.toNamed('/goods', arguments: {'goodsId': item['id']});
2025-07-21 15:46:30 +08:00
},
);
}
@override
void initState() {
super.initState();
controller = Get.find<ShopIndexController>();
2025-09-17 15:32:18 +08:00
// controller.initTabs(vsync: this);
controller.initTabs();
2025-07-21 15:46:30 +08:00
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[50],
body: Column(
children: [
_buildTopSection(),
// 内容区域
Expanded(
2025-09-17 15:32:18 +08:00
child: controller.tabList.isEmpty
2025-09-09 10:57:52 +08:00
? Center(child: CircularProgressIndicator())
: Obx(
() {
final tabs = controller.tabList.asMap().entries.map((entry) {
final idx = entry.key;
final item = entry.value;
return TabData(
index: idx,
title: Tab(
child: Center(
child: Text(
item['name'] ?? '',
style: const TextStyle(fontWeight: FontWeight.bold),
overflow: TextOverflow.ellipsis,
softWrap: false,
),
),
),
content: _buildTabContent(idx),
);
}).toList();
return DynamicTabBarWidget(
onTabControllerUpdated: (tabController) {
2025-09-17 15:32:18 +08:00
// controller.tabController = tabController;
// controller.initTabs(changeController: tabController, vsync: this);
controller.addTabListener(tabController);
2025-09-09 10:57:52 +08:00
// 强制选中第一个
if (tabController.index != 0) {
tabController.animateTo(0);
}
},
onTabChanged: (index) {
2025-09-12 15:45:53 +08:00
logger.w("当前选中ssssstab: $index");
2025-09-09 10:57:52 +08:00
},
dynamicTabs: tabs,
tabAlignment: TabAlignment.start,
isScrollable: true,
showNextIcon: false, // 显示前进图标
showBackIcon: false, // 显示后退图标
labelColor: FStyle.primaryColor,
indicatorColor: FStyle.primaryColor,
overlayColor: WidgetStateProperty.all(Colors.transparent),
unselectedLabelColor: Colors.black87,
indicator: const UnderlineTabIndicator(
borderSide: BorderSide(color: Color.fromARGB(255, 236, 108, 49), width: 2.0),
),
unselectedLabelStyle: const TextStyle(
fontSize: 14.0,
fontFamily: 'Microsoft YaHei',
),
labelStyle: const TextStyle(
fontSize: 14.0,
fontFamily: 'Microsoft YaHei',
fontWeight: FontWeight.bold,
),
dividerHeight: 0,
);
},
),
),
],
),
floatingActionButton: Obx(() {
final tabIndex = controller.currentTabIndex.value;
final currentTab = controller.tabs[tabIndex];
if (currentTab == null) return const SizedBox.shrink();
2025-08-21 10:50:38 +08:00
return Backtop(
controller: currentTab.scrollController,
offset: currentTab.scrollOffset.value,
);
}),
);
2025-08-21 10:50:38 +08:00
}
// 构建顶部固定区域
Widget _buildTopSection() {
2025-09-17 15:32:18 +08:00
if (controller.swiperData.isEmpty) {
return SizedBox();
}
2025-08-21 10:50:38 +08:00
return Column(
children: [
// 轮播图
Obx(() {
if (controller.swiperData.isEmpty) return const SizedBox.shrink();
if (controller.swiperData.length == 1) {
final imageUrl = controller.swiperData.first['images'] ?? '';
return Image.network(imageUrl, fit: BoxFit.fill, width: double.infinity, height: 240);
}
return SizedBox(
width: double.infinity,
height: 240,
child: Swiper(
itemCount: controller.swiperData.length,
autoplay: true,
loop: true,
pagination: const SwiperPagination(
builder: DotSwiperPaginationBuilder(
color: Colors.white70,
activeColor: Colors.white,
2025-07-21 15:46:30 +08:00
),
),
itemBuilder: (context, index) {
final imageUrl = controller.swiperData[index]['images'] ?? '';
return imageUrl.isNotEmpty ? Image.network(imageUrl, fit: BoxFit.fill) : const SizedBox.shrink();
},
),
);
}),
2025-08-21 10:50:38 +08:00
],
);
}
// 构建标签页内容
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),
),
);
}
2025-07-21 15:46:30 +08:00
2025-08-21 10:50:38 +08:00
// 添加 下拉刷新
return EasyRefresh(
onRefresh: () async {
await controller.refreshData(index);
},
header: ClassicHeader(
dragText: '下拉刷新',
armedText: '释放刷新',
2025-09-03 11:25:31 +08:00
readyText: '加载中...',
processingText: '加载中...',
processedText: '加载完成',
2025-08-21 10:50:38 +08:00
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]),
2025-07-21 15:46:30 +08:00
),
),
SliverToBoxAdapter(
2025-08-21 10:50:38 +08:00
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()),
2025-07-21 15:46:30 +08:00
),
),
),
],
),
2025-08-21 10:50:38 +08:00
);
});
}
// 空状态提示
Widget _emptyTip(String text) {
return Center(
child: Padding(
padding: const EdgeInsets.only(top: 50),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
2025-09-17 11:51:54 +08:00
Image.asset('assets/images/empty.png', width: 50, height: 50),
2025-08-21 10:50:38 +08:00
const SizedBox(height: 8),
Text(
text,
style: const TextStyle(color: Colors.grey, fontSize: 13),
),
],
),
2025-07-21 15:46:30 +08:00
),
);
}
}