diff --git a/packages/flutter/lib/src/widgets/safe_area.dart b/packages/flutter/lib/src/widgets/safe_area.dart index 03adc9010b..78ac59237c 100644 --- a/packages/flutter/lib/src/widgets/safe_area.dart +++ b/packages/flutter/lib/src/widgets/safe_area.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:math' as math; + import 'package:flutter/foundation.dart'; import 'basic.dart'; @@ -9,8 +11,8 @@ import 'debug.dart'; import 'framework.dart'; import 'media_query.dart'; -/// A widget that insets its child by sufficient padding to avoid -/// intrusions by the operating system. +/// A widget that insets its child by sufficient padding to avoid intrusions by +/// the operating system. /// /// For example, this will indent the child by enough to avoid the status bar at /// the top of the screen. @@ -18,6 +20,9 @@ import 'media_query.dart'; /// It will also indent the child by the amount necessary to avoid The Notch on /// the iPhone X, or other similar creative physical features of the display. /// +/// When a [minimum] padding is specified, the greater of the minimum padding +/// or the safe area padding will be applied. +/// /// See also: /// /// * [SliverSafeArea], for insetting slivers to avoid operating system @@ -29,13 +34,15 @@ import 'media_query.dart'; class SafeArea extends StatelessWidget { /// Creates a widget that avoids operating system interfaces. /// - /// The [left], [top], [right], and [bottom] arguments must not be null. + /// The [left], [top], [right], [bottom], and [minimum] arguments must not be + /// null. const SafeArea({ Key key, this.left: true, this.top: true, this.right: true, this.bottom: true, + this.minimum: EdgeInsets.zero, @required this.child, }) : assert(left != null), assert(top != null), @@ -56,6 +63,11 @@ class SafeArea extends StatelessWidget { /// Whether to avoid system intrusions on the bottom side of the screen. final bool bottom; + /// This minimum padding to apply. + /// + /// The greater of the minimum insets and the media padding will be applied. + final EdgeInsets minimum; + /// The widget below this widget in the tree. /// /// The padding on the [MediaQuery] for the [child] will be suitably adjusted @@ -70,10 +82,10 @@ class SafeArea extends StatelessWidget { final EdgeInsets padding = MediaQuery.of(context).padding; return new Padding( padding: new EdgeInsets.only( - left: left ? padding.left : 0.0, - top: top ? padding.top : 0.0, - right: right ? padding.right : 0.0, - bottom: bottom ? padding.bottom : 0.0, + left: math.max(left ? padding.left : 0.0, minimum.left), + top: math.max(top ? padding.top : 0.0, minimum.top), + right: math.max(right ? padding.right : 0.0, minimum.right), + bottom: math.max(bottom ? padding.bottom : 0.0, minimum.bottom), ), child: new MediaQuery.removePadding( context: context, @@ -106,6 +118,9 @@ class SafeArea extends StatelessWidget { /// on the iPhone X, or other similar creative physical features of the /// display. /// +/// When a [minimum] padding is specified, the greater of the minimum padding +/// or the safe area padding will be applied. +/// /// See also: /// /// * [SafeArea], for insetting widgets to avoid operating system intrusions. @@ -116,13 +131,14 @@ class SafeArea extends StatelessWidget { class SliverSafeArea extends StatelessWidget { /// Creates a sliver that avoids operating system interfaces. /// - /// The [left], [top], [right], and [bottom] arguments must not be null. + /// The [left], [top], [right], [bottom], and [minimum] arguments must not be null. const SliverSafeArea({ Key key, this.left: true, this.top: true, this.right: true, this.bottom: true, + this.minimum: EdgeInsets.zero, @required this.sliver, }) : assert(left != null), assert(top != null), @@ -143,6 +159,11 @@ class SliverSafeArea extends StatelessWidget { /// Whether to avoid system intrusions on the bottom side of the screen. final bool bottom; + /// This minimum padding to apply. + /// + /// The greater of the minimum padding and the media padding is be applied. + final EdgeInsets minimum; + /// The sliver below this sliver in the tree. /// /// The padding on the [MediaQuery] for the [sliver] will be suitably adjusted @@ -155,10 +176,10 @@ class SliverSafeArea extends StatelessWidget { final EdgeInsets padding = MediaQuery.of(context).padding; return new SliverPadding( padding: new EdgeInsets.only( - left: left ? padding.left : 0.0, - top: top ? padding.top : 0.0, - right: right ? padding.right : 0.0, - bottom: bottom ? padding.bottom : 0.0, + left: math.max(left ? padding.left : 0.0, minimum.left), + top: math.max(top ? padding.top : 0.0, minimum.top), + right: math.max(right ? padding.right : 0.0, minimum.right), + bottom: math.max(bottom ? padding.bottom : 0.0, minimum.bottom), ), sliver: new MediaQuery.removePadding( context: context, diff --git a/packages/flutter/test/widgets/safe_area_test.dart b/packages/flutter/test/widgets/safe_area_test.dart index e7a99237d1..176c3af7e1 100644 --- a/packages/flutter/test/widgets/safe_area_test.dart +++ b/packages/flutter/test/widgets/safe_area_test.dart @@ -22,6 +22,21 @@ void main() { expect(tester.getBottomRight(find.byType(Placeholder)), const Offset(780.0, 580.0)); }); + testWidgets('SafeArea - with minimums', (WidgetTester tester) async { + await tester.pumpWidget( + const MediaQuery( + data: const MediaQueryData(padding: const EdgeInsets.all(20.0)), + child: const SafeArea( + top: false, + minimum: const EdgeInsets.fromLTRB(0.0, 10.0, 20.0, 30.0), + child: const Placeholder(), + ), + ), + ); + expect(tester.getTopLeft(find.byType(Placeholder)), const Offset(20.0, 10.0)); + expect(tester.getBottomRight(find.byType(Placeholder)), const Offset(780.0, 570.0)); + }); + testWidgets('SafeArea - nested', (WidgetTester tester) async { await tester.pumpWidget( const MediaQuery( @@ -119,6 +134,24 @@ void main() { ]); }); + testWidgets('SliverSafeArea - basic', (WidgetTester tester) async { + await tester.pumpWidget( + buildWidget( + const EdgeInsets.all(20.0), + const SliverSafeArea( + top: false, + minimum: const EdgeInsets.fromLTRB(0.0, 10.0, 20.0, 30.0), + sliver: const SliverToBoxAdapter(child: const SizedBox(width: 800.0, height: 100.0, child: const Text('padded'))), + ), + ), + ); + verify(tester, [ + new Rect.fromLTWH(0.0, 0.0, 800.0, 100.0), + new Rect.fromLTWH(20.0, 110.0, 760.0, 100.0), + new Rect.fromLTWH(0.0, 240.0, 800.0, 100.0), + ]); + }); + testWidgets('SliverSafeArea - nested', (WidgetTester tester) async { await tester.pumpWidget( buildWidget(