Git Product home page Git Product logo

superlistapp / super_sliver_list Goto Github PK

View Code? Open in Web Editor NEW
199.0 199.0 13.0 6.95 MB

Drop-in replacement for SliverList and ListView that can handle large amount of items with variable extents and reliably jump / animate to any item.

Home Page: https://superlistapp.github.io/super_sliver_list/

License: MIT License

Kotlin 0.01% Swift 0.14% Objective-C 0.01% Dart 94.62% CMake 2.00% C++ 2.46% C 0.15% HTML 0.32% Shell 0.01% Ruby 0.29%

super_sliver_list's People

Contributors

kkimj avatar knopp avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

super_sliver_list's Issues

The list with sticky headers has performance issue

While scrolling the list which contains a sticky header it has lagging and slow scrolling even with just 1000 items only
Here is a sample of the live demo

Screen.Recording.2024-02-20.at.12.49.11.PM.mp4

NestedScrollView: animateToItem/jumpToItem not working

Thank you for this package. It works quite well, but I found a Use-Case which doesn't seem to work.

I use a NestedScrollView with a SliverHeader and a TabBar with two TabBarViews inside. Inside the TabBarViews I use a CustomScrollView as per the example available here: https://api.flutter.dev/flutter/widgets/NestedScrollView-class.html

So far so good, now inside the CustomScrollView I wanted to use the cool functionalities you offer; Checking the visibleRange with the ListController listener (this works great!) and jump or animate to the index inside the SuperSliverList.

I made an example app which shows what I want to achieve;
Inside the Products-Tab, I want to click on a category and animate to the index in the SuperSliverList. When I click on the first two categories, something still works. You can check if you press on "Snacks" it still scrolls down, but pressing on "Chocolates" doesn't do anything.

Thank you for looking at this, if you make this work it would be spectacular because it seems the "godfather" of indexed lists also has its problems when it comes to NestedScrollViews; google/flutter.widgets#32. Thank you.

Video:
https://github.com/superlistapp/super_sliver_list/assets/1687252/19a38da4-ada2-43f1-b14b-76717f2d0a4c

Sourcecode:

import 'dart:developer';

