diff --git a/.github/workflows/wtf_sliding_sheet.yml b/.github/workflows/wtf_sliding_sheet.yml new file mode 100644 index 0000000..8443eb9 --- /dev/null +++ b/.github/workflows/wtf_sliding_sheet.yml @@ -0,0 +1,28 @@ +name: WTF Sliding Sheet CI/CD + +on: + push: + branches: + pull_request: + branches: + +jobs: + analyze: + name: Analyze and pana check + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - uses: subosito/flutter-action@v2 + with: + flutter-version: '3.x' + channel: 'stable' + + - name: Analyzing + run: flutter analyze + + - name: Pana check + run: | + dart pub global activate pana + pana --exit-code-threshold 10 --no-warning diff --git a/CHANGELOG.md b/CHANGELOG.md index 633b138..5a25251 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,10 @@ +## 1.1.0 +* **Added** `customBuilder` [#15](https://github.com/What-the-Flutter/wtf_sliding_sheet/pull/15) + ## 1.0.0 -* **Fixed** [#7](https://github.com/flutterwtf/wtf_sliding_sheet/pull/7) -* **Added** flutter 3.10 support -* **Added** dart 3 support +* **Fixed** [#7](https://github.com/What-the-Flutter/wtf_sliding_sheet/pull/7) +* **Added** Flutter 3.10 support +* **Added** Dart 3 support ## 0.6.1 * **Migrate** to a new repository name @@ -48,4 +51,4 @@ * **Changed** the implemenation for bottom sheets. ## 0.1.0 -* Initial release \ No newline at end of file +* Initial release diff --git a/README.md b/README.md index e2a8d26..445c038 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,33 @@ # Sliding Sheet +

+ + What the Flutter + +

+ +

+

Crafted with passion by + + What the Flutter + 🦜 +

+

+ +

+ + Pub + + + Build Status + + + CodeFactor + +

