Repository: flutter
Issue: 'earliestUsefulChild != null': is not true upon using `SliverList.separated` builder
Labels: framework,f: scrolling,a: error message,has reproducible steps,P2,team-framework,triaged-framework,found in release: 3.16,found in release: 3.20

### Steps to reproduce

1. Run the example
2. Notice that the list gets displayed
3. Wait five seconds until the item list gets emptied
4. Notice error message in console

### Expected results

No error should be shown and an empty list should be displayed.

### Actual results

An error message gets shown.

The reason for this error is the implementation of `SliverChildBuilderDelegate.builder`, because it does not allow `separatorBuilder` to return `null`. And thus the builder implementation returns `null` for index 0, and the separator for index 1, causing the error.

A workaround is to use a custom `SliverChildBuilderDelegate.builder` similar to the one in the example below. This delegate allows `separatorBuilder` to return `null`, thus the `separatorBuilder` can return `null` exactly when `itemBuilder` returns `null`.

<details><summary>Workaround example</summary>

```dart
import 'dart:async';

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: MyWidget());
  }
}

class MyWidget extends StatefulWidget {
  const MyWidget({super.key});

  @override
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  var items = <int>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

  @override
  void initState() {
    Future.delayed(const Duration(seconds: 5)).then((value) {
      setState(() => items = []);
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SliverListWithCustomDelegate(items: items),
    );
  }
}

class SliverListWithCustomDelegate extends StatelessWidget {
  final List<int> items;

  const SliverListWithCustomDelegate({required this.items, super.key});

  @override
  Widget build(BuildContext context) {
    Widget? itemBuilder(context, index) {
      final item = items.elementAtOrNull(index);
      if (item == null) return null;
      return Text('Item $item');
    }

    Widget? separatorBuilder(context, index) {
      if (items.elementAtOrNull(index) == null) return null;
      return const Divider();
    }

    return CustomScrollView(
      slivers: [
        SliverList(
          delegate: SliverChildBuilderDelegate(
            (BuildContext context, int index) {
              final int itemIndex = index ~/ 2;
              if (index.isEven) {
                final widget = itemBuilder(context, itemIndex);
                return widget;
              } else {
                final widget = separatorBuilder(context, itemIndex);
                return widget;
              }
            },
            findChildIndexCallback: null,
            childCount: null,
            addAutomaticKeepAlives: true,
            addRepaintBoundaries: true,
            addSemanticIndexes: true,
            semanticIndexCallback: (Widget _, int index) {
              return index.isEven ? index ~/ 2 : null;
            },
          ),
        )
      ],
    );
  }
}
```
</details> 

This could be related to https://github.com/flutter/flutter/issues/130658.

### Code sample

<details open><summary>Code sample</summary>

```dart
import 'dart:async';

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: MyWidget());
  }
}

class MyWidget extends StatefulWidget {
  const MyWidget({super.key});

  @override
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  var items = <int>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

  @override
  void initState() {
    Future.delayed(const Duration(seconds: 5)).then((value) {
      setState(() => items = []);
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: [
          SliverList.separated(
            itemBuilder: (context, index) {
              final item = items.elementAtOrNull(index);
              if (item == null) return null;
              return Text('Item $item');
            },
            separatorBuilder: (context, index) {
              return const Divider();
            },
          ),
        ],
      ),
    );
  }
}
```

</details>


### Screenshots or Video

<details open>
<summary>Screenshots / Video demonstration</summary>

[Upload media here]

</details>


### Logs

<details open><summary>Logs</summary>