import 'package:flutter/material.dart';
import 'package:sliver_tools/sliver_tools.dart';
import 'package:super_sliver_list/super_sliver_list.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late final ScrollController verticalController;
  late final ScrollController horizontalController;
  late final ListController listController;

  @override
  void initState() {
    super.initState();

    verticalController = ScrollController();
    horizontalController = ScrollController();
    listController = ListController();
    listController.addListener(() {
      log('ListController visibleRange: ${listController.visibleRange?.$1} - ${listController.visibleRange?.$2}');
    });
  }

  @override
  void dispose() {
    verticalController.dispose();
    horizontalController.dispose();
    listController.dispose();
    super.dispose();
  }

  final List<String> tabs = <String>['Products', 'Checkout'];
  final Map<String, List<String>> categoriesProducts = {
    'Drinks': [
      'Coke',
      'Pepsi',
      'Fanta',
      'Sprite',
      'Mountain Dew',
      'Dr. Pepper',
      '7UP'
    ],
    'Snacks': [
      'Lays',
      'Doritos',
      'Cheetos',
      'Pringles',
      'Ruffles',
      'Tostitos',
      'Fritos'
    ],
    'Chocolates': [
      'Snickers',
      'Mars',
      'Twix',
      'KitKat',
      'Hershey',
      'Cadbury',
      'Milky Way'
    ],
    'Ice Creams': [
      'Vanilla',
      'Chocolate',
      'Strawberry',
      'Mint',
      'Butter Pecan',
      'Cookies & Cream',
      'Rocky Road'
    ],
    'Candies': [
      'Skittles',
      'M&M',
      'Jelly Beans',
      'Gummy Bears',
      'Sour Patch Kids',
      'Swedish Fish',
      'Twizzlers'
    ],
    'Cookies': [
      'Oreo',
      'Chips Ahoy',
      'Nutter Butter',
      'Milano',
      'Famous Amos',
      'Keebler',
      'Lorna Doone'
    ],
    'Chips': [
      'Ruffles',
      'Lays',
      'Doritos',
      'Cheetos',
      'Pringles',
      'Tostitos',
      'Fritos'
    ],
    'Biscuits': [
      'Digestive',
      'Marie',
      'Oreo',
      'Parle-G',
      'Good Day',
      'Hide & Seek',
      'Britannia'
    ],
    'Cakes': [
      'Chocolate',
      'Vanilla',
      'Strawberry',
      'Red Velvet',
      'Carrot',
      'Cheese',
      'Pound'
    ],
  };

  void verticalScrollToIndex(int index) {
    listController.animateToItem(
      index: index,
      scrollController: verticalController,
      alignment: 0.5,
      duration: (e) => const Duration(milliseconds: 500),
      curve: (e) => Curves.easeInOut,
    );
  }

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: tabs.length, // This is the number of tabs.
      child: Scaffold(
        body: SafeArea(
          child: NestedScrollView(
            controller: verticalController,
            headerSliverBuilder:
                (BuildContext context, bool innerBoxIsScrolled) {
              // These are the slivers that show up in the "outer" scroll view.
              return <Widget>[
                SliverOverlapAbsorber(
                  // This widget takes the overlapping behavior of the SliverAppBar,
                  // and redirects it to the SliverOverlapInjector below. If it is
                  // missing, then it is possible for the nested "inner" scroll view
                  // below to end up under the SliverAppBar even when the inner
                  // scroll view thinks it has not been scrolled.
                  // This is not necessary if the "headerSliverBuilder" only builds
                  // widgets that do not overlap the next sliver.
                  handle:
                      NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                  sliver: SliverAppBar(
                    title: const Text(
                        'Restaurant'), // This is the title in the app bar.
                    pinned: false,

                    expandedHeight: 150.0,
                    collapsedHeight: 80,
                    forceElevated: innerBoxIsScrolled,
                  ),
                ),
              ];
            },
            body: Column(
              children: [
                TabBar(
                  // These are the widgets to put in each tab in the tab bar.
                  tabs: tabs.map((String name) => Tab(text: name)).toList(),
                ),
                Expanded(
                  child: TabBarView(
                      // These are the contents of the tab views, below the tabs.
                      children: [
                        SafeArea(
                          top: false,
                          bottom: false,
                          child: Builder(
                            builder: (BuildContext context) {
                              return CustomScrollView(
                                key: PageStorageKey<String>('Products'),
                                slivers: <Widget>[
                                  SliverOverlapInjector(
                                    // This is the flip side of the SliverOverlapAbsorber
                                    // above.
                                    handle: NestedScrollView
                                        .sliverOverlapAbsorberHandleFor(
                                            context),
                                  ),
                                  SliverPinnedHeader(
                                    child: Container(
                                      color:
                                          Theme.of(context).colorScheme.surface,
                                      child: SingleChildScrollView(
                                        scrollDirection: Axis.horizontal,
                                        controller: horizontalController,
                                        child: Padding(
                                          padding: const EdgeInsets.all(8.0),
                                          child: Row(
                                            mainAxisSize: MainAxisSize.min,
                                            children: [
                                              ...categoriesProducts.keys.map(
                                                (category) => Padding(
                                                  padding:
                                                      const EdgeInsets.only(
                                                          right: 4.0),
                                                  child: TextButton(
                                                    onPressed: () {
                                                      verticalScrollToIndex(
                                                          categoriesProducts
                                                              .keys
                                                              .toList()
                                                              .indexOf(
                                                                  category));
                                                    },
                                                    child: Text(category),
                                                  ),
                                                ),
                                              )
                                            ],
                                          ),
                                        ),
                                      ),
                                    ),
                                  ),
                                  SuperSliverList(
                                    listController: listController,
                                    delegate: SliverChildBuilderDelegate(
                                        (BuildContext context, int index) {
                                      final category = categoriesProducts.keys
                                          .elementAt(index);
                                      final List<String> products =
                                          categoriesProducts[category] ?? [];

                                      return Card(
                                        elevation: 1,
                                        child: Column(
                                          mainAxisSize: MainAxisSize.min,
                                          children: [
                                            Padding(
                                              padding:
                                                  const EdgeInsets.all(8.0),
                                              child: Row(
                                                mainAxisAlignment:
                                                    MainAxisAlignment.center,
                                                children: [
                                                  Text(category,
                                                      style: Theme.of(context)
                                                          .textTheme
                                                          .headlineSmall),
                                                ],
                                              ),
                                            ),
                                            ...products.map((String product) {
                                              return ListTile(
                                                title: Text(product),
                                              );
                                            }).toList(),
                                          ],
                                        ),
                                      );
                                    },
                                        childCount:
                                            categoriesProducts.keys.length),
                                  ),
                                ],
                              );
                            },
                          ),
                        ),
                        SafeArea(
                          top: false,
                          bottom: false,
                          child: Builder(
                            builder: (BuildContext context) {
                              return CustomScrollView(
                                key: PageStorageKey<String>('Checkout'),
                                slivers: <Widget>[
                                  SliverOverlapInjector(
                                    // This is the flip side of the SliverOverlapAbsorber
                                    // above.
                                    handle: NestedScrollView
                                        .sliverOverlapAbsorberHandleFor(
                                            context),
                                  ),
                                  SliverPinnedHeader(
                                      child: Padding(
                                    padding: const EdgeInsets.all(8.0),
                                    child: Row(
                                      mainAxisSize: MainAxisSize.min,
                                      mainAxisAlignment: MainAxisAlignment.end,
                                      children: [
                                        ElevatedButton(
                                            child: Text('Buy'),
                                            onPressed: () {
                                              // ...
                                            })
                                      ],
                                    ),
                                  )),
                                  SliverPadding(
                                    padding: const EdgeInsets.all(8.0),
                                    sliver: SuperSliverList(
                                      delegate: SliverChildBuilderDelegate(
                                          (BuildContext context, int index) {
                                        return ListTile(
                                            title: Text(
                                                'Product ' + index.toString()));
                                      }, childCount: 30),
                                    ),
                                  ),
                                ],
                              );
                            },
                          ),
                        )
                      ]),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

Error: "'index >= 0 && flutter: index < _extents.length': is not true."

I got this error during build of a SuperSliverList :

flutter: ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
flutter: The following assertion was thrown during performLayout():
flutter: 'package:super_sliver_list/src/extent_list.dart': Failed assertion: line 143 pos 12: 'index >= 0 &&
flutter: index < _extents.length': is not true.
flutter:
flutter: The relevant error-causing widget was:
flutter:   SuperSliverList
flutter:   SuperSliverList:file://.../lib/src/widgets/paginated_sliver_list_view.dart:58:12
flutter:
flutter: When the exception was thrown, this was the stack:
flutter: #2      ExtentList.[] (package:super_sliver_list/src/extent_list.dart:143:12)
flutter: #3      ExtentManager.setExtent (package:super_sliver_list/src/extent_manager.dart:32:39)
flutter: #4      RenderSuperSliverList.layoutChild (package:super_sliver_list/src/render_object.dart:298:20)
flutter: #5      RenderSuperSliverList._performLayoutInner (package:super_sliver_list/src/render_object.dart:540:24)
flutter: #6      ExtentManager.performLayout (package:super_sliver_list/src/extent_manager.dart:108:13)
flutter: #7      RenderSuperSliverList.performLayout (package:super_sliver_list/src/render_object.dart:409:20)
flutter: #8      RenderObject.layout (package:flutter/src/rendering/object.dart:2546:7)
flutter: #9      RenderSuperSliverList.layout (package:super_sliver_list/src/render_object.dart:414:11)
flutter: #10     RenderSliverEdgeInsetsPadding.performLayout (package:flutter/src/rendering/sliver_padding.dart:139:12)
flutter: #11     RenderSliverPadding.performLayout (package:flutter/src/rendering/sliver_padding.dart:361:11)
flutter: #12     RenderObject.layout (package:flutter/src/rendering/object.dart:2546:7)
flutter: #13     RenderProxySliver.performLayout (package:flutter/src/rendering/proxy_sliver.dart:54:12)
flutter: #14     RenderObject.layout (package:flutter/src/rendering/object.dart:2546:7)
flutter: #15     RenderSliverEdgeInsetsPadding.performLayout (package:flutter/src/rendering/sliver_padding.dart:139:12)
flutter: #16     RenderSliverPadding.performLayout (package:flutter/src/rendering/sliver_padding.dart:361:11)
flutter: #17     RenderObject.layout (package:flutter/src/rendering/object.dart:2546:7)
flutter: #18     RenderViewportBase.layoutChildSequence (package:flutter/src/rendering/viewport.dart:601:13)

This error happened in a search page while updating a list rendered in the SuperSliverList (the list has been updated due to search query changes). I remember having the same error with the Flutter SliverList when updating the given list but the error was something like : earliestUsefulChild !=null is not true if I remember well.

Is this a bug from the package or is it related to a bad practice dealing with sliver list ? Should I rebuild the entire SliverList widget when my list changes ?

Assertion errors when animating to an item that is removed during the animation

Consider the following example:

import 'package:flutter/material.dart';
import 'package:super_sliver_list/super_sliver_list.dart';

void main() {
  runApp(const MainApp());
}

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: SuperListViewPage(),
    );
  }
}

class SuperListViewPage extends StatefulWidget {
  const SuperListViewPage({super.key});

  @override
  State<SuperListViewPage> createState() => _SuperListViewPageState();
}

class _SuperListViewPageState extends State<SuperListViewPage> {
  final _controller = ListController();
  final _scrollController = ScrollController();
  var _items = 100;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SuperListView.builder(
        controller: _scrollController,
        listController: _controller,
        itemCount: _items,
        itemBuilder: _buildItem,
      ),
      floatingActionButton: FloatingActionButton(onPressed: _onTap),
    );
  }

  void _onTap() {
    if (_items == 0) {
      _reset();
    } else {
      _scroll();
    }
  }

  void _scroll() {
    _controller.animateToItem(
      index: _items - 1,
      scrollController: _scrollController,
      alignment: .5,
      duration: (_) => const Duration(milliseconds: 300),
      curve: (_) => Curves.ease,
    );

    setState(() {
      _items = 0;
    });
  }

  void _reset() {
    setState(() {
      _items = 100;
    });
  }

  static Widget _buildItem(BuildContext context, int index) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 30),
      child: Center(
        child: Text('$index'),
      ),
    );
  }
}

