super_sliver_list

repository·main·Indexed 19 days ago

https://github.com/superlistapp/super_sliver_list

High-performance replacements for Flutter's ListView and SliverList, optimized for large lists with variable item sizes. It provides SuperListView and SuperSliverList to solve scrolling performance degradation and erratic scrollbar behavior. Key features include a ListController for jumping or animating to specific indices, advanced extent estimation and precalculation via ExtentPrecalculationPolicy, and SuperRangeMaintainingScrollPhysics to prevent scroll jumps when removing elements.

Tokens
4.7K
Snippets
16
Records
20
Agent score
62%

What's inside super_sliver_list

  1. Use SuperListView and SuperSliverList as drop-in replacements

    main

    You can replace standard Flutter ListView and SliverList with SuperListView and SuperSliverList to gain improved performance for large lists with variable item extents and more predictable scrollbar behavior.

    SuperListView works exactly like ListView.builder, and SuperSliverList works with any CustomScrollView configuration using standard delegates.

    // SuperListView usage
    SuperListView.builder(
      itemCount: 1000,
      itemBuilder: (context, index) {
        return ListTile(title: Text('Item $index'));
      },
    )
    
    // SuperSliverList usage
    CustomScrollView(
      slivers: <Widget>[
        SuperSliverList(
          delegate: SliverChildListDelegate(
            <Widget>[
              const Text("Item 1"),
              const Text('Item 2'),
            ],
          ),
        ),
      ],
    )
  2. Customize iOS launch screen assets

    main

    To customize the iOS launch screen, you can either replace the image files directly in the example/ios/Runner/Assets.xcassets/LaunchImage.imageset/ directory or use Xcode for a more visual approach.

    Using Xcode:

    1. Open the iOS workspace using open ios/Runner.xcworkspace.
    2. In the Xcode Project Navigator, select Runner/Assets.xcassets.
    3. Drag and drop your desired images into the asset catalog.
    open ios/Runner.xcworkspace
  3. Use SuperListView as a drop-in ListView replacement

    main

    SuperListView is a high-performance replacement for Flutter's ListView. It is designed to handle large numbers of children (tens or hundreds of thousands) with variable extents without requiring hardcoded extents or prototype children. It maintains the same API surface as ListView, making it a drop-in replacement.

    Key advantages over standard ListView:

    • Performance remains stable regardless of the number of children.
    • Supports jumping and animating to specific items via ListController.
    • Advanced extent estimation and precalculation to prevent scrollbar jumping.
    SuperListView(
      children: [ ... ],
      // or
      SuperListView.builder(
        itemBuilder: (context, index) => Text('Item $index'),
        itemCount: 1000,
      ),
    );
  4. Manage child scroll obstruction with ChildObstructionExtent

    main

    The ChildObstructionExtent class represents the amount of space from the leading and trailing edges where a sliver obstructs its child (e.g., a pinned header). This is used to calculate the correct offset when revealing a child item so that it isn't hidden behind obstructing slivers.

    It supports arithmetic operations (+ and -) to combine or subtract obstruction extents from multiple slivers.

    final obstruction = ChildObstructionExtent(
      leading: 50.0,
      trailing: 20.0,
    );
  5. Improve extent estimation in SuperSliverList

    main

    Because SuperSliverList estimates the size of items outside the viewport, scrollbar behavior might be slightly imprecise if estimates are poor. You can improve accuracy by providing an estimateExtent callback that returns an approximate height/extent for a given index.

    SuperSliverList(
        delegate: /*...*/,
        estimateExtent: (index) => 100.0, // Provide your own extent estimation
    )
  6. Precalculate item extents using ExtentPrecalculationPolicy

    main

    To reduce estimation errors, you can instruct SuperSliverList to asynchronously precalculate the actual extents of items. To do this, subclass ExtentPrecalculationPolicy and implement shouldPrecaculateExtents to define when precalculation should occur (e.g., for lists below a certain size).

    class MyPrecalculationPolicy extends ExtentPrecalculationPolicy {
      @override
      bool shouldPrecaculateExtents(ExtentPrecalculationContext context) {
        // Eagerly precalculate for lists with less than 100 items
        return context.numberOfItems < 100;
      }
    }
    
    // Usage
    SuperSliverList(
        delegate: /*...*/,
        extentPrecalculationPolicy: MyPrecalculationPolicy(),
    )
  7. Jump or animate to a specific item using ListController

    main

    To jump or animate to a specific index (even if the item is not yet built or laid out), provide a ListController to your SuperListView or SuperSliverList. You must also provide the underlying ScrollController to the controller methods.

    jumpToItem

    Immediately jumps to the specified index. Requires index, scrollController, and alignment (0.0 is top/start, 0.5 is center, 1.0 is bottom/end).

    animateToItem

    Animates to the specified index. Supports dynamic duration and curve based on the estimatedDistance to the target item.

    final _listController = ListController();
    final _scrollController = ScrollController();
    
    // In your widget tree
    SuperListView.builder(
      listController: _listController,
      controller: _scrollController,
      itemCount: 1000,
      itemBuilder: (context, index) => ListTile(title: Text('Item $index')),
    );
    
    // To jump
    _listController.jumpToItem(
      index: 50,
      scrollController: _scrollController,
      alignment: 0.5,
    );
    
    // To animate
    _listController.animateToItem(
      index: 50,
      scrollController: _scrollController,
      alignment: 0.5,
      duration: (estimatedDistance) => Duration(milliseconds: 250),
      curve: (estimatedDistance) => Curves.easeInOut,
    );
  8. Use delayPopulatingCacheArea for fast scrolling optimization

    main

    Set delayPopulatingCacheArea to true to optimize performance during rapid scrolling. When enabled, items in the cache area (the area just outside the visible viewport) are only built after the scrolling speed slows down. This prevents the CPU from being overwhelmed by building items that are being skipped over during fast swipes.

    SuperListView.builder(
      delayPopulatingCacheArea: true,
      itemBuilder: (context, index) => MyItem(index),
      itemCount: 10000,
    );
  9. Use ListController to jump or animate to items

    main

    The ListController provides an interface to interact with a SuperSliverList. It allows you to immediately jump to a specific item or animate the scroll position to reveal an item at a specific alignment in the viewport.

    Key capabilities:

    • Jump to item: Use jumpToItem for immediate positioning.
    • Animate to item: Use animateToItem for smooth transitions. This method accepts a ValueGetter<int?> for the index, allowing the target index to change (e.g., due to insertions/removals) during the animation.
    • Query extents: Retrieve the actual or estimated extent of an item using extentForIndex.
    • Manage extents: Manually invalidate extents via invalidateExtent or invalidateAllExtents to force recalculation.
    • List metadata: Access numberOfItems, visibleRange, and totalExtent.

    Note: The controller must be attached to a SuperSliverList before calling most methods, otherwise they will throw an error.

    class _MyState extends State<MyWidget> {
      final _listController = ListController();
      final _scrollController = ScrollController();
    
      @override
      Widget build(BuildContext context) {
        return SuperListView.builder(
          listController: _listController,
          controller: _scrollController,
          itemCount: 1000,
          itemBuilder: (context, index) {
            return ListTile(title: Text('Item $index'));
          },
        );
      }
    
      void jumpToItem(int index) {
        _listController.jumpToItem(
          index: index,
          scrollController: _scrollController,
          alignment: 0.5, // 0.0 = leading, 0.5 = middle, 1.0 = trailing
        );
      }
    }
  10. Use getOffsetToRevealExt for accurate item revealing

    main

    The getOffsetToRevealExt extension on RenderAbstractViewport is an enhanced version of Flutter's standard getOffsetToReveal. It accounts for childObstructionExtent set on slivers, ensuring that when an item is scrolled into view, it is not obscured by pinned headers or other obstructing slivers.

    It also allows the queried sliver to access the current OffsetToRevealContext via OffsetToRevealContext.current().

    // Inside a RenderAbstractViewport implementation
    RevealedOffset result = viewport.getOffsetToRevealExt(
      targetRenderObject,
      alignment: 0.5,
      esimationOnly: false,
      rect: targetRect,
      axis: Axis.vertical,
    );
  11. Use SuperSliverList constructors

    main

    The SuperSliverList widget is a drop-in replacement for SliverList that handles large amounts of variable-extent items efficiently. It provides three main constructors:

    1. .builder: Similar to SliverList.builder. Use this for large, dynamic lists where items are built on demand.
    2. .separated: Similar to SliverList.separated. Allows providing a separatorBuilder to inject widgets between items.
    3. .list: Similar to SliverList.list. Use this when you have a fixed, pre-defined list of child widgets.