Commit 875d8262 authored by Hans Muller's avatar Hans Muller

One Scaffold layout to rule them all.

parent d74a7517
......@@ -2,6 +2,7 @@
// 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 'dart:ui' as ui;
import 'package:flutter/rendering.dart';
......@@ -11,16 +12,23 @@ import 'constants.dart';
import 'material.dart';
import 'tool_bar.dart';
const double _kFloatingActionButtonMargin = 16.0; // TODO(hmuller): should be device dependent
const int _kBodyIndex = 0;
const int _kToolBarIndex = 1;
const int _kBottomSheetIndex = 2;
const int _kSnackBarIndex = 3;
const int _kFloatingActionButtonIndex = 4;
// This layout has the same effect as putting the toolbar and body in a column
// and making the body flexible. What's different is that in this case the
// toolbar appears -after- the body in the stacking order, so the toolbar's
// shadow is drawn on top of the body.
class _ToolBarAndBodyLayout extends MultiChildLayoutDelegate {
class _ScaffoldLayout extends MultiChildLayoutDelegate {
void performLayout(Size size, BoxConstraints constraints, int childCount) {
assert(childCount == 2);
assert(childCount == 5);
// This part of the layout has the same effect as putting the toolbar and
// body in a column and making the body flexible. What's different is that
// in this case the toolbar appears -after- the body in the stacking order,
// so the toolbar's shadow is drawn on top of the body.
final BoxConstraints toolBarConstraints = constraints.loosen().tightenWidth(size.width);
final Size toolBarSize = layoutChild(_kToolBarIndex, toolBarConstraints);
final double bodyHeight = size.height - toolBarSize.height;
......@@ -28,10 +36,34 @@ class _ToolBarAndBodyLayout extends MultiChildLayoutDelegate {
layoutChild(_kBodyIndex, bodyConstraints);
positionChild(_kToolBarIndex, Point.origin);
positionChild(_kBodyIndex, new Point(0.0, toolBarSize.height));
// The BottomSheet and the SnackBar are anchored to the bottom of the parent,
// they're as wide as the parent and are given their intrinsic height.
// If all three elements are present then either the center of the FAB straddles
// the top edge of the BottomSheet or the bottom of the FAB is
// _kFloatingActionButtonMargin above the SnackBar, whichever puts the FAB
// the farthest above the bottom of the parent. If only the FAB is has a
// non-zero height then it's inset from the parent's right and bottom edges
// by _kFloatingActionButtonMargin.
final BoxConstraints fullWidthConstraints = constraints.loosen().tightenWidth(size.width);
final Size bottomSheetSize = layoutChild(_kBottomSheetIndex, fullWidthConstraints);
final Size snackBarSize = layoutChild(_kSnackBarIndex, fullWidthConstraints);
final Size fabSize = layoutChild(_kFloatingActionButtonIndex, constraints.loosen());
positionChild(_kBottomSheetIndex, new Point(0.0, size.height - bottomSheetSize.height));
positionChild(_kSnackBarIndex, new Point(0.0, size.height - snackBarSize.height));
final double fabX = size.width - fabSize.width - _kFloatingActionButtonMargin;
double fabY = size.height - fabSize.height - _kFloatingActionButtonMargin;
if (snackBarSize.height > 0.0)
fabY = math.min(fabY, size.height - snackBarSize.height - fabSize.height - _kFloatingActionButtonMargin);
if (bottomSheetSize.height > 0.0)
fabY = math.min(fabY, size.height - bottomSheetSize.height - fabSize.height / 2.0);
positionChild(_kFloatingActionButtonIndex, new Point(fabX, fabY));
}
}
final _ToolBarAndBodyLayout _toolBarAndBodyLayout = new _ToolBarAndBodyLayout();
final _ScaffoldLayout _scaffoldLayout = new _ScaffoldLayout();
class Scaffold extends StatelessComponent {
Scaffold({
......@@ -39,53 +71,35 @@ class Scaffold extends StatelessComponent {
this.body,
this.toolBar,
this.snackBar,
this.floatingActionButton
this.floatingActionButton,
this.bottomSheet
}) : super(key: key);
final Widget body;
final ToolBar toolBar;
final Widget snackBar;
final Widget floatingActionButton;
final Widget bottomSheet;
Widget build(BuildContext context) {
final ToolBar paddedToolBar = toolBar?.withPadding(new EdgeDims.only(top: ui.window.padding.top));
final Widget paddedToolBar = toolBar?.withPadding(new EdgeDims.only(top: ui.window.padding.top));
final Widget materialBody = body != null ? new Material(child: body) : null;
Widget toolBarAndBody;
if (paddedToolBar != null && materialBody != null)
toolBarAndBody = new CustomMultiChildLayout(<Widget>[materialBody, paddedToolBar],
delegate: _toolBarAndBodyLayout
);
else
toolBarAndBody = paddedToolBar ?? materialBody;
final List<Widget> bottomColumnChildren = <Widget>[];
if (floatingActionButton != null)
bottomColumnChildren.add(new Padding(
// TODO(eseidel): These change based on device size!
padding: const EdgeDims.only(right: 16.0, bottom: 16.0),
child: floatingActionButton
));
// TODO(jackson): On tablet/desktop, minWidth = 288, maxWidth = 568
Widget constrainedSnackBar;
if (snackBar != null) {
bottomColumnChildren.add(new ConstrainedBox(
// TODO(jackson): On tablet/desktop, minWidth = 288, maxWidth = 568
constrainedSnackBar = new ConstrainedBox(
constraints: const BoxConstraints(maxHeight: kSnackBarHeight),
child: snackBar
));
}
final List<Widget> stackChildren = <Widget>[toolBarAndBody];
if (bottomColumnChildren.length > 0) {
stackChildren.add(new Positioned(
right: 0.0,
left: 0.0,
bottom: 0.0,
child: new Column(bottomColumnChildren, alignItems: FlexAlignItems.end)
));
);
}
return new Stack(stackChildren);
return new CustomMultiChildLayout(<Widget>[
materialBody ?? new Container(height: 0.0),
paddedToolBar ?? new Container(height: 0.0),
bottomSheet ?? new Container(height: 0.0),
constrainedSnackBar ?? new Container(height: 0.0),
floatingActionButton ?? new Container(height: 0.0)
],
delegate: _scaffoldLayout
);
}
}
......@@ -11,6 +11,9 @@ abstract class MultiChildLayoutDelegate {
final List<RenderBox> _indexToChild = <RenderBox>[];
/// Returns the size of this object given the incomming constraints.
/// The size cannot reflect the instrinsic sizes of the children.
/// If this layout has a fixed width or height the returned size
/// can reflect that.
Size getSize(BoxConstraints constraints) => constraints.biggest;
/// Ask the child to update its layout within the limits specified by
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment