329 lines
12 KiB
Dart
329 lines
12 KiB
Dart
/// 首页模板
|
|
library;
|
|
|
|
import 'package:card_swiper/card_swiper.dart';
|
|
import 'package:dynamic_tabbar/dynamic_tabbar.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/IM/im_friend_listeners.dart';
|
|
import 'package:loopin/components/backtop.dart';
|
|
import 'package:loopin/components/loading.dart';
|
|
import 'package:loopin/components/network_or_asset_image.dart';
|
|
import 'package:loopin/controller/shop_index_controller.dart';
|
|
import 'package:loopin/styles/index.dart';
|
|
import 'package:loopin/utils/index.dart';
|
|
|
|
class IndexPage extends StatefulWidget {
|
|
const IndexPage({super.key});
|
|
|
|
@override
|
|
State<IndexPage> createState() => _IndexPageState();
|
|
}
|
|
|
|
class _IndexPageState extends State<IndexPage> 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();
|
|
late ShopIndexController controller;
|
|
|
|
// 瀑布流卡片
|
|
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',
|
|
),
|
|
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']);
|
|
Get.toNamed('/goods', arguments: {'goodsId': item['id']});
|
|
},
|
|
);
|
|
}
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
controller = Get.find<ShopIndexController>();
|
|
controller.initTabs(vsync: this);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: Colors.grey[50],
|
|
body: Column(
|
|
children: [
|
|
_buildTopSection(),
|
|
// 内容区域
|
|
Expanded(
|
|
child: controller.tabController == null
|
|
? 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) {
|
|
controller.tabController = tabController;
|
|
// 强制选中第一个
|
|
if (tabController.index != 0) {
|
|
tabController.animateTo(0);
|
|
}
|
|
},
|
|
onTabChanged: (index) {
|
|
logger.w("当前选中tab: $index");
|
|
},
|
|
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();
|
|
|
|
return Backtop(
|
|
controller: currentTab.scrollController,
|
|
offset: currentTab.scrollOffset.value,
|
|
);
|
|
}),
|
|
);
|
|
}
|
|
|
|
// 构建顶部固定区域
|
|
Widget _buildTopSection() {
|
|
if (controller.tabController == null) {
|
|
return SizedBox();
|
|
}
|
|
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,
|
|
),
|
|
),
|
|
itemBuilder: (context, index) {
|
|
final imageUrl = controller.swiperData[index]['images'] ?? '';
|
|
return imageUrl.isNotEmpty ? Image.network(imageUrl, fit: BoxFit.fill) : const SizedBox.shrink();
|
|
},
|
|
),
|
|
);
|
|
}),
|
|
],
|
|
);
|
|
}
|
|
|
|
// 构建标签页内容
|
|
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: '加载中...',
|
|
processedText: '加载完成',
|
|
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),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|