flutter / flutter

Flutter makes it easy and fast to build beautiful apps for mobile and beyond
https://flutter.dev
BSD 3-Clause "New" or "Revised" License
166.81k stars 27.64k forks source link

`'earliestUsefulChild != null': is not true` is thrown if we set the previous laid out children to `null` in `ListView.builder` #130658

Open hasanmhallak opened 1 year ago

hasanmhallak commented 1 year ago

Is there an existing issue for this?

Steps to reproduce

  1. run the code provided below.
  2. Scroll down to end of the list.
  3. Press the FAB.
  4. Scroll back up to the begin of the list.

Expected results

since returning null in itemBuilder method will be signaling that we reach the end of the list when we scroll down I suppose it should works normally if we return null in the beginning of the list since the builder factory lazily load the items.

I'm not sure if that's intended or not.

The use case is I'm trying to implement a fetch on demand behavior, it works well when scrolling down and showing a 'Loading' until I fetch the new data, and I dispose the data that are no longer showing on the screen as I scroll down.

However when try to scroll back up I still apply the same logic (show loading until I get the data) but it throws 'earliestUsefulChild != null': is not true.

Actual results

════════ Exception caught by rendering library ═════════════════════════════════ 'package:flutter/src/rendering/sliver_list.dart': Failed assertion: line 186 pos 16: 'earliestUsefulChild != null': is not true. sliver_list.dart:186 The relevant error-causing widget was ListView main.dart:31 ════════════════════════════════════════════════════════════════════════════════

Code sample

Code sample ```dart import 'package:flutter/material.dart'; void main() { runApp( const MaterialApp(home: MyHomePage()), ); } class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State createState() => _MyHomePageState(); } class _MyHomePageState extends State { List items = List.generate(200, (i) { if (i > 180) { return 'Loading'; } return 'Item $i'; }); @override Widget build(BuildContext context) { return Scaffold( floatingActionButton: FloatingActionButton(onPressed: () { setState(() { items.replaceRange(10, 30, ['Loading']); }); }), body: ListView.builder( itemCount: items.length, itemBuilder: (context, index) { if (items[index] == 'Loading') return null; return Text(items[index]); }, ), ); } } ```

Screenshots or Video

Video https://github.com/flutter/flutter/assets/95232508/947a83ed-b18c-4c44-ad24-aa3f626640d3

Logs

No response

Flutter Doctor output

Doctor output ```console Doctor summary (to see all details, run flutter doctor -v): [✓] Flutter (Channel stable, 3.10.1, on macOS 13.2 22D49 darwin-arm64, locale en-EG) [✓] Android toolchain - develop for Android devices (Android SDK version 33.0.2) [✓] Xcode - develop for iOS and macOS (Xcode 14.2) [✓] Chrome - develop for the web [✓] Android Studio [✓] VS Code (version 1.79.2) [✓] Connected device (2 available) [✓] Network resources • No issues found! ```
danagbemava-nc commented 1 year ago

Reproducible using the code sample provided above.