```console
[   +1 ms] Connected to _flutterView/0x106047220.
[   +1 ms] Flutter run key commands.
[        ] r Hot reload. 🔥🔥🔥
[        ] R Hot restart.
[        ] h List all available interactive commands.
[        ] d Detach (terminate "flutter run" but leave application running).
[        ] c Clear the screen
[        ] q Quit (terminate the application on the device).
[        ] A Dart VM Service on iPhone 15 Pro is available at: http://127.0.0.1:57327/3qf0qSw5hPc=/
[  +96 ms] The Flutter DevTools debugger and profiler on iPhone 15 Pro is available at: http://127.0.0.1:9102?uri=http://127.0.0.1:57327/3qf0qSw5hPc=/
[+4948 ms] ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
                    The following assertion was thrown during performLayout():
                    'package:flutter/src/rendering/sliver_list.dart': Failed assertion: line 184 pos 16:
                    'earliestUsefulChild != null': is not true.
                    
                    Either the assertion indicates an error in the framework itself, or we should provide substantially
                    more information in this error message to help you determine and fix the underlying cause.
                    In either case, please report this assertion by filing a bug on GitHub:
                      https://github.com/flutter/flutter/issues/new?template=2_bug.yml
                    
                    The relevant error-causing widget was:
                      SliverList SliverList:file:///Users/tobias/Dev/drafts/sliver_list_separated_bug/lib/main.dart:41:22
                    
                    When the exception was thrown, this was the stack:
                    #2      RenderSliverList.performLayout (package:flutter/src/rendering/sliver_list.dart:184:16)
                    #3      RenderObject.layout (package:flutter/src/rendering/object.dart:2546:7)
                    #4      RenderViewportBase.layoutChildSequence (package:flutter/src/rendering/viewport.dart:601:13)
                    #5      RenderViewport._attemptLayout (package:flutter/src/rendering/viewport.dart:1554:12)
                    #6      RenderViewport.performLayout (package:flutter/src/rendering/viewport.dart:1463:20)
                    #7      RenderObject._layoutWithoutResize (package:flutter/src/rendering/object.dart:2385:7)
                    #8      PipelineOwner.flushLayout (package:flutter/src/rendering/object.dart:1025:18)
                    #9      PipelineOwner.flushLayout (package:flutter/src/rendering/object.dart:1038:15)
                    #10     RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:591:23)
                    #11     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:986:13)
                    #12     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:457:5)
                    #13     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1325:15)
                    #14     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1255:9)
                    #15     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1113:5)
                    #16     _invoke (dart:ui/hooks.dart:312:13)
                    #17     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:383:5)
                    #18     _drawFrame (dart:ui/hooks.dart:283:31)
                    (elided 2 frames from class _AssertionError)
                    
                    The following RenderObject was being processed when the exception was fired: RenderSliverList#62888 relayoutBoundary=up1 NEEDS-LAYOUT NEEDS-COMPOSITING-BITS-UPDATE:
                      needs compositing
                      creator: SliverList ← Viewport ← IgnorePointer-[GlobalKey#866dd] ← Semantics ← Listener ←
                        _GestureSemantics ← RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#be412] ←
                        Listener ← _ScrollableScope ← _ScrollSemantics-[GlobalKey#1c606] ←
                        NotificationListener<ScrollMetricsNotification> ← Scrollable ← ⋯
                      parentData: paintOffset=Offset(0.0, 0.0) (can use size)
                      constraints: SliverConstraints(AxisDirection.down, GrowthDirection.forward, ScrollDirection.idle,
                        scrollOffset: 0.0, remainingPaintExtent: 852.0, crossAxisExtent: 393.0, crossAxisDirection:
                        AxisDirection.right, viewportMainAxisExtent: 852.0, remainingCacheExtent: 1102.0, cacheOrigin:
                        0.0)
                      geometry: SliverGeometry(scrollExtent: 360.0, paintExtent: 360.0, maxPaintExtent: 360.0,
                        cacheExtent: 360.0)
                      currently live children: 1 to 19
                    This RenderObject had the following descendants (showing up to depth 5):
                        child with index 1: RenderRepaintBoundary#c3560 relayoutBoundary=up2
                          child: RenderConstrainedBox#6c681 relayoutBoundary=up3
                            child: RenderPositionedBox#cabaf
                              child: RenderPadding#4b2dc relayoutBoundary=up1
                                child: RenderConstrainedBox#c4c54 relayoutBoundary=up2
                        child with index 3: RenderRepaintBoundary#87d18 relayoutBoundary=up2
                          child: RenderConstrainedBox#72ca7 relayoutBoundary=up3
                            child: RenderPositionedBox#a08db
                              child: RenderPadding#088b0 relayoutBoundary=up1
                                child: RenderConstrainedBox#3a81b relayoutBoundary=up2
                        child with index 5: RenderRepaintBoundary#2b199 relayoutBoundary=up2
                          child: RenderConstrainedBox#a5bb4 relayoutBoundary=up3
                            child: RenderPositionedBox#41c02
                              child: RenderPadding#10470 relayoutBoundary=up1
                                child: RenderConstrainedBox#ae966 relayoutBoundary=up2
                        child with index 7: RenderRepaintBoundary#a5601 relayoutBoundary=up2
                          child: RenderConstrainedBox#2f776 relayoutBoundary=up3
                            child: RenderPositionedBox#14c99
                              child: RenderPadding#a035a relayoutBoundary=up1
                                child: RenderConstrainedBox#0d6cd relayoutBoundary=up2
                        child with index 9: RenderRepaintBoundary#2da5b relayoutBoundary=up2
                          child: RenderConstrainedBox#dd0a4 relayoutBoundary=up3
                            child: RenderPositionedBox#44caa
                              child: RenderPadding#a9c75 relayoutBoundary=up1
                                child: RenderConstrainedBox#9c7e3 relayoutBoundary=up2
                        ...(descendants list truncated after 25 lines)
                    ════════════════════════════════════════════════════════════════════════════════════════════════════
```

