Commit 89a09822 authored by Adam Barth's avatar Adam Barth

Simplify Scaffold

Rather than using a custom render object, we can just use a Stack.
parent 922aece1
......@@ -81,7 +81,7 @@ class AddressBookHome extends StatelessComponent {
Widget build(BuildContext context) {
return new Scaffold(
toolbar: buildToolBar(context),
toolBar: buildToolBar(context),
body: buildBody(context),
floatingActionButton: buildFloatingActionButton(context)
);
......
......@@ -192,7 +192,7 @@ final ThemeData _theme = new ThemeData(
class DemoHome extends StatelessComponent {
Widget build(BuildContext context) {
return new Scaffold(
toolbar: new ToolBar(center: new Text('Sky Demos')),
toolBar: new ToolBar(center: new Text('Sky Demos')),
body: new Material(
type: MaterialType.canvas,
child: new DemoList()
......
......@@ -219,7 +219,7 @@ class FeedFragmentState extends State<FeedFragment> {
Widget build(BuildContext context) {
return new Scaffold(
toolbar: buildToolBar(),
toolBar: buildToolBar(),
body: buildBody(),
snackBar: new Placeholder(key: _snackBarPlaceholderKey),
floatingActionButton: buildFloatingActionButton()
......
......@@ -105,7 +105,7 @@ class MealFragmentState extends State<MealFragment> {
Widget build(BuildContext context) {
return new Scaffold(
toolbar: buildToolBar(),
toolBar: buildToolBar(),
body: buildBody()
);
}
......
......@@ -200,7 +200,7 @@ class MeasurementFragmentState extends State<MeasurementFragment> {
Widget build(BuildContext context) {
return new Scaffold(
toolbar: buildToolBar(),
toolBar: buildToolBar(),
body: buildBody(context),
snackBar: new Placeholder(key: _snackBarPlaceholderKey)
);
......
......@@ -122,7 +122,7 @@ class SettingsFragmentState extends State<SettingsFragment> {
Widget build(BuildContext context) {
return new Scaffold(
toolbar: buildToolBar(),
toolBar: buildToolBar(),
body: buildSettingsPane(context)
);
}
......
......@@ -182,7 +182,7 @@ class MineDiggerState extends State<MineDigger> {
return new Title(
title: 'Mine Digger',
child: new Scaffold(
toolbar: buildToolBar(context),
toolBar: buildToolBar(context),
body: new Container(
child: new Center(child: board),
decoration: new BoxDecoration(backgroundColor: Colors.grey[50])
......
......@@ -237,7 +237,7 @@ class StockHomeState extends State<StockHome> {
Widget build(BuildContext context) {
return new Scaffold(
toolbar: _isSearching ? buildSearchBar() : buildToolBar(),
toolBar: _isSearching ? buildSearchBar() : buildToolBar(),
body: buildTabNavigator(),
snackBar: new Placeholder(key: _snackBarPlaceholderKey),
floatingActionButton: buildFloatingActionButton()
......
......@@ -119,7 +119,7 @@ class StockSettingsState extends State<StockSettings> {
Widget build(BuildContext context) {
return new Scaffold(
toolbar: buildToolBar(context),
toolBar: buildToolBar(context),
body: buildSettingsPane(context)
);
}
......
......@@ -25,7 +25,7 @@ class StockSymbolViewerState extends State<StockSymbolViewer> {
TextStyle headings = Theme.of(context).text.body2;
return new Scaffold(
toolbar: new ToolBar(
toolBar: new ToolBar(
left: new IconButton(
icon: 'navigation/arrow_back',
onPressed: config.navigator.pop
......
......@@ -342,7 +342,7 @@ class CardCollectionState extends State<CardCollection> {
}
return new Scaffold(
toolbar: buildToolBar(),
toolBar: buildToolBar(),
body: body
);
}
......
......@@ -34,7 +34,7 @@ class DatePickerDemoState extends State<DatePickerDemo> {
),
child: new Stack([
new Scaffold(
toolbar: new ToolBar(center: new Text("Date Picker")),
toolBar: new ToolBar(center: new Text("Date Picker")),
body: new Material(
child: new Row(
[new Text(_dateTime.toString())],
......
......@@ -101,7 +101,7 @@ class DragAndDropApp extends StatefulComponent {
class DragAndDropAppState extends State<DragAndDropApp> {
Widget build(BuildContext context) {
return new Scaffold(
toolbar: new ToolBar(
toolBar: new ToolBar(
center: new Text('Drag and Drop Flutter Demo')
),
body: new Material(
......
......@@ -89,7 +89,7 @@ class EnsureVisibleApp extends App {
child: new Title(
title: 'Cards',
child: new Scaffold(
toolbar: new ToolBar(center: new Text('Tap a Card')),
toolBar: new ToolBar(center: new Text('Tap a Card')),
body: cardCollection
)
)
......
......@@ -138,7 +138,7 @@ class OverlayGeometryAppState extends State<OverlayGeometryApp> {
Widget build(BuildContext context) {
List<Widget> layers = <Widget>[
new Scaffold(
toolbar: new ToolBar(center: new Text('Tap a Card')),
toolBar: new ToolBar(center: new Text('Tap a Card')),
body: new Container(
padding: const EdgeDims.symmetric(vertical: 12.0, horizontal: 8.0),
decoration: new BoxDecoration(backgroundColor: Theme.of(context).primarySwatch[50]),
......
......@@ -149,7 +149,7 @@ class PageableListAppState extends State<PageableListApp> {
return new IconTheme(
data: const IconThemeData(color: IconThemeColor.white),
child: new Scaffold(
toolbar: buildToolBar(),
toolBar: buildToolBar(),
body: buildBody(context)
)
);
......
......@@ -103,7 +103,7 @@ class ProgressIndicatorAppState extends State<ProgressIndicatorApp> {
child: new Title(
title: 'Progress Indicators',
child: new Scaffold(
toolbar: new ToolBar(center: new Text('Progress Indicators')),
toolBar: new ToolBar(center: new Text('Progress Indicators')),
body: new DefaultTextStyle(
style: Theme.of(context).text.title,
child: body
......
......@@ -59,7 +59,7 @@ class ScaleAppState extends State<ScaleApp> {
return new Theme(
data: new ThemeData.dark(),
child: new Scaffold(
toolbar: new ToolBar(
toolBar: new ToolBar(
center: new Text('Scale Demo')),
body: new Material(
type: MaterialType.canvas,
......
......@@ -124,7 +124,7 @@ class SectorApp extends App {
child: new Title(
title: 'Sector Layout',
child: new Scaffold(
toolbar: new ToolBar(
toolBar: new ToolBar(
center: new Text('Sector Layout in a Widget Tree')
),
body: buildBody()
......
......@@ -106,7 +106,7 @@ HAL: This mission is too important for me to allow you to jeopardize it.''';
color: Colors.grey[50],
child: interactiveBody
),
toolbar: new ToolBar(
toolBar: new ToolBar(
center: new Text('Hal and Dave')
)
)
......
......@@ -131,7 +131,7 @@ class TabbedNavigatorAppState extends State<TabbedNavigatorApp> {
);
return new Scaffold(
toolbar: toolbar,
toolBar: toolbar,
body: tabNavigator
);
}
......
......@@ -14,6 +14,7 @@ const double kStatusBarHeight = 50.0;
// Mobile Portrait: 56dp
// Tablet/Desktop: 64dp
const double kToolBarHeight = 56.0;
const double kSnackBarHeight = 52.0;
const double kMaterialDrawerHeight = 140.0;
const double kScrollbarSize = 10.0;
......
......@@ -6,247 +6,88 @@ import 'dart:sky' as sky;
import 'package:sky/material.dart';
import 'package:sky/rendering.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/framework.dart';
// Slots are painted in this order and hit tested in reverse of this order
enum _ScaffoldSlots {
body,
statusBar,
toolbar,
snackBar,
floatingActionButton,
drawer
}
class _RenderScaffold extends RenderBox {
_RenderScaffold({
RenderBox body,
RenderBox statusBar,
RenderBox toolbar,
RenderBox snackBar,
RenderBox floatingActionButton,
RenderBox drawer
}) {
this[_ScaffoldSlots.body] = body;
this[_ScaffoldSlots.statusBar] = statusBar;
this[_ScaffoldSlots.toolbar] = toolbar;
this[_ScaffoldSlots.snackBar] = snackBar;
this[_ScaffoldSlots.floatingActionButton] = floatingActionButton;
this[_ScaffoldSlots.drawer] = drawer;
}
Map<_ScaffoldSlots, RenderBox> _slots = new Map<_ScaffoldSlots, RenderBox>();
RenderBox operator[] (_ScaffoldSlots slot) => _slots[slot];
void operator[]= (_ScaffoldSlots slot, RenderBox value) {
RenderBox old = _slots[slot];
if (old == value)
return;
if (old != null)
dropChild(old);
if (value == null) {
_slots.remove(slot);
} else {
_slots[slot] = value;
adoptChild(value);
}
markNeedsLayout();
}
void attachChildren() {
for (_ScaffoldSlots slot in _ScaffoldSlots.values) {
RenderBox box = _slots[slot];
if (box != null)
box.attach();
}
}
void detachChildren() {
for (_ScaffoldSlots slot in _ScaffoldSlots.values) {
RenderBox box = _slots[slot];
if (box != null)
box.detach();
}
}
void visitChildren(RenderObjectVisitor visitor) {
for (_ScaffoldSlots slot in _ScaffoldSlots.values) {
RenderBox box = _slots[slot];
if (box != null)
visitor(box);
}
}
_ScaffoldSlots remove(RenderBox child) {
assert(child != null);
for (_ScaffoldSlots slot in _ScaffoldSlots.values) {
if (_slots[slot] == child) {
this[slot] = null;
return slot;
class Scaffold extends StatelessComponent {
Scaffold({
Key key,
this.body,
this.statusBar,
this.toolBar,
this.snackBar,
this.floatingActionButton
}) : super(key: key);
final Widget body;
final Widget statusBar;
final Widget toolBar;
final Widget snackBar;
final Widget floatingActionButton;
Widget build(BuildContext context) {
double toolBarHeight = 0.0;
if (toolBar != null)
toolBarHeight = kToolBarHeight + sky.view.paddingTop;
double statusBarHeight = 0.0;
if (statusBar != null)
statusBarHeight = kStatusBarHeight;
List<Widget> children = <Widget>[];
if (body != null) {
children.add(new Positioned(
top: toolBarHeight, right: 0.0, bottom: statusBarHeight, left: 0.0,
child: body
));
}
if (statusBar != null) {
children.add(new Positioned(
right: 0.0, bottom: 0.0, left: 0.0,
child: new SizedBox(
height: statusBarHeight,
child: statusBar
)
));
}
if (toolBar != null) {
children.add(new Positioned(
top: 0.0, right: 0.0, left: 0.0,
child: new SizedBox(
height: toolBarHeight,
child: toolBar
)
));
}
if (snackBar != null || floatingActionButton != null) {
List<Widget> floatingChildren = <Widget>[];
if (floatingActionButton != null) {
floatingChildren.add(new Padding(
// TODO(eseidel): These change based on device size!
padding: const EdgeDims.only(right: 16.0, bottom: 16.0),
child: floatingActionButton
));
}
}
return null;
}
bool get sizedByParent => true;
void performResize() {
size = constraints.biggest;
assert(!size.isInfinite);
}
// TODO(eseidel): These change based on device size!
// http://www.google.com/design/spec/layout/metrics-keylines.html#metrics-keylines-keylines-spacing
static const kButtonX = 16.0; // left from right edge of body
static const kButtonY = 16.0; // up from bottom edge of body
void performLayout() {
double bodyHeight = size.height;
double bodyPosition = 0.0;
double fabOffset = 0.0;
if (_slots[_ScaffoldSlots.statusBar] != null) {
RenderBox statusBar = _slots[_ScaffoldSlots.statusBar];
statusBar.layout(new BoxConstraints.tight(new Size(size.width, kStatusBarHeight)));
assert(statusBar.parentData is BoxParentData);
statusBar.parentData.position = new Point(0.0, size.height - kStatusBarHeight);
bodyHeight -= kStatusBarHeight;
}
if (_slots[_ScaffoldSlots.toolbar] != null) {
RenderBox toolbar = _slots[_ScaffoldSlots.toolbar];
double toolbarHeight = kToolBarHeight + sky.view.paddingTop;
toolbar.layout(new BoxConstraints.tight(new Size(size.width, toolbarHeight)));
assert(toolbar.parentData is BoxParentData);
toolbar.parentData.position = Point.origin;
bodyPosition += toolbarHeight;
bodyHeight -= toolbarHeight;
}
if (_slots[_ScaffoldSlots.body] != null) {
RenderBox body = _slots[_ScaffoldSlots.body];
body.layout(new BoxConstraints.tight(new Size(size.width, bodyHeight)));
assert(body.parentData is BoxParentData);
body.parentData.position = new Point(0.0, bodyPosition);
}
if (_slots[_ScaffoldSlots.snackBar] != null) {
RenderBox snackBar = _slots[_ScaffoldSlots.snackBar];
// TODO(jackson): On tablet/desktop, minWidth = 288, maxWidth = 568
snackBar.layout(
new BoxConstraints(minWidth: size.width, maxWidth: size.width, minHeight: 0.0, maxHeight: bodyHeight),
parentUsesSize: true
);
assert(snackBar.parentData is BoxParentData);
snackBar.parentData.position = new Point(0.0, bodyPosition + bodyHeight - snackBar.size.height);
fabOffset += snackBar.size.height;
}
if (_slots[_ScaffoldSlots.floatingActionButton] != null) {
RenderBox floatingActionButton = _slots[_ScaffoldSlots.floatingActionButton];
Size area = new Size(size.width - kButtonX, size.height - kButtonY);
floatingActionButton.layout(new BoxConstraints.loose(area), parentUsesSize: true);
assert(floatingActionButton.parentData is BoxParentData);
floatingActionButton.parentData.position = (area - floatingActionButton.size).toPoint() + new Offset(0.0, -fabOffset);
}
if (_slots[_ScaffoldSlots.drawer] != null) {
RenderBox drawer = _slots[_ScaffoldSlots.drawer];
drawer.layout(new BoxConstraints(minWidth: 0.0, maxWidth: size.width, minHeight: size.height, maxHeight: size.height));
assert(drawer.parentData is BoxParentData);
drawer.parentData.position = Point.origin;
}
}
void paint(PaintingContext context, Offset offset) {
for (_ScaffoldSlots slot in _ScaffoldSlots.values) {
RenderBox box = _slots[slot];
if (box != null) {
assert(box.parentData is BoxParentData);
context.paintChild(box, box.parentData.position + offset);
if (snackBar != null) {
floatingChildren.add(new ConstrainedBox(
constraints: const BoxConstraints(maxHeight: kSnackBarHeight),
child: snackBar
));
}
}
}
void hitTestChildren(HitTestResult result, { Point position }) {
for (_ScaffoldSlots slot in _ScaffoldSlots.values.reversed) {
RenderBox box = _slots[slot];
if (box != null) {
assert(box.parentData is BoxParentData);
if (box.hitTest(result, position: (position - box.parentData.position).toPoint()))
return;
}
children.add(new Positioned(
right: 0.0, bottom: statusBarHeight, left: 0.0,
child: new Column(floatingChildren, alignItems: FlexAlignItems.end)
));
}
}
String debugDescribeChildren(String prefix) {
return _slots.keys.map((slot) => '${prefix}${slot}: ${_slots[slot].toStringDeep(prefix)}').join();
}
}
class Scaffold extends RenderObjectWidget {
Scaffold({
Key key,
Widget body,
Widget statusBar,
Widget toolbar,
Widget snackBar,
Widget floatingActionButton,
Widget drawer
}) : super(key: key) {
_children[_ScaffoldSlots.body] = body;
_children[_ScaffoldSlots.statusBar] = statusBar;
_children[_ScaffoldSlots.toolbar] = toolbar;
_children[_ScaffoldSlots.snackBar] = snackBar;
_children[_ScaffoldSlots.floatingActionButton] = floatingActionButton;
_children[_ScaffoldSlots.drawer] = drawer;
}
final Map<_ScaffoldSlots, Widget> _children = new Map<_ScaffoldSlots, Widget>();
_RenderScaffold createRenderObject() => new _RenderScaffold();
_ScaffoldElement createElement() => new _ScaffoldElement(this);
}
class _ScaffoldElement extends RenderObjectElement<Scaffold> {
_ScaffoldElement(Scaffold widget) : super(widget);
Map<_ScaffoldSlots, Element> _children;
_RenderScaffold get renderObject => super.renderObject;
void visitChildren(ElementVisitor visitor) {
for (_ScaffoldSlots slot in _ScaffoldSlots.values) {
Element element = _children[slot];
if (element != null)
visitor(element);
}
}
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
_children = new Map<_ScaffoldSlots, Element>();
for (_ScaffoldSlots slot in _ScaffoldSlots.values) {
Element newChild = widget._children[slot]?.createElement();
_children[slot] = newChild;
newChild?.mount(this, slot);
}
}
void update(Scaffold newWidget) {
super.update(newWidget);
assert(widget == newWidget);
for (_ScaffoldSlots slot in _ScaffoldSlots.values) {
_children[slot] = updateChild(_children[slot], widget._children[slot], slot);
assert((_children[slot] == null) == (widget._children[slot] == null));
}
}
void insertChildRenderObject(RenderObject child, _ScaffoldSlots slot) {
renderObject[slot] = child;
}
void moveChildRenderObject(RenderObject child, dynamic slot) {
removeChildRenderObject(child);
insertChildRenderObject(child, slot);
}
void removeChildRenderObject(RenderObject child) {
assert(renderObject == child.parent);
renderObject.remove(child);
return new Stack(children);
}
}
......@@ -15,7 +15,6 @@ import 'package:sky/src/widgets/placeholder.dart';
import 'package:sky/src/widgets/theme.dart';
import 'package:sky/src/widgets/transitions.dart';
const double _kSnackHeight = 52.0;
const double _kSideMargins = 24.0;
const double _kVerticalPadding = 14.0;
const Color _kSnackBackground = const Color(0xFF323232);
......@@ -72,14 +71,14 @@ class SnackBar extends StatelessComponent {
performance: performance,
height: new AnimatedValue<double>(
0.0,
end: _kSnackHeight,
end: kSnackBarHeight,
curve: easeIn,
reverseCurve: easeOut
),
child: new ClipRect(
child: new OverflowBox(
minHeight: _kSnackHeight,
maxHeight: _kSnackHeight,
minHeight: kSnackBarHeight,
maxHeight: kSnackBarHeight,
child: new Material(
level: 2,
color: _kSnackBackground,
......
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