Skip to content

Commit 84d4d64

Browse files
committed
update: example
1 parent 99475a0 commit 84d4d64

File tree

2 files changed

+260
-0
lines changed

2 files changed

+260
-0
lines changed

example/lib/features/home/home_page.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import 'package:scrollview_observer_example/features/listview/listview_fixed_hei
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';
2626
import 'package:scrollview_observer_example/features/pageview/pageview_demo/pageview_demo_page.dart';
27+
import 'package:scrollview_observer_example/features/pageview/pageview_demo/pageview_parallax_item_listener_page.dart';
2728
import 'package:scrollview_observer_example/features/pageview/pageview_demo/pageview_parallax_page.dart';
2829
import 'package:scrollview_observer_example/features/scene/anchor_demo/anchor_page.dart';
2930
import 'package:scrollview_observer_example/features/scene/anchor_demo/anchor_waterfall_page.dart';
@@ -203,6 +204,12 @@ class HomePage extends StatelessWidget {
203204
return const PageViewParallaxPage();
204205
},
205206
),
207+
Tuple2<String, PageBuilder>(
208+
"PageView - Parallax ItemListener",
209+
() {
210+
return const PageViewParallaxItemListenerPage();
211+
},
212+
),
206213
Tuple2<String, PageBuilder>(
207214
"VideoList AutoPlay",
208215
() {
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
/*
2+
* @Author: LinXunFeng [email protected]
3+
* @Repo: https://github.com/fluttercandies/flutter_scrollview_observer
4+
* @Date: 2024-11-14 22:41:51
5+
*/
6+
7+
import 'package:flutter/material.dart';
8+
import 'package:flutter/rendering.dart';
9+
10+
import 'package:scrollview_observer/scrollview_observer.dart';
11+
12+
class PageViewParallaxItemListenerPage extends StatefulWidget {
13+
const PageViewParallaxItemListenerPage({Key? key}) : super(key: key);
14+
15+
@override
16+
State<PageViewParallaxItemListenerPage> createState() =>
17+
_PageViewParallaxItemListenerPageState();
18+
}
19+
20+
class _PageViewParallaxItemListenerPageState
21+
extends State<PageViewParallaxItemListenerPage> {
22+
late PageController pageController;
23+
24+
List<String> pageItemBgPicList = [
25+
'11898897',
26+
'26653530',
27+
'12974784',
28+
'943459',
29+
'4424178',
30+
'20433037',
31+
'4424137',
32+
'4955810',
33+
'4424137',
34+
'18847956',
35+
]
36+
.map((id) =>
37+
'https://images.pexels.com/photos/$id/pexels-photo-$id.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2')
38+
.toList();
39+
40+
int get pageItemCount => pageItemBgPicList.length;
41+
42+
List<ValueNotifier<double>> pageItemBgPicAlignmentXList = [];
43+
44+
final observerController = ListObserverController();
45+
46+
@override
47+
void initState() {
48+
super.initState();
49+
pageController = PageController(
50+
initialPage: 4,
51+
viewportFraction: 0.9,
52+
);
53+
pageItemBgPicAlignmentXList = List.generate(
54+
pageItemCount,
55+
(index) {
56+
return ValueNotifier<double>(0);
57+
},
58+
);
59+
60+
Future.delayed(const Duration(milliseconds: 100)).then((_) {
61+
observerController.dispatchOnceObserve();
62+
});
63+
}
64+
65+
@override
66+
void dispose() {
67+
pageController.dispose();
68+
for (var e in pageItemBgPicAlignmentXList) {
69+
e.dispose();
70+
}
71+
super.dispose();
72+
}
73+
74+
@override
75+
Widget build(BuildContext context) {
76+
return Scaffold(
77+
backgroundColor: Colors.amber[50],
78+
appBar: AppBar(
79+
title: const Text(
80+
"PageView - Parallax",
81+
style: TextStyle(
82+
color: Colors.white,
83+
),
84+
),
85+
backgroundColor: Colors.black87,
86+
leading: IconButton(
87+
icon: const Icon(
88+
Icons.arrow_back_ios_new,
89+
color: Colors.white,
90+
),
91+
onPressed: () {
92+
Navigator.of(context).pop();
93+
},
94+
),
95+
),
96+
body: Center(
97+
child: _buildPageView(),
98+
),
99+
);
100+
}
101+
102+
Widget _buildPageView() {
103+
Widget resultWidget = PageView.builder(
104+
controller: pageController,
105+
itemBuilder: (context, index) {
106+
// return _buildPageItem(index);
107+
return ParallaxItemView(
108+
index: index,
109+
imgUrl: pageItemBgPicList[index],
110+
);
111+
},
112+
itemCount: pageItemCount,
113+
);
114+
resultWidget = ListViewObserver(
115+
controller: observerController,
116+
child: resultWidget,
117+
triggerOnObserveType: ObserverTriggerOnObserveType.directly,
118+
customTargetRenderSliverType: (renderObj) {
119+
return renderObj is RenderSliverFillViewport;
120+
},
121+
);
122+
123+
resultWidget = SizedBox(
124+
height: (MediaQuery.sizeOf(context).height -
125+
MediaQuery.paddingOf(context).top -
126+
kToolbarHeight) *
127+
0.8,
128+
child: resultWidget,
129+
);
130+
return resultWidget;
131+
}
132+
}
133+
134+
class ParallaxItemView extends StatefulWidget {
135+
final int index;
136+
final String imgUrl;
137+
138+
const ParallaxItemView({
139+
Key? key,
140+
required this.index,
141+
required this.imgUrl,
142+
}) : super(key: key);
143+
144+
@override
145+
State<ParallaxItemView> createState() => _ParallaxItemViewState();
146+
}
147+
148+
class _ParallaxItemViewState extends State<ParallaxItemView> {
149+
ListViewObserverState? observerState;
150+
151+
final picAlignmentX = ValueNotifier<double>(0);
152+
153+
@override
154+
void didChangeDependencies() {
155+
super.didChangeDependencies();
156+
157+
removeListener();
158+
observerState = ListViewObserver.of(context)
159+
..addListener(
160+
onObserve: handleObserverResult,
161+
);
162+
}
163+
164+
@override
165+
void dispose() {
166+
removeListener();
167+
picAlignmentX.dispose();
168+
super.dispose();
169+
}
170+
171+
void removeListener() {
172+
observerState?.removeListener(
173+
onObserve: handleObserverResult,
174+
);
175+
observerState = null;
176+
}
177+
178+
void handleObserverResult(
179+
ListViewObserveModel result,
180+
) {
181+
if (result.displayingChildModelMap.isEmpty) return;
182+
final model = result.displayingChildModelMap[widget.index];
183+
if (model == null) {
184+
picAlignmentX.value = 0;
185+
return;
186+
}
187+
188+
picAlignmentX.value = 1 - model.displayPercentage;
189+
if (model.leadingMarginToViewport > 0) {
190+
picAlignmentX.value = -picAlignmentX.value;
191+
}
192+
193+
if (picAlignmentX.value > 1) {
194+
picAlignmentX.value = 1;
195+
} else if (picAlignmentX.value < -1) {
196+
picAlignmentX.value = -1;
197+
}
198+
}
199+
200+
@override
201+
Widget build(BuildContext context) {
202+
Widget resultWidget = Stack(
203+
alignment: AlignmentDirectional.center,
204+
children: [
205+
Positioned(
206+
left: 0,
207+
right: 0,
208+
top: 0,
209+
bottom: 0,
210+
child: _buildPageItemBgPicView(widget.index),
211+
),
212+
const SizedBox.expand(),
213+
_buildNum(widget.index),
214+
],
215+
);
216+
resultWidget = Container(
217+
margin: const EdgeInsets.symmetric(horizontal: 8),
218+
clipBehavior: Clip.antiAlias,
219+
decoration: BoxDecoration(
220+
color: Colors.blue[50],
221+
borderRadius: BorderRadius.circular(10),
222+
),
223+
child: resultWidget,
224+
);
225+
return resultWidget;
226+
}
227+
228+
Widget _buildNum(int index) {
229+
return Container(
230+
alignment: Alignment.center,
231+
width: 80,
232+
height: 80,
233+
decoration: BoxDecoration(
234+
color: Colors.white.withOpacity(0.8),
235+
borderRadius: BorderRadius.circular(10),
236+
),
237+
child: Text("Page $index"),
238+
);
239+
}
240+
241+
Widget _buildPageItemBgPicView(int index) {
242+
return ValueListenableBuilder(
243+
valueListenable: picAlignmentX,
244+
builder: (BuildContext context, double alignmentX, Widget? child) {
245+
return Image.network(
246+
widget.imgUrl,
247+
fit: BoxFit.cover,
248+
alignment: Alignment(alignmentX, 0),
249+
);
250+
},
251+
);
252+
}
253+
}

0 commit comments

Comments
 (0)