logs ``` flutter: ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════ flutter: The following assertion was thrown during performLayout(): flutter: 'package:flutter/src/rendering/sliver_list.dart': Failed assertion: line 186 pos 16: flutter: 'earliestUsefulChild != null': is not true. flutter: flutter: Either the assertion indicates an error in the framework itself, or we should provide substantially flutter: more information in this error message to help you determine and fix the underlying cause. flutter: In either case, please report this assertion by filing a bug on GitHub: flutter: https://github.com/flutter/flutter/issues/new?template=2_bug.yml flutter: flutter: The relevant error-causing widget was: flutter: ListView ListView:file:///Users/nexus/projects/nevercode/grd/lib/main.dart:31:22 flutter: flutter: When the exception was thrown, this was the stack: flutter: #2 RenderSliverList.performLayout (package:flutter/src/rendering/sliver_list.dart:186:16) flutter: #3 RenderObject.layout (package:flutter/src/rendering/object.dart:2491:7) flutter: #4 RenderSliverEdgeInsetsPadding.performLayout (package:flutter/src/rendering/sliver_padding.dart:139:12) flutter: #5 RenderSliverPadding.performLayout (package:flutter/src/rendering/sliver_padding.dart:361:11) flutter: #6 RenderObject.layout (package:flutter/src/rendering/object.dart:2491:7) flutter: #7 RenderViewportBase.layoutChildSequence (package:flutter/src/rendering/viewport.dart:534:13) flutter: #8 RenderViewport._attemptLayout (package:flutter/src/rendering/viewport.dart:1512:12) flutter: #9 RenderViewport.performLayout (package:flutter/src/rendering/viewport.dart:1421:20) flutter: #10 RenderObject._layoutWithoutResize (package:flutter/src/rendering/object.dart:2330:7) flutter: #11 PipelineOwner.flushLayout (package:flutter/src/rendering/object.dart:1011:18) flutter: #12 RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:494:19) flutter: #13 WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:926:13) flutter: #14 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:360:5) flutter: #15 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1297:15) flutter: #16 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1227:9) flutter: #17 SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1085:5) flutter: #18 _invoke (dart:ui/hooks.dart:170:13) flutter: #19 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:410:5) flutter: #20 _drawFrame (dart:ui/hooks.dart:140:31) flutter: (elided 2 frames from class _AssertionError) ```
flutter doctor -v ``` [!] Flutter (Channel stable, 3.10.6, on macOS 13.4.1 22F770820d darwin-arm64, locale en-GB) • Flutter version 3.10.6 on channel stable at /Users/nexus/dev/sdks/flutter ! Warning: `flutter` on your path resolves to /Users/nexus/dev/sdks/flutters/bin/flutter, which is not inside your current Flutter SDK checkout at /Users/nexus/dev/sdks/flutter. Consider adding /Users/nexus/dev/sdks/flutter/bin to the front of your path. ! Warning: `dart` on your path resolves to /Users/nexus/dev/sdks/flutters/bin/dart, which is not inside your current Flutter SDK checkout at /Users/nexus/dev/sdks/flutter. Consider adding /Users/nexus/dev/sdks/flutter/bin to the front of your path. • Upstream repository https://github.com/flutter/flutter.git • Framework revision f468f3366c (4 days ago), 2023-07-12 15:19:05 -0700 • Engine revision cdbeda788a • Dart version 3.0.6 • DevTools version 2.23.1 • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades. [✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0) • Android SDK at /Users/nexus/Library/Android/sdk • Platform android-33, build-tools 33.0.0 • Java binary at: /Users/nexus/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/222.4459.24.2221.10121639/Android Studio.app/Contents/jbr/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 14.3) • Xcode at /Applications/Xcode-14.3.0.app/Contents/Developer • Build 14E222b • CocoaPods version 1.12.1 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 2022.2) • Android Studio at /Users/nexus/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/222.4459.24.2221.10121639/Android Studio.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694) [✓] IntelliJ IDEA Ultimate Edition (version 2023.1.4) • IntelliJ at /Users/nexus/Applications/JetBrains Toolbox/IntelliJ IDEA Ultimate.app • Flutter plugin version 74.0.4 • Dart plugin version 231.9161.14 [✓] IntelliJ IDEA Ultimate Edition (version 2023.1.3) • IntelliJ at /Users/nexus/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/231.9161.38/IntelliJ IDEA.app • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart [✓] IntelliJ IDEA Ultimate Edition (version 2023.1.4) • IntelliJ at /Users/nexus/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/231.9225.16/IntelliJ IDEA.app • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart [✓] VS Code (version 1.80.1) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.68.0 [✓] Connected device (3 available) • Nexus (mobile) • 00008020-001875E83A38002E • ios • iOS 16.5.1 20F75 • macOS (desktop) • macos • darwin-arm64 • macOS 13.4.1 22F770820d darwin-arm64 • Chrome (web) • chrome • web-javascript • Google Chrome 114.0.5735.198 ! Error: Nexus is busy: Making Nexus ready for development. Xcode will continue when Nexus is finished. (code -10) [✓] Network resources • All expected network resources are available. ! Doctor found issues in 1 category. ``` ``` [✓] Flutter (Channel master, 3.13.0-3.0.pre.91, on macOS 13.4.1 22F770820d darwin-arm64, locale en-GB) • Flutter version 3.13.0-3.0.pre.91 on channel master at /Users/nexus/dev/sdks/flutters • Upstream repository https://github.com/flutter/flutter.git • Framework revision 5a866a031f (9 hours ago), 2023-07-16 17:36:30 -0700 • Engine revision 683087731f • Dart version 3.1.0 (build 3.1.0-315.0.dev) • DevTools version 2.25.0 [✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0) • Android SDK at /Users/nexus/Library/Android/sdk • Platform android-33, build-tools 33.0.0 • Java binary at: /Users/nexus/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/222.4459.24.2221.10121639/Android Studio.app/Contents/jbr/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 14.3) • Xcode at /Applications/Xcode-14.3.0.app/Contents/Developer • Build 14E222b • CocoaPods version 1.12.1 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 2022.2) • Android Studio at /Users/nexus/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/222.4459.24.2221.10121639/Android Studio.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694) [✓] IntelliJ IDEA Ultimate Edition (version 2023.1.3) • IntelliJ at /Users/nexus/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/231.9161.38/IntelliJ IDEA.app • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart [✓] IntelliJ IDEA Ultimate Edition (version 2023.1.4) • IntelliJ at /Users/nexus/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/231.9225.16/IntelliJ IDEA.app • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart [✓] VS Code (version 1.80.1) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.68.0 [✓] Connected device (3 available) • Nexus (mobile) • 00008020-001875E83A38002E • ios • iOS 16.5.1 20F75 • macOS (desktop) • macos • darwin-arm64 • macOS 13.4.1 22F770820d darwin-arm64 • Chrome (web) • chrome • web-javascript • Google Chrome 114.0.5735.198 ! Error: Nexus is busy: Making Nexus ready for development. Xcode will continue when Nexus is finished. (code -10) [✓] Network resources • All expected network resources are available. • No issues found! ```
lucavenir commented 11 months ago

