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.12k stars 27.43k forks source link

DraggableScrollableSheet fails to resize programatically #154067

Open amal-stack opened 2 months ago

amal-stack commented 2 months ago

Steps to reproduce

  1. Create a Scaffold widget with a persistent bottom sheet (bottomSheet) wrapped in a DraggableScrollableSheet.
  2. Assign a DraggableScrollableController to DraggableScrollableSheet to allow programmatic resizing of the sheet.
  3. Note that the default initialChildSize of DraggableScrollableSheet is 0.5. Or assign a custom initialChildSize.
  4. Define a button/widget that when pressed, resizes the sheet programatically to any size lesser than initialChildSize. Either of the jumpTo or animateTo method of DraggableSrollableController can be used.
  5. For instance:
    FilledButton(onPressed: () => _controller.jumpTo(0.2), child: Text('Minimize'));
  6. Run the app.
  7. Resize the sheet (either by dragging manually or programmatically with another button) to any size greater than the initialChildSize.
  8. Resize the sheet programmatically using the button defined above when the sheet is still at a size greater than initialChildSize.

(See the code sample below for a minimal, reproducible and fully-working example.)

Expected results

When the button is pressed, the DraggableScrollableSheet should resize to the correct size.

Actual results

When the button is pressed, the sheet resizes to initialChildSize instead of the specified size. This only happens when the sheet is resized using the controller to a size lesser than the initialChildSize and when the sheet's current size is greater than initialChildSize.

Cause

After some debugging, it appears that the issue is arising because a NotificationListener in Scaffold._maybeBuildPersistentBottomSheet is calling DraggableScrollableActuator.reset(context), causing the sheet to resize to its initial size. The notification listener is a local function in _maybeBuildPersistentBottomSheet called persistentBottomSheetExtentChanged.

This listener calls _persistentSheetHistoryEntry.remove() which triggers the LocalHistoryEntry's onRemove listener (defined in the same function) which in turn calls DraggableScrollableActuator.reset(context).

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', debugShowCheckedModeBanner: false, theme: ThemeData( colorSchemeSeed: Colors.blue, ), home: const MyHomePage(title: 'DraggableScrollableSheet Resize Issue'), ); } } class MyHomePage extends StatefulWidget { final String title; const MyHomePage({ super.key, required this.title, }); @override State createState() => _MyHomePageState(); } const _maxSheetSize = 0.9; const _initialSheetSize = 0.4; const _minSheetSize = 0.2; class _MyHomePageState extends State { final _controller = DraggableScrollableController(); bool _isResizing = false; Future _resizeSheet(double size) async { setState(() => _isResizing = true); await _controller.animateTo( size, duration: Durations.medium1, curve: Curves.linear, ); setState(() => _isResizing = false); } @override Widget build(BuildContext context) { final ThemeData( :colorScheme, :textTheme, ) = Theme.of(context); return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Container( constraints: BoxConstraints.expand(), color: colorScheme.inversePrimary, child: Center( child: Text( 'Resize the sheet to a size greater ' 'than ${_initialSheetSize} and hit "Min".', style: textTheme.titleLarge, ), ), ), bottomSheet: DraggableScrollableSheet( expand: false, initialChildSize: _initialSheetSize, maxChildSize: _maxSheetSize, minChildSize: _minSheetSize, controller: _controller, builder: (context, scrollController) => ListView( padding: EdgeInsets.all(32.0), controller: scrollController, children: [ ListenableBuilder( listenable: _controller, builder: (context, _) => Text( 'Current Size: ${_controller.size}', style: textTheme.displaySmall, ), ), Text('Is Resizing: ${_isResizing}'), const SizedBox(height: 16.0), Wrap( alignment: WrapAlignment.spaceBetween, children: [ FilledButton( child: Text('Max'), onPressed: () => _resizeSheet(_maxSheetSize), ), FilledButton( child: Text('Initial'), onPressed: () => _resizeSheet(_initialSheetSize), ), FilledButton( child: Text('Min'), onPressed: () { String message = _controller.size > _initialSheetSize ? 'Sheet should resize to ${_minSheetSize} but watch it resize to ${_initialSheetSize}.' : 'Sheet size is initial size or smaller, so sheet will be correctly resized to ${_minSheetSize}. ' 'Resize the sheet to a size greater than ${_initialSheetSize} to trigger the issue.'; ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( message, ), behavior: SnackBarBehavior.floating, ), ); _resizeSheet(_minSheetSize); }) ], ), ], ), ), ); } } ```

Screenshots or Video

Screenshots / Video demonstration

Logs

Logs

Flutter Doctor output

Doctor output The example is reproducible on DartPad with Dart 3.5.0 and Flutter 3.24.0.
danagbemava-nc commented 2 months ago

Reproducible using the code and steps provided above.

flutter doctor -v ``` [!] Flutter (Channel stable, 3.24.1, on macOS 14.6.1 23G93 darwin-arm64, locale en-US) • Flutter version 3.24.1 on channel stable at /Users/deanli/dev/stable ! The flutter binary is not on your path. Consider adding /Users/deanli/dev/stable/bin to your path. ! The dart binary is not on your path. Consider adding /Users/deanli/dev/stable/bin to your path. • Upstream repository https://github.com/flutter/flutter.git • Framework revision 5874a72aa4 (6 days ago), 2024-08-20 16:46:00 -0500 • Engine revision c9b9d5780d • Dart version 3.5.1 • DevTools version 2.37.2 • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades. ``` ``` [!] Flutter (Channel master, 3.25.0-1.0.pre.131, on macOS 14.6.1 23G93 darwin-arm64, locale en-US) • Flutter version 3.25.0-1.0.pre.131 on channel master at /Users/deanli/dev/master ! The flutter binary is not on your path. Consider adding /Users/deanli/dev/master/bin to your path. ! The dart binary is not on your path. Consider adding /Users/deanli/dev/master/bin to your path. • Upstream repository https://github.com/flutter/flutter.git • Framework revision a7eaca934d (6 hours ago), 2024-08-26 04:33:30 +0000 • Engine revision 365b0c70fa • Dart version 3.6.0 (build 3.6.0-175.0.dev) • DevTools version 2.39.0-dev.15 • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades. ```