When pressing on the floating button, an assertion error will be issued that the index does not exist, even though at the start of the animation it does.

It seems when performing animations, the simulation expects the final index to be present during the entire animation.
While this example is extreme in that removing of the item happens immediately after the animation start.

In addition, looking at:

animation.addListener(() {
final value = animation.value;
var targetPosition = extentManager.getOffsetToReveal(
index,
alignment,
rect: rect,
estimationOnly: value < 1.0,
);

It seems index is always used to determine the next position to use. Couldn't this also lead to cases where the items change in such a way that the index would be pointing to a different item not the one originally intended?

Horizontal List View

Hi

First of all Thank you for making this package.

is there a way to make a horizontal super sliver list?

ExtentList.totalExtent can drop below 0.0

Due to floating-point rounding, some sequence of updates to individual extents via ExtentList.setExtent can make total extent value to be below zero. Here's a simplified code that simulates the changes to two extents by same values (imagine two slivers changing sizes simultaneously):

void main() {
  final el = ExtentList();
  
  for (final newExtent in [31.500089999999997, 22.500045, 15.750045000000004, 9.000045000000004, 0.0]) {
    el.updateExtents(newExtent);
    print(el.totalExtent);
  }
}

class ExtentList {
  double totalExtent = 108.0;
  final extents = [54.0, 54.0];
  
  void updateExtents(double newExtent) {
    for (int index = 0; index < extents.length; index++) {
      totalExtent -= extents[index];
      extents[index] = newExtent;
      totalExtent += newExtent;
    }
  }
}

The totalExtent after each call to updateExtents() are as follows:

63.00018
45.00009
31.500090000000004
18.000090000000004
-3.552713678800501e-15

Is there a way to get which sticky header is currently active?

I have a calendar with an infinite list with one card for each date. Each date is a SliverStickyHeader that contains a header and a SuperSliverList with just 1 item.

I am able to scroll to specific SliverStickyHeader using animateTo whenever user selects a date on calendar, but I'm unable to figure out how to get a callback on which sticky header is current sticking/on-top for changing the date on calendar as the user scrolls.

[feat. request] Avoid rebuilding entire List when a single new item is added

Hello @knopp thx for this amazing plugin.
We tested it for a while and eventually adopted it in our product.

We are thinking about how to address a specific use case by which an item is added to the List after the List has been rendered and we should avoid to rebuild entire List's elements.

This use case is common with chat screen and userfeed, where the List is reversed.
Please have a look at below sample.

Screenshot_2024-03-27-12-02-38-415_dev henryleunghk flutter_native_text_input_example

We add 30 elements to the List with reverse: true.
User can tap the + icon to add the new element (item 31) which will be placed at the bottom of the list.

The problem is that all the elements of the List get rebuilt (coz index changed) and we should avoid this behaviour.
Is there a way to achieve this requirement by changing our code or adding a new feature to SuperList? Can we maybe instruct SuperList to rebuild "only the new added element" in this very event?

import 'package:flutter/material.dart';
import 'package:super_sliver_list/super_sliver_list.dart';
import 'package:equatable/equatable.dart';

/// A sample code to showcase a common use case by which
/// List size can be increased dynamically by adding a new element
/// and we should find a way to avoid rebuilding entire list elements.
void main() => runApp(const CustomScrollViewExampleApp());

class CustomScrollViewExampleApp extends StatelessWidget {
  const CustomScrollViewExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: CustomScrollViewExample(),
    );
  }
}