I'm having this exact issue, and about..

I'm not sure if that's intended or not.

..can we get a short answer about this question?

I'm heavily relying on the ability to return null on paginated / infinite view scroll lists (but also with slivers, grids, etc.). This is great because returning null effectively means "ok, no more items in this direction".

This is an extremely common practice when it comes to infinite scrollable paginated views with Riverpod, btw

hasanmhallak commented 11 months ago

Hey @lucavenir. While the approach of implementing a fetch-on-demand/scroll using the builder might be effective, there could be alternative strategies worth considering.

For a temporary fix, you can disable autoDispose in your notifier. see the original issue here

One alternative approach could be to rely on the scrolling position rather than the builder. The initial method fetches data only when the user reaches the end of the list, which may result in a suboptimal user experience. A suggestion is to consider listening to scrolling notifications and fetching a new page when the user reaches, for example, the last third of the last fetched page. This way, the user won't even notice that new data is being retrieved.

In terms of handling scrolling positions, this would involve creating only one notifier. Manually fetch the next page when the user reaches a certain scrolling position, and then update the state of the notifier with the old page added to it and the next page that was fetched. I'm not sure how this would be implemented in the new auto-generated Riverpod, but it might be worth exploring.

lucavenir commented 11 months ago

Most of the times I'm not using a Riverpod's Notifier for paginated infinite scroll view lists, and this issue is about null being a problem for the reverse scrollable direction, but nonetheless, thank you for dedicating some time to this.

I just want to underline that this is not a "riverpod issue", but a Flutter one. The fact that using the null guard comes handy with riverpod is just an implementation detail.