flutter - How to auto scroll to index with dynamic height programmatically
Your question
so, What's a temporary solution to make ListView with AutoScroll for dynamic height?
Screen Record
solution
scroll-to-index package
This package provides the scroll to index mechanism for fixed/variable row height for Flutter scrollable widget.
This is a widget level library, means you can use this mechanism inside any Flutter scrollable widget.
Usage
pubspec.yaml
scroll_to_index: any
Example From quire-io/scroll-to-index: scroll to index with fixed/variable row height inside Flutter scrollable widget
Usage - Controller
controller.scrollToIndex(index, preferPosition: AutoScrollPosition.begin)
Usage - ListView
ListView(scrollDirection: scrollDirection,controller: controller,children: randomList.map<Widget>((data) { final index = data[0]; final height = data[1]; return AutoScrollTag( key: ValueKey(index), controller: controller, index: index, child: Text('index: $index, height: $height'), highlightColor: Colors.black.withOpacity(0.1), );}).toList(),)
Extra anunixercoder asked
Is this for finite or infinite list ?
Using ListView.builder to simulate an infinite list
ListView.builder( scrollDirection: scrollDirection, controller: controller, itemBuilder: (context, i) => Padding( padding: EdgeInsets.all(8), child: _getRow(i, (min + rnd.nextInt(max - min)).toDouble()), ),
It got 18 right of 22 tries , about (81%) or near to it
Screen Record
so it not suported 100%
Ref :
https://raw.githubusercontent.com/quire-io/scroll-to-index/master/example/lib/main.dart
How to get a random number from range in dart? - Stack Overflow
1st screen record code
//Copyright (C) 2019 Potix Corporation. All Rights Reserved.//History: Tue Apr 24 09:29 CST 2019// Author: Jerry Chenimport 'dart:math' as math;import 'package:flutter/material.dart';import 'package:scroll_to_index/scroll_to_index.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Scroll To Index Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(title: 'Scroll To Index Demo'), ); }}class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState();}class _MyHomePageState extends State<MyHomePage> { static const maxCount = 100; final random = math.Random(); final scrollDirection = Axis.vertical; AutoScrollController controller; List<List<int>> randomList; @override void initState() { super.initState(); controller = AutoScrollController( viewportBoundaryGetter: () => Rect.fromLTRB(0, 0, 0, MediaQuery.of(context).padding.bottom), axis: scrollDirection ); randomList = List.generate(maxCount, (index) => <int>[index, (1000 * random.nextDouble()).toInt()]); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: ListView( scrollDirection: scrollDirection, controller: controller, children: randomList.map<Widget>((data) { return Padding( padding: EdgeInsets.all(8), child: _getRow(data[0], math.max(data[1].toDouble(), 50.0)), ); }).toList(), ), floatingActionButton: FloatingActionButton( onPressed: _scrollToIndex, tooltip: 'Increment', child: Text(counter.toString()), ), ); } int counter = -1; Future _scrollToIndex() async { setState(() { counter++; if (counter >= maxCount) counter = 0; }); await controller.scrollToIndex(counter, preferPosition: AutoScrollPosition.begin); controller.highlight(counter); } Widget _getRow(int index, double height) { return _wrapScrollTag( index: index, child: Container( padding: EdgeInsets.all(8), alignment: Alignment.topCenter, height: height, decoration: BoxDecoration( border: Border.all( color: Colors.lightBlue, width: 4 ), borderRadius: BorderRadius.circular(12) ), child: Text('index: $index, height: $height'), ) ); } Widget _wrapScrollTag({int index, Widget child}) => AutoScrollTag( key: ValueKey(index), controller: controller, index: index, child: child, highlightColor: Colors.black.withOpacity(0.1), );}
2nd screen record code :
//Copyright (C) 2019 Potix Corporation. All Rights Reserved.//History: Tue Apr 24 09:29 CST 2019// Author: Jerry Chenimport 'dart:math' as math;import 'package:flutter/material.dart';import 'package:scroll_to_index/scroll_to_index.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Scroll To Index Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(title: 'Scroll To Index Demo'), ); }}class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState();}class _MyHomePageState extends State<MyHomePage> { final scrollDirection = Axis.vertical; var rnd = math.Random(); int min = 50; int max = 200; AutoScrollController controller; @override void initState() { super.initState(); controller = AutoScrollController( viewportBoundaryGetter: () => Rect.fromLTRB(0, 0, 0, MediaQuery.of(context).padding.bottom), axis: scrollDirection); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: ListView.builder( scrollDirection: scrollDirection, controller: controller, itemBuilder: (context, i) => Padding( padding: EdgeInsets.all(8), child: _getRow(i, (min + rnd.nextInt(max - min)).toDouble()), ), ), floatingActionButton: FloatingActionButton( onPressed: _scrollToIndex, tooltip: 'Increment', child: Text(counter.toString()), ), ); } int counter = -1; Future _scrollToIndex() async { setState(() { counter++; }); await controller.scrollToIndex(counter, preferPosition: AutoScrollPosition.begin); controller.highlight(counter); } Widget _getRow(int index, double height) { return _wrapScrollTag( index: index, child: Container( padding: EdgeInsets.all(8), alignment: Alignment.topCenter, height: height, decoration: BoxDecoration( border: Border.all(color: Colors.lightBlue, width: 4), borderRadius: BorderRadius.circular(12)), child: Text('index: $index, height: $height'), )); } Widget _wrapScrollTag({int index, Widget child}) => AutoScrollTag( key: ValueKey(index), controller: controller, index: index, child: child, highlightColor: Colors.black.withOpacity(0.1), );}
This package might be the one you are looking for. You can assign id for each widget and enable an animation just like tags in HTML.
https://pub.dev/packages/scroll_to_id
import 'package:flutter/material.dart';import 'package:scroll_to_id/scroll_to_id.dart';void main() { runApp(MyApp());}class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState();}class _MyAppState extends State<MyApp> { final ScrollToId scrollToId = ScrollToId(); @override Widget build(BuildContext context) { return MaterialApp( title: 'Scroll to ID', theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( appBar: AppBar( title: const Text('Scroll to ID'), ), body: Stack( alignment: Alignment.topRight, children: [ InteractiveScrollViewer( scrollToId: scrollToId, children: [ ScrollContent( id: '1', child: Container( height: 600, color: Colors.green, ), ), ScrollContent( id: '2', child: Container( height: 800, color: Colors.red, ), ), ScrollContent( id: '3', child: Container( height: 300, color: Colors.yellow, ), ), ScrollContent( id: '4', child: Container( height: 700, color: Colors.blue, ), ), ], ), Container( decoration: BoxDecoration( border: Border.all(color: Colors.white, width: 3), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ GestureDetector( child: Container( width: 100, alignment: Alignment.center, height: 50, child: Text('1', style: TextStyle(color: Colors.white),), color: Colors.green, ), onTap: () { scrollToId.scroll(id: '1'); }, ), GestureDetector( child: Container( width: 100, alignment: Alignment.center, height: 50, child: Text('2', style: TextStyle(color: Colors.white),), color: Colors.red, ), onTap: () { scrollToId.scroll(id: '2'); }, ), GestureDetector( child: Container( width: 100, alignment: Alignment.center, height: 50, child: Text('3', style: TextStyle(color: Colors.white),), color: Colors.yellow, ), onTap: () { scrollToId.scroll(id: '3'); }, ), GestureDetector( child: Container( width: 100, alignment: Alignment.center, height: 50, child: Text('4', style: TextStyle(color: Colors.white),), color: Colors.blue, ), onTap: () { scrollToId.scroll(id: '4'); }, ), ], ), ) ], ), ), ); }}
How observe current offset index?
controller.addListener(() { AutoScrollTagState sts = controller.tagMap[0]; print(sts); });