Skip to content
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
112 changes: 103 additions & 9 deletions packages/material_ui/lib/src/scaffold.dart
Original file line number Diff line number Diff line change
Expand Up @@ -983,6 +983,46 @@ class _BodyBoxConstraints extends BoxConstraints {
Object.hash(super.hashCode, materialBannerHeight, bottomWidgetsHeight, appBarHeight);
}

// Used to communicate the keyboard inset computed by _ScaffoldLayout to the
// persistent bottom sheet, in the same way _BodyBoxConstraints communicates
// layout information to the body.
class _BottomSheetBoxConstraints extends BoxConstraints {
const _BottomSheetBoxConstraints({super.maxWidth, super.maxHeight, required this.keyboardInset})
: assert(keyboardInset >= 0);

// The distance between the bottom of the sheet's content, which avoids the
// keyboard, and the bottom edge of the sheet's Material, which extends
// behind it. Zero when the keyboard is closed or when
// Scaffold.resizeToAvoidBottomInset is false.
final double keyboardInset;

@override
bool operator ==(Object other) {
if (super != other) {
return false;
}
return other is _BottomSheetBoxConstraints && other.keyboardInset == keyboardInset;
}

@override
int get hashCode => Object.hash(super.hashCode, keyboardInset);
}

// Provides _BottomSheetBoxConstraints.keyboardInset to the _StandardBottomSheets
// in the Scaffold's bottom sheet slot, which are built before layout.
class _BottomSheetKeyboardInset extends InheritedWidget {
const _BottomSheetKeyboardInset({required this.inset, required super.child});

final double inset;

static double of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<_BottomSheetKeyboardInset>()?.inset ?? 0.0;
}

@override
bool updateShouldNotify(_BottomSheetKeyboardInset oldWidget) => inset != oldWidget.inset;
}

// Used when Scaffold.extendBody is true to wrap the scaffold's body in a MediaQuery
// whose padding accounts for the height of the bottomNavigationBar and/or the
// persistentFooterButtons.
Expand Down Expand Up @@ -1135,6 +1175,15 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
bottom - math.max(minInsets.bottom, bottomWidgetsHeight),
);

// When the keyboard lifts the bottom sheet's content above it, the sheet's
// Material keeps extending down to the edge otherwise available to the
// sheet (above any bottom bars), so that a translucent keyboard shows the
// sheet's color rather than the Scaffold's background.
final double bottomSheetKeyboardInset = math.max(
0.0,
bottom - bottomWidgetsHeight - contentBottom,
);

if (hasChild(_ScaffoldSlot.body)) {
double bodyMaxHeight = math.max(0.0, contentBottom - contentTop);

Expand Down Expand Up @@ -1189,14 +1238,23 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
}