class CustomScrollViewExample extends StatefulWidget {
  const CustomScrollViewExample({super.key});

  @override
  State<CustomScrollViewExample> createState() =>
      _CustomScrollViewExampleState();
}

class _CustomScrollViewExampleState extends State<CustomScrollViewExample> {

  ListController listController  = ListController();
  ScrollController get scrollController => ScrollController();

  // Populate the list with 30 items
  List<String> items = List<String>.generate(31, (i) => '$i');

  /// Scroll List to the bottom
  void _scrollToBottom() {
    listController.animateToItem(index: 0, duration: (estimatedDistance) {
      return const Duration(milliseconds: 300);
    }, curve: (estimatedDistance) {
      return Curves.easeIn;
    }, scrollController: scrollController, alignment: 1.0,);
  }


  @override
  Widget build(BuildContext context) {
    const Key centerKey = ValueKey<String>('bottom-sliver-list');
    return Scaffold(
      appBar: AppBar(
        title: const Text('Press on the plus to add items above and below'),
        leading: IconButton(
          icon: const Icon(Icons.add),
          onPressed: () {
            setState(() {
              items.insert(0, items.length.toString());
            });
            _scrollToBottom();
          },
        ),
      ),
      body: CustomScrollView(
        controller: scrollController,
        center: centerKey,
        cacheExtent: 0,
        reverse: true,
        physics: const AlwaysScrollableScrollPhysics(),
        semanticChildCount: items.length,
        slivers: <Widget>[
          SuperSliverList(
            key: centerKey,
            listController: listController,
            delegate: SliverChildBuilderDelegate(
                  (context, int index) {
                // LOGs section
                debugPrint("SliverList item $index rebulding");
                debugPrint("items index  ${items[index]}");
                final item = Item(name:'Item: ${items[index]})');
                // End log
                debugPrint("item ${item}");
                return Container(
                  alignment: Alignment.center,
                  color: Colors.blue[200 + int.parse(items[index]) % 4 * 100],
                  height: 100 + int.parse(items[index]) % 4 * 20.0,

                  // Option 1: we use a StatefulItem + Item + Equatable
                  // To try avoiding the rebuild of entire list when adding
                  // a new item
                  child: StatefulItem(item: item),

                  // Uncomment this to test Option 2
                  // child: Item(name:'Item: ${items[index]}'),

                );
              },
              childCount: items.length,
            ),
          ),
        ],
      ),
    );
  }
}



