| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'package:flutter/widgets.dart'; |
| |
| import 'colors.dart'; |
| |
| /// Implements a single iOS application page's layout. |
| /// |
| /// The scaffold lays out the navigation bar on top and the content between or |
| /// behind the navigation bar. |
| /// |
| /// See also: |
| /// |
| /// * [CupertinoTabScaffold], a similar widget for tabbed applications. |
| /// * [CupertinoPageRoute], a modal page route that typically hosts a |
| /// [CupertinoPageScaffold] with support for iOS-style page transitions. |
| class CupertinoPageScaffold extends StatelessWidget { |
| /// Creates a layout for pages with a navigation bar at the top. |
| const CupertinoPageScaffold({ |
| 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 |
| /// top of the screen. |
| /// |
| /// If translucent, the main content may slide behind it. |
| /// Otherwise, the main content's top margin will be offset by its height. |
| /// |
| /// The scaffold assumes the navigation bar will consume the [MediaQuery] top padding. |
| // TODO(xster): document its page transition animation when ready |
| final ObstructingPreferredSizeWidget navigationBar; |
| |
| /// Widget to show in the main content area. |
| /// |
| /// Content can slide under the [navigationBar] when they're translucent. |
| /// In that case, the child's [BuildContext]'s [MediaQuery] will have a |
| /// top padding indicating the area of obstructing overlap from the |
| /// [navigationBar]. |
| final Widget child; |
| |
| /// The color of the widget that underlies the entire scaffold. |
| /// |
| /// 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<Widget> stacked = <Widget>[]; |
| |
| Widget paddedContent = child; |
| if (navigationBar != null) { |
| final MediaQueryData existingMediaQuery = MediaQuery.of(context); |
| |
| // 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; |
| |
| // 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, bottom: bottomPadding), |
| child: child, |
| ); |
| } else { |
| paddedContent = new MediaQuery( |
| data: existingMediaQuery.copyWith( |
| padding: existingMediaQuery.padding.copyWith( |
| top: topPadding, |
| ), |
| ), |
| child: new Padding( |
| padding: new EdgeInsets.only(bottom: bottomPadding), |
| child: child, |
| ), |
| ); |
| } |
| } |
| |
| // The main content being at the bottom is added to the stack first. |
| stacked.add(paddedContent); |
| |
| if (navigationBar != null) { |
| stacked.add(new Positioned( |
| top: 0.0, |
| left: 0.0, |
| right: 0.0, |
| child: navigationBar, |
| )); |
| } |
| |
| return new DecoratedBox( |
| decoration: new BoxDecoration(color: backgroundColor), |
| child: new Stack( |
| children: stacked, |
| ), |
| ); |
| } |
| } |
| |
| /// Widget that has a preferred size and reports whether it fully obstructs |
| /// widgets behind it. |
| /// |
| /// Used by [CupertinoPageScaffold] to either shift away fully obstructed content |
| /// or provide a padding guide to partially obstructed content. |
| abstract class ObstructingPreferredSizeWidget extends PreferredSizeWidget { |
| /// If true, this widget fully obstructs widgets behind it by the specified |
| /// size. |
| /// |
| /// If false, this widget partially obstructs. |
| bool get fullObstruction; |
| } |