diff --git a/packages/flutter/lib/src/cupertino/page_scaffold.dart b/packages/flutter/lib/src/cupertino/page_scaffold.dart index efa8458c970..88f095a550e 100644 --- a/packages/flutter/lib/src/cupertino/page_scaffold.dart +++ b/packages/flutter/lib/src/cupertino/page_scaffold.dart @@ -22,8 +22,10 @@ class CupertinoPageScaffold extends StatelessWidget { Key key, this.navigationBar, this.backgroundColor = CupertinoColors.white, + this.resizeToAvoidBottomInset = true, @required this.child, }) : assert(child != null), + assert(resizeToAvoidBottomInset != null), super(key: key); /// The [navigationBar], typically a [CupertinoNavigationBar], is drawn at the @@ -49,6 +51,15 @@ class CupertinoPageScaffold extends StatelessWidget { /// By default uses [CupertinoColors.white] color. final Color backgroundColor; + /// Whether the [child] should size itself to avoid the window's bottom inset. + /// + /// For example, if there is an onscreen keyboard displayed above the + /// scaffold, the body can be resized to avoid overlapping the keyboard, which + /// prevents widgets inside the body from being obscured by the keyboard. + /// + /// Defaults to true. + final bool resizeToAvoidBottomInset; + @override Widget build(BuildContext context) { final List stacked = []; @@ -59,15 +70,20 @@ class CupertinoPageScaffold extends StatelessWidget { // TODO(xster): Use real size after partial layout instead of preferred size. // https://github.com/flutter/flutter/issues/12912 - final double topPadding = navigationBar.preferredSize.height - + existingMediaQuery.padding.top; + final double topPadding = + navigationBar.preferredSize.height + existingMediaQuery.padding.top; + + // Propagate bottom padding and include viewInsets if appropriate + final double bottomPadding = resizeToAvoidBottomInset + ? existingMediaQuery.viewInsets.bottom + : 0.0; // If navigation bar is opaquely obstructing, directly shift the main content // down. If translucent, let main content draw behind navigation bar but hint the // obstructed area. if (navigationBar.fullObstruction) { paddedContent = new Padding( - padding: new EdgeInsets.only(top: topPadding), + padding: new EdgeInsets.only(top: topPadding, bottom: bottomPadding), child: child, ); } else { @@ -77,7 +93,10 @@ class CupertinoPageScaffold extends StatelessWidget { top: topPadding, ), ), - child: child, + child: new Padding( + padding: new EdgeInsets.only(bottom: bottomPadding), + child: child, + ), ); } } diff --git a/packages/flutter/test/cupertino/scaffold_test.dart b/packages/flutter/test/cupertino/scaffold_test.dart index 31ef37a5481..b9be03278c9 100644 --- a/packages/flutter/test/cupertino/scaffold_test.dart +++ b/packages/flutter/test/cupertino/scaffold_test.dart @@ -25,6 +25,55 @@ void main() { expect(tester.getTopLeft(find.byType(Center)), const Offset(0.0, 0.0)); }); + testWidgets('Contents padding from viewInsets', (WidgetTester tester) async { + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: MediaQuery( + data: const MediaQueryData(viewInsets: EdgeInsets.only(bottom: 100.0)), + child: CupertinoPageScaffold( + navigationBar: const CupertinoNavigationBar( + middle: Text('Opaque'), + backgroundColor: Color(0xFFF8F8F8), + ), + child: Container(), + ), + ), + )); + + expect(tester.getSize(find.byType(Container)).height, 600.0 - 44.0 - 100.0); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: MediaQuery( + data: const MediaQueryData(viewInsets: EdgeInsets.only(bottom: 100.0)), + child: CupertinoPageScaffold( + navigationBar: const CupertinoNavigationBar( + middle: Text('Transparent'), + ), + child: Container(), + ), + ), + )); + + expect(tester.getSize(find.byType(Container)).height, 600.0 - 100.0); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: MediaQuery( + data: const MediaQueryData(viewInsets: EdgeInsets.only(bottom: 100.0)), + child: CupertinoPageScaffold( + navigationBar: const CupertinoNavigationBar( + middle: Text('Title'), + ), + resizeToAvoidBottomInset: false, + child: Container(), + ), + ), + )); + + expect(tester.getSize(find.byType(Container)).height, 600.0); + }); + testWidgets('Contents are between opaque bars', (WidgetTester tester) async { const Center page1Center = Center();