// Option 1: Use StatefulItem + Item + Equatable
// Shouldn't this inform flutter that widget hasn't changed
// and rebuild is not required ?
class StatefulItem extends StatefulWidget {
  final Item item;
  const StatefulItem({Key? key, required this.item}) : super(key: key);

  @override
  State<StatefulItem> createState() => _StatefulItemState();
}

class _StatefulItemState extends State<StatefulItem> with AutomaticKeepAliveClientMixin  {

  @override
  Widget build(BuildContext context) {
    return Text(
      widget.item.name,
    );
  }

  @override
  bool get wantKeepAlive => true;

}

// Item extends Equatable
class Item extends Equatable {
  final String name;

  const Item({required this.name});

  @override
  List<Object?> get props => [name];
}


/*
// Option 2: Simple Stateful item

class Item extends StatefulWidget {
  final String name;
  const Item({Key? key, required this.name}) : super(key: key);

  @override
  State<Item> createState() => _ItemState();
}

class _ItemState extends State<Item> with AutomaticKeepAliveClientMixin  {
  @override
  Widget build(BuildContext context) {
    return Text(
      widget.name,
    );
  }
  @override
  bool get wantKeepAlive => true;
}
*/

Would like to be able to use version 0.2.0 in flutter 3.13

Because super_sliver_list >=0.2.0-dev.1 depends on collection ^1.18.0 and every version of flutter from sdk depends on collection 1.17.2, super_sliver_list >=0.2.0-dev.1 is incompatible with flutter from sdk.
So, because xxx depends on both flutter from sdk and super_sliver_list ^0.2.0, version solving failed.

Docs borrowed from ListView hide the interesting parts

This library is very interesting! I'm beginning to read the code to understand what it's doing.

