diff --git a/packages/flutter/lib/src/material/material_app.dart b/packages/flutter/lib/src/material/material_app.dart index 3541e50c46e..cf15600fa39 100644 --- a/packages/flutter/lib/src/material/material_app.dart +++ b/packages/flutter/lib/src/material/material_app.dart @@ -68,17 +68,24 @@ class MaterialApp extends StatefulComponent { _MaterialAppState createState() => new _MaterialAppState(); } +EdgeDims _getPadding(ui.Window window) { + ui.WindowPadding padding = ui.window.padding; + return new EdgeDims.TRBL(padding.top, padding.right, padding.bottom, padding.left); +} + class _MaterialAppState extends State implements BindingObserver { GlobalObjectKey _navigator; Size _size; + EdgeDims _padding; LocaleQueryData _localeData; void initState() { super.initState(); _navigator = new GlobalObjectKey(this); _size = ui.window.size; + _padding = _getPadding(ui.window); didChangeLocale(ui.window.locale); WidgetFlutterBinding.instance.addObserver(this); } @@ -99,7 +106,12 @@ class _MaterialAppState extends State implements BindingObserver { return result; } - void didChangeSize(Size size) => setState(() { _size = size; }); + void didChangeSize(Size size) { + setState(() { + _size = size; + _padding = _getPadding(ui.window); + }); + } void didChangeLocale(ui.Locale locale) { if (config.onLocaleChanged != null) { @@ -138,7 +150,7 @@ class _MaterialAppState extends State implements BindingObserver { ThemeData theme = config.theme ?? new ThemeData.fallback(); Widget result = new MediaQuery( - data: new MediaQueryData(size: _size), + data: new MediaQueryData(size: _size, padding: _padding), child: new LocaleQuery( data: _localeData, child: new AnimatedTheme( diff --git a/packages/flutter/lib/src/material/scaffold.dart b/packages/flutter/lib/src/material/scaffold.dart index 4c861a6bb9d..1e1d2cff1d0 100644 --- a/packages/flutter/lib/src/material/scaffold.dart +++ b/packages/flutter/lib/src/material/scaffold.dart @@ -5,18 +5,17 @@ import 'dart:async'; import 'dart:collection'; import 'dart:math' as math; -import 'dart:ui' as ui; import 'package:flutter/animation.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; import 'bottom_sheet.dart'; +import 'drawer.dart'; +import 'icon_button.dart'; import 'material.dart'; import 'snack_bar.dart'; import 'tool_bar.dart'; -import 'drawer.dart'; -import 'icon_button.dart'; const double _kFloatingActionButtonMargin = 16.0; // TODO(hmuller): should be device dependent const Duration _kFloatingActionButtonSegue = const Duration(milliseconds: 400); @@ -31,6 +30,10 @@ enum _ScaffoldSlot { } class _ScaffoldLayout extends MultiChildLayoutDelegate { + _ScaffoldLayout({ this.padding }); + + final EdgeDims padding; + void performLayout(Size size, BoxConstraints constraints) { BoxConstraints looseConstraints = constraints.loosen(); @@ -41,18 +44,19 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate { // so the toolbar's shadow is drawn on top of the body. final BoxConstraints fullWidthConstraints = looseConstraints.tighten(width: size.width); - Size toolBarSize = Size.zero; + double contentTop = padding.top; + double contentBottom = size.height - padding.bottom; if (isChild(_ScaffoldSlot.toolBar)) { - toolBarSize = layoutChild(_ScaffoldSlot.toolBar, fullWidthConstraints); + contentTop = layoutChild(_ScaffoldSlot.toolBar, fullWidthConstraints).height; positionChild(_ScaffoldSlot.toolBar, Offset.zero); } if (isChild(_ScaffoldSlot.body)) { - final double bodyHeight = size.height - toolBarSize.height; + final double bodyHeight = contentBottom - contentTop; final BoxConstraints bodyConstraints = fullWidthConstraints.tighten(height: bodyHeight); layoutChild(_ScaffoldSlot.body, bodyConstraints); - positionChild(_ScaffoldSlot.body, new Offset(0.0, toolBarSize.height)); + positionChild(_ScaffoldSlot.body, new Offset(0.0, contentTop)); } // The BottomSheet and the SnackBar are anchored to the bottom of the parent, @@ -69,22 +73,22 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate { if (isChild(_ScaffoldSlot.bottomSheet)) { bottomSheetSize = layoutChild(_ScaffoldSlot.bottomSheet, fullWidthConstraints); - positionChild(_ScaffoldSlot.bottomSheet, new Offset((size.width - bottomSheetSize.width) / 2.0, size.height - bottomSheetSize.height)); + positionChild(_ScaffoldSlot.bottomSheet, new Offset((size.width - bottomSheetSize.width) / 2.0, contentBottom - bottomSheetSize.height)); } if (isChild(_ScaffoldSlot.snackBar)) { snackBarSize = layoutChild(_ScaffoldSlot.snackBar, fullWidthConstraints); - positionChild(_ScaffoldSlot.snackBar, new Offset(0.0, size.height - snackBarSize.height)); + positionChild(_ScaffoldSlot.snackBar, new Offset(0.0, contentBottom - snackBarSize.height)); } if (isChild(_ScaffoldSlot.floatingActionButton)) { final Size fabSize = layoutChild(_ScaffoldSlot.floatingActionButton, looseConstraints); final double fabX = size.width - fabSize.width - _kFloatingActionButtonMargin; - double fabY = size.height - fabSize.height - _kFloatingActionButtonMargin; + double fabY = contentBottom - fabSize.height - _kFloatingActionButtonMargin; if (snackBarSize.height > 0.0) - fabY = math.min(fabY, size.height - snackBarSize.height - fabSize.height - _kFloatingActionButtonMargin); + fabY = math.min(fabY, contentBottom - snackBarSize.height - fabSize.height - _kFloatingActionButtonMargin); if (bottomSheetSize.height > 0.0) - fabY = math.min(fabY, size.height - bottomSheetSize.height - fabSize.height / 2.0); + fabY = math.min(fabY, contentBottom - bottomSheetSize.height - fabSize.height / 2.0); positionChild(_ScaffoldSlot.floatingActionButton, new Offset(fabX, fabY)); } @@ -94,7 +98,9 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate { } } - bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) => false; + bool shouldRelayout(_ScaffoldLayout oldDelegate) { + return padding != oldDelegate.padding; + } } class _FloatingActionButtonTransition extends StatefulComponent { @@ -329,11 +335,11 @@ class ScaffoldState extends State { bool _shouldShowBackArrow; - Widget get _modifiedToolBar { + Widget _getModifiedToolBar(EdgeDims padding) { ToolBar toolBar = config.toolBar; if (toolBar == null) return null; - EdgeDims padding = new EdgeDims.only(top: ui.window.padding.top); + EdgeDims toolBarPadding = new EdgeDims.only(top: padding.top); Widget left = toolBar.left; if (left == null) { if (config.drawer != null) { @@ -354,13 +360,13 @@ class ScaffoldState extends State { } } return toolBar.copyWith( - padding: padding, + padding: toolBarPadding, left: left ); } Widget build(BuildContext context) { - final Widget materialBody = config.body != null ? new Material(child: config.body) : null; + EdgeDims padding = MediaQuery.of(context).padding; if (_snackBars.length > 0) { ModalRoute route = ModalRoute.of(context); @@ -373,9 +379,9 @@ class ScaffoldState extends State { } } - final Listchildren = new List(); - _addIfNonNull(children, materialBody, _ScaffoldSlot.body); - _addIfNonNull(children, _modifiedToolBar, _ScaffoldSlot.toolBar); + final List children = new List(); + _addIfNonNull(children, config.body, _ScaffoldSlot.body); + _addIfNonNull(children, _getModifiedToolBar(padding), _ScaffoldSlot.toolBar); if (_currentBottomSheet != null || (_dismissedBottomSheets != null && _dismissedBottomSheets.isNotEmpty)) { @@ -412,7 +418,14 @@ class ScaffoldState extends State { )); } - return new CustomMultiChildLayout(children: children, delegate: new _ScaffoldLayout()); + return new Material( + child: new CustomMultiChildLayout( + children: children, + delegate: new _ScaffoldLayout( + padding: padding + ) + ) + ); } } diff --git a/packages/flutter/lib/src/widgets/media_query.dart b/packages/flutter/lib/src/widgets/media_query.dart index a652b48a588..e35ab3f2196 100644 --- a/packages/flutter/lib/src/widgets/media_query.dart +++ b/packages/flutter/lib/src/widgets/media_query.dart @@ -16,11 +16,14 @@ enum Orientation { /// The result of a media query. class MediaQueryData { - const MediaQueryData({ this.size }); + const MediaQueryData({ this.size, this.padding }); /// The size of the media (e.g, the size of the screen). final Size size; + /// The padding around the edges of the media (e.g., the screen). + final EdgeDims padding; + /// The orientation of the media (e.g., whether the device is in landscape or portrait mode). Orientation get orientation { return size.width > size.height ? Orientation.landscape : Orientation.portrait; @@ -30,7 +33,8 @@ class MediaQueryData { if (other.runtimeType != runtimeType) return false; MediaQueryData typedOther = other; - return typedOther.size == size; + return typedOther.size == size + && typedOther.padding == padding; } int get hashCode => size.hashCode;