Open sjkyyt01 opened 1 month ago
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_sticky_header/flutter_sticky_header.dart';
import 'package:sliver_tools/sliver_tools.dart';
import '../../components/scrollview_observer/src/common/observer_typedef.dart';
import '../../components/scrollview_observer/src/sliver/sliver_observer_controller.dart';
import '../../components/scrollview_observer/src/sliver/sliver_observer_view.dart';
import '../../components/scrollview_observer/src/utils/src/nested_scroll_util.dart';
import '../../components/scrollview_observer/src/utils/src/observer_utils.dart';
import '../../components/scrollview_observer/src/utils/src/slivers.dart';
import '../demo/common/wigdets.dart';
class Category {
int id;
String categoryName;
List<Goods> goods;
Category({
this.id = 0,
this.categoryName = '',
this.goods = const [],
});
}
class Goods {
int id;
String goodsName;
Goods({
this.id = 0,
this.goodsName = '',
});
}
class Other4Page extends StatefulWidget {
const Other4Page({super.key});
@override
State<Other4Page> createState() => _Other4PageState();
}
class _Other4PageState extends State<Other4Page> with TickerProviderStateMixin {
List<Category> list = List.generate(10, (index) {
int categoryId = index + 1;
return Category(
id: categoryId,
categoryName: 'category_$categoryId',
goods: List.generate(10, (i) => Goods(id: i + categoryId * 10, goodsName: 'category_$categoryId - item_${i + categoryId * 10}')),
);
});
final nestedScrollViewKey = GlobalKey();
final appBarKey = GlobalKey();
final appHeaderKey = GlobalKey();
final tabBarKey = GlobalKey();
final scrollController = ScrollController();
late SliverObserverController sliverItemObserverController;
final nestedScrollUtil = NestedScrollUtil();
// late final SliverObserverController sliverObserverController;
Map<int, BuildContext> itemSliverIndexCtxMap = {};
Map<int, BuildContext> sliverIndexCtxMap = {};
ValueNotifier<int> tabCurrentSelectedIndex = ValueNotifier(0);
bool isIgnoreCalcTabBarIndex = false;
late TabController tabBarController;
@override
void initState() {
super.initState();
tabBarController = TabController(
length: 2,
vsync: this,
);
sliverItemObserverController = SliverObserverController(controller: scrollController);
nestedScrollUtil.outerScrollController = scrollController;
// sliverObserverController = SliverObserverController(
// controller: scrollController,
// );
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SliverViewObserver(
/// To observe which sliver is currently the first.
sliverContexts: () => sliverIndexCtxMap.values.toList(),
// customOverlap: (sliverContext) {
// return nestedScrollUtil.calcOverlap(
// nestedScrollViewKey: nestedScrollViewKey,
// sliverContext: sliverContext,
// );
// },
triggerOnObserveType: ObserverTriggerOnObserveType.directly,
dynamicLeadingOffset: () {
// Accumulate the height of all PersistentHeader.
return ObserverUtils.calcPersistentHeaderExtent(
key: appBarKey,
offset: scrollController.offset,
) +
1;
// To avoid tabBar index rebound.
},
onObserveViewport: (result) {
if (isIgnoreCalcTabBarIndex) return;
int? currentTabIndex;
final currentFirstSliverCtx = result.firstChild.sliverContext;
for (var sectionIndex in sliverIndexCtxMap.keys) {
final ctx = sliverIndexCtxMap[sectionIndex];
if (ctx == null) continue;
// If they are not the same sliver, continue.
if (currentFirstSliverCtx != ctx) continue;
// If the sliver is not visible, continue.
final visible = (ctx.findRenderObject() as RenderSliver).geometry?.visible ?? false;
if (!visible) continue;
currentTabIndex = sectionIndex;
break;
}
if (currentTabIndex == null) return;
updateTabBarIndex(currentTabIndex);
},
child: SliverViewObserver(
/// To observe sliver items and handle scrollTo.
controller: sliverItemObserverController,
sliverContexts: () => itemSliverIndexCtxMap.values.toList(),
child: NestedScrollView(
// key: nestedScrollViewKey,
controller: scrollController,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return [
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: MultiSliver(
// key: nestedScrollViewKey,
pushPinnedChildren: true,
children: [
SliverAppBar(
key: appBarKey,
forceElevated: innerBoxIsScrolled,
pinned: true,
title: const Text('Other4Page'),
),
SliverToBoxAdapter(
key: appHeaderKey,
child: Container(
height: 100,
color: Colors.red[200],
),
),
SliverPersistentHeader(
key: tabBarKey,
pinned: true,
delegate: SliverHeaderDelegate.fixedHeight(
height: 40,
child: Container(
color: Colors.blue,
child: TabBar(
controller: tabBarController,
tabs: const [
Tab(text: "Tab 1"),
Tab(text: "Tab 2"),
],
),
),
),
),
],
),
),
];
},
body: Builder(
builder: (context) {
return TabBarView(
controller: tabBarController,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 100,
child: Container(
height: 500,
color: Colors.blue[200],
child: const Text('data'),
),
),
Expanded(
child: CustomScrollView(
// controller: sliverItemObserverController.controller,
// physics: const RangeMaintainingScrollPhysics(),
physics: const ClampingScrollPhysics(),
slivers: [
SliverOverlapInjector(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
),
...List.generate(list.length, (mainIndex) {
return _buildSectionListView(mainIndex);
}),
],
),
),
],
),
Container(
height: 500,
color: Colors.white,
),
],
);
}
),
),
),
),
bottomNavigationBar: buildBottomNavigationBar(context),
);
}
Widget buildBottomNavigationBar(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: List.generate(list.length, (index) {
return Expanded(
child: InkWell(
onTap: () async {
updateTabBarIndex(index);
isIgnoreCalcTabBarIndex = true;
// await sliverItemObserverController.jumpTo(
// sliverContext: itemSliverIndexCtxMap[index],
// index: 0,
// isFixedHeight: true,
// offset: (offset) {
// return ObserverUtils.calcPersistentHeaderExtent(
// key: appBarKey,
// offset: offset,
// );
// },
// );
await sliverItemObserverController.animateTo(
sliverContext: itemSliverIndexCtxMap[index],
index: 0,
isFixedHeight: true,
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
offset: (offset) {
return ObserverUtils.calcPersistentHeaderExtent(
key: appBarKey,
offset: offset,
);
},
);
isIgnoreCalcTabBarIndex = false;
},
child: ValueListenableBuilder(
valueListenable: tabCurrentSelectedIndex,
builder: (BuildContext context, int value, Widget? child) {
return Container(
alignment: Alignment.center,
height: 40,
decoration: BoxDecoration(
border: Border.all(width: 0.5),
color: value == index ? Colors.amber : Colors.white,
),
child: Text(
list[index].categoryName,
),
);
},
),
),
);
}),
),
SizedBox(height: MediaQuery.paddingOf(context).bottom),
],
);
}
Widget _buildSectionListView(int mainIndex) {
return SliverObserveContext(
child: SliverStickyHeader(
header: Container(
color: Colors.red,
height: 40,
alignment: Alignment.centerLeft,
child: Text(
list[mainIndex].categoryName,
style: const TextStyle(color: Colors.white, fontSize: 16),
),
),
sliver: SliverFixedExtentList(
itemExtent: 120,
delegate: SliverChildBuilderDelegate(
(context, index) {
// Save the context of SliverList.
itemSliverIndexCtxMap[mainIndex] = context;
return Container(
color: Colors.white,
margin: const EdgeInsets.symmetric(vertical: 5),
height: 100,
child: Text(list[mainIndex].goods[index].goodsName),
);
},
childCount: list[mainIndex].goods.length,
),
),
),
onObserve: (context) {
// Save the context of the outermost sliver.
sliverIndexCtxMap[mainIndex] = context;
},
);
}
updateTabBarIndex(int index) {
if (index == tabCurrentSelectedIndex.value) return;
tabCurrentSelectedIndex.value = index;
}
}
上面这段代码商品列表在上移后会被挡住,不知道怎么改写
Platforms
dart
Description
我并不是一个资深的Flutter人员,目前在研究你的这个插件,想结合你这个做一个类似美团外卖或是饿了么店铺首页页面,通过你的示例进行结合,要么商品列表上移时,SliverAppBar无法上移,要么上移后会被SliverAppBar或是SliverPersistentHeader总价盖住,能否结合这些做一个类似美团外卖或是饿了么店铺首页的示例呢?
Why
No response