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.69k stars 27.61k forks source link

DraggableScrollableSheet _DraggableSheetExtent not reaching 0 during _SnappingSimulation #140701

Open vongrad opened 11 months ago

vongrad commented 11 months ago

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

Code sample ```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 createState() => _MyHomePageState(); } class _MyHomePageState extends State { @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( 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, ); }); }, ), ); } } ```

Screenshots or Video

Screenshots / Video demonstration [Upload media here]

https://github.com/flutter/flutter/assets/3223913/835b6863-59c2-4ed3-91a8-e60fc7f84e19

Logs

These are the extent values obtained from DraggableScrollableNotification ``` NotificationListener( 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 ```

Flutter Doctor output

Doctor output ```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! ```
danagbemava-nc commented 11 months ago

Reproducible using the code sample and steps provided above.

flutter doctor -v ``` [!] Flutter (Channel stable, 3.16.5, on macOS 14.1.2 23B92 darwin-arm64, locale en-GB) • Flutter version 3.16.5 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 78666c8dc5 (10 days ago), 2023-12-19 16:14:14 -0800 • Engine revision 3f3e560236 • Dart version 3.2.3 • DevTools version 2.28.4 • 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 34.0.0-rc1) • Android SDK at /Users/nexus/Library/Android/sdk • Platform android-34, build-tools 34.0.0-rc1 • Java binary at: /Users/nexus/Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b829.9-10027231) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 15.0) • Xcode at /Applications/Xcode-15.0.0-Release.Candidate.app/Contents/Developer • Build 15A240d • CocoaPods version 1.14.3 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 2022.3) • Android Studio at /Users/nexus/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.6b829.9-10027231) [✓] IntelliJ IDEA Ultimate Edition (version 2023.2.5) • IntelliJ at /Users/nexus/Applications/IntelliJ IDEA Ultimate.app • Flutter plugin version 76.3.4 • Dart plugin version 232.10072.19 [✓] VS Code (version 1.85.1) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.80.0 [✓] Connected device (3 available) • Nexus (mobile) • 00008020-001875E83A38002E • ios • iOS 17.2.1 21C66 • macOS (desktop) • macos • darwin-arm64 • macOS 14.1.2 23B92 darwin-arm64 • Chrome (web) • chrome • web-javascript • Google Chrome 120.0.6099.129 ! Error: Browsing on the local area network for Dean’s iPad. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac. The device must be opted into Developer Mode to connect wirelessly. (code -27) [✓] Network resources • All expected network resources are available. ! Doctor found issues in 1 category. ``` ``` [✓] Flutter (Channel master, 3.18.0-18.0.pre.44, on macOS 14.1.2 23B92 darwin-arm64, locale en-GB) • Flutter version 3.18.0-18.0.pre.44 on channel master at /Users/nexus/dev/sdks/flutters • Upstream repository https://github.com/flutter/flutter.git • Framework revision 420f18be76 (8 hours ago), 2023-12-29 13:45:36 +0800 • Engine revision 7a7ddc53cd • Dart version 3.3.0 (build 3.3.0-270.0.dev) • DevTools version 2.31.0-dev.0 [✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0-rc1) • Android SDK at /Users/nexus/Library/Android/sdk • Platform android-34, build-tools 34.0.0-rc1 • Java binary at: /Users/nexus/Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b829.9-10027231) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 15.0) • Xcode at /Applications/Xcode-15.0.0-Release.Candidate.app/Contents/Developer • Build 15A240d • CocoaPods version 1.14.3 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 2022.3) • Android Studio at /Users/nexus/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.6b829.9-10027231) [✓] IntelliJ IDEA Ultimate Edition (version 2023.2.5) • IntelliJ at /Users/nexus/Applications/IntelliJ IDEA Ultimate.app • Flutter plugin version 76.3.4 • Dart plugin version 232.10072.19 [✓] VS Code (version 1.85.1) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.80.0 [✓] Connected device (3 available) • Nexus (mobile) • 00008020-001875E83A38002E • ios • iOS 17.2.1 21C66 • macOS (desktop) • macos • darwin-arm64 • macOS 14.1.2 23B92 darwin-arm64 • Chrome (web) • chrome • web-javascript • Google Chrome 120.0.6099.129 ! Error: Browsing on the local area network for Dean’s iPad. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac. The device must be opted into Developer Mode to connect wirelessly. (code -27) [✓] Network resources • All expected network resources are available. • No issues found! ```
ClementCardonnel commented 2 months ago

I've noticed this issue as well using Flutter 3.24.1 and a Pixel 8a with Impeller enabled. On my end, it's random. 2 times out of 3 it works as expected, the remaining time the modal fails to close, leaving the UI in a confusing state. Here's some of my code in case it helps:

showAbout(BuildContext context) {
  // presentModally(context: context, content: const WelcomePamphlet());
  showModalBottomSheet(
    useSafeArea: true,
    context: context,
    isScrollControlled: true,
    showDragHandle: false,
    isDismissible: true,
    enableDrag: true,
    sheetAnimationStyle: AnimationStyle(curve: Curves.linear),
    builder: (context) => DraggableScrollableSheet(
      expand: false,
      snap: true,
      initialChildSize: 0.95,
      maxChildSize: 0.95,
      minChildSize: 0,
      shouldCloseOnMinExtent: true,
      builder: (context, scrollController) => WelcomePamphlet(
        scrollController: scrollController,
      ),
    ),
  );
}
class WelcomePamphlet extends StatelessWidget {
  ScrollController scrollController;

  WelcomePamphlet({super.key, required this.scrollController});

  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      controller: scrollController,
      slivers: [
        SliverAppBar(
          title: Text("My Title"),
          floating: true,
          snap: true,
          pinned: true,
        ),
        SliverList(
          delegate: SliverChildListDelegate(
            [
              // Content
            ],
          ),
        )
      ],
    );
  }
}

This issue prevents makes DraggableScrollableSheet unreliable to the point where I cannot recommend using it if the sheet needs to be dismissed.

vongrad commented 2 months ago

Any update on this?

Krushiler commented 4 weeks ago

Any updates?