Flutter package: Similar to a ListView, but lets you programmatically jump to any item, by index.

Overview

pub package

indexed_list_view

Similar to a ListView, but lets you programmatically jump to any item, by index. The index jump happens instantly, no matter if you have millions of items.

Limitation: The list is always infinite both to positive and negative indexes. In other words, it can be scrolled indefinitely both to the top and to the bottom.

You can define index bounds by giving it a minItemCount and maxItemCount, but this will not prevent the list from scrolling indefinitely. When showing items out of the index bounds, or when your itemBuilder returns null, it will ask the emptyItemBuilder to create an "empty" item to be displayed instead. As default, this will return empty containers.

Usage

Import the package

Add indexed_list_view as a dependency in your pubspec.yaml file, and then import it:

import 'package:indexed_list_view/indexed_list_view.dart';

Use the package

First, create an indexed scroll controller:

var controller = IndexedScrollController();

Optionally, you may setup an initial index and/or initial scroll offset:

var controller = IndexedScrollController(
    initialIndex: 75,
    initialScrollOffset : 30.0);    

Then, create the indexed list view, and pass it the controller:

IndexedListView.builder(
    controller: controller, 
    itemBuilder: itemBuilder);

There is also the separated constructor, same as ListView.separated:

IndexedListView.separated(
    controller: controller, 
    itemBuilder: itemBuilder,
    separatorBuilder: separatorBuilder);

To jump, use the controller methods like jumpToIndex :

controller.jumpToIndex(10000);

Details

The IndexedScrollController has not only an offset in pixels, but also an origin-index that indicates which item is considered to be at the offset position 0.0.

So there are two ways for you to move the list programmatically: You can change only the offset, or else change the originIndex and the offset at the same time.

To change the originIndex you make an "index jump". This jump is cheap, since it doesn't need to build all widgets between the old and new positions. It will just change the origin.

If you want to move the list programmatically you must create a scroll controller of type IndexedScrollController and pass it in the list constructor. However, if all you need is an infinite list without jumps, then there is no need to even create a controller.

You move the list programmatically by calling the controller methods.

Controller Methods

  1. jumpToIndex(index)

    The is the most common method for you to use. It jumps the origin-index to the given index, and the scroll-position to 0.0.

  2. jumpToIndexAndOffset(index, offset)

    Jumps the origin-index to the given index, and the scroll-position to offset, without animation.

  3. animateToIndex(index)

    If the current origin-index is already the same as the given index, animates the position from its current value to the offset position relative to the origin-index.

    However, if the current origin-index is different from the given index, this will jump to the new index, without any animation. In general, there are never animations when the index changes.

  4. animateToIndexAndOffset(index, offset)

    Same as animateToIndex() but also lets you specify the new offset.

  5. jumpTo(offset)

    Goes to origin-index "0", and then jumps the scroll position from its current value to the given offset, without animation.

  6. animateTo(offset)

    If the current origin-index is already "0", animates the position from its current value to the offset position.

    However, if the current origin-index is different from "0", this will jump to index "0" and the given offset, without any animation. In general, there are never animations when the index changes.

  7. jumpToWithSameOriginIndex(offset)

    Jumps the offset, relative to the current origin-index.

  8. animateToWithSameOriginIndex(offset)

    Animates the offset, relative to the current origin-index.

  9. jumpToRelative(offset)

    Jumps the offset, adding or subtracting from the current offset. It keeps the same origin-index.

  10. animateToRelative(offset)

    Animates the offset, adding or subtracting from the current offset. It keeps the same origin-index.

Don't forget to check the example tab. It shows an infinite list of items of different heights, and you may tap buttons to run some of the methods explained above.


Hopefully this widget will become obsolete when Flutter's original ListView allows for negative indexes and for indexed jumps. See: https://github.com/flutter/flutter/issues/12319

This package got some ideas from Collin Jackson's code in StackOverflow , and uses lots of code from Simon Lightfoot's infinite_listview.

The Flutter packages I've authored:

My Medium Articles:

My article in the official Flutter documentation:

---
Marcelo Glasberg:
https://github.com/marcglasberg
https://twitter.com/glasbergmarcelo
https://stackoverflow.com/users/3411681/marcg
https://medium.com/@marcglasberg

