@@ -88,12 +88,21 @@ class ListView extends StatefulWidget
8888 controller.shrinkWrap = Utils .optionalBool (value),
8989 'nestedScroll' : (value) =>
9090 controller.nestedScroll = Utils .optionalBool (value),
91+ 'initialScrollOffset' : (value) =>
92+ _controller.initialScrollOffset = Utils .optionalDouble (value),
93+ 'initialScrollIndex' : (value) =>
94+ _controller.initialScrollIndex = Utils .optionalInt (value),
9195 };
9296 }
9397
9498 @override
9599 Map <String , Function > methods () {
96- return {};
100+ return {
101+ 'scrollToOffset' : (num offset) => _controller.scrollToOffset (offset.toDouble ()),
102+ 'scrollToTop' : () => _controller.scrollToTop (),
103+ 'scrollToBottom' : () => _controller.scrollToBottom (),
104+ 'scrollToIndex' : (num index) => _controller.scrollToIndex (index.toInt ()),
105+ };
97106 }
98107
99108 @override
@@ -127,9 +136,69 @@ class ListViewController extends BoxLayoutController {
127136 bool ? shrinkWrap;
128137 bool ? nestedScroll;
129138
139+ // scroll position control
140+ double ? initialScrollOffset;
141+ int ? initialScrollIndex;
142+
130143 void _bind (ListViewState state) {
131144 widgetState = state;
132145 }
146+
147+ /// Scroll to a specific offset
148+ void scrollToOffset (double offset, {bool animated = true }) {
149+ if (scrollController == null || ! scrollController! .hasClients) return ;
150+ if (animated) {
151+ scrollController! .animateTo (
152+ offset,
153+ duration: const Duration (milliseconds: 300 ),
154+ curve: Curves .easeInOut,
155+ );
156+ } else {
157+ scrollController! .jumpTo (offset);
158+ }
159+ }
160+
161+ /// Scroll to the top
162+ void scrollToTop ({bool animated = true }) {
163+ scrollToOffset (0 , animated: animated);
164+ }
165+
166+ /// Scroll to the bottom
167+ void scrollToBottom ({bool animated = true }) {
168+ if (scrollController == null || ! scrollController! .hasClients) return ;
169+ final maxOffset = scrollController! .position.maxScrollExtent;
170+ scrollToOffset (maxOffset, animated: animated);
171+ }
172+
173+ /// Scroll to a specific item index
174+ void scrollToIndex (int index, {bool animated = true }) {
175+ if (scrollController == null || ! scrollController! .hasClients) return ;
176+ if (index < 0 ) index = 0 ;
177+
178+ // Get total item count from templated data
179+ final count = widgetState? .templatedDataList? .length ?? 0 ;
180+ if (count == 0 ) return ;
181+
182+ // Clamp index to valid range
183+ if (index >= count) index = count - 1 ;
184+
185+ final maxExtent = scrollController! .position.maxScrollExtent;
186+ final viewportHeight = scrollController! .position.viewportDimension;
187+ final totalContentHeight = maxExtent + viewportHeight;
188+
189+ // Calculate estimated item height
190+ final estimatedItemHeight = totalContentHeight / count;
191+
192+ // Calculate target offset
193+ double offset = index * estimatedItemHeight;
194+
195+ // Clamp to max scroll extent
196+ if (offset > maxExtent) {
197+ offset = maxExtent;
198+ }
199+
200+ scrollToOffset (offset, animated: animated);
201+ }
133202}
134203
135204class ListViewState extends EWidgetState <ListView >
@@ -141,6 +210,10 @@ class ListViewState extends EWidgetState<ListView>
141210 @override
142211 void initState () {
143212 showLoading = widget._controller.showLoading;
213+ // Create scroll controller with initial offset if not already set
214+ widget._controller.scrollController ?? = ScrollController (
215+ initialScrollOffset: widget._controller.initialScrollOffset ?? 0 ,
216+ );
144217 super .initState ();
145218 }
146219
@@ -161,6 +234,15 @@ class ListViewState extends EWidgetState<ListView>
161234 });
162235 });
163236 }
237+
238+ // Handle initialScrollIndex after first frame
239+ if (widget._controller.initialScrollIndex != null ) {
240+ WidgetsBinding .instance.addPostFrameCallback ((_) {
241+ if (mounted) {
242+ widget._controller.scrollToIndex (widget._controller.initialScrollIndex! );
243+ }
244+ });
245+ }
164246 }
165247
166248 @override
0 commit comments