在分页中,实现tab吸顶。
TDNavBar的screenAdaptation: true, 开启屏幕适配。
该属性已自动对不同手机状态栏高度进行适配。我们只需关注如何实现吸顶。
view
import 'package:ducafe_ui_core/ducafe_ui_core.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';
import 'package:xiaoshukeji/common/index.dart';
import 'index.dart';
// 1. SliverPersistentHeaderDelegate:必须实现的抽象类
class _StickyTabBarDelegate extends SliverPersistentHeaderDelegate {
final Widget child;
_StickyTabBarDelegate({required this.child});
@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
// shrinkOffset: 滚动距离
// overlapsContent: 是否与其他内容重叠
return Container(
color: AppTheme.pageBgColor,
child: child,
);
}
@override
double get maxExtent => 92.w; // 最大高度,已知tab高度72+上下padding:10
@override
double get minExtent => 92.w; // 最小高度
@override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) => true;
}
class RankingPage extends GetView<RankingController> {
const RankingPage({super.key});
// 头部皇冠位置
Widget _buildHeader() {
return <Widget>[
].toRow()
.card(color: AppTheme.pageBgColor)
.tight(width: 750.w,height: 300.w,);
}
// tab,可吸顶
Widget _buildTab() {
return <Widget>[
<Widget>[
TextWidget.body('日榜', size: 28.sp, weight: FontWeight.w600, color: AppTheme.textColorfff),
].toRow(mainAxisAlignment: MainAxisAlignment.center)
.card(color: AppTheme.primaryYellow)
.tight(width: 336.w,height: 72.w,),
<Widget>[
TextWidget.body('总榜', size: 28.sp, weight: FontWeight.w600, color: AppTheme.textColor6a7),
].toRow(mainAxisAlignment: MainAxisAlignment.center)
.card(color: AppTheme.navBarBgColor)
.tight(width: 336.w,height: 72.w,),
].toRow(mainAxisAlignment: MainAxisAlignment.spaceBetween);
}
// 数据列表
Widget _buildDataList() {
return SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return <Widget>[]
.toRow()
.paddingHorizontal(30.w)
.card(color: AppTheme.blockBgColor)
.tight(
width: 690.w,
height: 120.w,
)
.marginOnly(bottom: 20.w);
},
childCount: 20,
),
);
}
// 主视图
Widget _buildView() {
return SmartRefresher(
controller: controller.refreshController,
enablePullUp: true,
onRefresh: controller.onRefresh,
onLoading: controller.onLoading,
footer: const SmartRefresherFooterWidget(),
header: const SmartRefresherHeaderWidget(),
child: CustomScrollView(
slivers: [
// 头部
_buildHeader().sliverToBoxAdapter().sliverPaddingHorizontal(30.w),
// 2. SliverPersistentHeader:实现吸顶的核心组件
SliverPersistentHeader(
pinned: true, // 设置为 true 实现吸顶
delegate: _StickyTabBarDelegate(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 30.w, vertical: 10.w),
child: _buildTab(),
),
),
),
// 列表内容
_buildDataList().sliverPaddingHorizontal(30.w),
],
),
);
}
@override
Widget build(BuildContext context) {
return GetBuilder<RankingController>(
init: RankingController(),
id: "ranking",
builder: (_) {
return Scaffold(
backgroundColor: AppTheme.pageBgColor, // 自定义颜色
appBar: const TDNavBar(
height: 0,
titleColor: AppTheme.textColorfff,
titleFontWeight: FontWeight.w600,
backgroundColor: AppTheme.pageBgColor,
screenAdaptation: true, // 是否进行屏幕适配,默认true
useDefaultBack: false,
),
body: _buildView(),
);
},
);
}
}
controller
import 'package:get/get.dart';
import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart';
class RankingController extends GetxController {
RankingController();
List items = [];
/*
* 分页
* refreshController:分页控制器
* _page:分页
* _limit:每页条数
* _loadNewsSell:拉取数据(是否刷新)
* onLoading:上拉加载新商品
* onRefresh:下拉刷新
* */
final RefreshController refreshController = RefreshController(
initialRefresh: true,
);
// int _page = 1;
// int _limit = 20;
Future<bool> _loadNewsSell(bool isRefresh) async {
return false;
// var result = await ProductApi.products(ProductsReq(
// page:isRefresh ? 1:_page,
// prePage:_limit
// ));
// if(isRefresh){
// _page = 1;
// items.clear();
// }
// if(result.isNotEmpty){
// _page++;
// items.addAll(result);
// }
// // 是否是空
// return result.isEmpty;
}
// 上拉载入新商品
void onLoading() async {
if (items.isNotEmpty) {
try {
// 拉取数据是否为空 ? 设置暂无数据 : 加载完成
var isEmpty = await _loadNewsSell(false);
isEmpty
? refreshController.loadNoData()
: refreshController.loadComplete();
} catch (e) {
refreshController.loadFailed(); // 加载失败
}
} else {
refreshController.loadNoData(); // 设置无数据
}
update(["ranking"]);
}
// 下拉刷新
void onRefresh() async {
try {
await _loadNewsSell(true);
refreshController.refreshCompleted();
} catch (e) {
refreshController.refreshFailed();
}
update(["ranking"]);
}
_initData() {
update(["ranking"]);
}
void onTap() {}
// @override
// void onInit() {
// super.onInit();
// }
@override
void onReady() {
super.onReady();
_initData();
}
// @override
// void onClose() {
// super.onClose();
// }
}
记录tab切换
int currentTab = 0; // 当前选中的tab索引
// tab切换方法
void switchTab(int index) {
if (currentTab == index) return;
currentTab = index;
items.clear();// 切换tab时重置列表数据
refreshController.requestRefresh();
update(["ranking"]);
}
// tab切换
Widget _buildTab() {
return <Widget>[
<Widget>[
TextWidget.body('日榜', size: 28.sp, weight: FontWeight.w600, color: controller.currentTab == 0 ? AppTheme.textColorfff : AppTheme.textColor646),
].toRow(mainAxisAlignment: MainAxisAlignment.center)
.card(color: controller.currentTab == 0 ? AppTheme.primaryYellow : AppTheme.navBarBgColor)
.tight(width: 336.w,height: 72.w,).onTap(() {
controller.switchTab(0);
}),
<Widget>[
TextWidget.body('总榜', size: 28.sp, weight: FontWeight.w600, color: controller.currentTab == 1 ? AppTheme.textColorfff : AppTheme.textColor646),
].toRow(mainAxisAlignment: MainAxisAlignment.center)
.card(color: controller.currentTab == 1 ? AppTheme.primaryYellow : AppTheme.navBarBgColor )
.tight(width: 336.w,height: 72.w,).onTap(() {
controller.switchTab(1);
}),
].toRow(mainAxisAlignment: MainAxisAlignment.spaceBetween);
}