Comments
  • minScrollExtent = double.negativeinfinity causes perception of list hung

    minScrollExtent = double.negativeinfinity causes perception of list hung

    Sorry for filing one more issue :-(

    Since you allow infinite scrolling on either side, when I scroll to the top of the list and pull it down, it gives a perception that the list view is hung since it's probably still processing the negativeinfinity scroll extent. I tried setting minScrollExtent = 0.0 and it seems to work fine.

    Can you pl look into this as well?

    opened by jagan999 6
  • Issue in scrolling if the index remains the same

    Issue in scrolling if the index remains the same

    Hello, This code works awesomely well except I hit another issue today. I am using scrollToIndex when I go from a list screen to edit a record's details and then return back. Now if I keep editing the same record, the second time I return to the list screen, it scrolls to the first row (your code senses that there's no change in the index and if so, scrolls to 0.0) this is annoying. What happens if this check is removed in your code? Thanks

    opened by jagan999 6
  • Support for Shinkwrap property, like with ListView?

    Support for Shinkwrap property, like with ListView?

    Would it be possible to implement the shrinkwrap property, like ListView has, so that the IndexedListView doesn't expand to take up all of the available space given by the parent element when shrinkwrap=true?

    opened by rjgoodman 5
  • Finite List Implementaion

    Finite List Implementaion

    Here is a workaround that memics the finite list, while at the same time preserves the jumpToIndex functionality. It's not perfect, and it requires some tweaks, but it works at least.

    // main.dart
    import 'package:flutter/material.dart';
    import 'package:indexed_list_view/indexed_list_view.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          home: MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      int itemCount = 20; // <<< This is the only parameter that my solution requires
    
      int lastIndexRendered = 0;
    
      IndexedScrollController listController = IndexedScrollController();
    
      @override
      void initState() {
        void scrollListener() {
          if (lastIndexRendered < 0) {
            listController.jumpToIndex(0);
            setState(() {
              lastIndexRendered = 0;
            });
          } else if (lastIndexRendered > itemCount) {
            listController.jumpToIndexAndOffset(
              index: itemCount,
              offset: -listController.position.viewportDimension,
            );
            setState(() {
              lastIndexRendered = itemCount;
            });
          }
        }
    
        listController.addListener(scrollListener);
    
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: IndexedListView.builder(
            key: UniqueKey(),
            cacheExtent: 0.0,
            controller: listController,
            itemBuilder: (context, index) {
              lastIndexRendered = index;
              if (index < 0 || index > itemCount - 1) {
                return null;
              }
              return Padding(
                padding: const EdgeInsets.all(8.0),
                child: Card(
                  elevation: 10.0,
                  child: Padding(
                    padding: const EdgeInsets.all(16.0),
                    child: Text(
                        'Item ${index + 1}\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'),
                  ),
                ),
              );
            },
          ),
        );
      }
    }
    

    Copy the code above and run the app to see the solution in action. Then let's consider the drawbacks and how can this solution be integrated in your package. I should have created a pull request, but I'm not very experienced with dart itself.

    The first drawback is that we have to use a UniqueKey() in the IndexedListView.builder constructor. I don't know for sure if this could cause a performance issue. According to my tests I was able to create huge lists that scroll very well.

    The second is that we have to set cacheExtent to 0.0. I don't know too if there is some use-cases where it should be set to a specific value. The default value is 250.0 by the way. But for my solution to work it has to be 0.0.

    The third one, which is solvable but needs some work: suppose that we want to scroll to the last item. We have two cases here:

    If the height of the last item is bigger than the viewport height: we can safely jumpToIndex(lastItemIndex);, like this: image

    But if the height of the last item is less than the viewport height, we will end up with something like this image: image

    When it should look like this (because it's supposed that we cannot scroll past the bottom of the last item): image

    The solution for this is to use jumpToIndexAndOffset, and calculate the offset as follows: offset = the height of the last item - the height of the viewport. This works in general. If we know the height we can just use it in the equation. If we don't, we can use a globalKey (for the last item alone) and calculate the offset like this:

    ourController.jumpToIndexAndOffset(
      index: itemCount - 1,
      offset: (globalKey.currentContext.size.height - ourController.position.viewportDimension),
    );
    

    However, I found that if the scroll velocity is big: the globalKey.currentContext would be null sometimes.

    The other solution is more "hacky" and I did not try it myself yet, but I think it should work. If we define an effectively invisible widget just before the last item. For instance: a SizeBox with height of 1.0 and child of null. And whenever we want to scroll to the last item, we scroll to that invisible item like this:

    ourController.jumpToIndexAndOffset(
      index: itemCount - 1,
      offset: (1.0 - ourController.position.viewportDimension),
    );
    

    This would solve the problem, and we only have to deal with my solution as if itemCount is itemCount + 1, which is not that hard.

    Let me know what you think.

    opened by OsamaAbbas 5
  • Can we use

    Can we use "finite" list ?

    @marcglasberg

    Hi,

    Can we use finite list ? becuase my existing code use ListView.builder with exact item....

    or this plugin only support infinite list ?

    thanks

    opened by mestartlearncode 5
  • Scroll to index does not work

    Scroll to index does not work

    I copied the example code as is and tried to run it. When I click on any of the 3 buttons to scroll, it throws this error and does nothing.

    I/flutter (32287): ══╡ EXCEPTION CAUGHT BY FOUNDATION LIBRARY ╞════════════════════════════════════════════════════════ I/flutter (32287): The following assertion was thrown while dispatching notifications for IndexedScrollController: I/flutter (32287): A IndexedScrollController was used after being disposed. I/flutter (32287): Once you have called dispose() on a IndexedScrollController, it can no longer be used. I/flutter (32287): I/flutter (32287): When the exception was thrown, this was the stack: I/flutter (32287): #0 ChangeNotifier._debugAssertNotDisposed. (package:flutter/src/foundation/change_notifier.dart:61:9) I/flutter (32287): #1 ChangeNotifier._debugAssertNotDisposed (package:flutter/src/foundation/change_notifier.dart:67:6) I/flutter (32287): #2 ChangeNotifier.dispose (package:flutter/src/foundation/change_notifier.dart:134:12) I/flutter (32287): #3 ScrollController.dispose (package:flutter/src/widgets/scroll_controller.dart:198:11) I/flutter (32287): #4 _IndexedListViewState._addPositiveControllerListener. (package:indexed_list_view/indexed_list_view.dart:154:30) I/flutter (32287): #5 ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:161:21) I/flutter (32287): #6 IndexedScrollController.jumpToIndex (package:indexed_list_view/indexed_list_view.dart:72:7) I/flutter (32287): #7 _TestPageState.build. (file:///C:/Home/AndroidDevelopment/MoneyGuruFlutter/moneyguru/lib/ui/test_page.dart:212:47) I/flutter (32287): #8 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:503:14) I/flutter (32287): #9 _InkResponseState.build. (package:flutter/src/material/ink_well.dart:558:30) I/flutter (32287): #10 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:102:24) I/flutter (32287): #11 TapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:242:9) I/flutter (32287): #12 TapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:175:7) I/flutter (32287): #13 PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:315:9) I/flutter (32287): #14 PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:73:12) I/flutter (32287): #15 PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:101:11) I/flutter (32287): #16 _WidgetsFlutterBinding&BindingBase&GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:143:19) I/flutter (32287): #17 _WidgetsFlutterBinding&BindingBase&GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:121:22) I/flutter (32287): #18 _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:101:7) I/flutter (32287): #19 _WidgetsFlutterBinding&BindingBase&GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:64:7) I/flutter (32287): #20 _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:48:7) I/flutter (32287): #21 _invoke1 (dart:ui/hooks.dart:142:13) I/flutter (32287): #22 _dispatchPointerDataPacket (dart:ui/hooks.dart:99:5) I/flutter (32287): I/flutter (32287): The IndexedScrollController sending notification was: I/flutter (32287): IndexedScrollController#f752d(one client, offset 0.0)

    opened by jagan999 5
  • How to get current index value?

    How to get current index value?

    Hi, I have tried to use you library but now I am stucked here.

    controller.addListener(() { print("Now which index we are in "+controller.position.pixels.toString()); }); I have set listener I notice you cant get what is the current index which I have scroll too.

    Another thing. return (BuildContext context, int index) { // return Card( child: Container( //height: heights[index % 527], color: (index == 0) ? Colors.red : Colors.green, child: Center(child: Text('ITEM $index')), ), ); };

    How is this build I dont see any for loop or any list array here?

    opened by newbieflutter 4
  • IndexedListView scrolls up by itself without stopping

    IndexedListView scrolls up by itself without stopping

    ParentWidget(
        child: IndexedListView.builder(
            // ...
        )
    )
    

    While scrolling up, I have a logic that as loads more data from my backend. When new data arrives ParentWidget is rebuild and this makes the IndexedListView scroll up by itself without stopping. If I disable my logic to fetch new data then ParentWidget no longer gets rebuilt and the bug does not occur, so I think the issue happens when some parent of IndexedListView is rebuilt.

    opened by tudor07 3
  • [Feature Request] Quick Navigation (Scrollbar)

    [Feature Request] Quick Navigation (Scrollbar)

    Thanks for your great work, been using it for a while. Working like a charm. I think if someone needs a long list in flutter, they definitely need this.

    However, I need a scrollbar for the users to navigate quickly in the list. I think it's a common feature for a list, I found some libs to achieve the same features, but none of them is correctly functioning. Is that difficult to implement for the indexed list view?

    opened by rockingdice 3
  • how to create a limited items ListView

    how to create a limited items ListView

    such as I want to create a ListView contains 10 card .

    "IndexedListView.builder()" have not such keyword like "itemCount" . I don't know how to create it ?

    opened by sunshineinwater 3
  • Jump and animate confusion

    Jump and animate confusion

    First of all, great package. After some research yours is the only package that supports positive and negative ListView, which should really be a default behaviour in flutter but I digress.

    I noticed the following issues:

    1. The animateTo() method is not forwarding the duration and curve parameter to the animateToIndexAndOffset() method. I've had to override this controller to be able to use it effectively with duration and curve.
    2. It took me a lot of hours to try and get a snapping behaviour to work, mainly because of the confusion around the animateToIndex() method. Finally, after hours of frustration, and testing, the animatetToIndex does not really do what the name suggest. It only animates back to an anchor point known as the _originIndex, and jumps each time a new index is called. So I abandoned its usage and tried other methods. May I suggest that it is renamed to animateToAnchor(). This would make it more clear. If developer wants to jump to a specific index, they could just use jumpTo() instead. Baking in a use case for jumping to an index in an "animate" method is confusing.
    3. I ended up using the animateToWithSameOriginIndex() method to achieve what I thought animateToIndex would achieve. I feel like this should have been named animateTo() though because it actually does the animation since the origin does not change. The animateTo() should really be renamed to jumpOrAnimateToAnchor() because it really only animates if the anchor hasn't changed from a previous programatic jump.
    4. Overall, I just think the explicit use of the word jump and animate to do what they are suppose to do will make it less confusing.
    5. May I suggest the change in the name of _originIndex to _anchorIndex as anchor seems to be more meaningful to understand the purpose of the index.
    6. Wrapping the IndexedListView with a NotificationListener to listen for scroll events seem to clash with the animation/jump method. To overcome this issue I had to delay the call to the animation code. See below:
        if (scrollInfo is ScrollEndNotification) {
          if (widget.controller.offset != itemStartLocation) {
            // Controller will only register the animation after a short delay
            Future.delayed(
              const Duration(milliseconds: 1),
              () => widget.controller.animateToWithSameOriginIndex(itemStartLocation, duration: widget.snappingSpeed, curve: widget.animationCurve),
            );
          }
        }
    

    Sorry for the long post, but I do think that this is a really great package and I want it to gain more popularity and should really be adopted as part of the flutter SDK.

    opened by lhengl 2
  • ScrollController attached to multiple scroll views

    ScrollController attached to multiple scroll views

    Somehow, changing the controller, list gets rebuild but is not functional, as underlying scroll controller ends up being attached to multiple places.

    When the exception was thrown, this was the stack: 
    #2      ScrollController.position (package:flutter/src/widgets/scroll_controller.dart:108:12)
    #3      ScrollController.offset (package:flutter/src/widgets/scroll_controller.dart:115:24)
    #4      my code reading controller.offset
    #5      ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:308:24)
    #6      ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:308:24)
    #7      ScrollPosition.notifyListeners (package:flutter/src/widgets/scroll_position.dart:969:11)
    #8      ScrollPosition.setPixels (package:flutter/src/widgets/scroll_position.dart:278:9)
    #9      ScrollPositionWithSingleContext.setPixels (package:flutter/src/widgets/scroll_position_with_single_context.dart:82:18)
    

    I will try to dig deeper into the code to find an issue and suggest a fix, but maybe it is a known issue already?

    opened by AAverin 8
Owner
Marcelo Glasberg
Marcelo Glasberg
A customizable listview with A-Z side scrollbar to fast jump to the item of the selected character.

A customizable listview with A-Z side scrollbar to fast jump to the item of the selected character. Quick scroll through list via dragging through alphabets.

Hussain Al Lawati 1 Apr 3, 2022
A Flutter sticky headers & index ListView. Based on scrollable_positioned_list.

Language: English | 中文简体 azlistview A Flutter sticky headers & index ListView. Based on scrollable_positioned_list. AzListView, SuspensionView, IndexB

Flutter中国开源项目 971 Jan 7, 2023
Flutter Package: When your desired layout or animation is too complex for Columns and Rows, this widget lets you position/size/rotate/transform its child in complex ways.

align_positioned Widgets in this package: AlignPositioned AnimatedAlignPositioned AnimChain Why are these widgets an indispensable tool? When your des

Marcelo Glasberg 69 Dec 12, 2022
Animated Search Bar package lets you add a beautiful search bar to your Flutter app.

Animated Search Bar Animated Search Bar package lets you add a beautiful search bar to your Flutter app. Installation Add the latest version of packag

Mohammad Saleh 5 Aug 7, 2022
Flutter Sticky Headers - Lets you place

Flutter Sticky Headers Lets you place headers on scrollable content that will stick to the top of the container whilst the content is scrolled. Usage

Flutter Community 901 Dec 28, 2022
A flutter grouped list widget similar to the iOS UITableview method name.

group_tablelist A flutter grouped list widget similar to the iOS UITableview method name. Group tablelist package for Flutter. Features iOS tableview-

null 2 Aug 17, 2022
Similar to Weibo dynamics, WeChat circle of friends, nine grid view controls to display pictures. Support single big picture preview.

Similar to Weibo dynamics, WeChat circle of friends, nine grid view controls to display pictures. Support single big picture preview.

Flutter中国开源项目 296 Dec 28, 2022
A custom dropdown button lets the user select from a number of items

CircularDropDownMenu Description A custom dropdown button lets the user select from a number of items. The button shows the currently selected item as

Surya Dev Singh 2 Dec 5, 2020
Various Flutter widgets that are developed by Google but not by the core Flutter team

Flutter widgets This repository contains the source code for various Flutter widgets that are developed by Google but not by the core Flutter team. Is

Google 1.1k Jan 7, 2023
Flutter Triple Status Button can use toggle button but in three statuses.

triple_status_button Triple Status Button. Flutter Triple Status Button can use toggle button but in three statuses. Property Description height heigh

MahdiBagjani 2 Nov 13, 2021
Responsive Widgets Prefix allows you to add the "Responsive" prefix to any widget that needs scaling or font size increases

Responsive Widgets Prefix allows you to add the Responsive prefix to any widget that needs scaling or font size increases (for varying device screen sizes).

The Mobile Applications Community 2 Apr 18, 2022
A flutter demo app to practice List.generate with ListView.builder

ListView 3 A flutter demo app to practice List.generate with ListView.builder Developer Alexander Sosa (https://www.linkedin.com/in/alexander-sosa-asi

Alexander Sosa 0 Jan 3, 2022
Listview builder with image and text

listview_builder_with_image_and_text A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resource

null 0 May 4, 2022
📓 Storyboard your components with Flutterbook. Develop, document, & test any kind of Flutter component.

Flutterbook A storyboarding tool to accelerate the development of Flutter widgets. Inspired by Storybook.js. The package is built to support Windows a

Philip Vu 25 Oct 7, 2022
Show custom in-app notification with any Widgets in flutter

notify_inapp show custom in-app notification with any Widgets. Getting Started Add this to your package's pubspec.yaml file: dependencies: notify_in

NewTab 3 Aug 19, 2022
Widget, that can make any static located widget hidable

Installing See the official installing guidline from hidable/install Usage & Overview To start using Hidable widget, we have to create a ScrollControl

Anon 18 Dec 16, 2022
AsyncButtonBuilder offers a simple way to extend any type of button with an asynchronous aspect

async_button_builder AsyncButtonBuilder offers a simple way to extend any type of button with an asynchronous aspect. It allows adding loading, disabl

Nollie 22 Jul 10, 2022
The flutter_otp_text_field package for flutter is a TextField widget that allows you to display different style pin.

flutter_otp_text_field flutter_otp_text_field The flutter_otp_text_field package for flutter is a TextField widget that allows you to display differen

David-Legend 30 Nov 8, 2022
Flutter package which helps you to implement Ticket Widget in your app.

✨ Ticket Widget Flutter package which helps you to implement Ticket Widget in your app. The source code is 100% Dart, and it is an updated null safe v

Mujahid 7 Dec 30, 2022