flutter/lib/pages/index/index.dart

499 lines
20 KiB
Dart
Raw Normal View History

2025-07-21 15:46:30 +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_message.dart';
import 'package:loopin/components/custom_sticky_header.dart';
import '../../behavior/custom_scroll_behavior.dart';
import '../../components/backtop.dart';
import '../../components/loading.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': '评价中心'}
]
}
];
List<String> tabList = ['推荐', '美食', '娱乐', '文旅', '医疗', '房产'];
// 瀑布流列表
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': '美的MideaLED便携充电小台灯书桌学习阅读灯学生宿舍卧室床头灯学习台灯',
'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'
},
];
// 列表
List dataList = [];
// 是否加载中
bool isLoading = false;
late ScrollController scrollController = ScrollController();
late TabController tabController = TabController(initialIndex: 0, length: tabList.length, vsync: this);
final PageController pageController = PageController();
// 滚动位置
double scrollOffset = 0;
// 加载更多
loadMoreData() async {
setState(() {
isLoading = true;
});
await Future.delayed(Duration(seconds: 1));
setState(() {
dataList.addAll(waterfallData);
isLoading = false;
});
}
// 下拉刷新
Future<void> handleRefresh() async {
await Future.delayed(Duration(seconds: 1));
dataList.clear();
for (int i = 0; i < waterfallData.length; i++) {
dataList.add(waterfallData[i]);
}
if (mounted) {
setState(() {});
}
}
// 瀑布流卡片
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['image']}'),
Container(
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 5.0,
children: [
Text(
'${item['title']}',
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(
'已售${item['saleNum']}',
style: TextStyle(color: Colors.grey, fontSize: 10.0),
),
],
),
Text(
'${item['shop']}',
style: TextStyle(color: Colors.grey, fontSize: 12.0),
),
],
),
)
],
),
),
onTap: () {
Get.toNamed('/goods');
},
);
}
@override
void initState() {
super.initState();
scrollController.addListener(() {
setState(() {
scrollOffset = scrollController.offset;
});
if (scrollController.position.pixels == scrollController.position.maxScrollExtent) {
debugPrint('[index]滚动到底部');
if (!isLoading) {
loadMoreData();
}
}
});
// 初始化加载
handleRefresh();
}
@override
void dispose() {
scrollController.dispose();
tabController.dispose();
pageController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[50],
body: ScrollConfiguration(
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
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(200),
),
child: TextField(
decoration: InputDecoration(
isDense: true,
hintText: "2025百亿补贴",
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: [
Icon(
Icons.keyboard_voice,
color: Colors.black45,
size: 21.0,
),
Icon(
Icons.camera_alt_outlined,
color: Colors.black45,
size: 21.0,
),
],
),
),
contentPadding: EdgeInsets.symmetric(vertical: 0, horizontal: 10.0),
border: OutlineInputBorder(borderSide: BorderSide.none, borderRadius: BorderRadius.circular(30.0))),
cursorColor: Colors.black,
onChanged: (val) {
debugPrint(val);
},
),
),
),
),
actions: [
IconButton(
icon: Icon(Icons.shopping_cart_outlined),
onPressed: () {},
),
],
// 自定义伸缩区域(轮播图)
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Color(0xFFFF5000), Color(0xFFfcaec4)])),
child: FlexibleSpaceBar(
background: Swiper.children(
pagination: SwiperPagination(
builder: DotSwiperPaginationBuilder(
color: Colors.white70,
activeColor: Colors.white,
)),
indicatorLayout: PageIndicatorLayout.SCALE,
children: [
Image.network(
'https://m.360buyimg.com/babel/jfs/t20271217/224114/35/38178/150060/6760d559Fd654f946/968c156726b6e822.png',
fit: BoxFit.fill,
),
Image.network(
'https://m.360buyimg.com/babel/jfs/t20280117/88832/5/48468/139826/6789cbcfF4e0b2a3d/9dc54355b6f65c40.jpg',
fit: BoxFit.fill,
),
Image.network(
'https://m.360buyimg.com/babel/jfs/t20280108/255505/29/10540/137372/677ddbc1F6cdbbed0/bc477fadedef22a8.jpg',
fit: BoxFit.fill,
),
],
),
),
),
),
// 分类
// SliverToBoxAdapter(
// child: Container(
// margin: EdgeInsets.all(10.0),
// padding: EdgeInsets.symmetric(vertical: 10.0),
// height: 90.0,
// clipBehavior: Clip.antiAlias,
// decoration: BoxDecoration(
// color: Colors.white,
// borderRadius: BorderRadius.circular(15.0),
// ),
// child: Column(
// children: [
// Expanded(
// child: PageView.builder(
// controller: pageController,
// itemCount: cateList.length,
// itemBuilder: (context, index) {
// final item = cateList[index];
// return GridView.builder(
// shrinkWrap: true,
// padding: EdgeInsets.zero,
// physics: NeverScrollableScrollPhysics(),
// gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
// crossAxisCount: 4,
// ),
// itemCount: item['list'].length,
// itemBuilder: (BuildContext context, int index) {
// final citem = item['list'][index];
// // return Column(
// // spacing: 3.0,
// // children: [
// // if (citem['icon'] != null)
// // SvgPicture.asset(
// // 'assets/images/svg/${citem['icon']}',
// // height: 30.0,
// // width: 30.0,
// // ),
// // Text(citem['label']),
// // ],
// // );
// return GestureDetector(
// onTap: () {
// logger.i('点击了$index');
// // 跳转逻辑,用你自己的目标路由替换
// Get.toNamed('/order');
// },
// child: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// if (citem['icon'] != null)
// SvgPicture.asset(
// 'assets/images/svg/${citem['icon']}',
// height: 30.0,
// width: 30.0,
// ),
// Text(citem['label']),
// ],
// ),
// );
// },
// );
// },
// ),
// ),
// CustomPageViewIndicator(
// controller: pageController,
// count: cateList.length,
// color: Color(0xFFCECECE),
// activeColor: Color(0xFFFF5000),
// ),
// ],
// )),
// ),
// tabbar列表
SliverPersistentHeader(
pinned: true,
delegate: CustomStickyHeader(
child: PreferredSize(
preferredSize: Size.fromHeight(45.0),
child: Container(
color: Colors.white,
height: 45.0,
child: TabBar(
controller: tabController,
onTap: (index) {
logger.i('点击了第 $index 个 tab');
},
tabs: tabList.map((v) => Tab(text: v)).toList(),
isScrollable: false,
overlayColor: WidgetStateProperty.all(Colors.transparent),
unselectedLabelColor: Colors.black87,
labelColor: Color(0xFFFF5000),
indicatorColor: Color(0xFFFF5000),
indicatorSize: TabBarIndicatorSize.tab,
unselectedLabelStyle: TextStyle(fontSize: 15.0, fontFamily: 'Microsoft YaHei'),
labelStyle: TextStyle(fontSize: 15.0, fontFamily: 'Microsoft YaHei', fontWeight: FontWeight.w700),
dividerHeight: 0,
padding: EdgeInsets.symmetric(horizontal: 10.0),
labelPadding: EdgeInsets.symmetric(horizontal: 7.5),
indicatorPadding: EdgeInsets.symmetric(horizontal: 15.0, vertical: 5.0),
),
),
),
),
),
// 瀑布流列表
SliverToBoxAdapter(
child: Container(
padding: EdgeInsets.all(10.0),
child: Column(
children: [
dataList.isEmpty
?
// 初始loading提示
Column(
children: [
RefreshProgressIndicator(
backgroundColor: Colors.white,
color: Color(0xFFFF5000),
),
],
)
: MasonryGridView.count(
shrinkWrap: true,
padding: EdgeInsets.zero,
physics: NeverScrollableScrollPhysics(),
crossAxisCount: 2,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
itemCount: dataList.length + (isLoading ? 1 : 0),
itemBuilder: (BuildContext context, int index) {
if (index < dataList.length) {
return cardList(dataList[index]);
} else {
return SizedBox.shrink();
}
},
),
Opacity(opacity: dataList.isNotEmpty && isLoading ? 1 : 0, child: Loading(title: 'loading...')),
],
),
),
),
],
),
),
// 返回顶部
floatingActionButton: Backtop(controller: scrollController, offset: scrollOffset),
);
}
}