May I gently suggest not copy-pasting the documentation from upstream's ListView and related classes? This is seen currently at today's prerelease version:
https://pub.dev/documentation/super_sliver_list/0.2.0-dev.2/super_sliver_list/SuperListView-class.html
and in main.

This causes two problems:

  • Looking at any given piece of documentation, it's hard to be sure whether it's accurate as to SuperListView. In many places it currently says ListView when it means SuperListView, making it clear it's not yet edited for the SuperListView context. I figure you'll find and fix those before making a release… but even once you have, it's a lot of text, so it seems like it'll be hard for you to be sure you've gone through every line and caught all the more-subtle details that need updating.
  • Because it's just a lot of text, it's hard to find the most interesting parts, which are the parts describing how SuperListView is different. After all, I already know more or less how ListView works; and for details where I don't, I already have a place where I can look them up.

Instead, the strategy I'd recommend is:

  • Include as much hand-written documentation as you feel like writing. These docs are great!

  • Near the top of the [SuperListView] doc, say something like:

    The API of [SuperListView] is based on Flutter's [ListView], and most features of the API work exactly the same. For details and examples, see [ListView].

    And repeat the point in a "see also" item at the end.

  • Then just leave out all the text that would be copy-pasted.

  • Similarly, for the individual constructors and so on, copy just the one summary line at the top, and point to the corresponding ListView constructor. For example for SuperListView.builder:

    Creates a scrollable, linear array of widgets that are created on demand.

    This constructor corresponds to [ListView.builder]. See there for details on the parameters it has in common.

    Then add anything there is to say about the differences… which I'm not sure there is in this example. SuperListView.builder has a few parameters not found on ListView.builder, but they correspond directly to fields which have their own docs.

Error: "Null check operator used on a null value" on _performLayoutInner

I got this error on a iOS simulator in iOS version 17.2 will trying to render a SuperSliverList widget :

flutter: ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
flutter: The following _TypeError was thrown during performLayout():
flutter: Null check operator used on a null value
flutter:
flutter: The relevant error-causing widget was:
flutter:   SuperSliverList
flutter:   SuperSliverList:file:///.../core/lib/src/widgets/paginated_sliver_list_view.dart:58:12
flutter:
flutter: When the exception was thrown, this was the stack:
flutter: #0      RenderSuperSliverList._performLayoutInner (package:super_sliver_list/src/render_object.dart:434:43)
flutter: #1      ExtentManager.performLayout (package:super_sliver_list/src/extent_manager.dart:108:13)
flutter: #2      RenderSuperSliverList.performLayout (package:super_sliver_list/src/render_object.dart:408:20)
flutter: #3      RenderObject.layout (package:flutter/src/rendering/object.dart:2546:7)
flutter: #4      RenderSuperSliverList.layout (package:super_sliver_list/src/render_object.dart:413:11)
flutter: #5      RenderMultiSliver.layoutChild (package:sliver_tools/src/rendering/multi_sliver.dart:292:13)
flutter: #6      RenderMultiSliver._layoutChildSequence (package:sliver_tools/src/rendering/multi_sliver.dart:154:31)
flutter: #7      RenderMultiSliver.performLayout (package:sliver_tools/src/rendering/multi_sliver.dart:90:24)
flutter: #8      RenderObject.layout (package:flutter/src/rendering/object.dart:2546:7)
flutter: #9      RenderViewportBase.layoutChildSequence (package:flutter/src/rendering/viewport.dart:601:13)
flutter: #10     RenderShrinkWrappingViewport._attemptLayout (package:flutter/src/rendering/viewport.dart:1925:12)

The error seems to occured on the lib/src/render_object.dart file in the _perfomLayoutInner function because of a null check operator.

Please let me know if you need more information or if I missed a parameter in the SuperSliverList implementation.

initialScrollOffset is incorrect in 0.2.0

Use initialScrollOffset:

final scrollController = ScrollController(initialScrollOffset: index * 48);
CustomScrollView(
  controller: scrollController,
  ...
);

super_sliver_list 0.0.8 works fine.

super_sliver_list 0.2.0 initialScrollOffset is incorrect.

Feature Request, keep the scroll position when inserting new items on top of SuperListView.

I did try to jump back to the previous index after inserting new items at[0].
It works greate most of the time, occasionally a little bit off position.
Is it a good idea to have this feature built in?

Awesome work, I've tried lots of similar packages, SuperListView is the killer!
I'd like to contribute to it if you have such a plan.
Thank you for your outstanding work.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.