Skip to content

Commit 83256cb

Browse files
committed
update: example
1 parent 1eccb55 commit 83256cb

File tree

3 files changed

+283
-38
lines changed

3 files changed

+283
-38
lines changed

example/lib/features/custom_scrollview/custom_scrollview_demo/multi_sliver_demo_page.dart

Lines changed: 124 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66

77
import 'package:flutter/material.dart';
8+
import 'package:flutter/rendering.dart';
89
import 'package:flutter_sticky_header/flutter_sticky_header.dart';
910
import 'package:scrollview_observer/scrollview_observer.dart';
1011
import 'package:scrollview_observer_example/utils/random.dart';
@@ -32,15 +33,23 @@ class _MultiSliverDemoPageState extends State<MultiSliverDemoPage> {
3233
final appBarKey = GlobalKey();
3334

3435
final scrollController = ScrollController();
35-
late final SliverObserverController sliverObserverController;
36-
Map<int, BuildContext> contextList = {};
36+
late final SliverObserverController sliverItemObserverController;
37+
// late final SliverObserverController sliverObserverController;
38+
Map<int, BuildContext> itemSliverIndexCtxMap = {};
39+
Map<int, BuildContext> sliverIndexCtxMap = {};
40+
41+
ValueNotifier<int> tabCurrentSelectedIndex = ValueNotifier(0);
42+
bool isIgnoreCalcTabBarIndex = false;
3743

3844
@override
3945
void initState() {
4046
super.initState();
41-
sliverObserverController = SliverObserverController(
47+
sliverItemObserverController = SliverObserverController(
4248
controller: scrollController,
4349
);
50+
// sliverObserverController = SliverObserverController(
51+
// controller: scrollController,
52+
// );
4453

4554
for (var i = 0; i < 4; i++) {
4655
final tag = 'Section ${i + 1}';
@@ -54,26 +63,80 @@ class _MultiSliverDemoPageState extends State<MultiSliverDemoPage> {
5463

5564
@override
5665
Widget build(BuildContext context) {
66+
Widget resultWidget = _buildScrollView();
67+
resultWidget = _buildSliverItemObserver(child: resultWidget);
68+
resultWidget = _buildSliverObserver(child: resultWidget);
5769
return Scaffold(
58-
body: SliverViewObserver(
59-
controller: sliverObserverController,
60-
sliverContexts: () => contextList.values.toList(),
61-
child: CustomScrollView(
62-
controller: scrollController,
63-
physics: const ClampingScrollPhysics(),
64-
slivers: [
65-
SliverAppBar(
70+
body: resultWidget,
71+
bottomNavigationBar: buildBottomNavigationBar(context),
72+
);
73+
}
74+
75+
/// To observe sliver items and handle scrollTo.
76+
Widget _buildSliverItemObserver({
77+
required Widget child,
78+
}) {
79+
return SliverViewObserver(
80+
controller: sliverItemObserverController,
81+
sliverContexts: () => itemSliverIndexCtxMap.values.toList(),
82+
child: child,
83+
);
84+
}
85+
86+
/// To observe which sliver is currently the first.
87+
Widget _buildSliverObserver({
88+
required Widget child,
89+
}) {
90+
return SliverViewObserver(
91+
// controller: sliverObserverController,
92+
child: child,
93+
sliverContexts: () => sliverIndexCtxMap.values.toList(),
94+
triggerOnObserveType: ObserverTriggerOnObserveType.directly,
95+
dynamicLeadingOffset: () {
96+
// Accumulate the height of all PersistentHeader.
97+
return ObserverUtils.calcPersistentHeaderExtent(
6698
key: appBarKey,
67-
pinned: true,
68-
title: const Text('Multi Sliver'),
69-
),
70-
...List.generate(modelList.length, (mainIndex) {
71-
return _buildSectionListView(mainIndex);
72-
}),
73-
],
99+
offset: scrollController.offset,
100+
) +
101+
1; // To avoid tabBar index rebound.
102+
},
103+
onObserveViewport: (result) {
104+
if (isIgnoreCalcTabBarIndex) return;
105+
int? currentTabIndex;
106+
final currentFirstSliverCtx = result.firstChild.sliverContext;
107+
for (var sectionIndex in sliverIndexCtxMap.keys) {
108+
final ctx = sliverIndexCtxMap[sectionIndex];
109+
if (ctx == null) continue;
110+
// If they are not the same sliver, continue.
111+
if (currentFirstSliverCtx != ctx) continue;
112+
// If the sliver is not visible, continue.
113+
final visible =
114+
(ctx.findRenderObject() as RenderSliver).geometry?.visible ??
115+
false;
116+
if (!visible) continue;
117+
currentTabIndex = sectionIndex;
118+
break;
119+
}
120+
if (currentTabIndex == null) return;
121+
updateTabBarIndex(currentTabIndex);
122+
},
123+
);
124+
}
125+
126+
Widget _buildScrollView() {
127+
return CustomScrollView(
128+
controller: scrollController,
129+
physics: const ClampingScrollPhysics(),
130+
slivers: [
131+
SliverAppBar(
132+
key: appBarKey,
133+
pinned: true,
134+
title: const Text('Multi Sliver'),
74135
),
75-
),
76-
bottomNavigationBar: buildBottomNavigationBar(context),
136+
...List.generate(modelList.length, (mainIndex) {
137+
return _buildSectionListView(mainIndex);
138+
}),
139+
],
77140
);
78141
}
79142

@@ -85,9 +148,11 @@ class _MultiSliverDemoPageState extends State<MultiSliverDemoPage> {
85148
children: List.generate(modelList.length, (index) {
86149
return Expanded(
87150
child: InkWell(
88-
onTap: () {
89-
// sliverObserverController.jumpTo(
90-
// sliverContext: contextList[index],
151+
onTap: () async {
152+
updateTabBarIndex(index);
153+
isIgnoreCalcTabBarIndex = true;
154+
// await sliverItemObserverController.jumpTo(
155+
// sliverContext: itemSliverIndexCtxMap[index],
91156
// index: 0,
92157
// isFixedHeight: true,
93158
// offset: (offset) {
@@ -97,11 +162,11 @@ class _MultiSliverDemoPageState extends State<MultiSliverDemoPage> {
97162
// );
98163
// },
99164
// );
100-
sliverObserverController.animateTo(
101-
sliverContext: contextList[index],
165+
await sliverItemObserverController.animateTo(
166+
sliverContext: itemSliverIndexCtxMap[index],
102167
index: 0,
103168
isFixedHeight: true,
104-
duration: const Duration(seconds: 1),
169+
duration: const Duration(milliseconds: 200),
105170
curve: Curves.easeInOut,
106171
offset: (offset) {
107172
return ObserverUtils.calcPersistentHeaderExtent(
@@ -110,28 +175,35 @@ class _MultiSliverDemoPageState extends State<MultiSliverDemoPage> {
110175
);
111176
},
112177
);
178+
isIgnoreCalcTabBarIndex = false;
113179
},
114-
child: Container(
115-
alignment: Alignment.center,
116-
height: 40,
117-
decoration: BoxDecoration(
118-
border: Border.all(width: 0.5),
119-
),
120-
child: Text(
121-
modelList[index].tag,
122-
),
180+
child: ValueListenableBuilder(
181+
valueListenable: tabCurrentSelectedIndex,
182+
builder: (BuildContext context, int value, Widget? child) {
183+
return Container(
184+
alignment: Alignment.center,
185+
height: 40,
186+
decoration: BoxDecoration(
187+
border: Border.all(width: 0.5),
188+
color: value == index ? Colors.amber : Colors.white,
189+
),
190+
child: Text(
191+
modelList[index].tag,
192+
),
193+
);
194+
},
123195
),
124196
),
125197
);
126198
}),
127199
),
128-
SizedBox(height: MediaQuery.of(context).padding.bottom),
200+
SizedBox(height: MediaQuery.paddingOf(context).bottom),
129201
],
130202
);
131203
}
132204

133205
Widget _buildSectionListView(int mainIndex) {
134-
return SliverStickyHeader(
206+
Widget resultWidget = SliverStickyHeader(
135207
header: Container(
136208
height: 40,
137209
color: Colors.white,
@@ -145,7 +217,8 @@ class _MultiSliverDemoPageState extends State<MultiSliverDemoPage> {
145217
itemExtent: 120,
146218
delegate: SliverChildBuilderDelegate(
147219
(context, index) {
148-
contextList[mainIndex] = context;
220+
// Save the context of SliverList.
221+
itemSliverIndexCtxMap[mainIndex] = context;
149222
return Container(
150223
padding: const EdgeInsets.only(left: 12),
151224
color: RandomTool.color(),
@@ -159,5 +232,18 @@ class _MultiSliverDemoPageState extends State<MultiSliverDemoPage> {
159232
),
160233
),
161234
);
235+
resultWidget = SliverObserveContext(
236+
child: resultWidget,
237+
onObserve: (context) {
238+
// Save the context of the outermost sliver.
239+
sliverIndexCtxMap[mainIndex] = context;
240+
},
241+
);
242+
return resultWidget;
243+
}
244+
245+
updateTabBarIndex(int index) {
246+
if (index == tabCurrentSelectedIndex.value) return;
247+
tabCurrentSelectedIndex.value = index;
162248
}
163249
}

example/lib/features/home/home_page.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import 'package:scrollview_observer_example/features/listview/listview_dynamic_o
2323
import 'package:scrollview_observer_example/features/listview/listview_fixed_height_demo/listview_fixed_height_demo_page.dart';
2424
import 'package:scrollview_observer_example/features/listview/sliver_list_demo/sliver_list_demo_page.dart';
2525
import 'package:scrollview_observer_example/features/nested_scrollview/nested_scrollview_demo/nested_scrollview_demo_page.dart';
26+
import 'package:scrollview_observer_example/features/pageview/pageview_demo/pageview_demo_page.dart';
2627
import 'package:scrollview_observer_example/features/scene/anchor_demo/anchor_page.dart';
2728
import 'package:scrollview_observer_example/features/scene/anchor_demo/anchor_waterfall_page.dart';
2829
import 'package:scrollview_observer_example/features/scene/azlist_demo/azlist_page.dart';
@@ -189,6 +190,12 @@ class HomePage extends StatelessWidget {
189190
return const NestedScrollViewDemoPage();
190191
},
191192
),
193+
Tuple2<String, PageBuilder>(
194+
"PageView",
195+
() {
196+
return const PageViewDemoPage();
197+
},
198+
),
192199
Tuple2<String, PageBuilder>(
193200
"VideoList AutoPlay",
194201
() {

0 commit comments

Comments
 (0)