+ +--- + A widget that can be dragged and scrolled in a single gesture and snapped to a list of extents. @@ -16,7 +44,7 @@ Add it to your `pubspec.yaml` file: ```yaml dependencies: - wtf_sliding_sheet: ^1.0.0 + wtf_sliding_sheet: ^1.1.0 ``` Install packages from the command line @@ -280,3 +308,20 @@ return SheetListenerBuilder( The [example](https://github.com/What-the-Flutter/wtf_sliding_sheet/blob/master/example/lib/main.dart) for instance decreases the corner radius of the `SlidingSheet` as it gets dragged to the top and increases the headers top padding by the status bar height. Also, when content gets scrolled under the header it elevates. Because these are common Material behaviors, `SlidingSheet` supports those out of the box, which can be achieved by setting the `avoidStatusBar` field to `true`, `cornerRadiusOnFullscreen` to `0` and `liftOnScrollHeaderElevation` to the elevation. + +### Use cases + +**Scrollable content** + +![lifely_stories_bottom_sheet](https://github.com/What-the-Flutter/wtf_sliding_sheet/assets/94079414/67c4d178-02a5-4a3c-a447-6baadd1d144c) + +The GIF above showcases the user experience when dealing with scrollable content. With our package, you can easily implement a smooth and intuitive sliding sheet that seamlessly integrates with your application. + +--- + +**Confirmation sheet** + +![lifely_confimation_sheet](https://github.com/What-the-Flutter/wtf_sliding_sheet/assets/94079414/bdf1865b-73f2-4f80-a82b-4abe82a3705f) + +One of the simplest use cases is implementing easy confirmations within your application. The GIF above demonstrates it in action. With our package, you can effortlessly implement a sliding sheet that provides a smooth and intuitive confirmation experience. +Whether it's deleting a file, confirming a purchase, or accepting a prompt, the sliding sheet offers a straightforward and user-friendly approach. diff --git a/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt index e793a00..02cd738 100644 --- a/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt +++ b/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt @@ -2,5 +2,4 @@ package com.example.example import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity() { -} +class MainActivity: FlutterActivity(); diff --git a/example/lib/main.dart b/example/lib/main.dart index fc541df..750d3be 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -493,7 +493,7 @@ class _ExampleState extends State { isDismissable: isDismissable, dismissOnBackdropTap: true, isBackdropInteractable: true, - onDismissPrevented: (backButton, backDrop) async { + onDismissPrevented: ({required backButton, required backDrop}) async { HapticFeedback.heavyImpact(); if (backButton || backDrop) { diff --git a/example/pubspec.lock b/example/pubspec.lock index 4f0b620..0517915 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -212,10 +212,10 @@ packages: dependency: "direct main" description: name: wtf_sliding_sheet - sha256: "518772ea0fb602c7e59a84b0ac8269d8e53a3abd0c7a650ca3fe85090aa13578" + sha256: "363386c648af7be498b017f77d8bf5abdf3c502e5ab8943391835ea1549ce122" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "1.0.0" sdks: dart: ">=3.0.0-0 <4.0.0" flutter: ">=2.5.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 9f0b936..39a7d08 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -13,8 +13,7 @@ dependencies: sdk: flutter material_design_icons_flutter: ^6.0.7096 - - wtf_sliding_sheet: ^0.6.1 + wtf_sliding_sheet: ^1.0.0 dev_dependencies: flutter_lints: ^2.0.1 diff --git a/lib/src/sheet.dart b/lib/src/sheet.dart index 6e0db7c..5c8c098 100644 --- a/lib/src/sheet.dart +++ b/lib/src/sheet.dart @@ -15,17 +15,24 @@ part 'sheet_controller.dart'; part 'sheet_dialog.dart'; part 'sheet_state.dart'; -/// Widget for building sheet +/// Widget for building sheet. typedef SheetBuilder = Widget Function(BuildContext context, SheetState state); -/// Callback for changing state of the sheet +/// Widget for building sheet with custom scroll view. +typedef CustomSheetBuilder = Widget Function( + BuildContext context, + ScrollController controller, + SheetState state, +); + +/// Callback for changing state of the sheet. typedef SheetListener = void Function(SheetState state); -/// Callback prevented dismiss the dialog +/// Callback prevented dismiss the dialog. typedef OnDismissPreventedCallback = void Function( {required bool backButton, required bool backDrop}); -/// Callback for opening sheet +/// Callback for opening sheet. typedef OnOpenCallback = void Function(); /// A widget that can be dragged and scrolled in a single gesture and snapped @@ -37,7 +44,13 @@ class SlidingSheet extends StatefulWidget { /// The builder for the main content of the sheet that will be scrolled if /// the content is bigger than the height that the sheet can expand to. /// {@endtemplate} - final SheetBuilder builder; + final SheetBuilder? builder; + + /// {@template sliding_sheet.customBuilder} + /// Allows you to supply your own custom sroll view. Useful for infinite lists + /// that cannot be shrinkWrapped like long lists. + /// {@endtemplate} + final CustomSheetBuilder? customBuilder; /// {@template sliding_sheet.headerBuilder} /// The builder for a header that will be displayed at the top of the sheet @@ -251,6 +264,11 @@ class SlidingSheet extends StatefulWidget { /// The `builder` callback is used to build the main content of the sheet that /// will be scrolled if the content is bigger than the height that the sheet can expand to. /// + /// The `customBuilder` callback is used to build such the main content like + /// infinite lists that cannot be shrinkWrapped. + /// + /// Either the `builder` or the `customBuilder` must be specified. + /// /// The `headerBuilder` and `footerBuilder` can be used to build persistent widget on top /// and bottom respectively, that wont be scrolled but will delegate the interactions on /// them to the sheet. @@ -275,7 +293,8 @@ class SlidingSheet extends StatefulWidget { /// width is bigger than the `maxWidth` of the sheet. SlidingSheet({ Key? key, - required SheetBuilder builder, + SheetBuilder? builder, + CustomSheetBuilder? customBuilder, SheetBuilder? headerBuilder, SheetBuilder? footerBuilder, SnapSpec snapSpec = const SnapSpec(), @@ -310,6 +329,7 @@ class SlidingSheet extends StatefulWidget { }) : this._( key: key, builder: builder, + customBuilder: customBuilder, headerBuilder: headerBuilder, footerBuilder: footerBuilder, snapSpec: snapSpec, @@ -346,6 +366,7 @@ class SlidingSheet extends StatefulWidget { SlidingSheet._({ Key? key, required this.builder, + required this.customBuilder, required this.headerBuilder, required this.footerBuilder, required this.snapSpec, @@ -379,7 +400,12 @@ class SlidingSheet extends StatefulWidget { this.isDismissable = true, this.onDismissPrevented, this.onOpen, - }) : assert( + }) : + + /// Checking whether one of the `builder` and `customBuilder` is specified. + assert(builder != null || customBuilder != null), + assert(builder == null || customBuilder == null), + assert( snapSpec.snappings.length >= 2, 'There must be at least two snapping extents to snap in between.', ), @@ -427,8 +453,10 @@ class _SlidingSheetState extends State SheetController? sheetController; late _SlidingSheetScrollController controller; + bool get isCustom => widget.customBuilder != null; + // Whether the sheet has drawn its first frame. - bool get isLaidOut => availableHeight > 0 && childHeight > 0; + bool isLaidOut = false; // The total height of all sheet components. double get sheetHeight => @@ -622,6 +650,8 @@ class _SlidingSheetState extends State final prevFooterHeight = footerHeight; footerHeight = isFooterLaidOut ? footer!.size.height : 0; + isLaidOut = true; + if (mounted && (childHeight != prevChildHeight || headerHeight != prevHeaderHeight || @@ -644,8 +674,15 @@ class _SlidingSheetState extends State } if (availableHeight > 0) { - final num maxPossibleExtent = - isLaidOut ? (sheetHeight / availableHeight).clamp(0.0, 1.0) : 1.0; + final num maxPossibleExtent = () { + if (isCustom) { + return 1.0; + } else { + return isLaidOut + ? (sheetHeight / availableHeight).clamp(0.0, 1.0) + : 1.0; + } + }(); double extent = snap; switch (snapPositioning) { @@ -1052,21 +1089,26 @@ class _SlidingSheetState extends State Widget _buildScrollView() { Widget scrollView = Listener( onPointerUp: (_) => _handleNonDismissableSnapBack(), - child: SingleChildScrollView( - controller: controller, - physics: scrollSpec.physics, - padding: EdgeInsets.only( - top: !hasHeader ? padding.top : 0.0, - bottom: !hasFooter ? padding.bottom : 0.0, - ), - child: ConstrainedBox( - constraints: BoxConstraints(minHeight: widget.minHeight ?? 0.0), - child: SizeChangedLayoutNotifier( - key: childKey, - child: widget.builder(context, state), - ), - ), - ), + child: widget.customBuilder != null + ? Container( + key: childKey, + child: widget.customBuilder!(context, controller, state), + ) + : SingleChildScrollView( + controller: controller, + physics: scrollSpec.physics, + padding: EdgeInsets.only( + top: !hasHeader ? padding.top : 0.0, + bottom: !hasFooter ? padding.bottom : 0.0, + ), + child: ConstrainedBox( + constraints: BoxConstraints(minHeight: widget.minHeight ?? 0.0), + child: SizeChangedLayoutNotifier( + key: childKey, + child: widget.builder!(context, state), + ), + ), + ), ); if (scrollSpec.showScrollbar) { diff --git a/lib/src/sheet_dialog.dart b/lib/src/sheet_dialog.dart index f928a96..98f40e1 100644 --- a/lib/src/sheet_dialog.dart +++ b/lib/src/sheet_dialog.dart @@ -72,6 +72,7 @@ Future showSlidingBottomSheet( cornerRadiusOnFullscreen: dialog.cornerRadiusOnFullscreen, closeOnBackdropTap: dialog.dismissOnBackdropTap, builder: dialog.builder, + customBuilder: dialog.customBuilder, headerBuilder: dialog.headerBuilder, footerBuilder: dialog.footerBuilder, listener: dialog.listener, @@ -114,7 +115,10 @@ Future showSlidingBottomSheet( /// A wrapper class to show a [SlidingSheet] as a bottom sheet dialog. class SlidingSheetDialog { /// {@macro sliding_sheet.builder} - final SheetBuilder builder; + final SheetBuilder? builder; + + /// {@macro sliding_sheet.customBuilder} + final CustomSheetBuilder? customBuilder; /// {@macro sliding_sheet.headerBuilder} final SheetBuilder? headerBuilder; @@ -200,7 +204,8 @@ class SlidingSheetDialog { /// Creates a wrapper class to show a [SlidingSheet] as a bottom sheet dialog. const SlidingSheetDialog({ - required this.builder, + this.builder, + this.customBuilder, this.headerBuilder, this.footerBuilder, this.snapSpec = const SnapSpec(), diff --git a/pubspec.yaml b/pubspec.yaml index ba7a3ce..6424eaf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: wtf_sliding_sheet description: A widget that can be dragged and scrolled in a single gesture and snapped to a list of extents. -version: 1.0.0 +version: 1.1.0 homepage: https://pub.dev/packages/wtf_sliding_sheet repository: https://github.com/What-the-Flutter/wtf_sliding_sheet