mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Merge pull request #1952 from HansMuller/snack_bottom_fab
One Scaffold layout to rule them all Expanded the existing CustomMultiChildLayout to handle all of the Scaffold's children. This change also eliminates the FAB input dead-zone.
This commit is contained in:
commit
7a370b8aba
@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'dart:math' as math;
|
||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
@ -11,16 +12,23 @@ import 'constants.dart';
|
|||||||
import 'material.dart';
|
import 'material.dart';
|
||||||
import 'tool_bar.dart';
|
import 'tool_bar.dart';
|
||||||
|
|
||||||
|
const double _kFloatingActionButtonMargin = 16.0; // TODO(hmuller): should be device dependent
|
||||||
|
|
||||||
const int _kBodyIndex = 0;
|
const int _kBodyIndex = 0;
|
||||||
const int _kToolBarIndex = 1;
|
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
|
class _ScaffoldLayout extends MultiChildLayoutDelegate {
|
||||||
// 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 {
|
|
||||||
void performLayout(Size size, BoxConstraints constraints, int childCount) {
|
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 BoxConstraints toolBarConstraints = constraints.loosen().tightenWidth(size.width);
|
||||||
final Size toolBarSize = layoutChild(_kToolBarIndex, toolBarConstraints);
|
final Size toolBarSize = layoutChild(_kToolBarIndex, toolBarConstraints);
|
||||||
final double bodyHeight = size.height - toolBarSize.height;
|
final double bodyHeight = size.height - toolBarSize.height;
|
||||||
@ -28,10 +36,34 @@ class _ToolBarAndBodyLayout extends MultiChildLayoutDelegate {
|
|||||||
layoutChild(_kBodyIndex, bodyConstraints);
|
layoutChild(_kBodyIndex, bodyConstraints);
|
||||||
positionChild(_kToolBarIndex, Point.origin);
|
positionChild(_kToolBarIndex, Point.origin);
|
||||||
positionChild(_kBodyIndex, new Point(0.0, toolBarSize.height));
|
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 {
|
class Scaffold extends StatelessComponent {
|
||||||
Scaffold({
|
Scaffold({
|
||||||
@ -39,53 +71,35 @@ class Scaffold extends StatelessComponent {
|
|||||||
this.body,
|
this.body,
|
||||||
this.toolBar,
|
this.toolBar,
|
||||||
this.snackBar,
|
this.snackBar,
|
||||||
this.floatingActionButton
|
this.floatingActionButton,
|
||||||
|
this.bottomSheet
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final Widget body;
|
final Widget body;
|
||||||
final ToolBar toolBar;
|
final ToolBar toolBar;
|
||||||
final Widget snackBar;
|
final Widget snackBar;
|
||||||
final Widget floatingActionButton;
|
final Widget floatingActionButton;
|
||||||
|
final Widget bottomSheet;
|
||||||
|
|
||||||
Widget build(BuildContext context) {
|
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;
|
final Widget materialBody = body != null ? new Material(child: body) : null;
|
||||||
Widget toolBarAndBody;
|
Widget constrainedSnackBar;
|
||||||
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
|
|
||||||
if (snackBar != null) {
|
if (snackBar != null) {
|
||||||
bottomColumnChildren.add(new ConstrainedBox(
|
// TODO(jackson): On tablet/desktop, minWidth = 288, maxWidth = 568
|
||||||
|
constrainedSnackBar = new ConstrainedBox(
|
||||||
constraints: const BoxConstraints(maxHeight: kSnackBarHeight),
|
constraints: const BoxConstraints(maxHeight: kSnackBarHeight),
|
||||||
child: snackBar
|
child: snackBar
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
|
return new CustomMultiChildLayout(<Widget>[
|
||||||
final List<Widget> stackChildren = <Widget>[toolBarAndBody];
|
materialBody ?? new Container(height: 0.0),
|
||||||
|
paddedToolBar ?? new Container(height: 0.0),
|
||||||
if (bottomColumnChildren.length > 0) {
|
bottomSheet ?? new Container(height: 0.0),
|
||||||
stackChildren.add(new Positioned(
|
constrainedSnackBar ?? new Container(height: 0.0),
|
||||||
right: 0.0,
|
floatingActionButton ?? new Container(height: 0.0)
|
||||||
left: 0.0,
|
],
|
||||||
bottom: 0.0,
|
delegate: _scaffoldLayout
|
||||||
child: new Column(bottomColumnChildren, alignItems: FlexAlignItems.end)
|
);
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Stack(stackChildren);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,9 @@ abstract class MultiChildLayoutDelegate {
|
|||||||
final List<RenderBox> _indexToChild = <RenderBox>[];
|
final List<RenderBox> _indexToChild = <RenderBox>[];
|
||||||
|
|
||||||
/// Returns the size of this object given the incomming constraints.
|
/// 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;
|
Size getSize(BoxConstraints constraints) => constraints.biggest;
|
||||||
|
|
||||||
/// Ask the child to update its layout within the limits specified by
|
/// Ask the child to update its layout within the limits specified by
|
||||||
|
Loading…
Reference in New Issue
Block a user