if (hasChild(_ScaffoldSlot.bottomSheet)) {
final bottomSheetConstraints = BoxConstraints(
final double bottomSheetBottom = contentBottom + bottomSheetKeyboardInset;
final bottomSheetConstraints = _BottomSheetBoxConstraints(
maxWidth: fullWidthConstraints.maxWidth,
maxHeight: math.max(0.0, contentBottom - contentTop),
maxHeight: math.max(0.0, bottomSheetBottom - contentTop),
keyboardInset: bottomSheetKeyboardInset,
);
bottomSheetSize = layoutChild(_ScaffoldSlot.bottomSheet, bottomSheetConstraints);
final Size sheetSize = layoutChild(_ScaffoldSlot.bottomSheet, bottomSheetConstraints);
positionChild(
_ScaffoldSlot.bottomSheet,
Offset((size.width - bottomSheetSize.width) / 2.0, contentBottom - bottomSheetSize.height),
Offset((size.width - sheetSize.width) / 2.0, bottomSheetBottom - sheetSize.height),
);
// The floating action button is positioned relative to the sheet's
// content, which ends at contentBottom, not to the Material behind the
// keyboard.
bottomSheetSize = Size(
sheetSize.width,
math.max(0.0, sheetSize.height - bottomSheetKeyboardInset),
);
}

Expand Down Expand Up @@ -1997,6 +2055,13 @@ class Scaffold extends StatefulWidget {
/// [showBottomSheet] and [showModalBottomSheet]. Typically it's a widget
/// that includes [Material].
///
/// When [resizeToAvoidBottomInset] is true and the software keyboard is
/// shown, the sheet's content is kept above the keyboard while the sheet's
/// [Material] continues behind it, so that a translucent keyboard shows the
/// sheet's surface rather than the scaffold's [backgroundColor]. That surface
/// is configured with [BottomSheetThemeData]; decorations painted by the
/// [bottomSheet] widget itself do not extend behind the keyboard.
///
/// See also:
///
/// * [showBottomSheet], which displays a bottom sheet as a route that can
Expand Down Expand Up @@ -2735,6 +2800,12 @@ class ScaffoldState extends State<Scaffold>
///
/// </callout-box>
///
/// When [Scaffold.resizeToAvoidBottomInset] is true and the software keyboard
/// is shown, the sheet's content is kept above the keyboard while the sheet's
/// [Material] continues behind it, so that a translucent keyboard shows the
/// sheet's surface rather than the scaffold's background. Use
/// [backgroundColor] or [BottomSheetThemeData] to configure that surface.
///
/// See also:
///
/// * [BottomSheet], which becomes the parent of the widget returned by the
Expand Down Expand Up @@ -3164,9 +3235,16 @@ class ScaffoldState extends State<Scaffold>
double? snackBarWidth;

if (_currentBottomSheet != null || _dismissedBottomSheets.isNotEmpty) {
final Widget stack = Stack(
alignment: Alignment.bottomCenter,
children: <Widget>[..._dismissedBottomSheets, ?_currentBottomSheet?._widget],
final Widget stack = LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return _BottomSheetKeyboardInset(
inset: (constraints as _BottomSheetBoxConstraints).keyboardInset,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using a direct cast constraints as _BottomSheetBoxConstraints can lead to a runtime TypeError if the Scaffold undergoes dry layout, intrinsic height calculations, or other layout passes where the constraints might not be of the custom _BottomSheetBoxConstraints type.

Using a safe type check with is is much more robust and prevents potential crashes.

            inset: constraints is _BottomSheetBoxConstraints ? constraints.keyboardInset : 0.0,

child: Stack(
alignment: Alignment.bottomCenter,
children: <Widget>[..._dismissedBottomSheets, ?_currentBottomSheet?._widget],
),
);
},
);
_addIfNonNull(
children,
Expand Down Expand Up @@ -3505,6 +3583,14 @@ class _StandardBottomSheetState extends State<_StandardBottomSheet> {

@override
Widget build(BuildContext context) {
// Non-zero only while the keyboard lifts this sheet, see _ScaffoldLayout.
final double keyboardInset = _BottomSheetKeyboardInset.of(context);
// The constraints describe the content, so they grow by the part of the
// Material that extends behind the keyboard. This mirrors BottomSheet's own
// lookup; its defaults, used when both are null, don't limit the height.
final BoxConstraints? constraints =
widget.constraints ?? Theme.of(context).bottomSheetTheme.constraints;

return AnimatedBuilder(
animation: widget.animationController,
builder: (BuildContext context, Widget? child) {
Expand All @@ -3526,12 +3612,20 @@ class _StandardBottomSheetState extends State<_StandardBottomSheet> {
onDragStart: _handleDragStart,
onDragEnd: _handleDragEnd,
onClosing: widget.onClosing!,
builder: widget.builder,
// Extends the Material behind the keyboard. Always present, so the
// content keeps its state when the keyboard shows or hides.
builder: (BuildContext context) => Padding(
padding: EdgeInsets.only(bottom: keyboardInset),
child: widget.builder(context),
),
backgroundColor: widget.backgroundColor,
elevation: widget.elevation,
shape: widget.shape,
clipBehavior: widget.clipBehavior,
constraints: widget.constraints,
constraints: constraints?.copyWith(
minHeight: constraints.minHeight + keyboardInset,
maxHeight: constraints.maxHeight + keyboardInset,
),
),
),
),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
changelog: |
- Extends the `Material` of a `Scaffold` persistent bottom sheet behind the software keyboard when `resizeToAvoidBottomInset` is true, so that a translucent keyboard shows the sheet's surface instead of the scaffold's background color.
version: patch
Loading
Loading