Repository: flutter
Issue: DraggableScrollableSheet _DraggableSheetExtent not reaching 0 during _SnappingSimulation
Labels: framework,f: scrolling,has reproducible steps,P3,team-framework,triaged-framework,found in release: 3.16,found in release: 3.18

### Steps to reproduce

1. Run the sample code provided below
2. Drag the sheet slightly down just enough so that the ballistic simulation gets triggered
3. The modal sheet is not dismissed once the ballistic simulation completes. This is caused by the `_DraggableSheetExtent._currentSize` not hitting value of `0.0`, but instead getting stuck on extremely small values like `1.3877787807814457e-17`

### Expected results

The modal sheet is dismissed once the ballistic simulation completes.

### Actual results

The modal sheet is visible once the ballistic simulation completes. This does not happen always, but I can reproduce it about 1/4 of the time.

I believe the issue is within `_DraggableScrollableSheetScrollPosition`, specifically in `goBallistic` function. When using the `extent.snap`, we use `_SnappingSimulation`, which seems to be working fine. However a few lines further, a `tick` function is defined, where a `delta` is added to the current `extent` value: 

```
void tick() {
      final double delta = ballisticController.value - lastPosition;
      lastPosition = ballisticController.value;
      extent.addPixelDelta(delta, context.notificationContext!);
      if ((velocity > 0 && extent.isAtMax) || (velocity < 0 && extent.isAtMin)) {
        // Make sure we pass along enough velocity to keep scrolling - otherwise
        // we just "bounce" off the top making it look like the list doesn't
        // have more to scroll.
        velocity = ballisticController.velocity + (physics.toleranceFor(this).velocity * ballisticController.velocity.sign);
        super.goBallistic(velocity);
        ballisticController.stop();
      } else if (ballisticController.isCompleted) {
        super.goBallistic(0);
      }
}
```

I believe we could fix it in `else if (ballisticController.isCompleted)` by checking if the `extent` value is very close to zero (x <= 0.01) and if so, rounding it to 0.0. I however do not think this would be the right fix and we should rather see why we do not hit exact 0. This could perhaps be caused by `pixelsToSize` where the `availablePixels` are off by 1 or so.

### Code sample

<details open><summary>Code sample</summary>

```dart
import 'package:flutter/material.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> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Draggable Sheet Bug"),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          showModalBottomSheet(
              useSafeArea: true,
              isScrollControlled: true,
              backgroundColor: Colors.transparent,
              context: context,
              builder: (context) {
                return const ModalSheet();
              });
        },
        tooltip: 'Show',
        child: const Icon(Icons.add),
      ),
    );
  }
}

class ModalSheet extends StatelessWidget {
  const ModalSheet({super.key});

  @override
  Widget build(BuildContext context) {
    return NotificationListener<DraggableScrollableNotification>(
      onNotification: (notification) {
        print(notification.extent);
        return false;
      },
      child: DraggableScrollableSheet(
        shouldCloseOnMinExtent: true,
        minChildSize: 0,
        initialChildSize: 1,
        maxChildSize: 1,
        snapSizes: const [],
        expand: true,
        snap: true,
        builder: (context, scrollController) {
          return ListView.separated(
              controller: scrollController,
              itemBuilder: (context, index) {
                return Container(
                  height: 100,
                  color: Colors.red,
                  child: Text('Item $index'),
                );
              },
              itemCount: 20,
              separatorBuilder: (context, index) {
                return const SizedBox(
                  height: 2,
                );
              });
        },
      ),
    );
  }
}

```

</details>


### Screenshots or Video

<details open>
<summary>Screenshots / Video demonstration</summary>

[Upload media here]

</details>

https://github.com/flutter/flutter/assets/3223913/835b6863-59c2-4ed3-91a8-e60fc7f84e19



### Logs

<details open><summary>These are the extent values obtained from DraggableScrollableNotification</summary>

```
NotificationListener<DraggableScrollableNotification>(
      onNotification: (notification) {
        print(notification.extent);
        return false;
      }
)
```

As you can see, the extent never reaches 0.0 and stays on 3.122502256758253**e-17** value

```console
flutter: 0.9985287936107609
flutter: 0.9733081126523749
flutter: 0.9182429528126149
flutter: 0.8879781356625519
flutter: 0.8543398002274951
flutter: 0.8207135707192983
flutter: 0.7870893588655783
flutter: 0.7534530410849982
flutter: 0.7198288292312782
flutter: 0.6862005820686049
flutter: 0.6525763702148849
flutter: 0.6189501407066882
flutter: 0.5853239111984915
flutter: 0.5516996993447714
flutter: 0.5180694345276213
flutter: 0.4844391697104713
flutter: 0.45081495785675124
flutter: 0.41718671069407787
flutter: 0.38355241056797434
flutter: 0.3499221457508243
flutter: 0.3162959162426276
flutter: 0.28267372204338426
flutter: 0.2490474925351875
flutter: 0.2154192453725141
flutter: 0.18178898055536402
flutter: 0.14816678635612057
flutter: 0.11452845092106395
flutter: 0.08090423906734392
flutter: 0.04727599190467041
flutter: 0.013639674124090381
flutter: 3.122502256758253e-17
flutter: 3.122502256758253e-17
```

</details>


### Flutter Doctor output

<details open><summary>Doctor output</summary>

```console
[✓] Flutter (Channel stable, 3.16.0, on macOS 14.2.1 23C71 darwin-arm64, locale en-DK)
    • Flutter version 3.16.0 on channel stable at /Users/vongrad/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision db7ef5bf9f (6 weeks ago), 2023-11-15 11:25:44 -0800
    • Engine revision 74d16627b9
    • Dart version 3.2.0
    • DevTools version 2.28.2

[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    • Android SDK at /Users/vongrad/Library/Android/sdk
    • Platform android-34, build-tools 34.0.0
    • ANDROID_HOME = /Users/vongrad/Library/Android/sdk
    • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 17.0.7+0-17.0.7b1000.6-10550314)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 15.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 15C65
    • CocoaPods version 1.14.2

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2023.1)
    • Android Studio at /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.7+0-17.0.7b1000.6-10550314)

[✓] Connected device (4 available)
    • vongrad (mobile)       • 00008120-001814C13683C01E            • ios            • iOS 17.1.2 21B101
    • iPhone 15 Pro (mobile) • 3ED33087-E9F9-44EA-B99B-EAF7641B486E • ios            • com.apple.CoreSimulator.SimRuntime.iOS-17-2 (simulator)
    • macOS (desktop)        • macos                                • darwin-arm64   • macOS 14.2.1 23C71 darwin-arm64
    • Chrome (web)           • chrome                               • web-javascript • Google Chrome 120.0.6099.129

[✓] Network resources
    • All expected network resources are available.

• No issues found!
```

</details>