2025-09-29 02:34:41 +08:00
|
|
|
|
/// 首页模板
|
|
|
|
|
|
library;
|
|
|
|
|
|
|
|
|
|
|
|
import 'dart:ui';
|
|
|
|
|
|
|
|
|
|
|
|
import 'package:card_swiper/card_swiper.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/api/shop_api.dart';
|
|
|
|
|
|
import 'package:loopin/components/custom_pageview_indicator.dart';
|
|
|
|
|
|
import 'package:loopin/components/custom_sticky_header.dart';
|
|
|
|
|
|
import 'package:loopin/components/empty_tip.dart';
|
|
|
|
|
|
import 'package:loopin/components/loading.dart';
|
|
|
|
|
|
import 'package:loopin/components/network_or_asset_image.dart';
|
|
|
|
|
|
import 'package:loopin/service/http.dart';
|
|
|
|
|
|
import 'package:loopin/styles/index.dart';
|
|
|
|
|
|
import 'package:loopin/utils/index.dart';
|
|
|
|
|
|
|
|
|
|
|
|
import '../../behavior/custom_scroll_behavior.dart';
|
|
|
|
|
|
import '../../components/backtop.dart';
|
|
|
|
|
|
|
|
|
|
|
|
class IndexPage extends StatefulWidget {
|
|
|
|
|
|
const IndexPage({super.key});
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
|
State<IndexPage> createState() => _IndexPageState();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class _IndexPageState extends State<IndexPage> with TickerProviderStateMixin {
|
|
|
|
|
|
///-----------
|
|
|
|
|
|
|
|
|
|
|
|
// 瀑布流列表
|
|
|
|
|
|
List waterfallData = [
|
|
|
|
|
|
{
|
|
|
|
|
|
'price': 199.00,
|
|
|
|
|
|
'title': '韩料界的萨莉亚!',
|
|
|
|
|
|
'shop': '萨莉亚专卖店',
|
|
|
|
|
|
'image': 'https://qcloud.dpfile.com/pc/1c3egbzM_ICz90dhi6MAiTsazjxWYQcHCd-sbpD1Wqtph2eIJA04NCRvoGqL4_opG45IiB1YIyNuDTtqzVRwesm_qA1Pf8rFcayTY-n-rG8.jpg',
|
|
|
|
|
|
'saleNum': '2.1万'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
'price': 1499.90,
|
|
|
|
|
|
'title': '茅台(MOUTAI)飞天 53%vol 500ml 贵州茅台酒(带杯)',
|
|
|
|
|
|
'shop': '茅台京东自营旗舰店',
|
|
|
|
|
|
'image': 'https://img13.360buyimg.com/n1/jfs/t1/97097/12/15694/245806/5e7373e6Ec4d1b0ac/9d8c13728cc2544d.jpg',
|
|
|
|
|
|
'saleNum': '1254'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
'price': 18.90,
|
|
|
|
|
|
'title': '上海街头苹果糖!一口一个不吱声',
|
|
|
|
|
|
'shop': '芝洛洛自营旗舰店',
|
|
|
|
|
|
'image': 'https://p0.meituan.net/coverpic/f0eefdfa02619fb09ca53eacd4d97231123115.jpg',
|
|
|
|
|
|
'saleNum': '1.2万'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
'price': 59.00,
|
|
|
|
|
|
'title': '谁懂,就是这个菜,尝了第一口,立马决定加单了,真正的咸甜永动机啊🍬 去过云南的朋友都知道,当地的乳扇真的很好吃。',
|
|
|
|
|
|
'shop': '薄荷牛舌卷旗舰店',
|
|
|
|
|
|
'image': 'https://qcloud.dpfile.com/pc/UcW-v6AN1TxVTt9--5Kaw2-t4W55jUhEG_pM5S-w_AQ4IP3z9WxHzwJ9fOthIjEYY0q73sB2DyQcgmKUxZFQtw.jpg',
|
|
|
|
|
|
'saleNum': '1639'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
'price': 2499.00,
|
|
|
|
|
|
'title': '小米 REDMI K80 国家补贴 第三代骁龙 8 6550mAh大电池 澎湃OS 玄夜黑 12GB+256GB 红米5G至尊手机',
|
|
|
|
|
|
'shop': '小米京东自营旗舰店',
|
|
|
|
|
|
'image': 'https://img10.360buyimg.com/n1/s450x450_jfs/t1/264409/38/13856/102861/678dcfdaFb723c58f/5b97cf154bbba96c.jpg',
|
|
|
|
|
|
'saleNum': '9726'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
'price': 1.00,
|
|
|
|
|
|
'title': '圣菲尔伯爵法国红酒Saintfilcount干红葡萄酒珍藏13.5度单瓶送礼红酒 一元试饮',
|
|
|
|
|
|
'shop': '小森葡萄酒专营店',
|
|
|
|
|
|
'image': 'https://img10.360buyimg.com/n7/jfs/t1/226168/23/3411/118733/65537e5fF2db2d109/7d1d11a8013d6e8f.jpg',
|
|
|
|
|
|
'saleNum': '9.9万'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
'price': 42.00,
|
|
|
|
|
|
'title': '美的(Midea)LED便携充电小台灯书桌学习阅读灯学生宿舍卧室床头灯学习台灯',
|
|
|
|
|
|
'shop': '美的(Midea)旗舰店',
|
|
|
|
|
|
'image': 'https://img14.360buyimg.com/mobilecms/s360x360_jfs/t1/226233/4/10194/156936/658e8f88Fcfc9cb40/cea4a48783f11a7a.jpg',
|
|
|
|
|
|
'saleNum': '5106'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
'price': 22.90,
|
|
|
|
|
|
'title': '蒙都 羊杂500g 加热即食 京东超市肉干肉脯及礼包11.11真便宜',
|
|
|
|
|
|
'shop': '蒙都旗舰店',
|
|
|
|
|
|
'image': 'https://img10.360buyimg.com/n7/jfs/t1/155306/32/25324/231912/62d22fb8E4ffab855/c6001ee702fb240a.jpg',
|
|
|
|
|
|
'saleNum': '1.6万'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
'price': 19.90,
|
|
|
|
|
|
'title': '『 江西炒米粉 』本次最佳😋香就一个字话。锅气的香🔥干辣椒的焦香🌶️油的润香🐷蔬菜混合的清香🥬',
|
|
|
|
|
|
'shop': '去月球野餐嗎',
|
|
|
|
|
|
'image': 'https://qcloud.dpfile.com/pc/pOAOL-DQRBWfkVZIWYVoy0mMQf6_UutNlOpEpGkT_nz3b1n7ZbpikPgtXMhMsjXNY0q73sB2DyQcgmKUxZFQtw.jpg',
|
|
|
|
|
|
'saleNum': '3.2万'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
'price': 109.00,
|
|
|
|
|
|
'title': '附近新开业的,作为江西人当然要去试试。点了几个家常菜。',
|
|
|
|
|
|
'shop': '辣评新开江西菜',
|
|
|
|
|
|
'image': 'https://qcloud.dpfile.com/pc/HePD48CFNnS0kMZyf3Q391wxaW_zVgHimctthH__J6UI54HLPUkNt5e3qtP4Nl2G_aW_B6sGElzX-tSmYRvRnQxxxek7cKy7_R0W-KdxWUk.jpg',
|
|
|
|
|
|
'saleNum': '8764'
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
// 列表
|
|
|
|
|
|
RxList dataList = [].obs;
|
|
|
|
|
|
// 是否加载中
|
|
|
|
|
|
RxBool isLoading = false.obs;
|
|
|
|
|
|
RxBool isInitLoading = true.obs;
|
|
|
|
|
|
RxBool hasMore = true.obs;
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
RxInt currentIndex = 0.obs;
|
|
|
|
|
|
TextEditingController textEditingController = TextEditingController();
|
|
|
|
|
|
FocusNode focusNode = FocusNode();
|
|
|
|
|
|
TabController? tabController;
|
|
|
|
|
|
// 分类列表
|
|
|
|
|
|
RxList<dynamic> tabList = <dynamic>[].obs;
|
|
|
|
|
|
|
|
|
|
|
|
///轮播图数据
|
|
|
|
|
|
RxList<dynamic> swiperData = <dynamic>[].obs;
|
|
|
|
|
|
late ScrollController scrollController = ScrollController();
|
|
|
|
|
|
final PageController pageController = PageController();
|
|
|
|
|
|
// 滚动位置
|
|
|
|
|
|
double scrollOffset = 0;
|
|
|
|
|
|
int page = 1;
|
|
|
|
|
|
|
|
|
|
|
|
/// 初始化 Tab 分类
|
|
|
|
|
|
Future<void> initTabs() async {
|
|
|
|
|
|
page = 1;
|
|
|
|
|
|
currentIndex.value = 0;
|
|
|
|
|
|
isLoading.value = false;
|
|
|
|
|
|
hasMore.value = true;
|
|
|
|
|
|
dataList.value = [];
|
|
|
|
|
|
isInitLoading.value = true;
|
|
|
|
|
|
|
|
|
|
|
|
// 赋值 tab 数据
|
|
|
|
|
|
final res = await Http.post(ShopApi.shopCategory, data: {
|
|
|
|
|
|
'level': 1,
|
|
|
|
|
|
});
|
|
|
|
|
|
final data = res['data'] as List<dynamic>;
|
|
|
|
|
|
tabList.value = data;
|
|
|
|
|
|
// 如果之前有 controller,要先释放掉
|
|
|
|
|
|
tabController?.dispose();
|
|
|
|
|
|
tabController = TabController(
|
|
|
|
|
|
initialIndex: 0,
|
|
|
|
|
|
length: tabList.length,
|
|
|
|
|
|
vsync: this,
|
|
|
|
|
|
);
|
|
|
|
|
|
if (tabList.isNotEmpty) {
|
|
|
|
|
|
loadSwiperData();
|
|
|
|
|
|
loadData(currentIndex.value);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 加载swiper数据
|
|
|
|
|
|
Future<void> loadSwiperData() async {
|
|
|
|
|
|
final res = await Http.post(ShopApi.shopSwiperList, data: {
|
|
|
|
|
|
'type': 1,
|
|
|
|
|
|
});
|
|
|
|
|
|
final data = res['data'];
|
|
|
|
|
|
logger.w(res);
|
|
|
|
|
|
swiperData.assignAll(data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 切换数据
|
|
|
|
|
|
Future<void> changeData(int index) async {
|
2025-10-01 12:29:09 +08:00
|
|
|
|
dataList.clear();
|
2025-09-29 02:34:41 +08:00
|
|
|
|
if (isLoading.value) return;
|
|
|
|
|
|
isLoading.value = true;
|
|
|
|
|
|
final res = await Http.post(ShopApi.shopList, data: {
|
|
|
|
|
|
'size': 10,
|
|
|
|
|
|
'current': page,
|
|
|
|
|
|
'categoryId': tabList[index]['id'],
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
final data = res['data']['records'];
|
|
|
|
|
|
final total = res['data']['total'];
|
|
|
|
|
|
logger.w(res);
|
|
|
|
|
|
if (dataList.length >= total) {
|
|
|
|
|
|
hasMore.value = false;
|
2025-10-01 12:29:09 +08:00
|
|
|
|
isLoading.value = false;
|
|
|
|
|
|
return;
|
2025-09-29 02:34:41 +08:00
|
|
|
|
}
|
2025-10-01 12:29:09 +08:00
|
|
|
|
dataList.value = data;
|
2025-09-29 02:34:41 +08:00
|
|
|
|
page += 1;
|
|
|
|
|
|
isLoading.value = false;
|
|
|
|
|
|
isInitLoading.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 加载pageview数据
|
|
|
|
|
|
Future<void> loadData(int index) async {
|
|
|
|
|
|
if (isLoading.value) return;
|
|
|
|
|
|
isLoading.value = true;
|
|
|
|
|
|
final res = await Http.post(ShopApi.shopList, data: {
|
|
|
|
|
|
'size': 10,
|
|
|
|
|
|
'current': page,
|
|
|
|
|
|
'categoryId': tabList[index]['id'],
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
final data = res['data']['records'];
|
|
|
|
|
|
final total = res['data']['total'];
|
|
|
|
|
|
logger.w(res);
|
|
|
|
|
|
if (dataList.length >= total) {
|
|
|
|
|
|
hasMore.value = false;
|
2025-10-01 12:29:09 +08:00
|
|
|
|
isLoading.value = false;
|
|
|
|
|
|
return;
|
2025-09-29 02:34:41 +08:00
|
|
|
|
}
|
2025-10-01 12:29:09 +08:00
|
|
|
|
dataList.addAll(data);
|
2025-09-29 02:34:41 +08:00
|
|
|
|
page += 1;
|
2025-10-01 12:29:09 +08:00
|
|
|
|
logger.e(page);
|
2025-09-29 02:34:41 +08:00
|
|
|
|
isLoading.value = false;
|
|
|
|
|
|
isInitLoading.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
|
void initState() {
|
|
|
|
|
|
super.initState();
|
|
|
|
|
|
scrollController.addListener(() {
|
|
|
|
|
|
setState(() {
|
|
|
|
|
|
scrollOffset = scrollController.offset;
|
|
|
|
|
|
});
|
|
|
|
|
|
if (scrollController.position.pixels == scrollController.position.maxScrollExtent) {
|
|
|
|
|
|
debugPrint('[index]滚动到底部');
|
|
|
|
|
|
if (!isLoading.value) {
|
|
|
|
|
|
loadData(currentIndex.value);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
// 初始化加载
|
|
|
|
|
|
initTabs();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
|
void dispose() {
|
|
|
|
|
|
scrollController.dispose();
|
|
|
|
|
|
pageController.dispose();
|
|
|
|
|
|
super.dispose();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 瀑布流卡片
|
|
|
|
|
|
Widget cardList(item) {
|
|
|
|
|
|
if (item == null) {
|
|
|
|
|
|
return Loading(
|
|
|
|
|
|
title: '加载中',
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
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: [
|
|
|
|
|
|
NetworkOrAssetImage(
|
|
|
|
|
|
imageUrl: '${item['pic']}',
|
|
|
|
|
|
width: double.infinity,
|
|
|
|
|
|
placeholderAsset: 'assets/images/wait_loading.png',
|
|
|
|
|
|
),
|
|
|
|
|
|
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
|
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
|
return GestureDetector(
|
|
|
|
|
|
onTap: () {
|
|
|
|
|
|
focusNode.unfocus();
|
|
|
|
|
|
},
|
|
|
|
|
|
child: Scaffold(
|
|
|
|
|
|
body: ScrollConfiguration(
|
|
|
|
|
|
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
|
|
|
|
|
child: RefreshIndicator(
|
|
|
|
|
|
color: FStyle.primaryColor,
|
|
|
|
|
|
onRefresh: () async {
|
|
|
|
|
|
await initTabs();
|
|
|
|
|
|
},
|
|
|
|
|
|
child: CustomScrollView(
|
|
|
|
|
|
scrollBehavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
|
|
|
|
|
controller: scrollController,
|
|
|
|
|
|
slivers: [
|
|
|
|
|
|
SliverAppBar(
|
|
|
|
|
|
backgroundColor: Colors.transparent,
|
|
|
|
|
|
foregroundColor: Colors.white,
|
|
|
|
|
|
pinned: true,
|
|
|
|
|
|
expandedHeight: 200.0,
|
|
|
|
|
|
titleSpacing: 10.0,
|
|
|
|
|
|
// 搜索框(高斯模糊背景)
|
|
|
|
|
|
title: ClipRRect(
|
|
|
|
|
|
borderRadius: BorderRadius.circular(30.0),
|
|
|
|
|
|
child: BackdropFilter(
|
|
|
|
|
|
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
|
|
|
|
|
|
child: Container(
|
|
|
|
|
|
height: 45.0,
|
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
|
color: Colors.white.withAlpha(150),
|
|
|
|
|
|
),
|
|
|
|
|
|
child: TextField(
|
|
|
|
|
|
focusNode: focusNode,
|
|
|
|
|
|
controller: textEditingController,
|
|
|
|
|
|
decoration: InputDecoration(
|
|
|
|
|
|
isDense: true,
|
|
|
|
|
|
hintText: "热销商品",
|
|
|
|
|
|
hintStyle: TextStyle(fontSize: 15.0),
|
|
|
|
|
|
prefixIcon: Icon(
|
|
|
|
|
|
Icons.search,
|
|
|
|
|
|
color: Colors.black38,
|
|
|
|
|
|
size: 21.0,
|
|
|
|
|
|
),
|
|
|
|
|
|
suffixIcon: Container(
|
|
|
|
|
|
padding: EdgeInsets.only(right: 15.0),
|
|
|
|
|
|
child: Row(
|
|
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
|
|
spacing: 10.0,
|
|
|
|
|
|
children: [
|
|
|
|
|
|
TextButton(
|
|
|
|
|
|
onPressed: () {
|
|
|
|
|
|
focusNode.unfocus();
|
|
|
|
|
|
if (textEditingController.text.isNotEmpty) {
|
|
|
|
|
|
// 去搜索结果页,支持带着搜索文字和搜索tab索引
|
|
|
|
|
|
Get.toNamed(
|
|
|
|
|
|
'/search-result',
|
|
|
|
|
|
arguments: {'searchWords': textEditingController.text, 'tab': 1},
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
child: Text('搜索'),
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
contentPadding: EdgeInsets.symmetric(vertical: 0, horizontal: 10.0),
|
|
|
|
|
|
border: OutlineInputBorder(borderSide: BorderSide.none, borderRadius: BorderRadius.circular(30.0))),
|
|
|
|
|
|
cursorColor: Colors.black,
|
|
|
|
|
|
onSubmitted: (value) {
|
|
|
|
|
|
focusNode.unfocus();
|
|
|
|
|
|
if (value.isNotEmpty) {
|
|
|
|
|
|
// 去搜索结果页,支持带着搜索文字和搜索tab索引
|
|
|
|
|
|
Get.toNamed(
|
|
|
|
|
|
'/search-result',
|
|
|
|
|
|
arguments: {'searchWords': value, 'tab': 1},
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
|
|
// 自定义伸缩区域(轮播图)
|
|
|
|
|
|
flexibleSpace: Container(
|
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
|
gradient: LinearGradient(
|
|
|
|
|
|
begin: Alignment.topLeft,
|
|
|
|
|
|
end: Alignment.bottomRight,
|
|
|
|
|
|
colors: [Color(0xFFFF5000), Color(0xFFfcaec4)],
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
child: FlexibleSpaceBar(
|
|
|
|
|
|
background: Obx(() {
|
|
|
|
|
|
// 如果数据为空,返回一个空容器或占位图
|
|
|
|
|
|
if (swiperData.isEmpty) return SizedBox.shrink();
|
|
|
|
|
|
|
|
|
|
|
|
return SizedBox(
|
|
|
|
|
|
width: double.infinity,
|
|
|
|
|
|
child: Swiper.children(
|
|
|
|
|
|
pagination: SwiperPagination(
|
|
|
|
|
|
builder: DotSwiperPaginationBuilder(
|
|
|
|
|
|
color: Colors.white70,
|
|
|
|
|
|
activeColor: Colors.white,
|
|
|
|
|
|
size: 6.0,
|
|
|
|
|
|
activeSize: 8.0,
|
|
|
|
|
|
space: 4.0,
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
indicatorLayout: PageIndicatorLayout.SCALE,
|
|
|
|
|
|
children: swiperData.map<Widget>((itm) {
|
|
|
|
|
|
return NetworkOrAssetImage(
|
|
|
|
|
|
imageUrl: itm['images'],
|
|
|
|
|
|
placeholderAsset: 'assets/images/bk.jpg',
|
|
|
|
|
|
);
|
|
|
|
|
|
}).toList(),
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
}),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
|
|
// 分类
|
|
|
|
|
|
SliverPersistentHeader(
|
|
|
|
|
|
pinned: false,
|
|
|
|
|
|
delegate: CustomStickyHeader(
|
|
|
|
|
|
child: PreferredSize(
|
|
|
|
|
|
preferredSize: Size.fromHeight(110.0),
|
|
|
|
|
|
child: Obx(
|
|
|
|
|
|
() {
|
|
|
|
|
|
return Container(
|
|
|
|
|
|
margin: EdgeInsets.all(10.0),
|
|
|
|
|
|
padding: EdgeInsets.symmetric(vertical: 10.0),
|
|
|
|
|
|
height: 110.0,
|
|
|
|
|
|
clipBehavior: Clip.antiAlias,
|
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
|
borderRadius: BorderRadius.circular(15.0),
|
|
|
|
|
|
),
|
|
|
|
|
|
child: Column(
|
|
|
|
|
|
children: [
|
|
|
|
|
|
Expanded(
|
|
|
|
|
|
child: PageView.builder(
|
|
|
|
|
|
controller: pageController,
|
|
|
|
|
|
itemCount: (tabList.length / 4).ceil(),
|
|
|
|
|
|
itemBuilder: (context, pageIndex) {
|
|
|
|
|
|
final start = pageIndex * 4;
|
|
|
|
|
|
final end = (start + 4) > tabList.length ? tabList.length : (start + 4);
|
|
|
|
|
|
final pageItems = tabList.sublist(start, end);
|
|
|
|
|
|
return GridView.builder(
|
|
|
|
|
|
shrinkWrap: true,
|
|
|
|
|
|
padding: EdgeInsets.zero,
|
|
|
|
|
|
physics: NeverScrollableScrollPhysics(),
|
|
|
|
|
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
|
|
|
|
|
crossAxisCount: 4, // 一行
|
|
|
|
|
|
mainAxisSpacing: 0, // 行间距
|
|
|
|
|
|
),
|
|
|
|
|
|
itemCount: pageItems.length,
|
|
|
|
|
|
itemBuilder: (BuildContext context, int index) {
|
|
|
|
|
|
final citem = pageItems[index];
|
|
|
|
|
|
return GestureDetector(
|
|
|
|
|
|
behavior: HitTestBehavior.translucent,
|
|
|
|
|
|
onTap: () {
|
|
|
|
|
|
//切换index
|
|
|
|
|
|
final globalIndex = start + index;
|
|
|
|
|
|
logger.e(globalIndex);
|
|
|
|
|
|
if (globalIndex != currentIndex.value) {
|
|
|
|
|
|
currentIndex.value = globalIndex;
|
|
|
|
|
|
page = 1;
|
|
|
|
|
|
isLoading.value = false;
|
|
|
|
|
|
changeData(globalIndex);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
child: Column(
|
|
|
|
|
|
spacing: 3.0,
|
|
|
|
|
|
children: [
|
|
|
|
|
|
ClipOval(
|
|
|
|
|
|
child: NetworkOrAssetImage(
|
|
|
|
|
|
imageUrl: citem['icon'],
|
|
|
|
|
|
width: 30.0,
|
|
|
|
|
|
height: 30.0,
|
|
|
|
|
|
placeholderAsset: 'assets/images/wait_loading.png',
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
Text(citem['name']),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
// 数量够翻页才显示
|
|
|
|
|
|
CustomPageViewIndicator(
|
|
|
|
|
|
controller: pageController,
|
|
|
|
|
|
count: (tabList.length / 4).ceil(),
|
|
|
|
|
|
color: Color(0xFFCECECE),
|
|
|
|
|
|
activeColor: Color(0xFFFF5000),
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
|
|
// 瀑布流列表
|
|
|
|
|
|
SliverToBoxAdapter(
|
|
|
|
|
|
child: Obx(
|
|
|
|
|
|
() {
|
2025-10-01 12:29:09 +08:00
|
|
|
|
// if (isInitLoading.value) {
|
|
|
|
|
|
// return Column(
|
|
|
|
|
|
// children: [
|
|
|
|
|
|
// RefreshProgressIndicator(
|
|
|
|
|
|
// backgroundColor: Colors.white,
|
|
|
|
|
|
// color: Color(0xFFFF5000),
|
|
|
|
|
|
// ),
|
|
|
|
|
|
// ],
|
|
|
|
|
|
// );
|
|
|
|
|
|
// }
|
2025-09-29 02:34:41 +08:00
|
|
|
|
if (dataList.isEmpty) {
|
|
|
|
|
|
return EmptyTip();
|
|
|
|
|
|
}
|
|
|
|
|
|
return Container(
|
|
|
|
|
|
padding: EdgeInsets.all(10.0),
|
|
|
|
|
|
child: Column(
|
|
|
|
|
|
children: [
|
|
|
|
|
|
MasonryGridView.count(
|
|
|
|
|
|
shrinkWrap: true,
|
|
|
|
|
|
padding: EdgeInsets.zero,
|
|
|
|
|
|
physics: NeverScrollableScrollPhysics(),
|
|
|
|
|
|
crossAxisCount: 2,
|
|
|
|
|
|
mainAxisSpacing: 10.0,
|
|
|
|
|
|
crossAxisSpacing: 10.0,
|
|
|
|
|
|
itemCount: dataList.length,
|
|
|
|
|
|
itemBuilder: (BuildContext context, int index) {
|
|
|
|
|
|
return cardList(dataList[index]);
|
|
|
|
|
|
},
|
|
|
|
|
|
),
|
|
|
|
|
|
Opacity(opacity: dataList.isNotEmpty && isLoading.value ? 1 : 0, child: Loading(title: 'loading...')),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
// 返回顶部
|
|
|
|
|
|
floatingActionButton: Backtop(controller: scrollController, offset: scrollOffset),
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|