Skip to content

Commit 9077646

Browse files
authored
Fix bottom app bar height and end-contained fab location (#123746)
Fix bottom app bar height and end-contained fab location
1 parent 91d751f commit 9077646

File tree

3 files changed

+56
-13
lines changed

3 files changed

+56
-13
lines changed

packages/flutter/lib/src/material/bottom_app_bar.dart

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,12 @@ class _BottomAppBarState extends State<BottomAppBar> {
194194
final Color effectiveColor = isMaterial3 ? color : ElevationOverlay.applyOverlay(context, color, elevation);
195195
final Color shadowColor = widget.shadowColor ?? babTheme.shadowColor ?? defaults.shadowColor!;
196196

197-
final Widget child = Padding(
198-
padding: widget.padding ?? babTheme.padding ?? (isMaterial3 ? const EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0) : EdgeInsets.zero),
199-
child: widget.child,
197+
final Widget child = SizedBox(
198+
height: height,
199+
child: Padding(
200+
padding: widget.padding ?? babTheme.padding ?? (isMaterial3 ? const EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0) : EdgeInsets.zero),
201+
child: widget.child,
202+
),
200203
);
201204

202205
final Material material = Material(
@@ -209,16 +212,14 @@ class _BottomAppBarState extends State<BottomAppBar> {
209212
child: SafeArea(child: child),
210213
);
211214

212-
final PhysicalShape physicalShape = PhysicalShape(
215+
return PhysicalShape(
213216
clipper: clipper,
214217
elevation: elevation,
215218
shadowColor: shadowColor,
216219
color: effectiveColor,
217220
clipBehavior: widget.clipBehavior,
218221
child: material,
219-
);
220-
221-
return SizedBox(height: height, child: physicalShape);
222+
);
222223
}
223224
}
224225

packages/flutter/lib/src/material/floating_action_button_location.dart

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -630,20 +630,24 @@ mixin FabContainedOffsetY on StandardFabLocation {
630630
final double contentMargin = scaffoldGeometry.scaffoldSize.height - contentBottom;
631631
final double bottomViewPadding = scaffoldGeometry.minViewPadding.bottom;
632632
final double fabHeight = scaffoldGeometry.floatingActionButtonSize.height;
633-
final double bottomMinInset = scaffoldGeometry.minInsets.bottom;
634633

635-
double safeMargin = 0.0;
636-
if (contentMargin > bottomMinInset + fabHeight / 2.0) {
637-
// If contentMargin is higher than bottomMinInset enough to display the
634+
double safeMargin;
635+
if (contentMargin > bottomViewPadding + fabHeight) {
636+
// If contentMargin is higher than bottomViewPadding enough to display the
638637
// FAB without clipping, don't provide a margin
639638
safeMargin = 0.0;
640639
} else {
641640
safeMargin = bottomViewPadding;
642641
}
643642

644-
final double fabY = contentBottom - fabHeight / 2.0 - safeMargin;
643+
// This is to compute the distance between the content bottom to the top edge
644+
// of the floating action button. This can be negative if content margin is
645+
// too small.
646+
final double contentBottomToFabTop = (contentMargin - bottomViewPadding - fabHeight) / 2.0;
647+
final double fabY = contentBottom + contentBottomToFabTop;
645648
final double maxFabY = scaffoldGeometry.scaffoldSize.height - fabHeight - safeMargin;
646-
return math.min(maxFabY, fabY + contentMargin / 2);
649+
650+
return math.min(maxFabY, fabY);
647651
}
648652
}
649653

packages/flutter/test/material/bottom_app_bar_test.dart

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,44 @@ void main() {
616616
physicalShape = tester.widget(find.byType(PhysicalShape).at(0));
617617
expect(physicalShape.clipper.toString(), 'ShapeBorderClipper');
618618
});
619+
620+
testWidgets('BottomAppBar adds bottom padding to height', (WidgetTester tester) async {
621+
const double bottomPadding = 35.0;
622+
623+
await tester.pumpWidget(
624+
MediaQuery(
625+
data: const MediaQueryData(
626+
padding: EdgeInsets.only(bottom: bottomPadding),
627+
viewPadding: EdgeInsets.only(bottom: bottomPadding),
628+
),
629+
child: MaterialApp(
630+
theme: ThemeData(useMaterial3: true),
631+
home: Scaffold(
632+
floatingActionButtonLocation: FloatingActionButtonLocation.endContained,
633+
floatingActionButton: FloatingActionButton(onPressed: () { }),
634+
bottomNavigationBar: BottomAppBar(
635+
child: IconButton(
636+
icon: const Icon(Icons.search),
637+
onPressed: () {},
638+
),
639+
),
640+
),
641+
),
642+
)
643+
);
644+
645+
final Rect bottomAppBar = tester.getRect(find.byType(BottomAppBar));
646+
final Rect iconButton = tester.getRect(find.widgetWithIcon(IconButton, Icons.search));
647+
final Rect fab = tester.getRect(find.byType(FloatingActionButton));
648+
649+
// The height of the bottom app bar should be its height(default is 80.0) + bottom safe area height.
650+
expect(bottomAppBar.height, 80.0 + bottomPadding);
651+
652+
// The vertical position of the icon button and fab should be center of the area excluding the bottom padding.
653+
final double barCenter = bottomAppBar.topLeft.dy + (bottomAppBar.height - bottomPadding) / 2;
654+
expect(iconButton.center.dy, barCenter);
655+
expect(fab.center.dy, barCenter);
656+
});
619657
}
620658

621659
// The bottom app bar clip path computation is only available at paint time.

0 commit comments

Comments
 (0)