From 21d7615b45dc37b287a74a83f20227bda84a00d8 Mon Sep 17 00:00:00 2001 From: fujidaiti Date: Sun, 1 Dec 2024 21:36:35 +0900 Subject: [PATCH] Merge draggable and scrollable sheet APIs --- .../lib/showcase/ai_playlist_generator.dart | 18 +- example/lib/showcase/airbnb_mobile_app.dart | 37 +- example/lib/showcase/safari/actions.dart | 3 +- example/lib/showcase/safari/bookmark.dart | 2 +- example/lib/showcase/safari/menu.dart | 41 +- .../lib/showcase/todo_list/todo_editor.dart | 3 +- .../lib/tutorial/bottom_bar_visibility.dart | 2 +- example/lib/tutorial/bouncing_behaviors.dart | 17 +- .../lib/tutorial/cupertino_modal_sheet.dart | 4 +- .../lib/tutorial/declarative_modal_sheet.dart | 2 +- .../declarative_navigation_sheet.dart | 12 +- example/lib/tutorial/draggable_sheet.dart | 12 +- .../lib/tutorial/extent_driven_animation.dart | 2 +- .../lib/tutorial/imperative_modal_sheet.dart | 2 +- .../tutorial/imperative_navigation_sheet.dart | 6 +- ...le_declarative_modal_navigation_sheet.dart | 2 +- .../tutorial/keyboard_dismiss_behavior.dart | 3 +- .../navigation_sheet_and_keyboard.dart | 6 +- .../tutorial/scrollable_pageview_sheet.dart | 5 +- example/lib/tutorial/scrollable_sheet.dart | 3 +- .../lib/tutorial/sheet_content_scaffold.dart | 2 +- example/lib/tutorial/sheet_controller.dart | 2 +- example/lib/tutorial/sheet_draggable.dart | 92 ----- example/lib/tutorial/sheet_physics.dart | 2 +- .../textfield_with_multiple_stops.dart | 3 +- lib/smooth_sheets.dart | 1 - lib/src/draggable/draggable.dart | 2 - lib/src/draggable/draggable_sheet.dart | 98 ----- .../draggable/draggable_sheet_position.dart | 31 -- .../draggable_sheet_position_scope.dart | 59 --- lib/src/foundation/keyboard_dismissible.dart | 2 +- .../foundation/sheet_content_scaffold.dart | 2 +- lib/src/foundation/sheet_position_scope.dart | 2 +- lib/src/navigation/navigation.dart | 8 +- lib/src/navigation/navigation_route.dart | 227 ++++++++++- lib/src/navigation/navigation_routes.dart | 367 ------------------ .../navigation/navigation_sheet_activity.dart | 6 +- .../navigation/navigation_sheet_position.dart | 32 +- lib/src/scrollable/scrollable.dart | 2 +- lib/src/scrollable/scrollable_sheet.dart | 98 +++-- .../scrollable/scrollable_sheet_activity.dart | 29 +- .../scrollable/scrollable_sheet_physics.dart | 1 + .../scrollable/scrollable_sheet_position.dart | 8 +- .../sheet_content_scroll_position.dart | 2 +- .../sheet_draggable.dart | 13 +- test/draggable/draggable_sheet_test.dart | 197 ---------- test/foundation/sheet_notification_test.dart | 10 +- test/modal/modal_sheet_test.dart | 8 +- test/navigation/navigation_sheet_test.dart | 8 +- test/scrollable/scrollable_sheet_test.dart | 18 +- 50 files changed, 466 insertions(+), 1048 deletions(-) delete mode 100644 example/lib/tutorial/sheet_draggable.dart delete mode 100644 lib/src/draggable/draggable.dart delete mode 100644 lib/src/draggable/draggable_sheet.dart delete mode 100644 lib/src/draggable/draggable_sheet_position.dart delete mode 100644 lib/src/draggable/draggable_sheet_position_scope.dart delete mode 100644 lib/src/navigation/navigation_routes.dart rename lib/src/{draggable => scrollable}/sheet_draggable.dart (82%) delete mode 100644 test/draggable/draggable_sheet_test.dart diff --git a/example/lib/showcase/ai_playlist_generator.dart b/example/lib/showcase/ai_playlist_generator.dart index 7cc97a37..37c220a8 100644 --- a/example/lib/showcase/ai_playlist_generator.dart +++ b/example/lib/showcase/ai_playlist_generator.dart @@ -72,7 +72,7 @@ final _sheetShellRoute = ShellRoute( final _introRoute = GoRoute( path: 'intro', pageBuilder: (context, state) { - return const DraggableNavigationSheetPage(child: _IntroPage()); + return const NavigationSheetPage(child: _IntroPage()); }, routes: [_genreRoute], ); @@ -80,7 +80,7 @@ final _introRoute = GoRoute( final _genreRoute = GoRoute( path: 'genre', pageBuilder: (context, state) { - return const DraggableNavigationSheetPage(child: _SelectGenrePage()); + return const NavigationSheetPage(child: _SelectGenrePage()); }, routes: [_moodRoute], ); @@ -88,7 +88,7 @@ final _genreRoute = GoRoute( final _moodRoute = GoRoute( path: 'mood', pageBuilder: (context, state) { - return const DraggableNavigationSheetPage(child: _SelectMoodPage()); + return const NavigationSheetPage(child: _SelectMoodPage()); }, routes: [_seedTrackRoute], ); @@ -96,7 +96,10 @@ final _moodRoute = GoRoute( final _seedTrackRoute = GoRoute( path: 'seed-track', pageBuilder: (context, state) { - return const ScrollableNavigationSheetPage(child: _SelectSeedTrackPage()); + return const NavigationSheetPage( + scrollConfiguration: SheetScrollConfiguration(), + child: _SelectSeedTrackPage(), + ); }, routes: [_confirmRoute], ); @@ -104,7 +107,8 @@ final _seedTrackRoute = GoRoute( final _confirmRoute = GoRoute( path: 'confirm', pageBuilder: (context, state) { - return const ScrollableNavigationSheetPage( + return const NavigationSheetPage( + scrollConfiguration: SheetScrollConfiguration(), initialPosition: SheetAnchor.proportional(0.7), minPosition: SheetAnchor.proportional(0.7), physics: BouncingSheetPhysics( @@ -119,7 +123,7 @@ final _confirmRoute = GoRoute( final _generateRoute = GoRoute( path: 'generate', pageBuilder: (context, state) { - return const DraggableNavigationSheetPage(child: _GeneratingPage()); + return const NavigationSheetPage(child: _GeneratingPage()); }, ); @@ -718,6 +722,7 @@ const _moods = [ (label: 'Uplifting and Positive', emoji: '💪'), ]; +/* cSpell: disable */ const _seedTracks = [ "Groove Odyssey", "Funky Fusion Fiesta", @@ -740,3 +745,4 @@ const _seedTracks = [ "Brass Bliss Bouquet", "Funky Cosmic Carnival", ]; +/* cSpell: enable */ diff --git a/example/lib/showcase/airbnb_mobile_app.dart b/example/lib/showcase/airbnb_mobile_app.dart index 48bad25a..cbfc448a 100644 --- a/example/lib/showcase/airbnb_mobile_app.dart +++ b/example/lib/showcase/airbnb_mobile_app.dart @@ -176,7 +176,8 @@ class _ContentSheet extends StatelessWidget { ); return SheetViewport( - child: ScrollableSheet( + child: Sheet( + scrollConfiguration: const SheetScrollConfiguration(), physics: sheetPhysics, minPosition: minSheetPosition, child: SizedBox( @@ -209,26 +210,24 @@ class _ContentSheetHandle extends StatelessWidget @override Widget build(BuildContext context) { - return SheetDraggable( - child: SizedBox( - height: preferredSize.height, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 12), - child: Column( - mainAxisSize: MainAxisSize.max, - children: [ - buildIndicator(), - const SizedBox(height: 16), - Expanded( - child: Center( - child: Text( - '646 national park homes', - style: Theme.of(context).textTheme.labelLarge, - ), + return SizedBox( + height: preferredSize.height, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12), + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + buildIndicator(), + const SizedBox(height: 16), + Expanded( + child: Center( + child: Text( + '646 national park homes', + style: Theme.of(context).textTheme.labelLarge, ), ), - ], - ), + ), + ], ), ), ); diff --git a/example/lib/showcase/safari/actions.dart b/example/lib/showcase/safari/actions.dart index d00e4fa8..e092e50f 100644 --- a/example/lib/showcase/safari/actions.dart +++ b/example/lib/showcase/safari/actions.dart @@ -21,7 +21,8 @@ class EditActionsSheet extends StatelessWidget { @override Widget build(BuildContext context) { - return ScrollableSheet( + return Sheet( + scrollConfiguration: const SheetScrollConfiguration(), child: ClipRRect( borderRadius: BorderRadius.circular(16), child: SheetContentScaffold( diff --git a/example/lib/showcase/safari/bookmark.dart b/example/lib/showcase/safari/bookmark.dart index fba3584c..449f09e0 100644 --- a/example/lib/showcase/safari/bookmark.dart +++ b/example/lib/showcase/safari/bookmark.dart @@ -31,7 +31,7 @@ class EditBookmarkSheet extends StatelessWidget { Widget build(BuildContext context) { return SheetKeyboardDismissible( dismissBehavior: const SheetKeyboardDismissBehavior.onDragDown(), - child: DraggableSheet( + child: Sheet( child: ClipRRect( borderRadius: BorderRadius.circular(16), child: SheetContentScaffold( diff --git a/example/lib/showcase/safari/menu.dart b/example/lib/showcase/safari/menu.dart index 6e2d7bf1..cc3ca59d 100644 --- a/example/lib/showcase/safari/menu.dart +++ b/example/lib/showcase/safari/menu.dart @@ -25,7 +25,8 @@ class MenuSheet extends StatelessWidget { @override Widget build(BuildContext context) { const halfWayPosition = SheetAnchor.proportional(0.5); - return ScrollableSheet( + return Sheet( + scrollConfiguration: const SheetScrollConfiguration(), initialPosition: halfWayPosition, minPosition: halfWayPosition, physics: const BouncingSheetPhysics( @@ -190,27 +191,25 @@ class _TopBar extends StatelessWidget { ?.copyWith(color: CupertinoColors.secondaryLabel), ); - return SheetDraggable( - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 16, - ), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SiteIcon(url: faviconUrl), - const SizedBox(width: 16), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [pageTitle, displayUrl], - ), + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 16, + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SiteIcon(url: faviconUrl), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [pageTitle, displayUrl], ), - const SizedBox(width: 16), - const _CloseButton(), - ], - ), + ), + const SizedBox(width: 16), + const _CloseButton(), + ], ), ); } diff --git a/example/lib/showcase/todo_list/todo_editor.dart b/example/lib/showcase/todo_list/todo_editor.dart index 90ddb0da..f3bebb2c 100644 --- a/example/lib/showcase/todo_list/todo_editor.dart +++ b/example/lib/showcase/todo_list/todo_editor.dart @@ -150,7 +150,8 @@ class _TodoEditorState extends State { dismissBehavior: const SheetKeyboardDismissBehavior.onDragDown( isContentScrollAware: true, ), - child: ScrollableSheet( + child: Sheet( + scrollConfiguration: const SheetScrollConfiguration(), child: Container( clipBehavior: Clip.antiAlias, decoration: sheetShape, diff --git a/example/lib/tutorial/bottom_bar_visibility.dart b/example/lib/tutorial/bottom_bar_visibility.dart index 50625655..8a56ce3f 100644 --- a/example/lib/tutorial/bottom_bar_visibility.dart +++ b/example/lib/tutorial/bottom_bar_visibility.dart @@ -151,7 +151,7 @@ class _ExampleSheet extends StatelessWidget { return SafeArea( bottom: false, - child: DraggableSheet( + child: Sheet( minPosition: minSize, initialPosition: halfSize, physics: multiStopPhysics, diff --git a/example/lib/tutorial/bouncing_behaviors.dart b/example/lib/tutorial/bouncing_behaviors.dart index 1d83e7be..4fa7e407 100644 --- a/example/lib/tutorial/bouncing_behaviors.dart +++ b/example/lib/tutorial/bouncing_behaviors.dart @@ -34,9 +34,9 @@ class _Home extends StatelessWidget { ), ListTile( title: const Text('FixedBouncingBehavior'), - subtitle: const Text('with DraggableSheet'), + subtitle: const Text('with Sheet'), onTap: () => showModalSheet( - const _DraggableSheet( + const _Sheet( behavior: FixedBouncingBehavior( // Allows the sheet position to exceed the content bounds by ±50 pixels. SheetAnchor.pixels(50), @@ -61,9 +61,9 @@ class _Home extends StatelessWidget { ), ListTile( title: const Text('DirectionAwareBouncingBehavior'), - subtitle: const Text('with DraggableSheet'), + subtitle: const Text('with Sheet'), onTap: () => showModalSheet( - const _DraggableSheet( + const _Sheet( behavior: DirectionAwareBouncingBehavior( // Allows the sheet to bounce only when dragging it downwards. upward: SheetAnchor.pixels(0), @@ -86,7 +86,8 @@ class _ScrollableSheet extends StatelessWidget { @override Widget build(BuildContext context) { - return ScrollableSheet( + return Sheet( + scrollConfiguration: const SheetScrollConfiguration(), physics: BouncingSheetPhysics(behavior: behavior), child: Material( color: Colors.white, @@ -104,14 +105,14 @@ class _ScrollableSheet extends StatelessWidget { } } -class _DraggableSheet extends StatelessWidget { - const _DraggableSheet({required this.behavior}); +class _Sheet extends StatelessWidget { + const _Sheet({required this.behavior}); final BouncingBehavior behavior; @override Widget build(BuildContext context) { - return DraggableSheet( + return Sheet( physics: BouncingSheetPhysics(behavior: behavior), child: Container( height: 500, diff --git a/example/lib/tutorial/cupertino_modal_sheet.dart b/example/lib/tutorial/cupertino_modal_sheet.dart index e95aba6d..1e5426dc 100644 --- a/example/lib/tutorial/cupertino_modal_sheet.dart +++ b/example/lib/tutorial/cupertino_modal_sheet.dart @@ -73,7 +73,7 @@ class _HalfScreenSheet extends StatelessWidget { Widget build(BuildContext context) { // `CupertinoStackedTransition` won't start the transition animation until // the visible height of a modal sheet (the position) exceeds 50% of the screen height. - return const DraggableSheet( + return const Sheet( initialPosition: SheetAnchor.proportional(0.5), minPosition: SheetAnchor.proportional(0.5), physics: BouncingSheetPhysics( @@ -91,7 +91,7 @@ class _FullScreenSheet extends StatelessWidget { Widget build(BuildContext context) { // Wrap the sheet with `SheetDismissible` to // enable the pull-to-dismiss action. - return const DraggableSheet( + return const Sheet( child: _SheetContent(), ); } diff --git a/example/lib/tutorial/declarative_modal_sheet.dart b/example/lib/tutorial/declarative_modal_sheet.dart index 99e94d18..64ee988e 100644 --- a/example/lib/tutorial/declarative_modal_sheet.dart +++ b/example/lib/tutorial/declarative_modal_sheet.dart @@ -82,7 +82,7 @@ class _ExampleSheet extends StatelessWidget { } } }, - child: DraggableSheet( + child: Sheet( child: Card( color: Theme.of(context).colorScheme.secondaryContainer, margin: EdgeInsets.zero, diff --git a/example/lib/tutorial/declarative_navigation_sheet.dart b/example/lib/tutorial/declarative_navigation_sheet.dart index 753df13a..0cfc649f 100644 --- a/example/lib/tutorial/declarative_navigation_sheet.dart +++ b/example/lib/tutorial/declarative_navigation_sheet.dart @@ -34,10 +34,10 @@ final router = GoRouter( GoRoute( path: '/a', pageBuilder: (context, state) { - // Use DraggableNavigationSheetPage for a draggable page. + // Use ScrollableNavigationSheetPage for a draggable page. // If the page contains scrollable widget(s), consider using // ScrollableNavigationSheetPage instead. - return DraggableNavigationSheetPage( + return NavigationSheetPage( key: state.pageKey, child: const _ExampleSheetContent( title: '/a', @@ -50,7 +50,7 @@ final router = GoRouter( GoRoute( path: 'details', pageBuilder: (context, state) { - return DraggableNavigationSheetPage( + return NavigationSheetPage( key: state.pageKey, child: const _ExampleSheetContent( title: '/a/details', @@ -63,7 +63,7 @@ final router = GoRouter( GoRoute( path: 'info', pageBuilder: (context, state) { - return DraggableNavigationSheetPage( + return NavigationSheetPage( key: state.pageKey, child: const _ExampleSheetContent( title: '/a/details/info', @@ -80,7 +80,7 @@ final router = GoRouter( GoRoute( path: '/b', pageBuilder: (context, state) { - return DraggableNavigationSheetPage( + return NavigationSheetPage( key: state.pageKey, child: const _ExampleSheetContent( title: 'B', @@ -93,7 +93,7 @@ final router = GoRouter( GoRoute( path: 'details', pageBuilder: (context, state) { - return DraggableNavigationSheetPage( + return NavigationSheetPage( key: state.pageKey, child: const _ExampleSheetContent( title: 'B Details', diff --git a/example/lib/tutorial/draggable_sheet.dart b/example/lib/tutorial/draggable_sheet.dart index bf9d51d1..35ce42a9 100644 --- a/example/lib/tutorial/draggable_sheet.dart +++ b/example/lib/tutorial/draggable_sheet.dart @@ -2,11 +2,11 @@ import 'package:flutter/material.dart'; import 'package:smooth_sheets/smooth_sheets.dart'; void main() { - runApp(const _BasicDraggableSheetExample()); + runApp(const _BasicSheetExample()); } -class _BasicDraggableSheetExample extends StatelessWidget { - const _BasicDraggableSheetExample(); +class _BasicSheetExample extends StatelessWidget { + const _BasicSheetExample(); @override Widget build(BuildContext context) { @@ -42,11 +42,11 @@ class _MySheet extends StatelessWidget { ), ); - // Then, wrap the content in DraggableSheet. - // Note that DraggableSheet does not work with scrollable widgets. + // Then, wrap the content in Sheet. + // Note that Sheet does not work with scrollable widgets. // If you want to use a scrollable widget as its content, // use ScrollableSheet instead. - return DraggableSheet( + return Sheet( child: buildSheetBackground(context, content), ); } diff --git a/example/lib/tutorial/extent_driven_animation.dart b/example/lib/tutorial/extent_driven_animation.dart index dc891d4b..c828bee9 100644 --- a/example/lib/tutorial/extent_driven_animation.dart +++ b/example/lib/tutorial/extent_driven_animation.dart @@ -60,7 +60,7 @@ class _ExampleSheet extends StatelessWidget { ), ); - return DraggableSheet( + return Sheet( minPosition: minPosition, physics: physics, child: Card( diff --git a/example/lib/tutorial/imperative_modal_sheet.dart b/example/lib/tutorial/imperative_modal_sheet.dart index 13aea697..9f3eb215 100644 --- a/example/lib/tutorial/imperative_modal_sheet.dart +++ b/example/lib/tutorial/imperative_modal_sheet.dart @@ -66,7 +66,7 @@ class _ExampleSheet extends StatelessWidget { } } }, - child: DraggableSheet( + child: Sheet( minPosition: const SheetAnchor.proportional(0.5), child: Card( color: Theme.of(context).colorScheme.secondaryContainer, diff --git a/example/lib/tutorial/imperative_navigation_sheet.dart b/example/lib/tutorial/imperative_navigation_sheet.dart index 83e35a08..e6b9610a 100644 --- a/example/lib/tutorial/imperative_navigation_sheet.dart +++ b/example/lib/tutorial/imperative_navigation_sheet.dart @@ -38,8 +38,8 @@ class _ExampleSheet extends StatelessWidget { observers: [_transitionObserver], onGenerateInitialRoutes: (navigator, initialRoute) { return [ - // Use DraggableNavigationSheetRoute for a draggable page. - DraggableNavigationSheetRoute( + // Use ScrollableNavigationSheetRoute for a draggable page. + NavigationSheetRoute( builder: (context) { return const _DraggablePage(); }, @@ -69,7 +69,7 @@ class _DraggablePage extends StatelessWidget { void navigateToScrollablePage(BuildContext context) { // Use ScrollableNavigationSheetRoute for a scrollable page. - final route = ScrollableNavigationSheetRoute( + final route = NavigationSheetRoute( builder: (context) { return const _ScrollablePage(); }, diff --git a/example/lib/tutorial/ios_style_declarative_modal_navigation_sheet.dart b/example/lib/tutorial/ios_style_declarative_modal_navigation_sheet.dart index 4a0df672..561c167e 100644 --- a/example/lib/tutorial/ios_style_declarative_modal_navigation_sheet.dart +++ b/example/lib/tutorial/ios_style_declarative_modal_navigation_sheet.dart @@ -30,7 +30,7 @@ final router = GoRouter( GoRoute( path: 'modal', pageBuilder: (context, state) { - return DraggableNavigationSheetPage( + return NavigationSheetPage( key: state.pageKey, child: Container(color: Colors.white), ); diff --git a/example/lib/tutorial/keyboard_dismiss_behavior.dart b/example/lib/tutorial/keyboard_dismiss_behavior.dart index 5d7509f7..59bac96e 100644 --- a/example/lib/tutorial/keyboard_dismiss_behavior.dart +++ b/example/lib/tutorial/keyboard_dismiss_behavior.dart @@ -173,7 +173,8 @@ class _ExampleSheet extends StatelessWidget { return SheetKeyboardDismissible( dismissBehavior: keyboardDismissBehavior, - child: ScrollableSheet( + child: Sheet( + scrollConfiguration: const SheetScrollConfiguration(), child: SheetContentScaffold( appBar: AppBar(), body: body, diff --git a/example/lib/tutorial/navigation_sheet_and_keyboard.dart b/example/lib/tutorial/navigation_sheet_and_keyboard.dart index a5ff02bb..ae049e3c 100644 --- a/example/lib/tutorial/navigation_sheet_and_keyboard.dart +++ b/example/lib/tutorial/navigation_sheet_and_keyboard.dart @@ -30,7 +30,7 @@ final _router = GoRouter( GoRoute( path: 'a', pageBuilder: (context, state) { - return DraggableNavigationSheetPage( + return NavigationSheetPage( key: state.pageKey, child: const _EditablePageContent( height: 600, @@ -43,7 +43,7 @@ final _router = GoRouter( GoRoute( path: 'b', pageBuilder: (context, state) { - return DraggableNavigationSheetPage( + return NavigationSheetPage( key: state.pageKey, child: const _EditablePageContent( height: 300, @@ -56,7 +56,7 @@ final _router = GoRouter( GoRoute( path: 'c', pageBuilder: (context, state) { - return DraggableNavigationSheetPage( + return NavigationSheetPage( key: state.pageKey, child: const _EditablePageContent( nextLocation: '/', diff --git a/example/lib/tutorial/scrollable_pageview_sheet.dart b/example/lib/tutorial/scrollable_pageview_sheet.dart index 79d442c3..58e955a6 100644 --- a/example/lib/tutorial/scrollable_pageview_sheet.dart +++ b/example/lib/tutorial/scrollable_pageview_sheet.dart @@ -5,7 +5,7 @@ void main() { runApp(const _ScrollablePageViewSheetExample()); } -/// An example of [ScrollableSheet] + [PageView]. +/// An example of [Sheet] + [PageView]. class _ScrollablePageViewSheetExample extends StatelessWidget { const _ScrollablePageViewSheetExample(); @@ -42,7 +42,8 @@ class _MySheet extends StatelessWidget { @override Widget build(BuildContext context) { - return ScrollableSheet( + return Sheet( + scrollConfiguration: const SheetScrollConfiguration(), child: Material( child: SizedBox( height: 600, diff --git a/example/lib/tutorial/scrollable_sheet.dart b/example/lib/tutorial/scrollable_sheet.dart index e68281df..bb4ebc8c 100644 --- a/example/lib/tutorial/scrollable_sheet.dart +++ b/example/lib/tutorial/scrollable_sheet.dart @@ -46,7 +46,8 @@ class _MySheet extends StatelessWidget { ); // Just wrap the content in a ScrollableSheet! - final sheet = ScrollableSheet( + final sheet = Sheet( + scrollConfiguration: const SheetScrollConfiguration(), child: buildSheetBackground(context, content), // Optional: Comment out the following lines to add multiple stop positions. // diff --git a/example/lib/tutorial/sheet_content_scaffold.dart b/example/lib/tutorial/sheet_content_scaffold.dart index 1a1fd46c..96cc7f04 100644 --- a/example/lib/tutorial/sheet_content_scaffold.dart +++ b/example/lib/tutorial/sheet_content_scaffold.dart @@ -65,7 +65,7 @@ class _ExampleSheet extends StatelessWidget { ), ); - return DraggableSheet( + return Sheet( physics: physics, minPosition: const SheetAnchor.pixels(0), child: Card( diff --git a/example/lib/tutorial/sheet_controller.dart b/example/lib/tutorial/sheet_controller.dart index 4a538401..858332bb 100644 --- a/example/lib/tutorial/sheet_controller.dart +++ b/example/lib/tutorial/sheet_controller.dart @@ -88,7 +88,7 @@ class _ExampleSheet extends StatelessWidget { @override Widget build(BuildContext context) { - return DraggableSheet( + return Sheet( controller: controller, minPosition: const SheetAnchor.proportional(0.5), physics: const BouncingSheetPhysics( diff --git a/example/lib/tutorial/sheet_draggable.dart b/example/lib/tutorial/sheet_draggable.dart deleted file mode 100644 index 5b0573e7..00000000 --- a/example/lib/tutorial/sheet_draggable.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:smooth_sheets/smooth_sheets.dart'; - -void main() { - runApp(const _SheetDraggableExample()); -} - -class _SheetDraggableExample extends StatelessWidget { - const _SheetDraggableExample(); - - @override - Widget build(BuildContext context) { - return const MaterialApp( - home: Scaffold( - body: Stack( - children: [ - Placeholder(), - SheetViewport( - child: _ExampleSheet(), - ), - ], - ), - ), - ); - } -} - -class _ExampleSheet extends StatelessWidget { - const _ExampleSheet(); - - @override - Widget build(BuildContext context) { - final handle = Container( - alignment: Alignment.center, - padding: const EdgeInsets.symmetric(vertical: 12), - child: Container( - height: 8, - width: 56, - decoration: const ShapeDecoration( - color: Colors.black12, - shape: StadiumBorder(), - ), - ), - ); - - final content = ListView.builder( - itemCount: 50, - itemBuilder: (context, index) { - return ListTile( - title: Text('Item $index'), - ); - }, - ); - - final body = Column( - children: [ - // SheetDraggable enables the child widget to act as a drag handle for the sheet. - // Typically, you will want to use this widget when placing non-scrollable widget(s) - // in a ScrollableSheet, since it only works with scrollable widgets, so you can't - // drag the sheet by touching a non-scrollable area. Try removing SheetDraggable and - // you will see that the drag handle doesn't work as it should. - // - // Note that SheetDraggable is not needed when using DraggableSheet - // since it implicitly wraps the child widget with SheetDraggable. - SheetDraggable(child: handle), - Expanded(child: content), - ], - ); - - const minPosition = SheetAnchor.proportional(0.5); - const physics = BouncingSheetPhysics( - parent: SnappingSheetPhysics(), - ); - - return SafeArea( - bottom: false, - child: ScrollableSheet( - physics: physics, - minPosition: minPosition, - initialPosition: minPosition, - child: Card( - margin: EdgeInsets.zero, - color: Theme.of(context).colorScheme.secondaryContainer, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), - ), - child: body, - ), - ), - ); - } -} diff --git a/example/lib/tutorial/sheet_physics.dart b/example/lib/tutorial/sheet_physics.dart index 98c4f60a..98be6648 100644 --- a/example/lib/tutorial/sheet_physics.dart +++ b/example/lib/tutorial/sheet_physics.dart @@ -116,7 +116,7 @@ class _MySheet extends StatelessWidget { @override Widget build(BuildContext context) { - return DraggableSheet( + return Sheet( // The 'minPosition' and 'maxPosition' properties determine // how far the sheet can be dragged. Note that "position" // refers to the visible height of the sheet. For example, diff --git a/example/lib/tutorial/textfield_with_multiple_stops.dart b/example/lib/tutorial/textfield_with_multiple_stops.dart index eb544d50..ff42e2c6 100644 --- a/example/lib/tutorial/textfield_with_multiple_stops.dart +++ b/example/lib/tutorial/textfield_with_multiple_stops.dart @@ -15,7 +15,8 @@ class TextFieldWithMultipleStops extends StatelessWidget { children: [ const Scaffold(), SheetViewport( - child: ScrollableSheet( + child: Sheet( + scrollConfiguration: const SheetScrollConfiguration(), initialPosition: const SheetAnchor.proportional(0.7), minPosition: const SheetAnchor.proportional(0.4), physics: const BouncingSheetPhysics( diff --git a/lib/smooth_sheets.dart b/lib/smooth_sheets.dart index abbc8c9f..11b2c9e2 100644 --- a/lib/smooth_sheets.dart +++ b/lib/smooth_sheets.dart @@ -3,7 +3,6 @@ /// the iOS flavor), and more. library; -export 'src/draggable/draggable.dart'; export 'src/foundation/foundation.dart'; export 'src/modal/modal.dart'; export 'src/navigation/navigation.dart'; diff --git a/lib/src/draggable/draggable.dart b/lib/src/draggable/draggable.dart deleted file mode 100644 index 585b7e1c..00000000 --- a/lib/src/draggable/draggable.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'draggable_sheet.dart' show DraggableSheet; -export 'sheet_draggable.dart' show SheetDraggable; diff --git a/lib/src/draggable/draggable_sheet.dart b/lib/src/draggable/draggable_sheet.dart deleted file mode 100644 index ab4b9489..00000000 --- a/lib/src/draggable/draggable_sheet.dart +++ /dev/null @@ -1,98 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; - -import '../foundation/sheet_context.dart'; -import '../foundation/sheet_controller.dart'; -import '../foundation/sheet_gesture_tamperer.dart'; -import '../foundation/sheet_physics.dart'; -import '../foundation/sheet_position.dart'; -import '../foundation/sheet_theme.dart'; -import '../foundation/sheet_viewport.dart'; -import '../scrollable/scrollable_sheet.dart'; -import 'draggable_sheet_position_scope.dart'; -import 'sheet_draggable.dart'; - -/// A sheet that can be dragged. -/// -/// Note that this widget does not work with scrollable widgets. -/// Instead, use [ScrollableSheet] for this usecase. -class DraggableSheet extends StatefulWidget { - /// Creates a sheet that can be dragged. - /// - /// The maximum height will be equal to the [child]'s height. - /// - /// The [physics] determines how the sheet will behave when over-dragged - /// or under-dragged, or when the user stops dragging. - /// - /// The [hitTestBehavior] defaults to [HitTestBehavior.translucent]. - /// - /// See also: - /// - [A tutorial](https://github.com/fujidaiti/smooth_sheets/blob/main/cookbook/lib/tutorial/draggable_sheet.dart), - /// minimal code to use a draggable sheet. - const DraggableSheet({ - super.key, - this.hitTestBehavior = HitTestBehavior.translucent, - this.initialPosition = const SheetAnchor.proportional(1), - this.minPosition = const SheetAnchor.proportional(1), - this.maxPosition = const SheetAnchor.proportional(1), - this.physics, - required this.child, - this.controller, - }); - - final SheetAnchor initialPosition; - - /// {@macro SheetPositionConfig.minPosition} - final SheetAnchor minPosition; - - /// {@macro SheetPositionConfig.maxPosition} - final SheetAnchor maxPosition; - - /// {@macro SheetPositionConfig.physics} - final SheetPhysics? physics; - - /// An object that can be used to control and observe the sheet height. - final SheetController? controller; - - /// The content of the sheet. - final Widget child; - - /// How to behave during hit testing. - /// - /// This value will be passed to the constructor of internal [SheetDraggable]. - final HitTestBehavior hitTestBehavior; - - @override - State createState() => _DraggableSheetState(); -} - -class _DraggableSheetState extends State - with TickerProviderStateMixin, SheetContextStateMixin { - @override - Widget build(BuildContext context) { - final theme = SheetTheme.maybeOf(context); - final physics = widget.physics ?? theme?.physics ?? kDefaultSheetPhysics; - final gestureTamper = SheetGestureProxy.maybeOf(context); - final controller = - widget.controller ?? SheetControllerScope.maybeOf(context); - final viewport = SheetViewport.of(context); - - return DraggableSheetPositionScope( - context: this, - key: viewport.positionOwnerKey, - controller: controller, - initialPosition: widget.initialPosition, - minPosition: widget.minPosition, - maxPosition: widget.maxPosition, - physics: physics, - gestureTamperer: gestureTamper, - debugLabel: kDebugMode ? 'DraggableSheet' : null, - child: SheetContentViewport( - child: SheetDraggable( - behavior: widget.hitTestBehavior, - child: widget.child, - ), - ), - ); - } -} diff --git a/lib/src/draggable/draggable_sheet_position.dart b/lib/src/draggable/draggable_sheet_position.dart deleted file mode 100644 index 41439dba..00000000 --- a/lib/src/draggable/draggable_sheet_position.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:meta/meta.dart'; - -import '../foundation/sheet_position.dart'; - -@internal -class DraggableSheetPosition extends SheetPosition { - DraggableSheetPosition({ - required super.context, - required super.minPosition, - required super.maxPosition, - required this.initialPosition, - required super.physics, - super.gestureTamperer, - super.debugLabel, - }); - - /// {@template DraggableSheetPosition.initialPosition} - /// The initial position of the sheet. - /// {@endtemplate} - final SheetAnchor initialPosition; - - @override - void applyNewContentSize(Size contentSize) { - super.applyNewContentSize(contentSize); - if (maybePixels == null) { - setPixels(initialPosition.resolve(contentSize)); - } - } -} diff --git a/lib/src/draggable/draggable_sheet_position_scope.dart b/lib/src/draggable/draggable_sheet_position_scope.dart deleted file mode 100644 index 8d03013a..00000000 --- a/lib/src/draggable/draggable_sheet_position_scope.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:meta/meta.dart'; - -import '../foundation/sheet_context.dart'; -import '../foundation/sheet_position.dart'; -import '../foundation/sheet_position_scope.dart'; -import 'draggable_sheet_position.dart'; - -@internal -class DraggableSheetPositionScope - extends SheetPositionScope { - const DraggableSheetPositionScope({ - super.key, - super.controller, - super.isPrimary, - required super.context, - required this.initialPosition, - required super.minPosition, - required super.maxPosition, - required super.physics, - super.gestureTamperer, - this.debugLabel, - required super.child, - }); - - /// {@macro DraggableSheetPosition.initialPosition} - final SheetAnchor initialPosition; - - /// {@macro SheetPosition.debugLabel} - final String? debugLabel; - - @override - SheetPositionScopeState> createState() { - return _DraggableSheetPositionScopeState(); - } -} - -class _DraggableSheetPositionScopeState extends SheetPositionScopeState< - DraggableSheetPosition, DraggableSheetPositionScope> { - @override - bool shouldRebuildPosition(DraggableSheetPosition oldPosition) { - return widget.initialPosition != oldPosition.initialPosition || - widget.debugLabel != oldPosition.debugLabel || - super.shouldRebuildPosition(oldPosition); - } - - @override - DraggableSheetPosition buildPosition(SheetContext context) { - return DraggableSheetPosition( - context: context, - initialPosition: widget.initialPosition, - minPosition: widget.minPosition, - maxPosition: widget.maxPosition, - physics: widget.physics, - gestureTamperer: widget.gestureTamperer, - debugLabel: widget.debugLabel, - ); - } -} diff --git a/lib/src/foundation/keyboard_dismissible.dart b/lib/src/foundation/keyboard_dismissible.dart index 2ab5e9fb..2aefd3ea 100644 --- a/lib/src/foundation/keyboard_dismissible.dart +++ b/lib/src/foundation/keyboard_dismissible.dart @@ -12,7 +12,7 @@ import 'sheet_theme.dart'; /// ```dart /// return SheetKeyboardDismissible( /// dismissBehavior: const SheetKeyboardDismissBehavior.onDragDown(), -/// child: DraggableSheet( +/// child: Sheet( /// child: Container( /// color: Colors.white, /// width: double.infinity, diff --git a/lib/src/foundation/sheet_content_scaffold.dart b/lib/src/foundation/sheet_content_scaffold.dart index ce567eab..d1533744 100644 --- a/lib/src/foundation/sheet_content_scaffold.dart +++ b/lib/src/foundation/sheet_content_scaffold.dart @@ -3,7 +3,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; -import '../draggable/sheet_draggable.dart'; +import '../scrollable/sheet_draggable.dart'; import 'sheet_position.dart'; import 'sheet_position_scope.dart'; import 'sheet_viewport.dart'; diff --git a/lib/src/foundation/sheet_position_scope.dart b/lib/src/foundation/sheet_position_scope.dart index 068cd963..62be2445 100644 --- a/lib/src/foundation/sheet_position_scope.dart +++ b/lib/src/foundation/sheet_position_scope.dart @@ -239,7 +239,7 @@ abstract class SheetPositionScopeState +abstract class BasicNavigationSheetRoute extends PageRoute { - NavigationSheetRoute({super.settings}); + BasicNavigationSheetRoute({super.settings}); SheetPositionScopeKey get scopeKey => _scopeKey; late final SheetPositionScopeKey _scopeKey; @@ -46,7 +54,7 @@ abstract class NavigationSheetRoute throw FlutterError( 'A $SheetPositionScope that hosts a $NavigationSheetPosition ' 'is not found in the given context. This is likely because ' - 'this $NavigationSheetRoute is not a route of the navigator ' + 'this $BasicNavigationSheetRoute is not a route of the navigator ' 'enclosed by a $NavigationSheet.', ); } @@ -66,12 +74,12 @@ abstract class NavigationSheetRoute @override bool canTransitionFrom(TransitionRoute previousRoute) { - return previousRoute is NavigationSheetRoute; + return previousRoute is BasicNavigationSheetRoute; } @override bool canTransitionTo(TransitionRoute nextRoute) { - return nextRoute is NavigationSheetRoute; + return nextRoute is BasicNavigationSheetRoute; } @override @@ -144,7 +152,7 @@ class NavigationSheetRouteContent extends StatelessWidget { @override Widget build(BuildContext context) { assert(_debugAssertDependencies(context)); - final parentRoute = ModalRoute.of(context)! as NavigationSheetRoute; + final parentRoute = ModalRoute.of(context)! as BasicNavigationSheetRoute; final globalPosition = SheetPositionScope.of(context); final routeViewport = SheetContentViewport(child: child); @@ -203,7 +211,7 @@ class NavigationSheetRouteContent extends StatelessWidget { } final parentRoute = ModalRoute.of(context); - if (parentRoute is NavigationSheetRoute) { + if (parentRoute is BasicNavigationSheetRoute) { return true; } throw FlutterError( @@ -216,3 +224,208 @@ class NavigationSheetRouteContent extends StatelessWidget { return true; } } + +class _DraggableScrollableNavigationSheetRouteContent extends StatelessWidget { + const _DraggableScrollableNavigationSheetRouteContent({ + this.debugLabel, + required this.initialPosition, + required this.minPosition, + required this.maxPosition, + required this.physics, + required this.scrollConfiguration, + required this.dragConfiguration, + required this.child, + }); + + final String? debugLabel; + final SheetAnchor initialPosition; + final SheetAnchor minPosition; + final SheetAnchor maxPosition; + final SheetPhysics? physics; + final SheetScrollConfiguration? scrollConfiguration; + final SheetDragConfiguration? dragConfiguration; + final Widget child; + + @override + Widget build(BuildContext context) { + final theme = SheetTheme.maybeOf(context); + final gestureTamper = SheetGestureProxy.maybeOf(context); + + var physics = this.physics ?? theme?.physics ?? kDefaultSheetPhysics; + if (scrollConfiguration case final config?) { + physics = ScrollableSheetPhysics( + parent: physics, + maxScrollSpeedToInterrupt: + config.thresholdVelocityToInterruptBallisticScroll, + ); + } + + return NavigationSheetRouteContent( + scopeBuilder: (context, key, child) { + return ScrollableSheetPositionScope( + key: key, + context: context, + isPrimary: false, + initialPosition: initialPosition, + minPosition: minPosition, + maxPosition: maxPosition, + physics: physics, + gestureTamperer: gestureTamper, + child: child, + ); + }, + child: DraggableScrollableSheetContent( + scrollConfiguration: scrollConfiguration, + dragConfiguration: dragConfiguration, + child: child, + ), + ); + } +} + +class NavigationSheetRoute + extends BasicNavigationSheetRoute { + NavigationSheetRoute({ + super.settings, + this.maintainState = true, + this.scrollConfiguration, + this.dragConfiguration = const SheetDragConfiguration(), + this.transitionDuration = const Duration(milliseconds: 300), + this.initialPosition = const SheetAnchor.proportional(1), + this.minPosition = const SheetAnchor.proportional(1), + this.maxPosition = const SheetAnchor.proportional(1), + this.physics, + this.transitionsBuilder, + required this.builder, + }); + + final SheetAnchor initialPosition; + final SheetAnchor minPosition; + final SheetAnchor maxPosition; + final SheetPhysics? physics; + + @override + final bool maintainState; + + @override + final Duration transitionDuration; + + @override + final RouteTransitionsBuilder? transitionsBuilder; + + final WidgetBuilder builder; + + final SheetDragConfiguration? dragConfiguration; + final SheetScrollConfiguration? scrollConfiguration; + + @override + SheetPositionScopeKey createScopeKey() { + return SheetPositionScopeKey( + debugLabel: kDebugMode ? '$debugLabel:${describeIdentity(this)}' : null, + ); + } + + @override + Widget buildPage( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + ) { + return _DraggableScrollableNavigationSheetRouteContent( + debugLabel: '$NavigationSheetRoute(${settings.name})', + dragConfiguration: dragConfiguration, + scrollConfiguration: scrollConfiguration, + initialPosition: initialPosition, + minPosition: minPosition, + maxPosition: maxPosition, + physics: physics, + child: builder(context), + ); + } +} + +class NavigationSheetPage extends Page { + const NavigationSheetPage({ + super.key, + super.name, + super.arguments, + super.restorationId, + this.maintainState = true, + this.transitionDuration = const Duration(milliseconds: 300), + this.initialPosition = const SheetAnchor.proportional(1), + this.minPosition = const SheetAnchor.proportional(1), + this.maxPosition = const SheetAnchor.proportional(1), + this.dragConfiguration = const SheetDragConfiguration(), + this.scrollConfiguration, + this.physics, + this.transitionsBuilder, + required this.child, + }); + + /// {@macro flutter.widgets.ModalRoute.maintainState} + final bool maintainState; + + final Duration transitionDuration; + + final SheetAnchor initialPosition; + final SheetAnchor minPosition; + final SheetAnchor maxPosition; + + final SheetPhysics? physics; + + final RouteTransitionsBuilder? transitionsBuilder; + + final SheetDragConfiguration? dragConfiguration; + final SheetScrollConfiguration? scrollConfiguration; + + /// The content to be shown in the [Route] created by this page. + final Widget child; + + @override + Route createRoute(BuildContext context) { + return _PageBasedNavigationSheetRoute(page: this); + } +} + +class _PageBasedNavigationSheetRoute + extends BasicNavigationSheetRoute { + _PageBasedNavigationSheetRoute({ + required NavigationSheetPage page, + }) : super(settings: page); + + NavigationSheetPage get page => settings as NavigationSheetPage; + + @override + bool get maintainState => page.maintainState; + + @override + Duration get transitionDuration => page.transitionDuration; + + @override + RouteTransitionsBuilder? get transitionsBuilder => page.transitionsBuilder; + + @override + SheetPositionScopeKey createScopeKey() { + return SheetPositionScopeKey( + debugLabel: kDebugMode ? '$debugLabel:${describeIdentity(this)}' : null, + ); + } + + @override + Widget buildPage( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + ) { + return _DraggableScrollableNavigationSheetRouteContent( + debugLabel: '$NavigationSheetPage(${page.name})', + scrollConfiguration: page.scrollConfiguration, + dragConfiguration: page.dragConfiguration, + initialPosition: page.initialPosition, + minPosition: page.minPosition, + maxPosition: page.maxPosition, + physics: page.physics, + child: page.child, + ); + } +} diff --git a/lib/src/navigation/navigation_routes.dart b/lib/src/navigation/navigation_routes.dart deleted file mode 100644 index b6a8653b..00000000 --- a/lib/src/navigation/navigation_routes.dart +++ /dev/null @@ -1,367 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -import '../draggable/draggable_sheet_position.dart'; -import '../draggable/draggable_sheet_position_scope.dart'; -import '../draggable/sheet_draggable.dart'; -import '../foundation/sheet_gesture_tamperer.dart'; -import '../foundation/sheet_physics.dart'; -import '../foundation/sheet_position.dart'; -import '../foundation/sheet_position_scope.dart'; -import '../foundation/sheet_theme.dart'; -import '../scrollable/scrollable_sheet.dart'; -import '../scrollable/scrollable_sheet_position.dart'; -import '../scrollable/scrollable_sheet_position_scope.dart'; -import 'navigation_route.dart'; - -class _ScrollableNavigationSheetRouteContent extends StatelessWidget { - const _ScrollableNavigationSheetRouteContent({ - this.debugLabel, - required this.initialPosition, - required this.minPosition, - required this.maxPosition, - required this.physics, - required this.child, - }); - - final String? debugLabel; - final SheetAnchor initialPosition; - final SheetAnchor minPosition; - final SheetAnchor maxPosition; - final SheetPhysics? physics; - final Widget child; - - @override - Widget build(BuildContext context) { - final theme = SheetTheme.maybeOf(context); - final gestureTamper = SheetGestureProxy.maybeOf(context); - - return NavigationSheetRouteContent( - scopeBuilder: (context, key, child) { - return ScrollableSheetPositionScope( - key: key, - context: context, - isPrimary: false, - initialPosition: initialPosition, - minPosition: minPosition, - maxPosition: maxPosition, - physics: physics ?? theme?.physics ?? kDefaultSheetPhysics, - gestureTamperer: gestureTamper, - child: child, - ); - }, - child: ScrollableSheetContent(child: child), - ); - } -} - -class _DraggableNavigationSheetRouteContent extends StatelessWidget { - const _DraggableNavigationSheetRouteContent({ - this.debugLabel, - required this.initialPosition, - required this.minPosition, - required this.maxPosition, - required this.physics, - required this.child, - }); - - final String? debugLabel; - final SheetAnchor initialPosition; - final SheetAnchor minPosition; - final SheetAnchor maxPosition; - final SheetPhysics? physics; - final Widget child; - - @override - Widget build(BuildContext context) { - final theme = SheetTheme.maybeOf(context); - final physics = this.physics ?? theme?.physics ?? kDefaultSheetPhysics; - final gestureTamper = SheetGestureProxy.maybeOf(context); - - return NavigationSheetRouteContent( - scopeBuilder: (context, key, child) { - return DraggableSheetPositionScope( - key: key, - context: context, - isPrimary: false, - initialPosition: initialPosition, - minPosition: minPosition, - maxPosition: maxPosition, - physics: physics, - gestureTamperer: gestureTamper, - debugLabel: debugLabel, - child: child, - ); - }, - child: SheetDraggable(child: child), - ); - } -} - -class ScrollableNavigationSheetRoute - extends NavigationSheetRoute { - ScrollableNavigationSheetRoute({ - super.settings, - this.maintainState = true, - this.transitionDuration = const Duration(milliseconds: 300), - this.initialPosition = const SheetAnchor.proportional(1), - this.minPosition = const SheetAnchor.proportional(1), - this.maxPosition = const SheetAnchor.proportional(1), - this.physics, - this.transitionsBuilder, - required this.builder, - }); - - final SheetAnchor initialPosition; - final SheetAnchor minPosition; - final SheetAnchor maxPosition; - final SheetPhysics? physics; - - @override - final bool maintainState; - - @override - final Duration transitionDuration; - - @override - final RouteTransitionsBuilder? transitionsBuilder; - - final WidgetBuilder builder; - - @override - SheetPositionScopeKey createScopeKey() { - return SheetPositionScopeKey( - debugLabel: kDebugMode ? '$debugLabel:${describeIdentity(this)}' : null, - ); - } - - @override - Widget buildPage( - BuildContext context, - Animation animation, - Animation secondaryAnimation, - ) { - return _ScrollableNavigationSheetRouteContent( - debugLabel: '$ScrollableNavigationSheetRoute(${settings.name})', - initialPosition: initialPosition, - minPosition: minPosition, - maxPosition: maxPosition, - physics: physics, - child: builder(context), - ); - } -} - -class DraggableNavigationSheetRoute - extends NavigationSheetRoute { - DraggableNavigationSheetRoute({ - super.settings, - this.maintainState = true, - this.transitionDuration = const Duration(milliseconds: 300), - this.initialPosition = const SheetAnchor.proportional(1), - this.minPosition = const SheetAnchor.proportional(1), - this.maxPosition = const SheetAnchor.proportional(1), - this.physics, - this.transitionsBuilder, - required this.builder, - }); - - final SheetAnchor initialPosition; - final SheetAnchor minPosition; - final SheetAnchor maxPosition; - final SheetPhysics? physics; - - @override - final bool maintainState; - - @override - final Duration transitionDuration; - - @override - final RouteTransitionsBuilder? transitionsBuilder; - - final WidgetBuilder builder; - - @override - SheetPositionScopeKey createScopeKey() { - return SheetPositionScopeKey( - debugLabel: kDebugMode ? '$debugLabel:${describeIdentity(this)}' : null, - ); - } - - @override - Widget buildPage( - BuildContext context, - Animation animation, - Animation secondaryAnimation, - ) { - return _DraggableNavigationSheetRouteContent( - debugLabel: '$DraggableNavigationSheetRoute(${settings.name})', - initialPosition: initialPosition, - minPosition: minPosition, - maxPosition: maxPosition, - physics: physics, - child: builder(context), - ); - } -} - -class ScrollableNavigationSheetPage extends Page { - const ScrollableNavigationSheetPage({ - super.key, - super.name, - super.arguments, - super.restorationId, - this.maintainState = true, - this.transitionDuration = const Duration(milliseconds: 300), - this.initialPosition = const SheetAnchor.proportional(1), - this.minPosition = const SheetAnchor.proportional(1), - this.maxPosition = const SheetAnchor.proportional(1), - this.physics, - this.transitionsBuilder, - required this.child, - }); - - /// {@macro flutter.widgets.ModalRoute.maintainState} - final bool maintainState; - - final Duration transitionDuration; - - final SheetAnchor initialPosition; - final SheetAnchor minPosition; - final SheetAnchor maxPosition; - - final SheetPhysics? physics; - - final RouteTransitionsBuilder? transitionsBuilder; - - /// The content to be shown in the [Route] created by this page. - final Widget child; - - @override - Route createRoute(BuildContext context) { - return _PageBasedScrollableNavigationSheetRoute(page: this); - } -} - -class _PageBasedScrollableNavigationSheetRoute - extends NavigationSheetRoute { - _PageBasedScrollableNavigationSheetRoute({ - required ScrollableNavigationSheetPage page, - }) : super(settings: page); - - ScrollableNavigationSheetPage get page => - settings as ScrollableNavigationSheetPage; - - @override - bool get maintainState => page.maintainState; - - @override - Duration get transitionDuration => page.transitionDuration; - - @override - RouteTransitionsBuilder? get transitionsBuilder => page.transitionsBuilder; - - @override - SheetPositionScopeKey createScopeKey() { - return SheetPositionScopeKey( - debugLabel: kDebugMode ? '$debugLabel:${describeIdentity(this)}' : null, - ); - } - - @override - Widget buildPage( - BuildContext context, - Animation animation, - Animation secondaryAnimation, - ) { - return _ScrollableNavigationSheetRouteContent( - debugLabel: '$ScrollableNavigationSheetPage(${page.name})', - initialPosition: page.initialPosition, - minPosition: page.minPosition, - maxPosition: page.maxPosition, - physics: page.physics, - child: page.child, - ); - } -} - -class DraggableNavigationSheetPage extends Page { - const DraggableNavigationSheetPage({ - super.key, - super.name, - super.arguments, - super.restorationId, - this.maintainState = true, - this.transitionDuration = const Duration(milliseconds: 300), - this.initialPosition = const SheetAnchor.proportional(1), - this.minPosition = const SheetAnchor.proportional(1), - this.maxPosition = const SheetAnchor.proportional(1), - this.physics, - this.transitionsBuilder, - required this.child, - }); - - /// {@macro flutter.widgets.ModalRoute.maintainState} - final bool maintainState; - - final Duration transitionDuration; - - final SheetAnchor initialPosition; - final SheetAnchor minPosition; - final SheetAnchor maxPosition; - - final SheetPhysics? physics; - - final RouteTransitionsBuilder? transitionsBuilder; - - /// The content to be shown in the [Route] created by this page. - final Widget child; - - @override - Route createRoute(BuildContext context) { - return _PageBasedDraggableNavigationSheetRoute(page: this); - } -} - -class _PageBasedDraggableNavigationSheetRoute - extends NavigationSheetRoute { - _PageBasedDraggableNavigationSheetRoute({ - required DraggableNavigationSheetPage page, - }) : super(settings: page); - - DraggableNavigationSheetPage get page => - settings as DraggableNavigationSheetPage; - - @override - bool get maintainState => page.maintainState; - - @override - Duration get transitionDuration => page.transitionDuration; - - @override - RouteTransitionsBuilder? get transitionsBuilder => page.transitionsBuilder; - - @override - SheetPositionScopeKey createScopeKey() { - return SheetPositionScopeKey( - debugLabel: kDebugMode ? '$debugLabel:${describeIdentity(this)}' : null, - ); - } - - @override - Widget buildPage( - BuildContext context, - Animation animation, - Animation secondaryAnimation, - ) { - return _DraggableNavigationSheetRouteContent( - debugLabel: '$DraggableNavigationSheetPage(${page.name})', - initialPosition: page.initialPosition, - minPosition: page.minPosition, - maxPosition: page.maxPosition, - physics: page.physics, - child: page.child, - ); - } -} diff --git a/lib/src/navigation/navigation_sheet_activity.dart b/lib/src/navigation/navigation_sheet_activity.dart index a2b968cc..cb9bf3c3 100644 --- a/lib/src/navigation/navigation_sheet_activity.dart +++ b/lib/src/navigation/navigation_sheet_activity.dart @@ -21,8 +21,8 @@ class TransitionSheetActivity extends NavigationSheetActivity { required this.animationCurve, }); - final NavigationSheetRoute currentRoute; - final NavigationSheetRoute nextRoute; + final BasicNavigationSheetRoute currentRoute; + final BasicNavigationSheetRoute nextRoute; final Animation animation; final Curve animationCurve; late final Animation _curvedAnimation; @@ -73,7 +73,7 @@ class TransitionSheetActivity extends NavigationSheetActivity { class ProxySheetActivity extends NavigationSheetActivity { ProxySheetActivity({required this.route}); - final NavigationSheetRoute route; + final BasicNavigationSheetRoute route; @override SheetStatus get status => diff --git a/lib/src/navigation/navigation_sheet_position.dart b/lib/src/navigation/navigation_sheet_position.dart index bbf21948..638cb51c 100644 --- a/lib/src/navigation/navigation_sheet_position.dart +++ b/lib/src/navigation/navigation_sheet_position.dart @@ -32,12 +32,12 @@ class NavigationSheetPosition extends SheetPosition { _lastReportedTransition = transition; // TODO: Provide a way to customize animation curves. switch (transition) { - case NoTransition(:final NavigationSheetRoute currentRoute): + case NoTransition(:final BasicNavigationSheetRoute currentRoute): beginActivity(ProxySheetActivity(route: currentRoute)); case ForwardTransition( - :final NavigationSheetRoute originRoute, - :final NavigationSheetRoute destinationRoute, + :final BasicNavigationSheetRoute originRoute, + :final BasicNavigationSheetRoute destinationRoute, :final animation, ): beginActivity(TransitionSheetActivity( @@ -48,8 +48,8 @@ class NavigationSheetPosition extends SheetPosition { )); case BackwardTransition( - :final NavigationSheetRoute originRoute, - :final NavigationSheetRoute destinationRoute, + :final BasicNavigationSheetRoute originRoute, + :final BasicNavigationSheetRoute destinationRoute, :final animation, ): beginActivity(TransitionSheetActivity( @@ -60,8 +60,8 @@ class NavigationSheetPosition extends SheetPosition { )); case UserGestureTransition( - :final NavigationSheetRoute currentRoute, - :final NavigationSheetRoute previousRoute, + :final BasicNavigationSheetRoute currentRoute, + :final BasicNavigationSheetRoute previousRoute, :final animation, ): beginActivity(TransitionSheetActivity( @@ -82,7 +82,7 @@ class NavigationSheetPosition extends SheetPosition { @override void goIdle() { switch (_lastReportedTransition) { - case NoTransition(:final NavigationSheetRoute currentRoute): + case NoTransition(:final BasicNavigationSheetRoute currentRoute): beginActivity(ProxySheetActivity(route: currentRoute)); case _: super.goIdle(); @@ -149,7 +149,7 @@ class NavigationSheetPosition extends SheetPosition { final lastTransition = _lastReportedTransition; if (lastTransition is NoTransition && lastTransition.currentRoute.isFirst && - lastTransition.currentRoute is! NavigationSheetRoute) { + lastTransition.currentRoute is! BasicNavigationSheetRoute) { throw FlutterError( 'The first route in the navigator enclosed by a NavigationSheet ' 'must be a NavigationSheetRoute, but actually it is a ' @@ -168,27 +168,27 @@ class NavigationSheetPosition extends SheetPosition { switch ((_lastReportedTransition, activity)) { // Allowed patterns. case ( - NoTransition(currentRoute: NavigationSheetRoute()), + NoTransition(currentRoute: BasicNavigationSheetRoute()), ProxySheetActivity(), ): case ( ForwardTransition( - originRoute: NavigationSheetRoute(), - destinationRoute: NavigationSheetRoute(), + originRoute: BasicNavigationSheetRoute(), + destinationRoute: BasicNavigationSheetRoute(), ), TransitionSheetActivity(), ): case ( BackwardTransition( - originRoute: NavigationSheetRoute(), - destinationRoute: NavigationSheetRoute(), + originRoute: BasicNavigationSheetRoute(), + destinationRoute: BasicNavigationSheetRoute(), ), TransitionSheetActivity(), ): case ( UserGestureTransition( - currentRoute: NavigationSheetRoute(), - previousRoute: NavigationSheetRoute(), + currentRoute: BasicNavigationSheetRoute(), + previousRoute: BasicNavigationSheetRoute(), ), TransitionSheetActivity(), ): diff --git a/lib/src/scrollable/scrollable.dart b/lib/src/scrollable/scrollable.dart index e6518d4d..df699d7c 100644 --- a/lib/src/scrollable/scrollable.dart +++ b/lib/src/scrollable/scrollable.dart @@ -1,2 +1,2 @@ -export 'scrollable_sheet.dart' show ScrollableSheet; +export 'scrollable_sheet.dart' show Sheet, SheetScrollConfiguration; export 'sheet_scrollable.dart' show SheetScrollable; diff --git a/lib/src/scrollable/scrollable_sheet.dart b/lib/src/scrollable/scrollable_sheet.dart index 1be1db79..9f3cfc63 100644 --- a/lib/src/scrollable/scrollable_sheet.dart +++ b/lib/src/scrollable/scrollable_sheet.dart @@ -10,17 +10,40 @@ import '../foundation/sheet_physics.dart'; import '../foundation/sheet_position.dart'; import '../foundation/sheet_theme.dart'; import '../foundation/sheet_viewport.dart'; +import 'scrollable_sheet_physics.dart'; import 'scrollable_sheet_position_scope.dart'; +import 'sheet_draggable.dart'; import 'sheet_scrollable.dart'; -class ScrollableSheet extends StatefulWidget { - const ScrollableSheet({ +@immutable +class SheetScrollConfiguration { + const SheetScrollConfiguration({ + this.thresholdVelocityToInterruptBallisticScroll = double.infinity, + }); + + // TODO: Come up with a better name. + final double thresholdVelocityToInterruptBallisticScroll; +} + +@immutable +class SheetDragConfiguration { + const SheetDragConfiguration({ + this.hitTestBehavior = HitTestBehavior.translucent, + }); + + final HitTestBehavior hitTestBehavior; +} + +class Sheet extends StatefulWidget { + const Sheet({ super.key, this.initialPosition = const SheetAnchor.proportional(1), this.minPosition = const SheetAnchor.proportional(1), this.maxPosition = const SheetAnchor.proportional(1), this.physics, this.controller, + this.scrollConfiguration, + this.dragConfiguration = const SheetDragConfiguration(), required this.child, }); @@ -40,24 +63,36 @@ class ScrollableSheet extends StatefulWidget { /// An object that can be used to control and observe the sheet height. final SheetController? controller; + final SheetScrollConfiguration? scrollConfiguration; + + final SheetDragConfiguration? dragConfiguration; + /// The content of the sheet. final Widget child; @override - State createState() => _ScrollableSheetState(); + State createState() => _SheetState(); } -class _ScrollableSheetState extends State +class _SheetState extends State with TickerProviderStateMixin, SheetContextStateMixin { @override Widget build(BuildContext context) { final theme = SheetTheme.maybeOf(context); - final physics = widget.physics ?? theme?.physics ?? kDefaultSheetPhysics; + var physics = widget.physics ?? theme?.physics ?? kDefaultSheetPhysics; final gestureTamper = SheetGestureProxy.maybeOf(context); final controller = widget.controller ?? SheetControllerScope.maybeOf(context); final viewport = SheetViewport.of(context); + if (widget.scrollConfiguration case final config?) { + physics = ScrollableSheetPhysics( + parent: physics, + maxScrollSpeedToInterrupt: + config.thresholdVelocityToInterruptBallisticScroll, + ); + } + return ScrollableSheetPositionScope( context: this, key: viewport.positionOwnerKey, @@ -69,39 +104,52 @@ class _ScrollableSheetState extends State gestureTamperer: gestureTamper, debugLabel: kDebugMode ? 'ScrollableSheet' : null, child: SheetContentViewport( - child: ScrollableSheetContent(child: widget.child), + child: DraggableScrollableSheetContent( + scrollConfiguration: widget.scrollConfiguration, + dragConfiguration: widget.dragConfiguration, + child: widget.child, + ), ), ); } } @internal -class ScrollableSheetContent extends StatelessWidget { - const ScrollableSheetContent({ +class DraggableScrollableSheetContent extends StatelessWidget { + const DraggableScrollableSheetContent({ super.key, - this.debugLabel, - this.keepScrollOffset = true, - this.initialScrollOffset = 0, + required this.scrollConfiguration, + required this.dragConfiguration, required this.child, }); - final String? debugLabel; - final bool keepScrollOffset; - final double initialScrollOffset; + final SheetScrollConfiguration? scrollConfiguration; + + final SheetDragConfiguration? dragConfiguration; + final Widget child; @override Widget build(BuildContext context) { - return SheetScrollable( - debugLabel: debugLabel, - keepScrollOffset: keepScrollOffset, - initialScrollOffset: initialScrollOffset, - builder: (context, controller) { - return PrimaryScrollController( - controller: controller, - child: child, - ); - }, - ); + var result = child; + if (dragConfiguration case final config?) { + result = SheetDraggable( + behavior: config.hitTestBehavior, + child: result, + ); + } + if (scrollConfiguration != null) { + final child = result; + result = SheetScrollable( + builder: (context, controller) { + return PrimaryScrollController( + controller: controller, + child: child, + ); + }, + ); + } + + return result; } } diff --git a/lib/src/scrollable/scrollable_sheet_activity.dart b/lib/src/scrollable/scrollable_sheet_activity.dart index 1bc15f9c..8d1a3369 100644 --- a/lib/src/scrollable/scrollable_sheet_activity.dart +++ b/lib/src/scrollable/scrollable_sheet_activity.dart @@ -10,6 +10,7 @@ import '../foundation/sheet_drag.dart'; import '../foundation/sheet_status.dart'; import '../internal/float_comp.dart'; import 'scrollable_sheet.dart'; +import 'scrollable_sheet_physics.dart'; import 'scrollable_sheet_position.dart'; import 'sheet_content_scroll_activity.dart'; import 'sheet_content_scroll_position.dart'; @@ -136,7 +137,7 @@ abstract class ScrollableSheetActivity } /// A [SheetActivity] that either scrolls a scrollable content of -/// a [ScrollableSheet] or drags the sheet itself as the user drags +/// a [Sheet] or drags the sheet itself as the user drags /// their finger across the screen. /// /// The [scrollPosition], which is associated with the scrollable content, @@ -152,7 +153,7 @@ class DragScrollDrivenSheetActivity extends ScrollableSheetActivity scrollPosition.axisDirection != AxisDirection.up) { throw FlutterError( 'The axis direction of the scroll position associated with a ' - '$ScrollableSheet must be either $AxisDirection.down ' + '$Sheet must be either $AxisDirection.down ' 'or $AxisDirection.up, but the provided scroll position has an ' 'axis direction of ${scrollPosition.axisDirection}.', ); @@ -229,7 +230,7 @@ class DragScrollDrivenSheetActivity extends ScrollableSheetActivity } /// A [SheetActivity] that animates either a scrollable content of -/// a [ScrollableSheet] or the sheet itself based on a physics simulation. +/// a [Sheet] or the sheet itself based on a physics simulation. /// /// The [scrollPosition], which is associated with the scrollable content, /// must have a [SheetContentBallisticScrollActivity] as its activity throughout @@ -273,16 +274,18 @@ class BallisticScrollDrivenSheetActivity extends ScrollableSheetActivity return; } - final scrollExtentBefore = scrollPosition.extentBefore; - final scrollExtentAfter = scrollPosition.extentAfter; - final shouldInterruptBallisticScroll = - ((cmp.isApprox(scrollExtentBefore, 0) && velocity < 0) || - (cmp.isApprox(scrollExtentAfter, 0) && velocity > 0)) && - owner.physics - .shouldInterruptBallisticScroll(velocity, owner.snapshot); - - if (shouldInterruptBallisticScroll) { - _end(); + // TODO: Refactor this! + if (owner.physics case final ScrollableSheetPhysics scrollAwarePhysics) { + final scrollExtentBefore = scrollPosition.extentBefore; + final scrollExtentAfter = scrollPosition.extentAfter; + final shouldInterruptBallisticScroll = + ((cmp.isApprox(scrollExtentBefore, 0) && velocity < 0) || + (cmp.isApprox(scrollExtentAfter, 0) && velocity > 0)) && + scrollAwarePhysics.shouldInterruptBallisticScroll( + velocity, owner.snapshot); + if (shouldInterruptBallisticScroll) { + _end(); + } } } diff --git a/lib/src/scrollable/scrollable_sheet_physics.dart b/lib/src/scrollable/scrollable_sheet_physics.dart index 0c07f994..ee1bae20 100644 --- a/lib/src/scrollable/scrollable_sheet_physics.dart +++ b/lib/src/scrollable/scrollable_sheet_physics.dart @@ -4,6 +4,7 @@ import 'package:meta/meta.dart'; import '../foundation/sheet_physics.dart'; import '../foundation/sheet_position.dart'; +// TODO: Rename to ScrollAwareSheetPhysics. @internal class ScrollableSheetPhysics extends SheetPhysics with SheetPhysicsMixin { const ScrollableSheetPhysics({ diff --git a/lib/src/scrollable/scrollable_sheet_position.dart b/lib/src/scrollable/scrollable_sheet_position.dart index bf475635..4516d99e 100644 --- a/lib/src/scrollable/scrollable_sheet_position.dart +++ b/lib/src/scrollable/scrollable_sheet_position.dart @@ -22,20 +22,16 @@ class ScrollableSheetPosition extends SheetPosition required this.initialPosition, required super.minPosition, required super.maxPosition, - // TODO: Change the type to `ScrollAwareSheetPhysics`. - required SheetPhysics physics, + required super.physics, super.gestureTamperer, super.debugLabel, - }) : super(physics: ScrollableSheetPhysics.wrap(physics)); + }); /// {@template ScrollableSheetPosition.initialPosition} /// The initial position of the sheet. /// {@endtemplate} final SheetAnchor initialPosition; - @override - ScrollableSheetPhysics get physics => super.physics as ScrollableSheetPhysics; - // TODO: Stop scroll animations when a non-scrollable activity starts. final _scrollPositions = HashSet(); diff --git a/lib/src/scrollable/sheet_content_scroll_position.dart b/lib/src/scrollable/sheet_content_scroll_position.dart index 25ba89d8..d72526ae 100644 --- a/lib/src/scrollable/sheet_content_scroll_position.dart +++ b/lib/src/scrollable/sheet_content_scroll_position.dart @@ -45,7 +45,7 @@ abstract class SheetContentScrollPositionOwner { }); } -/// A [ScrollPosition] for a scrollable content in a [ScrollableSheet]. +/// A [ScrollPosition] for a scrollable content in a [Sheet]. @internal // TODO: Rename to SheetScrollPosition. class SheetContentScrollPosition extends ScrollPositionWithSingleContext { diff --git a/lib/src/draggable/sheet_draggable.dart b/lib/src/scrollable/sheet_draggable.dart similarity index 82% rename from lib/src/draggable/sheet_draggable.dart rename to lib/src/scrollable/sheet_draggable.dart index 9d326bcd..65cc9cdd 100644 --- a/lib/src/draggable/sheet_draggable.dart +++ b/lib/src/scrollable/sheet_draggable.dart @@ -4,22 +4,13 @@ import 'package:flutter/widgets.dart'; import '../foundation/sheet_position.dart'; import '../foundation/sheet_position_scope.dart'; -import '../scrollable/scrollable_sheet.dart'; -import 'draggable_sheet.dart'; +import 'scrollable_sheet.dart'; /// A widget that makes its child as a drag-handle for a sheet. /// /// Typically, this widget is used when placing non-scrollable widget(s) -/// in a [ScrollableSheet], since it only works with scrollable widgets, +/// in a [Sheet], since it only works with scrollable widgets, /// so you can't drag the sheet by touching a non-scrollable area. -/// -/// Note that [SheetDraggable] is not needed when using [DraggableSheet] -/// since it implicitly wraps the child widget with [SheetDraggable]. -/// -/// See also: -/// - [A tutorial](https://github.com/fujidaiti/smooth_sheets/blob/main/cookbook/lib/tutorial/sheet_draggable.dart), -/// in which a [SheetDraggable] is used to create a drag-handle for -/// a [ScrollableSheet]. class SheetDraggable extends StatefulWidget { /// Creates a drag-handle for a sheet. /// diff --git a/test/draggable/draggable_sheet_test.dart b/test/draggable/draggable_sheet_test.dart deleted file mode 100644 index 0eb7aab3..00000000 --- a/test/draggable/draggable_sheet_test.dart +++ /dev/null @@ -1,197 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:smooth_sheets/smooth_sheets.dart'; -import 'package:smooth_sheets/src/foundation/sheet_controller.dart'; - -import '../src/keyboard_inset_simulation.dart'; - -class _TestApp extends StatelessWidget { - const _TestApp({ - this.useMaterial = false, - required this.child, - }); - - final bool useMaterial; - final Widget child; - - @override - Widget build(BuildContext context) { - if (useMaterial) { - return MaterialApp( - home: child, - ); - } else { - return Directionality( - textDirection: TextDirection.ltr, - child: MediaQuery( - data: const MediaQueryData(), - child: child, - ), - ); - } - } -} - -class _TestSheetContent extends StatelessWidget { - const _TestSheetContent({ - this.height = 500, - }); - - final double? height; - - @override - Widget build(BuildContext context) { - return Container( - height: height, - width: double.infinity, - color: Colors.white, - ); - } -} - -void main() { - testWidgets('Inherited controller should be attached', (tester) async { - final controller = SheetController(); - await tester.pumpWidget( - SheetControllerScope( - controller: controller, - child: const _TestApp( - child: SheetViewport( - child: DraggableSheet( - child: _TestSheetContent(), - ), - ), - ), - ), - ); - - expect(controller.hasClient, isTrue, - reason: 'The controller should have a client.'); - }); - - // Regression test for https://github.com/fujidaiti/smooth_sheets/issues/14 - testWidgets('Opening keyboard does not interrupt sheet animation', - (tester) async { - final controller = SheetController(); - final sheetKey = GlobalKey(); - final keyboardSimulationKey = GlobalKey(); - - await tester.pumpWidget( - _TestApp( - useMaterial: true, - child: KeyboardInsetSimulation( - key: keyboardSimulationKey, - keyboardHeight: 200, - child: SheetViewport( - child: DraggableSheet( - key: sheetKey, - controller: controller, - minPosition: const SheetAnchor.pixels(200), - initialPosition: const SheetAnchor.pixels(200), - child: const Material( - child: _TestSheetContent( - height: 500, - ), - ), - ), - ), - ), - ), - ); - - expect(controller.metrics.pixels, 200, - reason: 'The sheet should be at the initial position.'); - expect(controller.metrics.minPixels < controller.metrics.maxPixels, isTrue, - reason: 'The sheet should be draggable.'); - - // Start animating the sheet to the max position. - unawaited( - controller.animateTo( - const SheetAnchor.proportional(1), - duration: const Duration(milliseconds: 250), - ), - ); - // Then, show the keyboard while the animation is running. - unawaited( - keyboardSimulationKey.currentState! - .showKeyboard(const Duration(milliseconds: 250)), - ); - await tester.pumpAndSettle(); - expect(MediaQuery.viewInsetsOf(sheetKey.currentContext!).bottom, 200, - reason: 'The keyboard should be fully shown.'); - expect( - controller.metrics.pixels, - controller.metrics.maxPixels, - reason: 'After the keyboard is fully shown, ' - 'the entire sheet should also be visible.', - ); - }); - - group('SheetKeyboardDismissible', () { - late FocusNode focusNode; - late ScrollController scrollController; - late Widget testWidget; - - setUp(() { - focusNode = FocusNode(); - scrollController = ScrollController(); - testWidget = _TestApp( - useMaterial: true, - child: SheetKeyboardDismissible( - dismissBehavior: const SheetKeyboardDismissBehavior.onDrag( - isContentScrollAware: true, - ), - child: SheetViewport( - child: DraggableSheet( - child: Material( - child: TextField( - focusNode: focusNode, - scrollController: scrollController, - maxLines: 2, - ), - ), - ), - ), - ), - ); - }); - - tearDown(() { - focusNode.dispose(); - scrollController.dispose(); - }); - - testWidgets('should dismiss the keyboard when dragging', (tester) async { - await tester.pumpWidget(testWidget); - - final textField = find.byType(TextField); - await tester.showKeyboard(textField); - expect(focusNode.hasFocus, isTrue, - reason: 'The keyboard should be shown.'); - - await tester.drag(textField, const Offset(0, -40)); - await tester.pumpAndSettle(); - expect(focusNode.hasFocus, isFalse, - reason: 'Downward dragging should dismiss the keyboard.'); - }); - - testWidgets('should dismiss the keyboard when scrolling', (tester) async { - await tester.pumpWidget(testWidget); - - final textField = find.byType(TextField); - await tester.enterText(textField, 'Hello, world! ' * 100); - await tester.pumpAndSettle(); - expect(focusNode.hasFocus, isTrue, - reason: 'The keyboard should be shown.'); - expect(scrollController.position.extentBefore, greaterThan(0), - reason: 'The text field should be able to scroll downwards.'); - - await tester.drag(textField, const Offset(0, 40)); - await tester.pumpAndSettle(); - expect(focusNode.hasFocus, isFalse, - reason: 'Downward dragging should dismiss the keyboard.'); - }); - }); -} diff --git a/test/foundation/sheet_notification_test.dart b/test/foundation/sheet_notification_test.dart index 80896658..f5693ff3 100644 --- a/test/foundation/sheet_notification_test.dart +++ b/test/foundation/sheet_notification_test.dart @@ -3,13 +3,13 @@ import 'dart:async'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:smooth_sheets/src/draggable/draggable_sheet.dart'; import 'package:smooth_sheets/src/foundation/sheet_controller.dart'; import 'package:smooth_sheets/src/foundation/sheet_notification.dart'; import 'package:smooth_sheets/src/foundation/sheet_physics.dart'; import 'package:smooth_sheets/src/foundation/sheet_position.dart'; import 'package:smooth_sheets/src/foundation/sheet_status.dart'; import 'package:smooth_sheets/src/foundation/sheet_viewport.dart'; +import 'package:smooth_sheets/src/scrollable/scrollable_sheet.dart'; void main() { testWidgets( @@ -25,7 +25,7 @@ void main() { return false; }, child: SheetViewport( - child: DraggableSheet( + child: Sheet( minPosition: const SheetAnchor.pixels(0), // Disable the snapping effect physics: const ClampingSheetPhysics(), @@ -170,7 +170,7 @@ void main() { return false; }, child: SheetViewport( - child: DraggableSheet( + child: Sheet( controller: controller, minPosition: const SheetAnchor.pixels(0), // Disable the snapping effect @@ -248,7 +248,7 @@ void main() { return false; }, child: SheetViewport( - child: DraggableSheet( + child: Sheet( // Make sure the sheet can't be dragged minPosition: const SheetAnchor.proportional(1), maxPosition: const SheetAnchor.proportional(1), @@ -325,7 +325,7 @@ void main() { reportedNotifications.add(notification); return false; }, - child: DraggableSheet( + child: Sheet( minPosition: const SheetAnchor.pixels(0), // Disable the snapping effect physics: const ClampingSheetPhysics(), diff --git a/test/modal/modal_sheet_test.dart b/test/modal/modal_sheet_test.dart index dce50e26..91229abe 100644 --- a/test/modal/modal_sheet_test.dart +++ b/test/modal/modal_sheet_test.dart @@ -84,7 +84,7 @@ void main() { swipeDismissSensitivity: sensitivity, builder: (context) { return SheetViewport( - child: DraggableSheet( + child: Sheet( child: Container( key: const Key('sheet'), color: Colors.white, @@ -219,7 +219,7 @@ void main() { swipeDismissible: true, builder: (context) { return SheetViewport( - child: DraggableSheet( + child: Sheet( child: PopScope( canPop: false, onPopInvoked: (didPop) { @@ -282,7 +282,7 @@ void main() { transitionCurve: Curves.easeInOut, builder: (context) { return SheetViewport( - child: DraggableSheet( + child: Sheet( child: PopScope( canPop: false, onPopInvoked: (didPop) async { @@ -428,7 +428,7 @@ void main() { ModalRoute.of(context)! as ModalSheetRouteMixin; return SheetViewport( - child: DraggableSheet( + child: Sheet( child: Container( key: const Key('sheet'), color: Colors.white, diff --git a/test/navigation/navigation_sheet_test.dart b/test/navigation/navigation_sheet_test.dart index 3c54dc64..ebcd79f0 100644 --- a/test/navigation/navigation_sheet_test.dart +++ b/test/navigation/navigation_sheet_test.dart @@ -130,7 +130,7 @@ class _TestDraggablePageWidget extends StatelessWidget { Duration transitionDuration = const Duration(milliseconds: 300), SheetPhysics? physics, }) { - return DraggableNavigationSheetRoute( + return NavigationSheetRoute( physics: physics, initialPosition: initialPosition, minPosition: minPosition, @@ -210,7 +210,7 @@ class _TestScrollablePageWidget extends StatelessWidget { SheetPhysics? physics, void Function(int index)? onTapItem, }) { - return ScrollableNavigationSheetRoute( + return NavigationSheetRoute( physics: physics, initialPosition: initialPosition, minPosition: minPosition, @@ -379,7 +379,7 @@ void main() { 'Works with DropdownButton without crashing', (tester) async { String? selectedOption = 'Option 1'; - final routeWithDropdownButton = DraggableNavigationSheetRoute( + final routeWithDropdownButton = NavigationSheetRoute( builder: (context) { return Scaffold( body: Center( @@ -557,7 +557,7 @@ void main() { setUp(() { focusNode = FocusNode(); scrollController = ScrollController(); - final routeWithTextField = DraggableNavigationSheetRoute( + final routeWithTextField = NavigationSheetRoute( builder: (context) { return Material( child: TextField( diff --git a/test/scrollable/scrollable_sheet_test.dart b/test/scrollable/scrollable_sheet_test.dart index d969420b..dc719fff 100644 --- a/test/scrollable/scrollable_sheet_test.dart +++ b/test/scrollable/scrollable_sheet_test.dart @@ -80,7 +80,8 @@ void main() { controller: controller, child: const _TestApp( child: SheetViewport( - child: ScrollableSheet( + child: Sheet( + scrollConfiguration: SheetScrollConfiguration(), child: _TestSheetContent(), ), ), @@ -106,9 +107,10 @@ void main() { key: keyboardSimulationKey, keyboardHeight: 200, child: SheetViewport( - child: ScrollableSheet( + child: Sheet( key: sheetKey, controller: controller, + scrollConfiguration: const SheetScrollConfiguration(), minPosition: const SheetAnchor.pixels(200), initialPosition: const SheetAnchor.pixels(200), child: const _TestSheetContent(height: 500), @@ -158,7 +160,8 @@ void main() { await tester.pumpWidget( _TestApp( child: SheetViewport( - child: ScrollableSheet( + child: Sheet( + scrollConfiguration: const SheetScrollConfiguration(), controller: controller, child: Builder( builder: (context) { @@ -216,7 +219,8 @@ void main() { await tester.pumpWidget( _TestApp( child: SheetViewport( - child: ScrollableSheet( + child: Sheet( + scrollConfiguration: const SheetScrollConfiguration(), child: Builder( builder: (context) { // TODO(fujita): Refactor this line after #116 is resolved. @@ -291,7 +295,8 @@ void main() { isContentScrollAware: true, ), child: SheetViewport( - child: ScrollableSheet( + child: Sheet( + scrollConfiguration: const SheetScrollConfiguration(), child: Material( child: Column( children: [ @@ -357,7 +362,8 @@ void main() { setUp(() { testWidget = SheetViewport( - child: ScrollableSheet( + child: Sheet( + scrollConfiguration: const SheetScrollConfiguration(), child: Builder( builder: (context) { scrollController = PrimaryScrollController.of(context);