Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NonScrollingWoltModalSheetPage page type #127

Merged
merged 3 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 56 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,11 @@ Here is an example that shows all the modal sheet elements in use:

## Usage of WoltModalSheet Pages

The WoltModalSheet library provides two primary classes for constructing
modal sheet pages: `SliverWoltModalSheetPage` and `WoltModalSheetPage`.
Understanding the use cases and functionalities of these classes is key to
creating performant and easy to construct modal sheets.
The WoltModalSheet library provides three primary classes for constructing
modal sheet pages: `WoltModalSheetPage`, `SliverWoltModalSheetPage`, and
`NonScrollingWoltModalSheetPage`. Understanding the use cases and
functionalities of these classes is key to creating performant,
easy-to-construct modal sheets.

### SliverWoltModalSheetPage

Expand All @@ -212,6 +213,7 @@ SliverWoltModalSheetPage(
```

### WoltModalSheetPage

WoltModalSheetPage provides a simpler alternative for pages that primarily
consist of a single widget or a straightforward layout. It automatically
wraps the child widget in a SliverToBoxAdapter, making it suitable for use
Expand All @@ -232,10 +234,56 @@ WoltModalSheetPage(
)
```

### Choosing Between the Two
* Use `SliverWoltModalSheetPage` when your modal sheet requires complex scrolling behaviors or needs to display a list of items.
* Choose WoltModalSheetPage for simpler content layouts or when working with
a single widget.
### NonScrollingWoltModalSheetPage

`NonScrollingWoltModalSheetPage` is designed to display content which is
flexible in height but unlikely to require scrolling. This class is ideal
for content that adapts to the available vertical space within the modal
sheet's maximum height, but is unlikely to exceed that height and require
scrolling.

Key Features:
* Adaptability: Designed for content with flexible height but fixed or
intrinsic dimensions.
* Flex Layout: Can utilize the Flex layout model of a Column for effective
space management.
* Non-Scrolling: Best for content that fits within the modal sheet's maximum
height without needing scrolling.

*Warning:* If there is a risk that the content's height might exceed the modal
sheet's maximum height, leading to overflow, it is recommended to use
SliverWoltModalSheetPage or WoltModalSheetPage instead. These classes
provide scrolling capabilities to handle larger content effectively using
slivers.

```dart
NonScrollingWoltModalSheetPage(
child: MyFlexibleHeightWidget(),
// Additional properties...
)

```

This class extends SliverWoltModalSheetPage, offering a streamlined approach
to handle non-scrolling content within a modal sheet.

### Choosing between the three
When deciding which class to use for your modal sheet, consider the following guidelines:

* WoltModalSheetPage: Choose this for simpler content layouts, especially
when working with a single widget. It's best suited for straightforward
layouts that don't require the complexities of Slivers.

* SliverWoltModalSheetPage: Opt for this class when your modal sheet
requires complex scrolling behaviors or needs to display a long list of
items. It's ideal for dynamic content layouts that benefit from the advanced
capabilities of Flutter's Sliver widgets.

* NonScrollingWoltModalSheetPage: This class is best when your content is
flexible in height but unlikely to require scrolling. It’s perfect for
modal sheets where the content fits within the modal's maximum height
without the need for scrollable behavior. Use this for content with fixed
or intrinsic dimensions that need to adapt to available vertical space.

### Migration from 0.1.x to 0.2.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,41 +43,44 @@ class WoltModalSheetMainContent extends StatelessWidget {
page.trailingNavBarWidget != null
? navBarHeight
: 0.0;
final isNonScrollingPage = page is NonScrollingWoltModalSheetPage;
final scrollView = CustomScrollView(
shrinkWrap: true,
physics: themeData?.mainContentScrollPhysics ??
defaultThemeData.mainContentScrollPhysics,
controller: scrollController,
slivers: [
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index == 0) {
final heroImage = page.heroImage;
return heroImage != null
? WoltModalSheetHeroImage(
topBarHeight: topBarHeight,
heroImage: heroImage,
heroImageHeight: heroImageHeight,
)
// If top bar layer is always visible, the padding is explicitly added to the
// scroll view since top bar will not be integrated to scroll view at all.
// Otherwise, we implicitly create a spacing as a part of the scroll view.
: SizedBox(
height: isTopBarLayerAlwaysVisible ? 0 : topBarHeight);
} else {
final pageTitle = page.pageTitle;
return KeyedSubtree(
key: pageTitleKey,
child: pageTitle ?? const SizedBox.shrink(),
);
}
},
childCount: 2,
if (!isNonScrollingPage)
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index == 0) {
final heroImage = page.heroImage;
return heroImage != null
? WoltModalSheetHeroImage(
topBarHeight: topBarHeight,
heroImage: heroImage,
heroImageHeight: heroImageHeight,
)
// If top bar layer is always visible, the padding is explicitly added to the
// scroll view since top bar will not be integrated to scroll view at all.
// Otherwise, we implicitly create a spacing as a part of the scroll view.
: SizedBox(
height:
isTopBarLayerAlwaysVisible ? 0 : topBarHeight);
} else {
final pageTitle = page.pageTitle;
return KeyedSubtree(
key: pageTitleKey,
child: pageTitle ?? const SizedBox.shrink(),
);
}
},
childCount: 2,
),
),
),
...page.mainContentSlivers,
if (page.forceMaxHeight)
if (page.forceMaxHeight && !isNonScrollingPage)
const SliverFillRemaining(
hasScrollBody: false,
child: SizedBox.shrink(),
Expand Down
22 changes: 12 additions & 10 deletions lib/src/content/wolt_modal_sheet_animated_switcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -342,15 +342,16 @@ class _WoltModalSheetAnimatedSwitcherState
final shouldShowTopBarTitle = hasTopBarLayer && _page.topBar == null;
Widget? navigationToolbarMiddle;
if (shouldShowTopBarTitle) {
navigationToolbarMiddle = isTopBarLayerAlwaysVisible
? Center(child: topBarTitle)
: WoltModalSheetTopBarTitleFlow(
page: _page,
scrollController: _currentPageScrollController,
titleKey: _pageTitleKey,
topBarTitle: topBarTitle,
softKeyboardClosedListenable: _softKeyboardClosedNotifier,
);
navigationToolbarMiddle =
isTopBarLayerAlwaysVisible || _page is NonScrollingWoltModalSheetPage
? Center(child: topBarTitle)
: WoltModalSheetTopBarTitleFlow(
page: _page,
scrollController: _currentPageScrollController,
titleKey: _pageTitleKey,
topBarTitle: topBarTitle,
softKeyboardClosedListenable: _softKeyboardClosedNotifier,
);
}
return PaginatingWidgetsGroup(
mainContentAnimatedBuilder: MainContentAnimatedBuilder(
Expand All @@ -376,7 +377,8 @@ class _WoltModalSheetAnimatedSwitcherState
pageTransitionState: WoltModalSheetPageTransitionState.incoming,
controller: animationController,
child: hasTopBarLayer
? (isTopBarLayerAlwaysVisible
? (isTopBarLayerAlwaysVisible ||
_page is NonScrollingWoltModalSheetPage
? WoltModalSheetTopBar(page: _page)
: WoltModalSheetTopBarFlow(
page: _page,
Expand Down
42 changes: 42 additions & 0 deletions lib/src/modal_page/non_scrolling_wolt_modal_sheet_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
import 'package:wolt_modal_sheet/wolt_modal_sheet.dart';

/// A [NonScrollingWoltModalSheetPage] is a specialized page for the [WoltModalSheet]
/// that is designed to display content which is flexible in height but unlikely
/// to require scrolling.
///
/// This class is designed for content that needs to adapt to the available
/// vertical space within the modal sheet's maximum height, but is unlikely
/// to exceed that height and require scrolling. It is ideal for content that
/// is flexible in height but has fixed (or intrinsic) dimensions and is laid
/// out using the [Flex] layout model of a [Column].
///
/// Warning:
/// - If there is a risk that the content's height might exceed the modal
/// sheet's maximum height, leading to overflow, it is recommended to use
/// [SliverWoltModalSheetPage] or [WoltModalSheetPage] instead. These classes
/// provide scrolling capabilities to handle larger content effectively using slivers.
class NonScrollingWoltModalSheetPage extends SliverWoltModalSheetPage {
/// A [Widget] that represents the main content displayed in the page.
/// This is a shortcut for providing a list of Sliver widgets with only one Sliver widget which
/// is [SliverFillViewport].
final Widget child;

/// Creates a page to be built within [WoltScrollableModalSheet].
NonScrollingWoltModalSheetPage({
required this.child,
super.backgroundColor,
super.enableDrag,
super.leadingNavBarWidget,
super.trailingNavBarWidget,
super.hasTopBarLayer = false,
super.topBar,
super.topBarTitle,
super.navBarHeight,
}) : super(
isTopBarLayerAlwaysVisible: hasTopBarLayer,
mainContentSlivers: [
SliverFillViewport(delegate: SliverChildListDelegate([child])),
],
);
}
1 change: 1 addition & 0 deletions lib/wolt_modal_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ library wolt_modal_sheet;

export 'src/modal_page/sliver_wolt_modal_sheet_page.dart';
export 'src/modal_page/wolt_modal_sheet_page.dart';
export 'src/modal_page/non_scrolling_wolt_modal_sheet_page.dart';
export 'src/modal_type/wolt_modal_type.dart';
export 'src/theme/wolt_modal_sheet_theme_data.dart';
export 'src/wolt_modal_sheet.dart';
Expand Down
15 changes: 15 additions & 0 deletions playground/lib/home/pages/multi_page_path_name.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:playground/home/pages/root_sheet_page.dart';
import 'package:playground/home/pages/sheet_page_with_custom_top_bar.dart';
import 'package:playground/home/pages/sheet_page_with_dynamic_page_properties.dart';
import 'package:playground/home/pages/sheet_page_with_non_scrolling_layout.dart';
import 'package:playground/home/pages/sheet_page_with_forced_max_height.dart';
import 'package:playground/home/pages/sheet_page_with_hero_image.dart';
import 'package:playground/home/pages/sheet_page_with_lazy_list.dart';
Expand All @@ -17,6 +18,7 @@ enum MultiPagePathName {
noTitleNoTopBar(pageCount: 2, queryParamName: "noTitleNoTopBar"),
customTopBar(pageCount: 2, queryParamName: "customTopBar"),
dynamicPageProperties(pageCount: 2, queryParamName: "dynamicPageProperties"),
flexibleLayout(pageCount: 2, queryParamName: "flexibleLayout"),
allPagesPath(pageCount: 6, queryParamName: "all");

static const defaultPath = MultiPagePathName.allPagesPath;
Expand Down Expand Up @@ -80,6 +82,16 @@ enum MultiPagePathName {
onBackPressed: goToPreviousPage,
isLastPage: isLastPage,
);
NonScrollingWoltModalSheetPage nonScrollingSheetLayout(
BuildContext context, {
bool isLastPage = true,
}) =>
SheetPageWithNonScrollingLayout.build(
nextPagePressed: () => isLastPage ? close(context) : goToNextPage(),
onClosed: () => close(context),
onBackPressed: goToPreviousPage,
isLastPage: isLastPage,
);
WoltModalSheetPage dynamicPageProperties(BuildContext context,
{bool isLastPage = true}) =>
SheetPageWithDynamicPageProperties.build(
Expand Down Expand Up @@ -112,6 +124,8 @@ enum MultiPagePathName {
return (context) => [root(context), customTopBar(context)];
case MultiPagePathName.dynamicPageProperties:
return (context) => [root(context), dynamicPageProperties(context)];
case MultiPagePathName.flexibleLayout:
return (context) => [root(context), nonScrollingSheetLayout(context)];
case MultiPagePathName.allPagesPath:
return (context) => [
root(context),
Expand All @@ -121,6 +135,7 @@ enum MultiPagePathName {
noTitleNoTopBar(context, isLastPage: false),
customTopBar(context, isLastPage: false),
dynamicPageProperties(context, isLastPage: false),
nonScrollingSheetLayout(context, isLastPage: false),
forcedMaxHeight(context, isLastPage: true),
];
}
Expand Down
5 changes: 5 additions & 0 deletions playground/lib/home/pages/root_sheet_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ class RootSheetPage {
value: MultiPagePathName.dynamicPageProperties,
isSelected: false,
),
WoltSelectionListItemData(
title: 'NonScrollingWoltModalSheetPage example',
value: MultiPagePathName.flexibleLayout,
isSelected: false,
),
WoltSelectionListItemData(
title: 'All the pages in one flow',
value: MultiPagePathName.allPagesPath,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import 'package:demo_ui_components/demo_ui_components.dart';
import 'package:flutter/material.dart';
import 'package:wolt_modal_sheet/wolt_modal_sheet.dart';

class SheetPageWithNonScrollingLayout {
SheetPageWithNonScrollingLayout._();

static NonScrollingWoltModalSheetPage build({
required VoidCallback nextPagePressed,
required VoidCallback onBackPressed,
required VoidCallback onClosed,
bool isLastPage = true,
}) {
const textStyle = TextStyle(fontSize: 24, fontWeight: FontWeight.bold);
return NonScrollingWoltModalSheetPage(
leadingNavBarWidget:
WoltModalSheetBackButton(onBackPressed: onBackPressed),
trailingNavBarWidget: WoltModalSheetCloseButton(onClosed: onClosed),
hasTopBarLayer: true,
navBarHeight: 72.0,
topBarTitle: const Text('Non-scrolling page'),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Flexible(
flex: 2,
child: Center(
child: Text('Flex: 2', style: textStyle),
),
),
const Flexible(
flex: 3,
child: ColoredBox(
color: Colors.amber,
child: Center(child: Text('Flex: 3', style: textStyle)),
),
),
Flexible(
flex: 1,
child: GestureDetector(
onTap: isLastPage ? onClosed : nextPagePressed,
child: ColoredBox(
color: Colors.green,
child: Center(
child: Text(
"Flex: 1\n Tap here to ${isLastPage ? 'close' : 'go to the next page'}",
style: textStyle,
textAlign: TextAlign.center,
),
),
),
),
),
],
),
);
}
}