</details>


### Flutter Doctor output

<details open><summary>Doctor output</summary>

```console
[!] Flutter (Channel stable, 3.16.9, on macOS 14.1.1 23B81 darwin-arm64, locale en-DE)
    • Flutter version 3.16.9 on channel stable at /Users/tobias/dev/tools/flutter
    ! Warning: `flutter` on your path resolves to /Users/tobias/Dev/Tools/flutter/bin/flutter, which is not inside your current Flutter SDK checkout at /Users/tobias/dev/tools/flutter. Consider adding
      /Users/tobias/dev/tools/flutter/bin to the front of your path.
    ! Warning: `dart` on your path resolves to /opt/homebrew/Cellar/dart/3.2.2/libexec/bin/dart, which is not inside your current Flutter SDK checkout at /Users/tobias/dev/tools/flutter. Consider adding
      /Users/tobias/dev/tools/flutter/bin to the front of your path.
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 41456452f2 (3 weeks ago), 2024-01-25 10:06:23 -0800
    • Engine revision f40e976bed
    • Dart version 3.2.6
    • DevTools version 2.28.5
    • 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.2)
    • Android SDK at /Users/tobias/Library/Android/sdk
    • Platform android-33, build-tools 33.0.2
    • ANDROID_HOME = /Users/tobias/Library/Android/sdk
    • Java binary at: /Users/tobias/Applications/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 15.2)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 15C500b
    • CocoaPods version 1.14.3

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2022.2)
    • Android Studio at /Users/tobias/Applications/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.3.2)
    • IntelliJ at /Users/tobias/Applications/IntelliJ IDEA Ultimate.app
    • Flutter plugin version 77.3.1
    • Dart plugin version 233.13135.65

[✓] VS Code (version 1.86.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension can be installed from:
      🔨 https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter

[✓] Connected device (3 available)            
    • iPhone 15 Pro (mobile) • 7510165F-9770-40B8-89DA-B031AF1D4A7C • ios            • com.apple.CoreSimulator.SimRuntime.iOS-17-2 (simulator)
    • macOS (desktop)        • macos                                • darwin-arm64   • macOS 14.1.1 23B81 darwin-arm64
    • Chrome (web)           • chrome                               • web-javascript • Google Chrome 121.0.6167.160

[✓] Network resources
    • All expected network resources are available.

! Doctor found issues in 1 category.
```

</details>