From ff3e636635c2c40e4ae64ab6486b0041b38fbf53 Mon Sep 17 00:00:00 2001 From: Viktor Lidholt Date: Mon, 5 Oct 2015 16:13:32 -0700 Subject: [PATCH 01/16] Makes physics bodies fixture elements settable --- .../flutter_sprites/lib/physics_body.dart | 74 ++++++++++++++++--- 1 file changed, 65 insertions(+), 9 deletions(-) diff --git a/packages/flutter_sprites/lib/physics_body.dart b/packages/flutter_sprites/lib/physics_body.dart index e9446b65e1..d42fa8d98b 100644 --- a/packages/flutter_sprites/lib/physics_body.dart +++ b/packages/flutter_sprites/lib/physics_body.dart @@ -9,10 +9,10 @@ class PhysicsBody { PhysicsBody(this.shape, { this.tag: null, this.type: PhysicsBodyType.dynamic, - this.density: 1.0, - this.friction: 0.0, - this.restitution: 0.0, - this.isSensor: false, + double density: 1.0, + double friction: 0.0, + double restitution: 0.0, + bool isSensor: false, this.linearVelocity: Offset.zero, this.angularVelocity: 0.0, this.linearDampening: 0.0, @@ -23,7 +23,12 @@ class PhysicsBody { this.bullet: false, this.active: true, this.gravityScale: 1.0 - }); + }) { + this.density = density; + this.friction = friction; + this.restitution = restitution; + this.isSensor = isSensor; + } Object tag; @@ -31,10 +36,61 @@ class PhysicsBody { PhysicsBodyType type; - double density; - double friction; - double restitution; - bool isSensor; + double _density; + + double get density => _density; + + set density(double density) { + _density = density; + + if (_body == null) + return; + for(box2d.Fixture f = _body.getFixtureList(); f != null; f = f.getNext()) { + f.setDensity(density); + } + } + + double _friction; + + double get friction => _friction; + + set friction(double friction) { + _friction = friction; + + if (_body == null) + return; + for(box2d.Fixture f = _body.getFixtureList(); f != null; f = f.getNext()) { + f.setFriction(friction); + } + } + + double _restitution; + + double get restitution => _restitution; + + set restitution(double restitution) { + _restitution = restitution; + + if (_body == null) + return; + for(box2d.Fixture f = _body.getFixtureList(); f != null; f = f.getNext()) { + f.setRestitution(restitution); + } + } + + bool _isSensor; + + bool get isSensor => _isSensor; + + set isSensor(bool isSensor) { + _isSensor = isSensor; + + if (_body == null) + return; + for(box2d.Fixture f = _body.getFixtureList(); f != null; f = f.getNext()) { + f.setSensor(isSensor); + } + } Offset linearVelocity; double angularVelocity; From fbf8174c87eb4cab85d0ebf43f672cb8493c63da Mon Sep 17 00:00:00 2001 From: Hixie Date: Mon, 5 Oct 2015 15:52:36 -0700 Subject: [PATCH 02/16] Fix Focus Focus.at() and company should be on Focus, not FocusState. _notifyDescendants() was using the wrong runtimeType. Let InheritedWidget update the descendants during build. When you setState() during build, assert that you're not markNeedsBuild()ing someone who isn't a descendant. Typo in Widget.toString(). --- examples/address_book/lib/main.dart | 14 ++- packages/flutter/lib/src/widgets/binding.dart | 19 +++- packages/flutter/lib/src/widgets/focus.dart | 96 +++++++++---------- .../flutter/lib/src/widgets/framework.dart | 67 +++++++++++-- packages/flutter/lib/src/widgets/input.dart | 6 +- .../unit/test/widget/build_scope_test.dart | 4 + packages/unit/test/widget/focus_test.dart | 63 ++++++++++++ 7 files changed, 195 insertions(+), 74 deletions(-) create mode 100644 packages/unit/test/widget/focus_test.dart diff --git a/examples/address_book/lib/main.dart b/examples/address_book/lib/main.dart index 2bca9be10f..26c0e4aefa 100644 --- a/examples/address_book/lib/main.dart +++ b/examples/address_book/lib/main.dart @@ -53,14 +53,12 @@ class AddressBookHome extends StatelessComponent { ); } - static final GlobalKey nameKey = new GlobalKey(); - static final GlobalKey phoneKey = new GlobalKey(); - static final GlobalKey emailKey = new GlobalKey(); - static final GlobalKey addressKey = new GlobalKey(); - static final GlobalKey ringtoneKey = new GlobalKey(); - static final GlobalKey noteKey = new GlobalKey(); - static final GlobalKey fillKey = new GlobalKey(); - static final GlobalKey emoticonKey = new GlobalKey(); + static final GlobalKey nameKey = new GlobalKey(label: 'name field'); + static final GlobalKey phoneKey = new GlobalKey(label: 'phone field'); + static final GlobalKey emailKey = new GlobalKey(label: 'email field'); + static final GlobalKey addressKey = new GlobalKey(label: 'address field'); + static final GlobalKey ringtoneKey = new GlobalKey(label: 'ringtone field'); + static final GlobalKey noteKey = new GlobalKey(label: 'note field'); Widget buildBody(BuildContext context) { return new Material( diff --git a/packages/flutter/lib/src/widgets/binding.dart b/packages/flutter/lib/src/widgets/binding.dart index 59056dfd4f..2699254b6a 100644 --- a/packages/flutter/lib/src/widgets/binding.dart +++ b/packages/flutter/lib/src/widgets/binding.dart @@ -41,7 +41,7 @@ class WidgetFlutterBinding extends FlutterBinding { Element.finalizeTree(); } - List _dirtyElements = new List(); + List _dirtyElements = []; /// Adds an element to the dirty elements list so that it will be rebuilt /// when buildDirtyElements is called. @@ -62,10 +62,19 @@ class WidgetFlutterBinding extends FlutterBinding { return; BuildableElement.lockState(() { _dirtyElements.sort((BuildableElement a, BuildableElement b) => a.depth - b.depth); - for (BuildableElement element in _dirtyElements) - element.rebuild(); + int dirtyCount = _dirtyElements.length; + int index = 0; + while (index < dirtyCount) { + _dirtyElements[index].rebuild(); + index += 1; + if (dirtyCount < _dirtyElements.length) { + _dirtyElements.sort((BuildableElement a, BuildableElement b) => a.depth - b.depth); + dirtyCount = _dirtyElements.length; + } + } + assert(!_dirtyElements.any((BuildableElement element) => element.dirty)); _dirtyElements.clear(); - }); + }, building: true); assert(_dirtyElements.isEmpty); } } @@ -76,7 +85,7 @@ void runApp(Widget app) { WidgetFlutterBinding.instance.renderViewElement.update( WidgetFlutterBinding.instance.describeApp(app) ); - }); + }, building: true); } void debugDumpApp() { diff --git a/packages/flutter/lib/src/widgets/focus.dart b/packages/flutter/lib/src/widgets/focus.dart index 478c77173e..098fcf954e 100644 --- a/packages/flutter/lib/src/widgets/focus.dart +++ b/packages/flutter/lib/src/widgets/focus.dart @@ -75,6 +75,52 @@ class Focus extends StatefulComponent { final bool autofocus; final Widget child; + static bool at(BuildContext context, Widget widget, { bool autofocus: true }) { + assert(widget != null); + assert(widget.key is GlobalKey); + _FocusScope focusScope = context.inheritedWidgetOfType(_FocusScope); + if (focusScope != null) { + if (autofocus) + focusScope._setFocusedWidgetIfUnset(widget.key); + return focusScope.scopeFocused && + focusScope.focusedScope == null && + focusScope.focusedWidget == widget.key; + } + return true; + } + + static bool _atScope(BuildContext context, Widget widget, { bool autofocus: true }) { + assert(widget != null); + _FocusScope focusScope = context.inheritedWidgetOfType(_FocusScope); + if (focusScope != null) { + if (autofocus) + focusScope._setFocusedScopeIfUnset(widget.key); + assert(widget.key != null); + return focusScope.scopeFocused && + focusScope.focusedScope == widget.key; + } + return true; + } + + // Don't call moveTo() from your build() function, it's intended to be called + // from event listeners, e.g. in response to a finger tap or tab key. + + static void moveTo(BuildContext context, Widget widget) { + assert(widget != null); + assert(widget.key is GlobalKey); + _FocusScope focusScope = context.inheritedWidgetOfType(_FocusScope); + if (focusScope != null) + focusScope.focusState._setFocusedWidget(widget.key); + } + + static void _moveScopeTo(BuildContext context, Focus component) { + assert(component != null); + assert(component.key != null); + _FocusScope focusScope = context.inheritedWidgetOfType(_FocusScope); + if (focusScope != null) + focusScope.focusState._setFocusedScope(component.key); + } + FocusState createState() => new FocusState(); } @@ -155,7 +201,7 @@ class FocusState extends State { void initState() { super.initState(); if (config.autofocus) - FocusState._moveScopeTo(context, config); + Focus._moveScopeTo(context, config); _updateWidgetRemovalListener(_focusedWidget); _updateScopeRemovalListener(_focusedScope); } @@ -169,56 +215,10 @@ class FocusState extends State { Widget build(BuildContext context) { return new _FocusScope( focusState: this, - scopeFocused: FocusState._atScope(context, config), + scopeFocused: Focus._atScope(context, config), focusedScope: _focusedScope == _noFocusedScope ? null : _focusedScope, focusedWidget: _focusedWidget, child: config.child ); } - - static bool at(BuildContext context, Widget widget, { bool autofocus: true }) { - assert(widget != null); - assert(widget.key is GlobalKey); - _FocusScope focusScope = context.inheritedWidgetOfType(_FocusScope); - if (focusScope != null) { - if (autofocus) - focusScope._setFocusedWidgetIfUnset(widget.key); - return focusScope.scopeFocused && - focusScope.focusedScope == null && - focusScope.focusedWidget == widget.key; - } - return true; - } - - static bool _atScope(BuildContext context, Widget widget, { bool autofocus: true }) { - assert(widget != null); - _FocusScope focusScope = context.inheritedWidgetOfType(_FocusScope); - if (focusScope != null) { - if (autofocus) - focusScope._setFocusedScopeIfUnset(widget.key); - assert(widget.key != null); - return focusScope.scopeFocused && - focusScope.focusedScope == widget.key; - } - return true; - } - - // Don't call moveTo() from your build() function, it's intended to be called - // from event listeners, e.g. in response to a finger tap or tab key. - - static void moveTo(BuildContext context, Widget widget) { - assert(widget != null); - assert(widget.key is GlobalKey); - _FocusScope focusScope = context.inheritedWidgetOfType(_FocusScope); - if (focusScope != null) - focusScope.focusState._setFocusedWidget(widget.key); - } - - static void _moveScopeTo(BuildContext context, Focus component) { - assert(component != null); - assert(component.key != null); - _FocusScope focusScope = context.inheritedWidgetOfType(_FocusScope); - if (focusScope != null) - focusScope.focusState._setFocusedScope(component.key); - } } diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart index 05ce1e691c..16c63fc423 100644 --- a/packages/flutter/lib/src/widgets/framework.dart +++ b/packages/flutter/lib/src/widgets/framework.dart @@ -186,8 +186,8 @@ abstract class Widget { final List data = []; debugFillDescription(data); if (data.isEmpty) - return 'name'; - return 'name(${data.join("; ")})'; + return '$name'; + return '$name(${data.join("; ")})'; } void debugFillDescription(List description) { } @@ -550,7 +550,7 @@ abstract class Element implements BuildContext { /// Wrapper around visitChildren for BuildContext. void visitChildElements(void visitor(Element element)) { // don't allow visitChildElements() during build, since children aren't necessarily built yet - assert(BuildableElement._debugStateLockLevel == 0); + assert(!BuildableElement._debugStateLocked); visitChildren(visitor); } @@ -858,6 +858,8 @@ abstract class BuildableElement extends Element { assert(_child != null); } + static BuildableElement _debugCurrentBuildTarget; + /// Reinvokes the build() method of the StatelessComponent object (for /// stateless components) or the State object (for stateful components) and /// then updates the widget tree. @@ -874,6 +876,12 @@ abstract class BuildableElement extends Element { assert(_debugLifecycleState == _ElementLifecycle.active); assert(_debugStateLocked); assert(_debugSetAllowIgnoredCallsToMarkNeedsBuild(true)); + BuildableElement debugPreviousBuildTarget; + assert(() { + debugPreviousBuildTarget = _debugCurrentBuildTarget; + _debugCurrentBuildTarget = this; + return true; + }); Widget built; try { built = _builder(this); @@ -896,12 +904,19 @@ abstract class BuildableElement extends Element { built = new ErrorWidget(); _child = updateChild(null, built, slot); } + + assert(() { + assert(_debugCurrentBuildTarget == this); + _debugCurrentBuildTarget = debugPreviousBuildTarget; + return true; + }); } static BuildScheduler scheduleBuildFor; static int _debugStateLockLevel = 0; static bool get _debugStateLocked => _debugStateLockLevel > 0; + static bool _debugBuilding = false; /// Establishes a scope in which component build functions can run. /// @@ -913,13 +928,31 @@ abstract class BuildableElement extends Element { /// After unwinding the last build scope on the stack, the framework verifies /// that each global key is used at most once and notifies listeners about /// changes to global keys. - static void lockState(void callback()) { - _debugStateLockLevel += 1; + static void lockState(void callback(), { bool building: false }) { + assert(_debugStateLockLevel >= 0); + assert(() { + if (building) { + assert(!_debugBuilding); + assert(_debugCurrentBuildTarget == null); + _debugBuilding = true; + } + _debugStateLockLevel += 1; + return true; + }); try { callback(); } finally { - _debugStateLockLevel -= 1; + assert(() { + _debugStateLockLevel -= 1; + if (building) { + assert(_debugBuilding); + assert(_debugCurrentBuildTarget == null); + _debugBuilding = false; + } + return true; + }); } + assert(_debugStateLockLevel >= 0); } /// Marks the element as dirty and adds it to the global list of widgets to @@ -934,10 +967,23 @@ abstract class BuildableElement extends Element { if (!_active) return; assert(_debugLifecycleState == _ElementLifecycle.active); - assert(!_debugStateLocked || (_debugAllowIgnoredCallsToMarkNeedsBuild && dirty)); + assert(() { + if (_debugBuilding) { + bool foundTarget = false; + visitAncestorElements((Element element) { + if (element == _debugCurrentBuildTarget) { + foundTarget = true; + return false; + } + return true; + }); + if (foundTarget) + return true; + } + return !_debugStateLocked || (_debugAllowIgnoredCallsToMarkNeedsBuild && dirty); + }); if (dirty) return; - assert(!_debugStateLocked); _dirty = true; assert(scheduleBuildFor != null); scheduleBuildFor(this); @@ -1101,11 +1147,12 @@ class InheritedElement extends StatelessComponentElement { } void _notifyDescendants() { - final Type ourRuntimeType = runtimeType; + final Type ourRuntimeType = widget.runtimeType; void notifyChildren(Element child) { if (child._dependencies != null && - child._dependencies.contains(ourRuntimeType)) + child._dependencies.contains(ourRuntimeType)) { child.dependenciesChanged(); + } if (child.runtimeType != ourRuntimeType) child.visitChildren(notifyChildren); } diff --git a/packages/flutter/lib/src/widgets/input.dart b/packages/flutter/lib/src/widgets/input.dart index d892944b1d..5ec60a81e1 100644 --- a/packages/flutter/lib/src/widgets/input.dart +++ b/packages/flutter/lib/src/widgets/input.dart @@ -71,7 +71,7 @@ class InputState extends ScrollableState { Widget buildContent(BuildContext context) { ThemeData themeData = Theme.of(context); - bool focused = FocusState.at(context, config); + bool focused = Focus.at(context, config); if (focused && !_keyboardHandle.attached) { _keyboardHandle = keyboard.show(_editableValue.stub, config.keyboardType); @@ -122,11 +122,11 @@ class InputState extends ScrollableState { ) ), onPointerDown: (_) { - if (FocusState.at(context, config)) { + if (Focus.at(context, config)) { assert(_keyboardHandle.attached); _keyboardHandle.showByRequest(); } else { - FocusState.moveTo(context, config); + Focus.moveTo(context, config); // we'll get told to rebuild and we'll take care of the keyboard then } } diff --git a/packages/unit/test/widget/build_scope_test.dart b/packages/unit/test/widget/build_scope_test.dart index 927c1e46a9..d54c4f534b 100644 --- a/packages/unit/test/widget/build_scope_test.dart +++ b/packages/unit/test/widget/build_scope_test.dart @@ -66,6 +66,10 @@ class BadDisposeWidgetState extends State { void main() { dynamic cachedException; + // ** WARNING ** + // THIS TEST OVERRIDES THE NORMAL EXCEPTION HANDLING + // AND DOES NOT REPORT EXCEPTIONS FROM THE FRAMEWORK + setUp(() { assert(cachedException == null); debugWidgetsExceptionHandler = (String context, dynamic exception, StackTrace stack) { diff --git a/packages/unit/test/widget/focus_test.dart b/packages/unit/test/widget/focus_test.dart new file mode 100644 index 0000000000..8806e6e30b --- /dev/null +++ b/packages/unit/test/widget/focus_test.dart @@ -0,0 +1,63 @@ +import 'package:sky/widgets.dart'; +import 'package:test/test.dart'; + +import 'widget_tester.dart'; + +class TestFocusable extends StatelessComponent { + TestFocusable(this.no, this.yes, GlobalKey key) : super(key: key); + final String no; + final String yes; + Widget build(BuildContext context) { + bool focused = Focus.at(context, this); + return new GestureDetector( + onTap: () { Focus.moveTo(context, this); }, + child: new Text(focused ? yes : no) + ); + } +} + +void main() { + test('Can have multiple focused children and they update accordingly', () { + testWidgets((WidgetTester tester) { + GlobalKey keyA = new GlobalKey(); + GlobalKey keyB = new GlobalKey(); + tester.pumpWidget( + new Focus( + child: new Column([ + // reverse these when you fix https://github.com/flutter/engine/issues/1495 + new TestFocusable('b', 'B FOCUSED', keyB), + new TestFocusable('a', 'A FOCUSED', keyA), + ]) + ) + ); + expect(tester.findText('a'), isNull); + expect(tester.findText('A FOCUSED'), isNotNull); + expect(tester.findText('b'), isNotNull); + expect(tester.findText('B FOCUSED'), isNull); + tester.tap(tester.findText('A FOCUSED')); + tester.pump(); + expect(tester.findText('a'), isNull); + expect(tester.findText('A FOCUSED'), isNotNull); + expect(tester.findText('b'), isNotNull); + expect(tester.findText('B FOCUSED'), isNull); + tester.tap(tester.findText('A FOCUSED')); + tester.pump(); + expect(tester.findText('a'), isNull); + expect(tester.findText('A FOCUSED'), isNotNull); + expect(tester.findText('b'), isNotNull); + expect(tester.findText('B FOCUSED'), isNull); + tester.tap(tester.findText('b')); + tester.pump(); + expect(tester.findText('a'), isNotNull); + expect(tester.findText('A FOCUSED'), isNull); + expect(tester.findText('b'), isNull); + expect(tester.findText('B FOCUSED'), isNotNull); + tester.tap(tester.findText('a')); + tester.pump(); + expect(tester.findText('a'), isNull); + expect(tester.findText('A FOCUSED'), isNotNull); + expect(tester.findText('b'), isNotNull); + expect(tester.findText('B FOCUSED'), isNull); + }); + }); +} From 2eec30111a6f7801079353e535a43a346e6c5557 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Sun, 4 Oct 2015 14:22:35 -0700 Subject: [PATCH 03/16] Use Navigator to drive SnackBar Now SnackBar is an ephemeral route that uses a Placeholder to put itself into the Scaffold. Fixes #673 --- examples/fitness/lib/feed.dart | 41 +++------- examples/fitness/lib/main.dart | 1 - examples/fitness/lib/measurement.dart | 20 ++--- examples/stocks/lib/main.dart | 1 - examples/stocks/lib/stock_home.dart | 36 +++------ .../flutter/lib/src/widgets/framework.dart | 6 +- .../flutter/lib/src/widgets/navigator.dart | 4 - .../flutter/lib/src/widgets/placeholder.dart | 30 +++++++ .../flutter/lib/src/widgets/popup_menu.dart | 2 +- .../flutter/lib/src/widgets/snack_bar.dart | 80 +++++++++++-------- packages/flutter/lib/widgets.dart | 1 + 11 files changed, 112 insertions(+), 110 deletions(-) create mode 100644 packages/flutter/lib/src/widgets/placeholder.dart diff --git a/examples/fitness/lib/feed.dart b/examples/fitness/lib/feed.dart index b7555f92e6..eb78b29812 100644 --- a/examples/fitness/lib/feed.dart +++ b/examples/fitness/lib/feed.dart @@ -58,11 +58,9 @@ class FeedFragment extends StatefulComponent { } class FeedFragmentState extends State { + final GlobalKey _snackBarPlaceholderKey = new GlobalKey(); FitnessMode _fitnessMode = FitnessMode.feed; - PerformanceStatus _snackBarStatus = PerformanceStatus.dismissed; - bool _isShowingSnackBar = false; - void _handleFitnessModeChange(FitnessMode value) { setState(() { _fitnessMode = value; @@ -119,15 +117,17 @@ class FeedFragmentState extends State { ); } - FitnessItem _undoItem; - void _handleItemDismissed(FitnessItem item) { config.onItemDeleted(item); - setState(() { - _undoItem = item; - _isShowingSnackBar = true; - _snackBarStatus = PerformanceStatus.forward; - }); + showSnackBar( + navigator: config.navigator, + placeholderKey: _snackBarPlaceholderKey, + content: new Text("Item deleted."), + actions: [new SnackBarAction(label: "UNDO", onPressed: () { + config.onItemCreated(item); + config.navigator.pop(); + })] + ); } Widget buildChart() { @@ -198,25 +198,6 @@ class FeedFragmentState extends State { } } - void _handleUndo() { - config.onItemCreated(_undoItem); - setState(() { - _undoItem = null; - _isShowingSnackBar = false; - }); - } - - Widget buildSnackBar() { - if (_snackBarStatus == PerformanceStatus.dismissed) - return null; - return new SnackBar( - showing: _isShowingSnackBar, - content: new Text("Item deleted."), - actions: [new SnackBarAction(label: "UNDO", onPressed: _handleUndo)], - onDismissed: () { setState(() { _snackBarStatus = PerformanceStatus.dismissed; }); } - ); - } - void _handleActionButtonPressed() { showDialog(config.navigator, (NavigatorState navigator) => new AddItemDialog(navigator)).then((routeName) { if (routeName != null) @@ -240,7 +221,7 @@ class FeedFragmentState extends State { return new Scaffold( toolbar: buildToolBar(), body: buildBody(), - snackBar: buildSnackBar(), + snackBar: new Placeholder(key: _snackBarPlaceholderKey), floatingActionButton: buildFloatingActionButton() ); } diff --git a/examples/fitness/lib/main.dart b/examples/fitness/lib/main.dart index 98f87db579..ecc10820b5 100644 --- a/examples/fitness/lib/main.dart +++ b/examples/fitness/lib/main.dart @@ -5,7 +5,6 @@ library fitness; import 'package:playfair/playfair.dart' as playfair; -import 'package:sky/animation.dart'; import 'package:sky/material.dart'; import 'package:sky/painting.dart'; import 'package:sky/widgets.dart'; diff --git a/examples/fitness/lib/measurement.dart b/examples/fitness/lib/measurement.dart index 695d82a4c6..975d2766c1 100644 --- a/examples/fitness/lib/measurement.dart +++ b/examples/fitness/lib/measurement.dart @@ -112,9 +112,10 @@ class MeasurementFragment extends StatefulComponent { } class MeasurementFragmentState extends State { + final GlobalKey _snackBarPlaceholderKey = new GlobalKey(); + String _weight = ""; DateTime _when = new DateTime.now(); - String _errorMessage = null; void _handleSave() { double parsedWeight; @@ -122,9 +123,11 @@ class MeasurementFragmentState extends State { parsedWeight = double.parse(_weight); } on FormatException catch(e) { print("Exception $e"); - setState(() { - _errorMessage = "Save failed"; - }); + showSnackBar( + navigator: config.navigator, + placeholderKey: _snackBarPlaceholderKey, + content: new Text('Save failed') + ); } config.onCreated(new Measurement(when: _when, weight: parsedWeight)); config.navigator.pop(); @@ -195,18 +198,11 @@ class MeasurementFragmentState extends State { ); } - Widget buildSnackBar() { - if (_errorMessage == null) - return null; - // TODO(jackson): This doesn't show up, unclear why. - return new SnackBar(content: new Text(_errorMessage), showing: true); - } - Widget build(BuildContext context) { return new Scaffold( toolbar: buildToolBar(), body: buildBody(context), - snackBar: buildSnackBar() + snackBar: new Placeholder(key: _snackBarPlaceholderKey) ); } } diff --git a/examples/stocks/lib/main.dart b/examples/stocks/lib/main.dart index a931d547ea..61c8d62a4e 100644 --- a/examples/stocks/lib/main.dart +++ b/examples/stocks/lib/main.dart @@ -8,7 +8,6 @@ import 'dart:async'; import 'dart:math' as math; import 'dart:sky' as sky; -import 'package:sky/animation.dart'; import 'package:sky/gestures.dart'; import 'package:sky/material.dart'; import 'package:sky/painting.dart'; diff --git a/examples/stocks/lib/stock_home.dart b/examples/stocks/lib/stock_home.dart index 091e379f67..f79388757a 100644 --- a/examples/stocks/lib/stock_home.dart +++ b/examples/stocks/lib/stock_home.dart @@ -6,8 +6,6 @@ part of stocks; typedef void ModeUpdater(StockMode mode); -const Duration _kSnackbarSlideDuration = const Duration(milliseconds: 200); - class StockHome extends StatefulComponent { StockHome(this.navigator, this.stocks, this.symbols, this.stockMode, this.modeUpdater); @@ -22,12 +20,10 @@ class StockHome extends StatefulComponent { class StockHomeState extends State { + final GlobalKey _snackBarPlaceholderKey = new GlobalKey(); bool _isSearching = false; String _searchQuery; - PerformanceStatus _snackBarStatus = PerformanceStatus.dismissed; - bool _isSnackBarShowing = false; - void _handleSearchBegin() { config.navigator.pushState(this, (_) { setState(() { @@ -217,28 +213,18 @@ class StockHomeState extends State { } void _handleUndo() { - setState(() { - _isSnackBarShowing = false; - }); - } - - GlobalKey snackBarKey = new GlobalKey(label: 'snackbar'); - Widget buildSnackBar() { - if (_snackBarStatus == PerformanceStatus.dismissed) - return null; - return new SnackBar( - showing: _isSnackBarShowing, - content: new Text("Stock purchased!"), - actions: [new SnackBarAction(label: "UNDO", onPressed: _handleUndo)], - onDismissed: () { setState(() { _snackBarStatus = PerformanceStatus.dismissed; }); } - ); + config.navigator.pop(); } void _handleStockPurchased() { - setState(() { - _isSnackBarShowing = true; - _snackBarStatus = PerformanceStatus.forward; - }); + showSnackBar( + navigator: config.navigator, + placeholderKey: _snackBarPlaceholderKey, + content: new Text("Stock purchased!"), + actions: [ + new SnackBarAction(label: "UNDO", onPressed: _handleUndo) + ] + ); } Widget buildFloatingActionButton() { @@ -253,7 +239,7 @@ class StockHomeState extends State { return new Scaffold( toolbar: _isSearching ? buildSearchBar() : buildToolBar(), body: buildTabNavigator(), - snackBar: buildSnackBar(), + snackBar: new Placeholder(key: _snackBarPlaceholderKey), floatingActionButton: buildFloatingActionButton() ); } diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart index 05ce1e691c..47478d2662 100644 --- a/packages/flutter/lib/src/widgets/framework.dart +++ b/packages/flutter/lib/src/widgets/framework.dart @@ -50,7 +50,7 @@ typedef void GlobalKeyRemoveListener(GlobalKey key); /// A GlobalKey is one that must be unique across the entire application. It is /// used by components that need to communicate with other components across the /// application's element tree. -abstract class GlobalKey extends Key { +abstract class GlobalKey extends Key { const GlobalKey.constructor() : super.constructor(); // so that subclasses can call us, since the Key() factory constructor shadows the implicit constructor /// Constructs a LabeledGlobalKey, which is a GlobalKey with a label used for debugging. @@ -96,9 +96,9 @@ abstract class GlobalKey extends Key { Element get _currentElement => _registry[this]; BuildContext get currentContext => _currentElement; Widget get currentWidget => _currentElement?.widget; - State get currentState { + T get currentState { Element element = _currentElement; - if (element is StatefulComponentElement) + if (element is StatefulComponentElement) return element.state; return null; } diff --git a/packages/flutter/lib/src/widgets/navigator.dart b/packages/flutter/lib/src/widgets/navigator.dart index d69d75a3a6..fa0bb19fc4 100644 --- a/packages/flutter/lib/src/widgets/navigator.dart +++ b/packages/flutter/lib/src/widgets/navigator.dart @@ -107,10 +107,6 @@ class NavigatorState extends State { void pop([dynamic result]) { setState(() { - while (currentRoute.ephemeral) { - currentRoute.didPop(null); - _currentPosition -= 1; - } assert(_currentPosition > 0); currentRoute.didPop(result); _currentPosition -= 1; diff --git a/packages/flutter/lib/src/widgets/placeholder.dart b/packages/flutter/lib/src/widgets/placeholder.dart new file mode 100644 index 0000000000..69c79184f7 --- /dev/null +++ b/packages/flutter/lib/src/widgets/placeholder.dart @@ -0,0 +1,30 @@ +// Copyright 2015 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:sky/src/widgets/basic.dart'; +import 'package:sky/src/widgets/framework.dart'; + +class Placeholder extends StatefulComponent { + Placeholder({ Key key }) : super(key: key); + + PlaceholderState createState() => new PlaceholderState(); +} + +class PlaceholderState extends State { + Widget get child => _child; + Widget _child; + void set child(Widget child) { + if (_child == child) + return; + setState(() { + _child = child; + }); + } + + Widget build(BuildContext context) { + if (_child != null) + return child; + return new SizedBox(width: 0.0, height: 0.0); + } +} diff --git a/packages/flutter/lib/src/widgets/popup_menu.dart b/packages/flutter/lib/src/widgets/popup_menu.dart index c6af624244..85295fd7c4 100644 --- a/packages/flutter/lib/src/widgets/popup_menu.dart +++ b/packages/flutter/lib/src/widgets/popup_menu.dart @@ -137,7 +137,7 @@ class MenuRoute extends Route { return result; } - bool get ephemeral => false; // we could make this true, but then we'd have to use popRoute(), not pop(), in menus + bool get ephemeral => true; bool get modal => true; bool get opaque => false; Duration get transitionDuration => _kMenuDuration; diff --git a/packages/flutter/lib/src/widgets/snack_bar.dart b/packages/flutter/lib/src/widgets/snack_bar.dart index 757d9a683a..9e72a0d8fb 100644 --- a/packages/flutter/lib/src/widgets/snack_bar.dart +++ b/packages/flutter/lib/src/widgets/snack_bar.dart @@ -6,21 +6,20 @@ import 'package:sky/animation.dart'; import 'package:sky/gestures.dart'; import 'package:sky/material.dart'; import 'package:sky/painting.dart'; -import 'package:sky/src/widgets/animated_component.dart'; import 'package:sky/src/widgets/basic.dart'; import 'package:sky/src/widgets/framework.dart'; import 'package:sky/src/widgets/gesture_detector.dart'; import 'package:sky/src/widgets/material.dart'; +import 'package:sky/src/widgets/navigator.dart'; +import 'package:sky/src/widgets/placeholder.dart'; import 'package:sky/src/widgets/theme.dart'; import 'package:sky/src/widgets/transitions.dart'; -typedef void SnackBarDismissedCallback(); - const Duration _kSlideInDuration = const Duration(milliseconds: 200); -const double kSnackHeight = 52.0; -const double kSideMargins = 24.0; -const double kVerticalPadding = 14.0; -const Color kSnackBackground = const Color(0xFF323232); +const double _kSnackHeight = 52.0; +const double _kSideMargins = 24.0; +const double _kVerticalPadding = 14.0; +const Color _kSnackBackground = const Color(0xFF323232); class SnackBarAction extends StatelessComponent { SnackBarAction({Key key, this.label, this.onPressed }) : super(key: key) { @@ -34,70 +33,60 @@ class SnackBarAction extends StatelessComponent { return new GestureDetector( onTap: onPressed, child: new Container( - margin: const EdgeDims.only(left: kSideMargins), - padding: const EdgeDims.symmetric(vertical: kVerticalPadding), + margin: const EdgeDims.only(left: _kSideMargins), + padding: const EdgeDims.symmetric(vertical: _kVerticalPadding), child: new Text(label) ) ); } } -class SnackBar extends AnimatedComponent { +class SnackBar extends StatelessComponent { SnackBar({ Key key, this.content, this.actions, - bool showing, - this.onDismissed - }) : super(key: key, direction: showing ? AnimationDirection.forward : AnimationDirection.reverse, duration: _kSlideInDuration) { + this.performance + }) : super(key: key) { assert(content != null); } final Widget content; final List actions; - final SnackBarDismissedCallback onDismissed; - - SnackBarState createState() => new SnackBarState(); -} - -class SnackBarState extends AnimatedState { - void handleDismissed() { - if (config.onDismissed != null) - config.onDismissed(); - } + final PerformanceView performance; Widget build(BuildContext context) { List children = [ new Flexible( child: new Container( - margin: const EdgeDims.symmetric(vertical: kVerticalPadding), + margin: const EdgeDims.symmetric(vertical: _kVerticalPadding), child: new DefaultTextStyle( style: Typography.white.subhead, - child: config.content + child: content ) ) ) ]; - if (config.actions != null) - children.addAll(config.actions); + if (actions != null) + children.addAll(actions); return new SquashTransition( - performance: performance.view, + performance: performance, height: new AnimatedValue( 0.0, - end: kSnackHeight, + end: _kSnackHeight, curve: easeIn, reverseCurve: easeOut ), child: new ClipRect( child: new OverflowBox( - minHeight: kSnackHeight, - maxHeight: kSnackHeight, + minHeight: _kSnackHeight, + maxHeight: _kSnackHeight, child: new Material( level: 2, - color: kSnackBackground, + color: _kSnackBackground, type: MaterialType.canvas, child: new Container( - margin: const EdgeDims.symmetric(horizontal: kSideMargins), + margin: const EdgeDims.symmetric(horizontal: _kSideMargins), child: new DefaultTextStyle( style: new TextStyle(color: Theme.of(context).accentColor), child: new Row(children) @@ -109,3 +98,28 @@ class SnackBarState extends AnimatedState { ); } } + +class _SnackBarRoute extends Route { + _SnackBarRoute({ this.content, this.actions }); + + final Widget content; + final List actions; + + bool get hasContent => false; + bool get ephemeral => true; + bool get modal => false; + Duration get transitionDuration => _kSlideInDuration; + + Widget build(NavigatorState navigator, PerformanceView nextRoutePerformance) => null; +} + +void showSnackBar({ NavigatorState navigator, GlobalKey placeholderKey, Widget content, List actions }) { + Route route = new _SnackBarRoute(); + SnackBar snackBar = new SnackBar( + content: content, + actions: actions, + performance: route.performance + ); + placeholderKey.currentState.child = snackBar; + navigator.push(route); +} diff --git a/packages/flutter/lib/widgets.dart b/packages/flutter/lib/widgets.dart index df63f53c19..d43db17293 100644 --- a/packages/flutter/lib/widgets.dart +++ b/packages/flutter/lib/widgets.dart @@ -37,6 +37,7 @@ export 'src/widgets/material_button.dart'; export 'src/widgets/mimic.dart'; export 'src/widgets/mixed_viewport.dart'; export 'src/widgets/navigator.dart'; +export 'src/widgets/placeholder.dart'; export 'src/widgets/popup_menu.dart'; export 'src/widgets/popup_menu_item.dart'; export 'src/widgets/progress_indicator.dart'; From 706b272eafb23f48bbe60d155bd9c710aa5d4e78 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Mon, 5 Oct 2015 16:39:13 -0700 Subject: [PATCH 04/16] Create the Performance for a Route earlier Instead of waiting until build(), we now create the performance for a route in its constructor. --- .../flutter/lib/src/widgets/navigator.dart | 70 ++++++++++--------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/packages/flutter/lib/src/widgets/navigator.dart b/packages/flutter/lib/src/widgets/navigator.dart index d69d75a3a6..332a2aa0e7 100644 --- a/packages/flutter/lib/src/widgets/navigator.dart +++ b/packages/flutter/lib/src/widgets/navigator.dart @@ -18,7 +18,7 @@ class RouteArguments { typedef Widget RouteBuilder(RouteArguments args); typedef RouteBuilder RouteGenerator(String name); typedef void StateRouteCallback(StateRoute route); -typedef void NotificationCallback(); +typedef void _RouteCallback(Route route); class Navigator extends StatefulComponent { Navigator({ @@ -51,7 +51,7 @@ class NavigatorState extends State { PageRoute route = new PageRoute(config.routes['/']); assert(route != null); assert(!route.ephemeral); - _history.add(route); + _insertRoute(route); } void pushState(State owner, Function callback) { @@ -76,6 +76,13 @@ class NavigatorState extends State { push(new PageRoute(builder)); } + void _insertRoute(Route route) { + _history.insert(_currentPosition, route); + route._onDismissed = _handleRouteDismissed; + route._onRemoveRoute = _handleRemoveRoute; + route.didPush(); + } + void push(Route route) { assert(!_debugCurrentlyHaveRoute(route)); setState(() { @@ -84,8 +91,8 @@ class NavigatorState extends State { currentRoute.didPop(null); _currentPosition -= 1; } - _history.insert(_currentPosition + 1, route); _currentPosition += 1; + _insertRoute(route); }); } @@ -122,6 +129,19 @@ class NavigatorState extends State { return index >= 0 && index <= _currentPosition; } + void _handleRouteDismissed(Route route) { + assert(_history.contains(route)); + if (_history.lastIndexOf(route) <= _currentPosition) + popRoute(route); + } + + void _handleRemoveRoute(Route route) { + assert(_history.contains(route)); + setState(() { + _history.remove(route); + }); + } + Widget build(BuildContext context) { List visibleRoutes = new List(); bool alreadyInsertModalBarrier = false; @@ -132,20 +152,6 @@ class NavigatorState extends State { assert(!route.modal); continue; } - route.ensurePerformance( - direction: (i <= _currentPosition) ? AnimationDirection.forward : AnimationDirection.reverse - ); - route._onDismissed = () { - assert(_history.contains(route)); - if (_history.lastIndexOf(route) <= _currentPosition) - popRoute(route); - }; - route._onRemoveRoute = () { - assert(_history.contains(route)); - setState(() { - _history.remove(route); - }); - }; visibleRoutes.add( new KeyedSubtree( key: new ObjectKey(route), @@ -171,11 +177,14 @@ class NavigatorState extends State { abstract class Route { + Route() { + _performance = createPerformance(); + } PerformanceView get performance => _performance?.view; Performance _performance; - NotificationCallback _onDismissed; - NotificationCallback _onRemoveRoute; + _RouteCallback _onDismissed; + _RouteCallback _onRemoveRoute; Performance createPerformance() { Duration duration = transitionDuration; @@ -184,26 +193,15 @@ abstract class Route { ..addStatusListener((PerformanceStatus status) { if (status == PerformanceStatus.dismissed) { if (_onDismissed != null) - _onDismissed(); + _onDismissed(this); if (_onRemoveRoute != null) - _onRemoveRoute(); + _onRemoveRoute(this); } }); } return null; } - void ensurePerformance({ AnimationDirection direction }) { - assert(direction != null); - if (_performance == null) - _performance = createPerformance(); - if (_performance != null) { - PerformanceStatus desiredStatus = direction == AnimationDirection.forward ? PerformanceStatus.forward : PerformanceStatus.reverse; - if (_performance.status != desiredStatus) - _performance.play(direction); - } - } - /// If hasContent is true, then the route represents some on-screen state. /// /// If hasContent is false, then no performance will be created, and the values of @@ -262,9 +260,15 @@ abstract class Route { bool get isActuallyOpaque => (performance == null || _performance.isCompleted) && opaque; Widget build(NavigatorState navigator, PerformanceView nextRoutePerformance); + + void didPush() { + _performance?.forward(); + } + void didPop([dynamic result]) { + _performance?.reverse(); if (performance == null && _onRemoveRoute != null) - _onRemoveRoute(); + _onRemoveRoute(this); } String toString() => '$runtimeType()'; From 330af86bdca0ddc186374e7ff2ca092e30a22221 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Mon, 5 Oct 2015 17:35:51 -0700 Subject: [PATCH 05/16] Delete AnimatedComponent This class has no clients. --- .../lib/src/widgets/animated_component.dart | 50 ------------------- packages/flutter/lib/widgets.dart | 1 - 2 files changed, 51 deletions(-) delete mode 100644 packages/flutter/lib/src/widgets/animated_component.dart diff --git a/packages/flutter/lib/src/widgets/animated_component.dart b/packages/flutter/lib/src/widgets/animated_component.dart deleted file mode 100644 index cf6acbc34f..0000000000 --- a/packages/flutter/lib/src/widgets/animated_component.dart +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2015 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:sky/animation.dart'; -import 'package:sky/src/widgets/framework.dart'; - -abstract class AnimatedComponent extends StatefulComponent { - const AnimatedComponent({ Key key, this.direction, this.duration }) : super(key: key); - - final Duration duration; - final AnimationDirection direction; -} - -abstract class AnimatedState extends State { - void initState() { - super.initState(); - _performance = new Performance(duration: config.duration); - performance.addStatusListener(_handleAnimationStatusChanged); - if (buildDependsOnPerformance) { - performance.addListener(() { - setState(() { - // We don't actually have any state to change, per se, - // we just know that we have in fact changed state. - }); - }); - } - performance.play(config.direction); - } - - void didUpdateConfig(T oldConfig) { - performance.duration = config.duration; - if (config.duration != oldConfig.duration || config.direction != oldConfig.direction) - performance.play(config.direction); - } - - Performance get performance => _performance; - Performance _performance; - - void _handleAnimationStatusChanged(PerformanceStatus status) { - if (status == PerformanceStatus.completed) - handleCompleted(); - else if (status == PerformanceStatus.dismissed) - handleDismissed(); - } - - bool get buildDependsOnPerformance => false; - void handleCompleted() { } - void handleDismissed() { } -} diff --git a/packages/flutter/lib/widgets.dart b/packages/flutter/lib/widgets.dart index d43db17293..93eabd8dba 100644 --- a/packages/flutter/lib/widgets.dart +++ b/packages/flutter/lib/widgets.dart @@ -5,7 +5,6 @@ /// The Flutter widget framework. library widgets; -export 'src/widgets/animated_component.dart'; export 'src/widgets/animated_container.dart'; export 'src/widgets/app.dart'; export 'src/widgets/basic.dart'; From 7ac6ab6fc7b1163fe9e44aacaa3e0c932ee2d515 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Mon, 5 Oct 2015 21:34:51 -0700 Subject: [PATCH 06/16] PageableList should take initialPage instead of initialScrollOffset Fixes #900 --- packages/flutter/lib/src/widgets/scrollable.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/flutter/lib/src/widgets/scrollable.dart b/packages/flutter/lib/src/widgets/scrollable.dart index da4d940a96..bf4a08605d 100644 --- a/packages/flutter/lib/src/widgets/scrollable.dart +++ b/packages/flutter/lib/src/widgets/scrollable.dart @@ -575,7 +575,7 @@ typedef void PageChangedCallback(int newPage); class PageableList extends ScrollableList { PageableList({ Key key, - double initialScrollOffset, + int initialPage, ScrollDirection scrollDirection: ScrollDirection.horizontal, ScrollListener onScroll, List items, @@ -588,7 +588,7 @@ class PageableList extends ScrollableList { this.curve: ease }) : super( key: key, - initialScrollOffset: initialScrollOffset, + initialScrollOffset: initialPage == null ? null : initialPage * itemExtent, scrollDirection: scrollDirection, onScroll: onScroll, items: items, From 922aece1d575cf56f885ee29744c8b0eba02e5ad Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Mon, 5 Oct 2015 21:54:33 -0700 Subject: [PATCH 07/16] Mark most State subclasses private In the vast majority of cases, folks should be interacting with the Widget rather than its State. Fixes #267 --- .../lib/src/widgets/animated_container.dart | 4 +- packages/flutter/lib/src/widgets/app.dart | 6 +- packages/flutter/lib/src/widgets/basic.dart | 4 +- .../flutter/lib/src/widgets/date_picker.dart | 18 ++-- packages/flutter/lib/src/widgets/dialog.dart | 11 +-- .../flutter/lib/src/widgets/dismissable.dart | 12 +-- .../flutter/lib/src/widgets/drag_target.dart | 4 +- .../flutter/lib/src/widgets/drawer_item.dart | 4 +- .../flutter/lib/src/widgets/flat_button.dart | 4 +- .../src/widgets/floating_action_button.dart | 4 +- .../lib/src/widgets/gesture_detector.dart | 4 +- .../lib/src/widgets/homogeneous_viewport.dart | 6 +- packages/flutter/lib/src/widgets/icon.dart | 4 +- .../flutter/lib/src/widgets/ink_well.dart | 20 ++-- packages/flutter/lib/src/widgets/input.dart | 4 +- .../flutter/lib/src/widgets/material.dart | 14 +-- .../lib/src/widgets/material_button.dart | 2 +- packages/flutter/lib/src/widgets/mimic.dart | 6 +- .../lib/src/widgets/mixed_viewport.dart | 6 +- .../flutter/lib/src/widgets/popup_menu.dart | 6 +- .../lib/src/widgets/progress_indicator.dart | 4 +- packages/flutter/lib/src/widgets/radio.dart | 12 +-- .../lib/src/widgets/raised_button.dart | 4 +- .../flutter/lib/src/widgets/scaffold.dart | 98 +++++++++---------- .../flutter/lib/src/widgets/snack_bar.dart | 3 +- packages/flutter/lib/src/widgets/tabs.dart | 46 ++++----- .../flutter/lib/src/widgets/transitions.dart | 4 +- 27 files changed, 154 insertions(+), 160 deletions(-) diff --git a/packages/flutter/lib/src/widgets/animated_container.dart b/packages/flutter/lib/src/widgets/animated_container.dart index e9c56a115a..12f65ad1ea 100644 --- a/packages/flutter/lib/src/widgets/animated_container.dart +++ b/packages/flutter/lib/src/widgets/animated_container.dart @@ -77,10 +77,10 @@ class AnimatedContainer extends StatefulComponent { final Curve curve; final Duration duration; - AnimatedContainerState createState() => new AnimatedContainerState(); + _AnimatedContainerState createState() => new _AnimatedContainerState(); } -class AnimatedContainerState extends State { +class _AnimatedContainerState extends State { AnimatedBoxConstraintsValue _constraints; AnimatedBoxDecorationValue _decoration; AnimatedBoxDecorationValue _foregroundDecoration; diff --git a/packages/flutter/lib/src/widgets/app.dart b/packages/flutter/lib/src/widgets/app.dart index ea317a4f0b..c38da71607 100644 --- a/packages/flutter/lib/src/widgets/app.dart +++ b/packages/flutter/lib/src/widgets/app.dart @@ -38,7 +38,7 @@ class App extends StatefulComponent { 'This might be a sign that you have not upgraded to our new Widgets framework.'; 'For more details see: https://groups.google.com/forum/#!topic/flutter-dev/hcX3OvLws9c'; '...or look at our examples: https://github.com/flutter/engine/tree/master/examples'; - return routes != null; + return routes != null; }); } @@ -47,10 +47,10 @@ class App extends StatefulComponent { final Map routes; final RouteGenerator onGenerateRoute; - AppState createState() => new AppState(); + _AppState createState() => new _AppState(); } -class AppState extends State { +class _AppState extends State { GlobalObjectKey _navigator; diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart index 22f7d3e6a0..7b17690178 100644 --- a/packages/flutter/lib/src/widgets/basic.dart +++ b/packages/flutter/lib/src/widgets/basic.dart @@ -808,10 +808,10 @@ class ImageListener extends StatefulComponent { final ImageFit fit; final ImageRepeat repeat; - ImageListenerState createState() => new ImageListenerState(); + _ImageListenerState createState() => new _ImageListenerState(); } -class ImageListenerState extends State { +class _ImageListenerState extends State { void initState() { super.initState(); config.image.addListener(_handleImageChanged); diff --git a/packages/flutter/lib/src/widgets/date_picker.dart b/packages/flutter/lib/src/widgets/date_picker.dart index 5773c0a264..56f4ed7fb3 100644 --- a/packages/flutter/lib/src/widgets/date_picker.dart +++ b/packages/flutter/lib/src/widgets/date_picker.dart @@ -39,10 +39,10 @@ class DatePicker extends StatefulComponent { final DateTime firstDate; final DateTime lastDate; - DatePickerState createState() => new DatePickerState(); + _DatePickerState createState() => new _DatePickerState(); } -class DatePickerState extends State { +class _DatePickerState extends State { DatePickerMode _mode = DatePickerMode.day; void _handleModeChanged(DatePickerMode mode) { @@ -70,7 +70,7 @@ class DatePickerState extends State { static const double _calendarHeight = 210.0; Widget build(BuildContext context) { - Widget header = new DatePickerHeader( + Widget header = new _DatePickerHeader( selectedDate: config.selectedDate, mode: _mode, onModeChanged: _handleModeChanged @@ -107,8 +107,8 @@ class DatePickerState extends State { } // Shows the selected date in large font and toggles between year and day mode -class DatePickerHeader extends StatelessComponent { - DatePickerHeader({ this.selectedDate, this.mode, this.onModeChanged }) { +class _DatePickerHeader extends StatelessComponent { + _DatePickerHeader({ this.selectedDate, this.mode, this.onModeChanged }) { assert(selectedDate != null); assert(mode != null); } @@ -290,10 +290,10 @@ class MonthPicker extends ScrollableWidgetList { final DateTime firstDate; final DateTime lastDate; - MonthPickerState createState() => new MonthPickerState(); + _MonthPickerState createState() => new _MonthPickerState(); } -class MonthPickerState extends ScrollableWidgetListState { +class _MonthPickerState extends ScrollableWidgetListState { void initState() { super.initState(); _updateCurrentDate(); @@ -363,10 +363,10 @@ class YearPicker extends ScrollableWidgetList { final DateTime firstDate; final DateTime lastDate; - YearPickerState createState() => new YearPickerState(); + _YearPickerState createState() => new _YearPickerState(); } -class YearPickerState extends ScrollableWidgetListState { +class _YearPickerState extends ScrollableWidgetListState { int get itemCount => config.lastDate.year - config.firstDate.year + 1; List buildItems(BuildContext context, int start, int count) { diff --git a/packages/flutter/lib/src/widgets/dialog.dart b/packages/flutter/lib/src/widgets/dialog.dart index 4efe02b5a9..688f0f9174 100644 --- a/packages/flutter/lib/src/widgets/dialog.dart +++ b/packages/flutter/lib/src/widgets/dialog.dart @@ -131,16 +131,15 @@ class Dialog extends StatelessComponent { } } -const Duration _kTransitionDuration = const Duration(milliseconds: 150); - -class DialogRoute extends Route { - DialogRoute({ this.completer, this.builder }); +class _DialogRoute extends Route { + _DialogRoute({ this.completer, this.builder }); final Completer completer; final RouteBuilder builder; - Duration get transitionDuration => _kTransitionDuration; bool get opaque => false; + Duration get transitionDuration => const Duration(milliseconds: 150); + Widget build(NavigatorState navigator, PerformanceView nextRoutePerformance) { return new FadeTransition( performance: performance, @@ -157,7 +156,7 @@ class DialogRoute extends Route { Future showDialog(NavigatorState navigator, DialogBuilder builder) { Completer completer = new Completer(); - navigator.push(new DialogRoute( + navigator.push(new _DialogRoute( completer: completer, builder: (RouteArguments args) { return new Focus( diff --git a/packages/flutter/lib/src/widgets/dismissable.dart b/packages/flutter/lib/src/widgets/dismissable.dart index b9c11e2ecf..b47729bdd0 100644 --- a/packages/flutter/lib/src/widgets/dismissable.dart +++ b/packages/flutter/lib/src/widgets/dismissable.dart @@ -39,15 +39,15 @@ class Dismissable extends StatefulComponent { this.direction: DismissDirection.horizontal }) : super(key: key); - Widget child; - ResizedCallback onResized; - DismissedCallback onDismissed; - DismissDirection direction; + final Widget child; + final ResizedCallback onResized; + final DismissedCallback onDismissed; + final DismissDirection direction; - DismissableState createState() => new DismissableState(); + _DismissableState createState() => new _DismissableState(); } -class DismissableState extends State { +class _DismissableState extends State { void initState() { super.initState(); _fadePerformance = new Performance(duration: _kCardDismissFadeout); diff --git a/packages/flutter/lib/src/widgets/drag_target.dart b/packages/flutter/lib/src/widgets/drag_target.dart index 51800d7891..5d1533c270 100644 --- a/packages/flutter/lib/src/widgets/drag_target.dart +++ b/packages/flutter/lib/src/widgets/drag_target.dart @@ -62,10 +62,10 @@ class Draggable extends StatefulComponent { final Offset feedbackOffset; final DragAnchor dragAnchor; - DraggableState createState() => new DraggableState(); + _DraggableState createState() => new _DraggableState(); } -class DraggableState extends State { +class _DraggableState extends State { DragRoute _route; void _startDrag(sky.PointerEvent event) { diff --git a/packages/flutter/lib/src/widgets/drawer_item.dart b/packages/flutter/lib/src/widgets/drawer_item.dart index 2850d9418d..c41ac4a321 100644 --- a/packages/flutter/lib/src/widgets/drawer_item.dart +++ b/packages/flutter/lib/src/widgets/drawer_item.dart @@ -23,10 +23,10 @@ class DrawerItem extends StatefulComponent { final GestureTapCallback onPressed; final bool selected; - DrawerItemState createState() => new DrawerItemState(); + _DrawerItemState createState() => new _DrawerItemState(); } -class DrawerItemState extends ButtonState { +class _DrawerItemState extends ButtonState { TextStyle _getTextStyle(ThemeData themeData) { TextStyle result = themeData.text.body2; if (config.selected) diff --git a/packages/flutter/lib/src/widgets/flat_button.dart b/packages/flutter/lib/src/widgets/flat_button.dart index 02beead355..164b087f59 100644 --- a/packages/flutter/lib/src/widgets/flat_button.dart +++ b/packages/flutter/lib/src/widgets/flat_button.dart @@ -20,10 +20,10 @@ class FlatButton extends MaterialButton { enabled: enabled, onPressed: onPressed); - FlatButtonState createState() => new FlatButtonState(); + _FlatButtonState createState() => new _FlatButtonState(); } -class FlatButtonState extends MaterialButtonState { +class _FlatButtonState extends MaterialButtonState { Color getColor(BuildContext context) { if (!config.enabled || !highlight) return null; diff --git a/packages/flutter/lib/src/widgets/floating_action_button.dart b/packages/flutter/lib/src/widgets/floating_action_button.dart index 6b7d4b66c3..d2fb663cab 100644 --- a/packages/flutter/lib/src/widgets/floating_action_button.dart +++ b/packages/flutter/lib/src/widgets/floating_action_button.dart @@ -27,10 +27,10 @@ class FloatingActionButton extends StatefulComponent { final Color backgroundColor; final GestureTapCallback onPressed; - FloatingActionButtonState createState() => new FloatingActionButtonState(); + _FloatingActionButtonState createState() => new _FloatingActionButtonState(); } -class FloatingActionButtonState extends ButtonState { +class _FloatingActionButtonState extends ButtonState { Widget buildContent(BuildContext context) { IconThemeColor iconThemeColor = IconThemeColor.white; Color materialColor = config.backgroundColor; diff --git a/packages/flutter/lib/src/widgets/gesture_detector.dart b/packages/flutter/lib/src/widgets/gesture_detector.dart index fb3407908b..c2d1454c13 100644 --- a/packages/flutter/lib/src/widgets/gesture_detector.dart +++ b/packages/flutter/lib/src/widgets/gesture_detector.dart @@ -57,10 +57,10 @@ class GestureDetector extends StatefulComponent { final GestureScaleUpdateCallback onScaleUpdate; final GestureScaleEndCallback onScaleEnd; - GestureDetectorState createState() => new GestureDetectorState(); + _GestureDetectorState createState() => new _GestureDetectorState(); } -class GestureDetectorState extends State { +class _GestureDetectorState extends State { final PointerRouter _router = FlutterBinding.instance.pointerRouter; TapGestureRecognizer _tap; diff --git a/packages/flutter/lib/src/widgets/homogeneous_viewport.dart b/packages/flutter/lib/src/widgets/homogeneous_viewport.dart index 19dade6689..85f8009bc4 100644 --- a/packages/flutter/lib/src/widgets/homogeneous_viewport.dart +++ b/packages/flutter/lib/src/widgets/homogeneous_viewport.dart @@ -30,7 +30,7 @@ class HomogeneousViewport extends RenderObjectWidget { final ScrollDirection direction; final double startOffset; - HomogeneousViewportElement createElement() => new HomogeneousViewportElement(this); + _HomogeneousViewportElement createElement() => new _HomogeneousViewportElement(this); // we don't pass constructor arguments to the RenderBlockViewport() because until // we know our children, the constructor arguments we could give have no effect @@ -48,8 +48,8 @@ class HomogeneousViewport extends RenderObjectWidget { // all the actual work is done in the element } -class HomogeneousViewportElement extends RenderObjectElement { - HomogeneousViewportElement(HomogeneousViewport widget) : super(widget); +class _HomogeneousViewportElement extends RenderObjectElement { + _HomogeneousViewportElement(HomogeneousViewport widget) : super(widget); List _children = const []; int _layoutFirstIndex; diff --git a/packages/flutter/lib/src/widgets/icon.dart b/packages/flutter/lib/src/widgets/icon.dart index dc95312ba3..5dcee05232 100644 --- a/packages/flutter/lib/src/widgets/icon.dart +++ b/packages/flutter/lib/src/widgets/icon.dart @@ -61,7 +61,7 @@ class Icon extends StatelessComponent { final IconThemeColor color; final sky.ColorFilter colorFilter; - String getColorSuffix(BuildContext context) { + String _getColorSuffix(BuildContext context) { IconThemeColor iconThemeColor = color; if (iconThemeColor == null) { IconThemeData iconThemeData = IconTheme.of(context); @@ -90,7 +90,7 @@ class Icon extends StatelessComponent { // TODO(eseidel): This clearly isn't correct. Not sure what would be. // Should we use the ios images on ios? String density = 'drawable-xxhdpi'; - String colorSuffix = getColorSuffix(context); + String colorSuffix = _getColorSuffix(context); return new AssetImage( bundle: _iconBundle, name: '${category}/${density}/ic_${subtype}_${colorSuffix}_${size}dp.png', diff --git a/packages/flutter/lib/src/widgets/ink_well.dart b/packages/flutter/lib/src/widgets/ink_well.dart index 9abe7745da..df493434bd 100644 --- a/packages/flutter/lib/src/widgets/ink_well.dart +++ b/packages/flutter/lib/src/widgets/ink_well.dart @@ -26,8 +26,8 @@ double _getSplashTargetSize(Size bounds, Point position) { return math.max(math.max(d1, d2), math.max(d3, d4)).ceil().toDouble(); } -class InkSplash { - InkSplash(this.position, this.well) { +class _InkSplash { + _InkSplash(this.position, this.well) { _targetRadius = _getSplashTargetSize(well.size, position); _radius = new AnimatedValue( _kSplashInitialSize, end: _targetRadius, curve: easeOut); @@ -42,7 +42,7 @@ class InkSplash { } final Point position; - final RenderInkWell well; + final _RenderInkWell well; double _targetRadius; double _pinnedRadius; @@ -98,8 +98,8 @@ class InkSplash { } } -class RenderInkWell extends RenderProxyBox { - RenderInkWell({ +class _RenderInkWell extends RenderProxyBox { + _RenderInkWell({ RenderBox child, GestureTapCallback onTap, GestureLongPressCallback onLongPress @@ -122,7 +122,7 @@ class RenderInkWell extends RenderProxyBox { _syncLongPressRecognizer(); } - final List _splashes = new List(); + final List<_InkSplash> _splashes = new List<_InkSplash>(); TapGestureRecognizer _tap; LongPressGestureRecognizer _longPress; @@ -131,7 +131,7 @@ class RenderInkWell extends RenderProxyBox { if (event.type == 'pointerdown' && (_tap != null || _longPress != null)) { _tap?.addPointer(event); _longPress?.addPointer(event); - _splashes.add(new InkSplash(entry.localPosition, this)); + _splashes.add(new _InkSplash(entry.localPosition, this)); } } @@ -196,7 +196,7 @@ class RenderInkWell extends RenderProxyBox { canvas.save(); canvas.translate(offset.dx, offset.dy); canvas.clipRect(Point.origin & size); - for (InkSplash splash in _splashes) + for (_InkSplash splash in _splashes) splash.paint(canvas); canvas.restore(); } @@ -215,9 +215,9 @@ class InkWell extends OneChildRenderObjectWidget { final GestureTapCallback onTap; final GestureLongPressCallback onLongPress; - RenderInkWell createRenderObject() => new RenderInkWell(onTap: onTap, onLongPress: onLongPress); + _RenderInkWell createRenderObject() => new _RenderInkWell(onTap: onTap, onLongPress: onLongPress); - void updateRenderObject(RenderInkWell renderObject, InkWell oldWidget) { + void updateRenderObject(_RenderInkWell renderObject, InkWell oldWidget) { renderObject.onTap = onTap; renderObject.onLongPress = onLongPress; } diff --git a/packages/flutter/lib/src/widgets/input.dart b/packages/flutter/lib/src/widgets/input.dart index 5ec60a81e1..d3a1373073 100644 --- a/packages/flutter/lib/src/widgets/input.dart +++ b/packages/flutter/lib/src/widgets/input.dart @@ -39,10 +39,10 @@ class Input extends Scrollable { final String placeholder; final StringValueChanged onChanged; - InputState createState() => new InputState(); + _InputState createState() => new _InputState(); } -class InputState extends ScrollableState { +class _InputState extends ScrollableState { String _value; EditableString _editableValue; KeyboardHandle _keyboardHandle = KeyboardHandle.unattached; diff --git a/packages/flutter/lib/src/widgets/material.dart b/packages/flutter/lib/src/widgets/material.dart index eb79290ec9..c14b29fa6f 100644 --- a/packages/flutter/lib/src/widgets/material.dart +++ b/packages/flutter/lib/src/widgets/material.dart @@ -12,7 +12,7 @@ import 'package:sky/src/widgets/theme.dart'; enum MaterialType { canvas, card, circle, button } -const Map edges = const { +const Map _kEdges = const { MaterialType.canvas: null, MaterialType.card: 2.0, MaterialType.circle: null, @@ -35,7 +35,7 @@ class Material extends StatelessComponent { final int level; final Color color; - Color getBackgroundColor(BuildContext context) { + Color _getBackgroundColor(BuildContext context) { if (color != null) return color; switch (type) { @@ -55,10 +55,10 @@ class Material extends StatelessComponent { style: Theme.of(context).text.body1, child: contents ); - if (edges[type] != null) { + if (_kEdges[type] != null) { contents = new ClipRRect( - xRadius: edges[type], - yRadius: edges[type], + xRadius: _kEdges[type], + yRadius: _kEdges[type], child: contents ); } @@ -69,8 +69,8 @@ class Material extends StatelessComponent { curve: ease, duration: const Duration(milliseconds: 200), decoration: new BoxDecoration( - backgroundColor: getBackgroundColor(context), - borderRadius: edges[type], + backgroundColor: _getBackgroundColor(context), + borderRadius: _kEdges[type], boxShadow: level == 0 ? null : shadows[level], shape: type == MaterialType.circle ? Shape.circle : Shape.rectangle ), diff --git a/packages/flutter/lib/src/widgets/material_button.dart b/packages/flutter/lib/src/widgets/material_button.dart index b18cc313c1..70161208c5 100644 --- a/packages/flutter/lib/src/widgets/material_button.dart +++ b/packages/flutter/lib/src/widgets/material_button.dart @@ -9,7 +9,7 @@ import 'package:sky/src/widgets/framework.dart'; import 'package:sky/src/widgets/ink_well.dart'; import 'package:sky/src/widgets/material.dart'; -// Rather than using this class directly, please use FlatButton or RaisedButton. +/// Rather than using this class directly, please use FlatButton or RaisedButton. abstract class MaterialButton extends StatefulComponent { MaterialButton({ Key key, diff --git a/packages/flutter/lib/src/widgets/mimic.dart b/packages/flutter/lib/src/widgets/mimic.dart index 961db37986..45bb12f782 100644 --- a/packages/flutter/lib/src/widgets/mimic.dart +++ b/packages/flutter/lib/src/widgets/mimic.dart @@ -9,7 +9,7 @@ import 'package:sky/src/widgets/framework.dart'; class MimicableKey { MimicableKey._(this._state); - final MimicableState _state; + final _MimicableState _state; Rect get globalBounds => _state._globalBounds; @@ -35,10 +35,10 @@ class Mimicable extends StatefulComponent { final Widget child; - MimicableState createState() => new MimicableState(); + _MimicableState createState() => new _MimicableState(); } -class MimicableState extends State { +class _MimicableState extends State { Size _size; bool _beingMimicked = false; diff --git a/packages/flutter/lib/src/widgets/mixed_viewport.dart b/packages/flutter/lib/src/widgets/mixed_viewport.dart index 5336e80c71..120fd3457f 100644 --- a/packages/flutter/lib/src/widgets/mixed_viewport.dart +++ b/packages/flutter/lib/src/widgets/mixed_viewport.dart @@ -31,7 +31,7 @@ class MixedViewport extends RenderObjectWidget { final ExtentsUpdateCallback onExtentsUpdate; final InvalidatorAvailableCallback onInvalidatorAvailable; - MixedViewportElement createElement() => new MixedViewportElement(this); + _MixedViewportElement createElement() => new _MixedViewportElement(this); // we don't pass constructor arguments to the RenderBlockViewport() because until // we know our children, the constructor arguments we could give have no effect @@ -60,8 +60,8 @@ class _ChildKey { String toString() => "_ChildKey(type: $type, key: $key)"; } -class MixedViewportElement extends RenderObjectElement { - MixedViewportElement(MixedViewport widget) : super(widget) { +class _MixedViewportElement extends RenderObjectElement { + _MixedViewportElement(MixedViewport widget) : super(widget) { if (widget.onInvalidatorAvailable != null) widget.onInvalidatorAvailable(invalidate); } diff --git a/packages/flutter/lib/src/widgets/popup_menu.dart b/packages/flutter/lib/src/widgets/popup_menu.dart index 85295fd7c4..930b2513c9 100644 --- a/packages/flutter/lib/src/widgets/popup_menu.dart +++ b/packages/flutter/lib/src/widgets/popup_menu.dart @@ -121,8 +121,8 @@ class MenuPosition { final double left; } -class MenuRoute extends Route { - MenuRoute({ this.completer, this.position, this.builder, this.level }); +class _MenuRoute extends Route { + _MenuRoute({ this.completer, this.position, this.builder, this.level }); final Completer completer; final MenuPosition position; @@ -169,7 +169,7 @@ class MenuRoute extends Route { Future showMenu({ NavigatorState navigator, MenuPosition position, PopupMenuItemsBuilder builder, int level: 4 }) { Completer completer = new Completer(); - navigator.push(new MenuRoute( + navigator.push(new _MenuRoute( completer: completer, position: position, builder: builder, diff --git a/packages/flutter/lib/src/widgets/progress_indicator.dart b/packages/flutter/lib/src/widgets/progress_indicator.dart index c661fced9e..92cdeda620 100644 --- a/packages/flutter/lib/src/widgets/progress_indicator.dart +++ b/packages/flutter/lib/src/widgets/progress_indicator.dart @@ -31,10 +31,10 @@ abstract class ProgressIndicator extends StatefulComponent { Widget _buildIndicator(BuildContext context, double performanceValue); - ProgressIndicatorState createState() => new ProgressIndicatorState(); + _ProgressIndicatorState createState() => new _ProgressIndicatorState(); } -class ProgressIndicatorState extends State { +class _ProgressIndicatorState extends State { ValuePerformance _performance; diff --git a/packages/flutter/lib/src/widgets/radio.dart b/packages/flutter/lib/src/widgets/radio.dart index ab51bea716..53f487d08b 100644 --- a/packages/flutter/lib/src/widgets/radio.dart +++ b/packages/flutter/lib/src/widgets/radio.dart @@ -14,7 +14,7 @@ const sky.Color _kDarkOffColor = const sky.Color(0xB2FFFFFF); typedef RadioValueChanged(Object value); -class Radio extends StatefulComponent { +class Radio extends StatelessComponent { Radio({ Key key, this.value, @@ -28,13 +28,9 @@ class Radio extends StatefulComponent { final Object groupValue; final RadioValueChanged onChanged; - RadioState createState() => new RadioState(); -} - -class RadioState extends State { Color _getColor(BuildContext context) { ThemeData themeData = Theme.of(context); - if (config.value == config.groupValue) + if (value == groupValue) return themeData.accentColor; return themeData.brightness == ThemeBrightness.light ? _kLightOffColor : _kDarkOffColor; } @@ -44,7 +40,7 @@ class RadioState extends State { const double kOuterRadius = kDiameter / 2; const double kInnerRadius = 5.0; return new GestureDetector( - onTap: () => config.onChanged(config.value), + onTap: () => onChanged(value), child: new Container( margin: const EdgeDims.symmetric(horizontal: 5.0), width: kDiameter, @@ -60,7 +56,7 @@ class RadioState extends State { canvas.drawCircle(const Point(kOuterRadius, kOuterRadius), kOuterRadius, paint); // Draw the inner circle - if (config.value == config.groupValue) { + if (value == groupValue) { paint.setStyle(sky.PaintingStyle.fill); canvas.drawCircle(const Point(kOuterRadius, kOuterRadius), kInnerRadius, paint); } diff --git a/packages/flutter/lib/src/widgets/raised_button.dart b/packages/flutter/lib/src/widgets/raised_button.dart index 22c596511c..6d5e5487c2 100644 --- a/packages/flutter/lib/src/widgets/raised_button.dart +++ b/packages/flutter/lib/src/widgets/raised_button.dart @@ -22,10 +22,10 @@ class RaisedButton extends MaterialButton { assert(enabled != null); } - RaisedButtonState createState() => new RaisedButtonState(); + _RaisedButtonState createState() => new _RaisedButtonState(); } -class RaisedButtonState extends MaterialButtonState { +class _RaisedButtonState extends MaterialButtonState { Color getColor(BuildContext context) { if (config.enabled) { switch (Theme.of(context).brightness) { diff --git a/packages/flutter/lib/src/widgets/scaffold.dart b/packages/flutter/lib/src/widgets/scaffold.dart index 9e4b1e1dd5..bff2d0d8f1 100644 --- a/packages/flutter/lib/src/widgets/scaffold.dart +++ b/packages/flutter/lib/src/widgets/scaffold.dart @@ -9,7 +9,7 @@ import 'package:sky/rendering.dart'; import 'package:sky/src/widgets/framework.dart'; // Slots are painted in this order and hit tested in reverse of this order -enum ScaffoldSlots { +enum _ScaffoldSlots { body, statusBar, toolbar, @@ -18,9 +18,9 @@ enum ScaffoldSlots { drawer } -class RenderScaffold extends RenderBox { +class _RenderScaffold extends RenderBox { - RenderScaffold({ + _RenderScaffold({ RenderBox body, RenderBox statusBar, RenderBox toolbar, @@ -28,17 +28,17 @@ class RenderScaffold extends RenderBox { 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; + 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 _slots = new Map(); - RenderBox operator[] (ScaffoldSlots slot) => _slots[slot]; - void operator[]= (ScaffoldSlots slot, RenderBox value) { + 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; @@ -54,7 +54,7 @@ class RenderScaffold extends RenderBox { } void attachChildren() { - for (ScaffoldSlots slot in ScaffoldSlots.values) { + for (_ScaffoldSlots slot in _ScaffoldSlots.values) { RenderBox box = _slots[slot]; if (box != null) box.attach(); @@ -62,7 +62,7 @@ class RenderScaffold extends RenderBox { } void detachChildren() { - for (ScaffoldSlots slot in ScaffoldSlots.values) { + for (_ScaffoldSlots slot in _ScaffoldSlots.values) { RenderBox box = _slots[slot]; if (box != null) box.detach(); @@ -70,16 +70,16 @@ class RenderScaffold extends RenderBox { } void visitChildren(RenderObjectVisitor visitor) { - for (ScaffoldSlots slot in ScaffoldSlots.values) { + for (_ScaffoldSlots slot in _ScaffoldSlots.values) { RenderBox box = _slots[slot]; if (box != null) visitor(box); } } - ScaffoldSlots remove(RenderBox child) { + _ScaffoldSlots remove(RenderBox child) { assert(child != null); - for (ScaffoldSlots slot in ScaffoldSlots.values) { + for (_ScaffoldSlots slot in _ScaffoldSlots.values) { if (_slots[slot] == child) { this[slot] = null; return slot; @@ -103,15 +103,15 @@ class RenderScaffold extends RenderBox { double bodyHeight = size.height; double bodyPosition = 0.0; double fabOffset = 0.0; - if (_slots[ScaffoldSlots.statusBar] != null) { - RenderBox statusBar = _slots[ScaffoldSlots.statusBar]; + 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]; + 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); @@ -119,14 +119,14 @@ class RenderScaffold extends RenderBox { bodyPosition += toolbarHeight; bodyHeight -= toolbarHeight; } - if (_slots[ScaffoldSlots.body] != null) { - RenderBox body = _slots[ScaffoldSlots.body]; + 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]; + 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), @@ -136,15 +136,15 @@ class RenderScaffold extends RenderBox { 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]; + 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]; + 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; @@ -152,7 +152,7 @@ class RenderScaffold extends RenderBox { } void paint(PaintingContext context, Offset offset) { - for (ScaffoldSlots slot in ScaffoldSlots.values) { + for (_ScaffoldSlots slot in _ScaffoldSlots.values) { RenderBox box = _slots[slot]; if (box != null) { assert(box.parentData is BoxParentData); @@ -162,7 +162,7 @@ class RenderScaffold extends RenderBox { } void hitTestChildren(HitTestResult result, { Point position }) { - for (ScaffoldSlots slot in ScaffoldSlots.values.reversed) { + for (_ScaffoldSlots slot in _ScaffoldSlots.values.reversed) { RenderBox box = _slots[slot]; if (box != null) { assert(box.parentData is BoxParentData); @@ -187,30 +187,30 @@ class Scaffold extends RenderObjectWidget { 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; + _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 _children = new Map(); + final Map<_ScaffoldSlots, Widget> _children = new Map<_ScaffoldSlots, Widget>(); - RenderScaffold createRenderObject() => new RenderScaffold(); + _RenderScaffold createRenderObject() => new _RenderScaffold(); - ScaffoldElement createElement() => new ScaffoldElement(this); + _ScaffoldElement createElement() => new _ScaffoldElement(this); } -class ScaffoldElement extends RenderObjectElement { - ScaffoldElement(Scaffold widget) : super(widget); +class _ScaffoldElement extends RenderObjectElement { + _ScaffoldElement(Scaffold widget) : super(widget); - Map _children; + Map<_ScaffoldSlots, Element> _children; - RenderScaffold get renderObject => super.renderObject; + _RenderScaffold get renderObject => super.renderObject; void visitChildren(ElementVisitor visitor) { - for (ScaffoldSlots slot in ScaffoldSlots.values) { + for (_ScaffoldSlots slot in _ScaffoldSlots.values) { Element element = _children[slot]; if (element != null) visitor(element); @@ -219,8 +219,8 @@ class ScaffoldElement extends RenderObjectElement { void mount(Element parent, dynamic newSlot) { super.mount(parent, newSlot); - _children = new Map(); - for (ScaffoldSlots slot in ScaffoldSlots.values) { + _children = new Map<_ScaffoldSlots, Element>(); + for (_ScaffoldSlots slot in _ScaffoldSlots.values) { Element newChild = widget._children[slot]?.createElement(); _children[slot] = newChild; newChild?.mount(this, slot); @@ -230,13 +230,13 @@ class ScaffoldElement extends RenderObjectElement { void update(Scaffold newWidget) { super.update(newWidget); assert(widget == newWidget); - for (ScaffoldSlots slot in ScaffoldSlots.values) { + 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) { + void insertChildRenderObject(RenderObject child, _ScaffoldSlots slot) { renderObject[slot] = child; } diff --git a/packages/flutter/lib/src/widgets/snack_bar.dart b/packages/flutter/lib/src/widgets/snack_bar.dart index 9e72a0d8fb..cf2d55d24f 100644 --- a/packages/flutter/lib/src/widgets/snack_bar.dart +++ b/packages/flutter/lib/src/widgets/snack_bar.dart @@ -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 Duration _kSlideInDuration = const Duration(milliseconds: 200); const double _kSnackHeight = 52.0; const double _kSideMargins = 24.0; const double _kVerticalPadding = 14.0; @@ -108,7 +107,7 @@ class _SnackBarRoute extends Route { bool get hasContent => false; bool get ephemeral => true; bool get modal => false; - Duration get transitionDuration => _kSlideInDuration; + Duration get transitionDuration => const Duration(milliseconds: 200); Widget build(NavigatorState navigator, PerformanceView nextRoutePerformance) => null; } diff --git a/packages/flutter/lib/src/widgets/tabs.dart b/packages/flutter/lib/src/widgets/tabs.dart index bcee5e115e..e5dc0f7dce 100644 --- a/packages/flutter/lib/src/widgets/tabs.dart +++ b/packages/flutter/lib/src/widgets/tabs.dart @@ -19,8 +19,8 @@ import 'package:sky/src/widgets/scrollable.dart'; import 'package:sky/src/widgets/theme.dart'; import 'package:sky/src/widgets/transitions.dart'; -typedef void SelectedIndexChanged(int selectedIndex); -typedef void LayoutChanged(Size size, List widths); +typedef void TabSelectedIndexChanged(int selectedIndex); +typedef void TabLayoutChanged(Size size, List widths); // See https://www.google.com/design/spec/components/tabs.html#tabs-specs const double _kTabHeight = 46.0; @@ -33,14 +33,14 @@ const int _kTabIconSize = 24; const double _kTabBarScrollDrag = 0.025; const Duration _kTabBarScroll = const Duration(milliseconds: 200); -class TabBarParentData extends BoxParentData with +class _TabBarParentData extends BoxParentData with ContainerParentDataMixin { } -class RenderTabBar extends RenderBox with - ContainerRenderObjectMixin, - RenderBoxContainerDefaultsMixin { +class _RenderTabBar extends RenderBox with + ContainerRenderObjectMixin, + RenderBoxContainerDefaultsMixin { - RenderTabBar(this.onLayoutChanged); + _RenderTabBar(this.onLayoutChanged); int _selectedIndex; int get selectedIndex => _selectedIndex; @@ -97,8 +97,8 @@ class RenderTabBar extends RenderBox with } void setupParentData(RenderBox child) { - if (child.parentData is! TabBarParentData) - child.parentData = new TabBarParentData(); + if (child.parentData is! _TabBarParentData) + child.parentData = new _TabBarParentData(); } double getMinIntrinsicWidth(BoxConstraints constraints) { @@ -109,7 +109,7 @@ class RenderTabBar extends RenderBox with RenderBox child = firstChild; while (child != null) { maxWidth = math.max(maxWidth, child.getMinIntrinsicWidth(widthConstraints)); - assert(child.parentData is TabBarParentData); + assert(child.parentData is _TabBarParentData); child = child.parentData.nextSibling; } double width = isScrollable ? maxWidth : maxWidth * childCount; @@ -124,7 +124,7 @@ class RenderTabBar extends RenderBox with RenderBox child = firstChild; while (child != null) { maxWidth = math.max(maxWidth, child.getMaxIntrinsicWidth(widthConstraints)); - assert(child.parentData is TabBarParentData); + assert(child.parentData is _TabBarParentData); child = child.parentData.nextSibling; } double width = isScrollable ? maxWidth : maxWidth * childCount; @@ -148,7 +148,7 @@ class RenderTabBar extends RenderBox with RenderBox child = firstChild; while (child != null) { child.layout(tabConstraints); - assert(child.parentData is TabBarParentData); + assert(child.parentData is _TabBarParentData); child.parentData.position = new Point(x, 0.0); x += tabWidth; child = child.parentData.nextSibling; @@ -166,7 +166,7 @@ class RenderTabBar extends RenderBox with RenderBox child = firstChild; while (child != null) { child.layout(tabConstraints, parentUsesSize: true); - assert(child.parentData is TabBarParentData); + assert(child.parentData is _TabBarParentData); child.parentData.position = new Point(x, 0.0); x += child.size.width; child = child.parentData.nextSibling; @@ -176,7 +176,7 @@ class RenderTabBar extends RenderBox with Size layoutSize; List layoutWidths; - LayoutChanged onLayoutChanged; + TabLayoutChanged onLayoutChanged; void reportLayoutChangedIfNeeded() { assert(onLayoutChanged != null); @@ -250,7 +250,7 @@ class RenderTabBar extends RenderBox with int index = 0; RenderBox child = firstChild; while (child != null) { - assert(child.parentData is TabBarParentData); + assert(child.parentData is _TabBarParentData); context.paintChild(child, child.parentData.position + offset); if (index++ == selectedIndex) _paintIndicator(context.canvas, child, offset); @@ -278,15 +278,15 @@ class _TabBarWrapper extends MultiChildRenderObjectWidget { final Rect indicatorRect; final bool textAndIcons; final bool isScrollable; - final LayoutChanged onLayoutChanged; + final TabLayoutChanged onLayoutChanged; - RenderTabBar createRenderObject() { - RenderTabBar result = new RenderTabBar(onLayoutChanged); + _RenderTabBar createRenderObject() { + _RenderTabBar result = new _RenderTabBar(onLayoutChanged); updateRenderObject(result, null); return result; } - void updateRenderObject(RenderTabBar renderObject, _TabBarWrapper oldWidget) { + void updateRenderObject(_RenderTabBar renderObject, _TabBarWrapper oldWidget) { renderObject.selectedIndex = selectedIndex; renderObject.backgroundColor = backgroundColor; renderObject.indicatorColor = indicatorColor; @@ -399,13 +399,13 @@ class TabBar extends Scrollable { final Iterable labels; final int selectedIndex; - final SelectedIndexChanged onChanged; + final TabSelectedIndexChanged onChanged; final bool isScrollable; - TabBarState createState() => new TabBarState(); + _TabBarState createState() => new _TabBarState(); } -class TabBarState extends ScrollableState { +class _TabBarState extends ScrollableState { void initState() { super.initState(); _indicatorAnimation = new ValuePerformance() @@ -598,7 +598,7 @@ class TabNavigator extends StatelessComponent { final List views; final int selectedIndex; - final SelectedIndexChanged onChanged; + final TabSelectedIndexChanged onChanged; final bool isScrollable; void _handleSelectedIndexChanged(int tabIndex) { diff --git a/packages/flutter/lib/src/widgets/transitions.dart b/packages/flutter/lib/src/widgets/transitions.dart index 4f00c3694d..5f3d52bf01 100644 --- a/packages/flutter/lib/src/widgets/transitions.dart +++ b/packages/flutter/lib/src/widgets/transitions.dart @@ -21,10 +21,10 @@ abstract class TransitionComponent extends StatefulComponent { Widget build(BuildContext context); - TransitionState createState() => new TransitionState(); + _TransitionState createState() => new _TransitionState(); } -class TransitionState extends State { +class _TransitionState extends State { void initState() { super.initState(); config.performance.addListener(_performanceChanged); From 6cf105fe3e02526006421e22e49657a3b7cd7d42 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Mon, 5 Oct 2015 23:35:06 -0700 Subject: [PATCH 08/16] ToolBar with a TabBar shouldn't have a shadow This patch makes the level of the ToolBar configurable. I've also cleaned up the Tab code slightly. For some reason, there's still a hairline between the ToolBar and the TabBar. We might need to rethink how we draw the background a bit here. Fixes #1454 --- examples/stocks/lib/stock_home.dart | 31 ++++++----- .../flutter/lib/src/material/constants.dart | 7 ++- .../flutter/lib/src/widgets/material.dart | 2 +- packages/flutter/lib/src/widgets/tabs.dart | 55 +++++++------------ .../flutter/lib/src/widgets/tool_bar.dart | 27 +++++---- 5 files changed, 53 insertions(+), 69 deletions(-) diff --git a/examples/stocks/lib/stock_home.dart b/examples/stocks/lib/stock_home.dart index f79388757a..9c914b71c0 100644 --- a/examples/stocks/lib/stock_home.dart +++ b/examples/stocks/lib/stock_home.dart @@ -126,22 +126,23 @@ class StockHomeState extends State { Widget buildToolBar() { return new ToolBar( - left: new IconButton( - icon: "navigation/menu", - onPressed: _showDrawer + level: 0, + left: new IconButton( + icon: "navigation/menu", + onPressed: _showDrawer + ), + center: new Text('Stocks'), + right: [ + new IconButton( + icon: "action/search", + onPressed: _handleSearchBegin ), - center: new Text('Stocks'), - right: [ - new IconButton( - icon: "action/search", - onPressed: _handleSearchBegin - ), - new IconButton( - icon: "navigation/more_vert", - onPressed: _handleMenuShow - ) - ] - ); + new IconButton( + icon: "navigation/more_vert", + onPressed: _handleMenuShow + ) + ] + ); } int selectedTabIndex = 0; diff --git a/packages/flutter/lib/src/material/constants.dart b/packages/flutter/lib/src/material/constants.dart index b106845c05..f33eb1a3ca 100644 --- a/packages/flutter/lib/src/material/constants.dart +++ b/packages/flutter/lib/src/material/constants.dart @@ -17,7 +17,8 @@ const double kToolBarHeight = 56.0; const double kMaterialDrawerHeight = 140.0; const double kScrollbarSize = 10.0; -const double kScrollbarFadeDuration = 250.0; -const double kScrollbarFadeDelay = 300.0; +const Duration kScrollbarFadeDuration = const Duration(milliseconds: 250); +const Duration kScrollbarFadeDelay = const Duration(milliseconds: 300); const double kFadingEdgeLength = 12.0; -const double kPressedStateDuration = 64.0; +const double kPressedStateDuration = 64.0; // units? +const Duration kThemeChangeDuration = const Duration(milliseconds: 200); diff --git a/packages/flutter/lib/src/widgets/material.dart b/packages/flutter/lib/src/widgets/material.dart index c14b29fa6f..138994cfd7 100644 --- a/packages/flutter/lib/src/widgets/material.dart +++ b/packages/flutter/lib/src/widgets/material.dart @@ -67,7 +67,7 @@ class Material extends StatelessComponent { style: Theme.of(context).text.body1, child: new AnimatedContainer( curve: ease, - duration: const Duration(milliseconds: 200), + duration: kThemeChangeDuration, decoration: new BoxDecoration( backgroundColor: _getBackgroundColor(context), borderRadius: _kEdges[type], diff --git a/packages/flutter/lib/src/widgets/tabs.dart b/packages/flutter/lib/src/widgets/tabs.dart index e5dc0f7dce..89367d9977 100644 --- a/packages/flutter/lib/src/widgets/tabs.dart +++ b/packages/flutter/lib/src/widgets/tabs.dart @@ -11,6 +11,7 @@ import 'package:sky/gestures.dart'; import 'package:sky/material.dart'; import 'package:sky/painting.dart'; import 'package:sky/rendering.dart'; +import 'package:sky/src/widgets/animated_container.dart'; import 'package:sky/src/widgets/basic.dart'; import 'package:sky/src/widgets/framework.dart'; import 'package:sky/src/widgets/icon.dart'; @@ -51,15 +52,6 @@ class _RenderTabBar extends RenderBox with } } - Color _backgroundColor; - Color get backgroundColor => _backgroundColor; - void set backgroundColor(Color value) { - if (_backgroundColor != value) { - _backgroundColor = value; - markNeedsPaint(); - } - } - Color _indicatorColor; Color get indicatorColor => _indicatorColor; void set indicatorColor(Color value) { @@ -240,13 +232,6 @@ class _RenderTabBar extends RenderBox with } void paint(PaintingContext context, Offset offset) { - if (backgroundColor != null) { - double width = layoutWidths != null - ? layoutWidths.reduce((sum, width) => sum + width) - : size.width; - Rect rect = offset & new Size(width, size.height); - context.canvas.drawRect(rect, new Paint()..color = backgroundColor); - } int index = 0; RenderBox child = firstChild; while (child != null) { @@ -264,7 +249,6 @@ class _TabBarWrapper extends MultiChildRenderObjectWidget { Key key, List children, this.selectedIndex, - this.backgroundColor, this.indicatorColor, this.indicatorRect, this.textAndIcons, @@ -273,7 +257,6 @@ class _TabBarWrapper extends MultiChildRenderObjectWidget { }) : super(key: key, children: children); final int selectedIndex; - final Color backgroundColor; final Color indicatorColor; final Rect indicatorRect; final bool textAndIcons; @@ -288,7 +271,6 @@ class _TabBarWrapper extends MultiChildRenderObjectWidget { void updateRenderObject(_RenderTabBar renderObject, _TabBarWrapper oldWidget) { renderObject.selectedIndex = selectedIndex; - renderObject.backgroundColor = backgroundColor; renderObject.indicatorColor = indicatorColor; renderObject.indicatorRect = indicatorRect; renderObject.textAndIcons = textAndIcons; @@ -537,7 +519,7 @@ class _TabBarState extends ScrollableState { textAndIcons = true; } - Widget tabBar = new IconTheme( + Widget content = new IconTheme( data: new IconThemeData(color: iconThemeColor), child: new DefaultTextStyle( style: textStyle, @@ -548,7 +530,6 @@ class _TabBarState extends ScrollableState { return new _TabBarWrapper( children: tabs, selectedIndex: config.selectedIndex, - backgroundColor: backgroundColor, indicatorColor: indicatorColor, indicatorRect: _indicatorRect.value, textAndIcons: textAndIcons, @@ -560,16 +541,23 @@ class _TabBarState extends ScrollableState { ) ); - if (!config.isScrollable) - return tabBar; + if (config.isScrollable) { + content = new SizeObserver( + callback: _handleViewportSizeChanged, + child: new Viewport( + scrollDirection: ScrollDirection.horizontal, + scrollOffset: new Offset(scrollOffset, 0.0), + child: content + ) + ); + } - return new SizeObserver( - callback: _handleViewportSizeChanged, - child: new Viewport( - scrollDirection: ScrollDirection.horizontal, - scrollOffset: new Offset(scrollOffset, 0.0), - child: tabBar - ) + return new AnimatedContainer( + decoration: new BoxDecoration( + backgroundColor: backgroundColor + ), + duration: kThemeChangeDuration, + child: content ); } } @@ -601,18 +589,13 @@ class TabNavigator extends StatelessComponent { final TabSelectedIndexChanged onChanged; final bool isScrollable; - void _handleSelectedIndexChanged(int tabIndex) { - if (onChanged != null) - onChanged(tabIndex); - } - Widget build(BuildContext context) { assert(views != null && views.isNotEmpty); assert(selectedIndex >= 0 && selectedIndex < views.length); return new Column([ new TabBar( labels: views.map((view) => view.label), - onChanged: _handleSelectedIndexChanged, + onChanged: onChanged, selectedIndex: selectedIndex, isScrollable: isScrollable ), diff --git a/packages/flutter/lib/src/widgets/tool_bar.dart b/packages/flutter/lib/src/widgets/tool_bar.dart index 543531ae63..b92c938efe 100644 --- a/packages/flutter/lib/src/widgets/tool_bar.dart +++ b/packages/flutter/lib/src/widgets/tool_bar.dart @@ -4,35 +4,36 @@ import 'package:sky/material.dart'; import 'package:sky/painting.dart'; +import 'package:sky/src/widgets/animated_container.dart'; import 'package:sky/src/widgets/basic.dart'; import 'package:sky/src/widgets/framework.dart'; import 'package:sky/src/widgets/icon.dart'; import 'package:sky/src/widgets/theme.dart'; -import 'package:sky/src/rendering/flex.dart'; class ToolBar extends StatelessComponent { - ToolBar({ Key key, this.left, this.center, this.right, + this.level: 2, this.backgroundColor }) : super(key: key); final Widget left; final Widget center; final List right; + final int level; final Color backgroundColor; Widget build(BuildContext context) { - Color toolbarColor = backgroundColor; + Color color = backgroundColor; IconThemeData iconThemeData; TextStyle centerStyle = Typography.white.title; TextStyle sideStyle = Typography.white.body1; - if (toolbarColor == null) { + if (color == null) { ThemeData themeData = Theme.of(context); - toolbarColor = themeData.primaryColor; + color = themeData.primaryColor; if (themeData.primaryColorBrightness == ThemeBrightness.light) { centerStyle = Typography.black.title; sideStyle = Typography.black.body2; @@ -44,11 +45,9 @@ class ToolBar extends StatelessComponent { List children = new List(); - // left children if (left != null) children.add(left); - // center children (left-aligned, but takes all remaining space) children.add( new Flexible( child: new Padding( @@ -58,11 +57,16 @@ class ToolBar extends StatelessComponent { ) ); - // right children if (right != null) children.addAll(right); - Widget content = new Container( + Widget content = new AnimatedContainer( + duration: kThemeChangeDuration, + padding: new EdgeDims.symmetric(horizontal: 8.0), + decoration: new BoxDecoration( + backgroundColor: color, + boxShadow: level == 0 ? null : shadows[2] + ), child: new DefaultTextStyle( style: sideStyle, child: new Column([ @@ -73,11 +77,6 @@ class ToolBar extends StatelessComponent { ], justifyContent: FlexJustifyContent.end ) - ), - padding: new EdgeDims.symmetric(horizontal: 8.0), - decoration: new BoxDecoration( - backgroundColor: toolbarColor, - boxShadow: shadows[2] ) ); From 018cef616056e84e9eda462c6a3d9d634a64fea1 Mon Sep 17 00:00:00 2001 From: Viktor Lidholt Date: Tue, 6 Oct 2015 09:28:05 -0700 Subject: [PATCH 09/16] Adds default text style --- examples/game/lib/main.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/game/lib/main.dart b/examples/game/lib/main.dart index 2cb407aa39..51366d9a40 100644 --- a/examples/game/lib/main.dart +++ b/examples/game/lib/main.dart @@ -119,8 +119,10 @@ class GameDemoState extends State { width: 128.0, height: 128.0 ), - new Text( - "Last Score: $_lastScore", + new DefaultTextStyle( + child: new Text( + "Last Score: $_lastScore" + ), style: new TextStyle(fontSize:20.0) ) ], From 6c30e01befdbe913bf873c345d54c840e456fc41 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Tue, 6 Oct 2015 09:33:33 -0700 Subject: [PATCH 10/16] Make MimicableState public again This state object has a public API. --- packages/flutter/lib/src/widgets/mimic.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/flutter/lib/src/widgets/mimic.dart b/packages/flutter/lib/src/widgets/mimic.dart index 45bb12f782..961db37986 100644 --- a/packages/flutter/lib/src/widgets/mimic.dart +++ b/packages/flutter/lib/src/widgets/mimic.dart @@ -9,7 +9,7 @@ import 'package:sky/src/widgets/framework.dart'; class MimicableKey { MimicableKey._(this._state); - final _MimicableState _state; + final MimicableState _state; Rect get globalBounds => _state._globalBounds; @@ -35,10 +35,10 @@ class Mimicable extends StatefulComponent { final Widget child; - _MimicableState createState() => new _MimicableState(); + MimicableState createState() => new MimicableState(); } -class _MimicableState extends State { +class MimicableState extends State { Size _size; bool _beingMimicked = false; From 7f6346789f48248a9424b827cb0869c5ab079a7e Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Tue, 6 Oct 2015 09:51:00 -0700 Subject: [PATCH 11/16] Rev pub package --- packages/flutter/pubspec.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/flutter/pubspec.yaml b/packages/flutter/pubspec.yaml index 0abdceb98e..20565dfdd8 100644 --- a/packages/flutter/pubspec.yaml +++ b/packages/flutter/pubspec.yaml @@ -1,5 +1,5 @@ name: sky -version: 0.0.52 +version: 0.0.53 author: Flutter Authors description: A framework for writing Flutter applications homepage: http://flutter.io @@ -9,8 +9,8 @@ dependencies: mojo_services: '>=0.1.0 <0.2.0' mojo: '>=0.1.0 <0.2.0' newton: ^0.1.4 - sky_engine: ^0.0.30 - sky_services: ^0.0.30 + sky_engine: ^0.0.31 + sky_services: ^0.0.31 sky_tools: ^0.0.15 vector_math: ^1.4.3 intl: ^0.12.4+2 From 5ac4d1ea281c9d7dd872f439b17b7c1ce2062e4c Mon Sep 17 00:00:00 2001 From: Viktor Lidholt Date: Tue, 6 Oct 2015 09:59:16 -0700 Subject: [PATCH 12/16] Adds forwarding of properties to box2d in sprite physics --- .../flutter_sprites/lib/physics_body.dart | 76 ++++++++++++++++--- 1 file changed, 64 insertions(+), 12 deletions(-) diff --git a/packages/flutter_sprites/lib/physics_body.dart b/packages/flutter_sprites/lib/physics_body.dart index d42fa8d98b..dac140513a 100644 --- a/packages/flutter_sprites/lib/physics_body.dart +++ b/packages/flutter_sprites/lib/physics_body.dart @@ -13,10 +13,10 @@ class PhysicsBody { double friction: 0.0, double restitution: 0.0, bool isSensor: false, - this.linearVelocity: Offset.zero, - this.angularVelocity: 0.0, + Offset linearVelocity: Offset.zero, + double angularVelocity: 0.0, this.linearDampening: 0.0, - this.angularDampening: 0.0, + double awakeangularDampening: 0.0, this.allowSleep: true, this.awake: true, this.fixedRotation: false, @@ -28,11 +28,15 @@ class PhysicsBody { this.friction = friction; this.restitution = restitution; this.isSensor = isSensor; + + this.linearVelocity = linearVelocity; + this.angularVelocity = angularVelocity; + this.angularDampening = angularDampening; } Object tag; - PhysicsShape shape; + final PhysicsShape shape; PhysicsBodyType type; @@ -45,7 +49,7 @@ class PhysicsBody { if (_body == null) return; - for(box2d.Fixture f = _body.getFixtureList(); f != null; f = f.getNext()) { + for (box2d.Fixture f = _body.getFixtureList(); f != null; f = f.getNext()) { f.setDensity(density); } } @@ -59,7 +63,7 @@ class PhysicsBody { if (_body == null) return; - for(box2d.Fixture f = _body.getFixtureList(); f != null; f = f.getNext()) { + for (box2d.Fixture f = _body.getFixtureList(); f != null; f = f.getNext()) { f.setFriction(friction); } } @@ -73,7 +77,7 @@ class PhysicsBody { if (_body == null) return; - for(box2d.Fixture f = _body.getFixtureList(); f != null; f = f.getNext()) { + for (box2d.Fixture f = _body.getFixtureList(); f != null; f = f.getNext()) { f.setRestitution(restitution); } } @@ -87,17 +91,65 @@ class PhysicsBody { if (_body == null) return; - for(box2d.Fixture f = _body.getFixtureList(); f != null; f = f.getNext()) { + for (box2d.Fixture f = _body.getFixtureList(); f != null; f = f.getNext()) { f.setSensor(isSensor); } } - Offset linearVelocity; - double angularVelocity; + Offset _linearVelocity; - double linearDampening; + Offset get linearVelocity { + if (_body == null) + return _linearVelocity; + else { + double dx = _body.linearVelocity.x * _physicsNode.b2WorldToNodeConversionFactor; + double dy = _body.linearVelocity.y * _physicsNode.b2WorldToNodeConversionFactor; + return new Offset(dx, dy); + } + } - double angularDampening; + set linearVelocity(Offset linearVelocity) { + _linearVelocity = linearVelocity; + + if (_body != null) { + Vector2 vec = new Vector2( + linearVelocity.dx / _physicsNode.b2WorldToNodeConversionFactor, + linearVelocity.dy / _physicsNode.b2WorldToNodeConversionFactor + ); + _body.linearVelocity = vec; + } + } + + double _angularVelocity; + + double get angularVelocity { + if (_body == null) + return _angularVelocity; + else + return _body.angularVelocity; + } + + set angularVelocity(double angularVelocity) { + _angularVelocity = angularVelocity; + + if (_body != null) { + _body.angularVelocity = angularVelocity; + } + } + + // TODO: Should this be editable in box2d.Body ? + final double linearDampening; + + double _angularDampening; + + double get angularDampening => _angularDampening; + + set angularDampening(double angularDampening) { + _angularDampening = angularDampening; + + if (_body != null) + _body.angularDamping = angularDampening; + } bool allowSleep; From 89a09822bf8d36274a1dc25675d57f91de872b69 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Mon, 5 Oct 2015 22:28:09 -0700 Subject: [PATCH 13/16] Simplify Scaffold Rather than using a custom render object, we can just use a Stack. --- examples/address_book/lib/main.dart | 2 +- examples/demo_launcher/lib/main.dart | 2 +- examples/fitness/lib/feed.dart | 2 +- examples/fitness/lib/meal.dart | 2 +- examples/fitness/lib/measurement.dart | 2 +- examples/fitness/lib/settings.dart | 2 +- examples/mine_digger/lib/main.dart | 2 +- examples/stocks/lib/stock_home.dart | 2 +- examples/stocks/lib/stock_settings.dart | 2 +- examples/stocks/lib/stock_symbol_viewer.dart | 2 +- examples/widgets/card_collection.dart | 2 +- examples/widgets/date_picker.dart | 2 +- examples/widgets/drag_and_drop.dart | 2 +- examples/widgets/ensure_visible.dart | 2 +- examples/widgets/overlay_geometry.dart | 2 +- examples/widgets/pageable_list.dart | 2 +- examples/widgets/progress_indicator.dart | 2 +- examples/widgets/scale.dart | 2 +- examples/widgets/sector.dart | 2 +- examples/widgets/styled_text.dart | 2 +- examples/widgets/tabs.dart | 2 +- .../flutter/lib/src/material/constants.dart | 1 + .../flutter/lib/src/widgets/scaffold.dart | 291 ++++-------------- .../flutter/lib/src/widgets/snack_bar.dart | 7 +- 24 files changed, 91 insertions(+), 250 deletions(-) diff --git a/examples/address_book/lib/main.dart b/examples/address_book/lib/main.dart index 26c0e4aefa..0c66e80f3e 100644 --- a/examples/address_book/lib/main.dart +++ b/examples/address_book/lib/main.dart @@ -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) ); diff --git a/examples/demo_launcher/lib/main.dart b/examples/demo_launcher/lib/main.dart index 2bbef2362b..5f466f4002 100644 --- a/examples/demo_launcher/lib/main.dart +++ b/examples/demo_launcher/lib/main.dart @@ -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() diff --git a/examples/fitness/lib/feed.dart b/examples/fitness/lib/feed.dart index eb78b29812..cf075f0f8a 100644 --- a/examples/fitness/lib/feed.dart +++ b/examples/fitness/lib/feed.dart @@ -219,7 +219,7 @@ class FeedFragmentState extends State { Widget build(BuildContext context) { return new Scaffold( - toolbar: buildToolBar(), + toolBar: buildToolBar(), body: buildBody(), snackBar: new Placeholder(key: _snackBarPlaceholderKey), floatingActionButton: buildFloatingActionButton() diff --git a/examples/fitness/lib/meal.dart b/examples/fitness/lib/meal.dart index e66fb9f900..f7e94f06ab 100644 --- a/examples/fitness/lib/meal.dart +++ b/examples/fitness/lib/meal.dart @@ -105,7 +105,7 @@ class MealFragmentState extends State { Widget build(BuildContext context) { return new Scaffold( - toolbar: buildToolBar(), + toolBar: buildToolBar(), body: buildBody() ); } diff --git a/examples/fitness/lib/measurement.dart b/examples/fitness/lib/measurement.dart index 975d2766c1..5adbca1f69 100644 --- a/examples/fitness/lib/measurement.dart +++ b/examples/fitness/lib/measurement.dart @@ -200,7 +200,7 @@ class MeasurementFragmentState extends State { Widget build(BuildContext context) { return new Scaffold( - toolbar: buildToolBar(), + toolBar: buildToolBar(), body: buildBody(context), snackBar: new Placeholder(key: _snackBarPlaceholderKey) ); diff --git a/examples/fitness/lib/settings.dart b/examples/fitness/lib/settings.dart index 27cb9a0bfb..9831818340 100644 --- a/examples/fitness/lib/settings.dart +++ b/examples/fitness/lib/settings.dart @@ -122,7 +122,7 @@ class SettingsFragmentState extends State { Widget build(BuildContext context) { return new Scaffold( - toolbar: buildToolBar(), + toolBar: buildToolBar(), body: buildSettingsPane(context) ); } diff --git a/examples/mine_digger/lib/main.dart b/examples/mine_digger/lib/main.dart index 4c78fe30c6..112d5a3b80 100644 --- a/examples/mine_digger/lib/main.dart +++ b/examples/mine_digger/lib/main.dart @@ -182,7 +182,7 @@ class MineDiggerState extends State { 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]) diff --git a/examples/stocks/lib/stock_home.dart b/examples/stocks/lib/stock_home.dart index f79388757a..cda963b0f7 100644 --- a/examples/stocks/lib/stock_home.dart +++ b/examples/stocks/lib/stock_home.dart @@ -237,7 +237,7 @@ class StockHomeState extends State { Widget build(BuildContext context) { return new Scaffold( - toolbar: _isSearching ? buildSearchBar() : buildToolBar(), + toolBar: _isSearching ? buildSearchBar() : buildToolBar(), body: buildTabNavigator(), snackBar: new Placeholder(key: _snackBarPlaceholderKey), floatingActionButton: buildFloatingActionButton() diff --git a/examples/stocks/lib/stock_settings.dart b/examples/stocks/lib/stock_settings.dart index 269afe19a6..3686a737ff 100644 --- a/examples/stocks/lib/stock_settings.dart +++ b/examples/stocks/lib/stock_settings.dart @@ -119,7 +119,7 @@ class StockSettingsState extends State { Widget build(BuildContext context) { return new Scaffold( - toolbar: buildToolBar(context), + toolBar: buildToolBar(context), body: buildSettingsPane(context) ); } diff --git a/examples/stocks/lib/stock_symbol_viewer.dart b/examples/stocks/lib/stock_symbol_viewer.dart index ab57b1558e..f2407c0820 100644 --- a/examples/stocks/lib/stock_symbol_viewer.dart +++ b/examples/stocks/lib/stock_symbol_viewer.dart @@ -25,7 +25,7 @@ class StockSymbolViewerState extends State { 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 diff --git a/examples/widgets/card_collection.dart b/examples/widgets/card_collection.dart index fb0458b0ab..359b3db226 100644 --- a/examples/widgets/card_collection.dart +++ b/examples/widgets/card_collection.dart @@ -342,7 +342,7 @@ class CardCollectionState extends State { } return new Scaffold( - toolbar: buildToolBar(), + toolBar: buildToolBar(), body: body ); } diff --git a/examples/widgets/date_picker.dart b/examples/widgets/date_picker.dart index 5e64a4a87b..48d8e30313 100644 --- a/examples/widgets/date_picker.dart +++ b/examples/widgets/date_picker.dart @@ -34,7 +34,7 @@ class DatePickerDemoState extends State { ), 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())], diff --git a/examples/widgets/drag_and_drop.dart b/examples/widgets/drag_and_drop.dart index f35934c3af..8a7829dc79 100644 --- a/examples/widgets/drag_and_drop.dart +++ b/examples/widgets/drag_and_drop.dart @@ -101,7 +101,7 @@ class DragAndDropApp extends StatefulComponent { class DragAndDropAppState extends State { Widget build(BuildContext context) { return new Scaffold( - toolbar: new ToolBar( + toolBar: new ToolBar( center: new Text('Drag and Drop Flutter Demo') ), body: new Material( diff --git a/examples/widgets/ensure_visible.dart b/examples/widgets/ensure_visible.dart index 4bb5d94f8c..ddd1891953 100644 --- a/examples/widgets/ensure_visible.dart +++ b/examples/widgets/ensure_visible.dart @@ -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 ) ) diff --git a/examples/widgets/overlay_geometry.dart b/examples/widgets/overlay_geometry.dart index 6aa66e91ed..cdbc56d253 100644 --- a/examples/widgets/overlay_geometry.dart +++ b/examples/widgets/overlay_geometry.dart @@ -138,7 +138,7 @@ class OverlayGeometryAppState extends State { Widget build(BuildContext context) { List layers = [ 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]), diff --git a/examples/widgets/pageable_list.dart b/examples/widgets/pageable_list.dart index e68ebbc87a..4ad845dc22 100644 --- a/examples/widgets/pageable_list.dart +++ b/examples/widgets/pageable_list.dart @@ -149,7 +149,7 @@ class PageableListAppState extends State { return new IconTheme( data: const IconThemeData(color: IconThemeColor.white), child: new Scaffold( - toolbar: buildToolBar(), + toolBar: buildToolBar(), body: buildBody(context) ) ); diff --git a/examples/widgets/progress_indicator.dart b/examples/widgets/progress_indicator.dart index b4b2b410a1..a83de62df1 100644 --- a/examples/widgets/progress_indicator.dart +++ b/examples/widgets/progress_indicator.dart @@ -103,7 +103,7 @@ class ProgressIndicatorAppState extends State { 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 diff --git a/examples/widgets/scale.dart b/examples/widgets/scale.dart index 679de951e4..626547010d 100644 --- a/examples/widgets/scale.dart +++ b/examples/widgets/scale.dart @@ -59,7 +59,7 @@ class ScaleAppState extends State { 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, diff --git a/examples/widgets/sector.dart b/examples/widgets/sector.dart index c8a01dbae7..d32d39944a 100644 --- a/examples/widgets/sector.dart +++ b/examples/widgets/sector.dart @@ -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() diff --git a/examples/widgets/styled_text.dart b/examples/widgets/styled_text.dart index 94197cb6a1..a21da29b11 100644 --- a/examples/widgets/styled_text.dart +++ b/examples/widgets/styled_text.dart @@ -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') ) ) diff --git a/examples/widgets/tabs.dart b/examples/widgets/tabs.dart index 0b36e8ae39..540b3327f9 100644 --- a/examples/widgets/tabs.dart +++ b/examples/widgets/tabs.dart @@ -131,7 +131,7 @@ class TabbedNavigatorAppState extends State { ); return new Scaffold( - toolbar: toolbar, + toolBar: toolbar, body: tabNavigator ); } diff --git a/packages/flutter/lib/src/material/constants.dart b/packages/flutter/lib/src/material/constants.dart index b106845c05..0ec7112ce7 100644 --- a/packages/flutter/lib/src/material/constants.dart +++ b/packages/flutter/lib/src/material/constants.dart @@ -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; diff --git a/packages/flutter/lib/src/widgets/scaffold.dart b/packages/flutter/lib/src/widgets/scaffold.dart index bff2d0d8f1..bd02c3c4ca 100644 --- a/packages/flutter/lib/src/widgets/scaffold.dart +++ b/packages/flutter/lib/src/widgets/scaffold.dart @@ -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; - } - } - 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); - } - } - } - - 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; - } - } - } - - String debugDescribeChildren(String prefix) { - return _slots.keys.map((slot) => '${prefix}${slot}: ${_slots[slot].toStringDeep(prefix)}').join(); - } -} - -class Scaffold extends RenderObjectWidget { +class Scaffold extends StatelessComponent { 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; - } + this.body, + this.statusBar, + this.toolBar, + this.snackBar, + this.floatingActionButton + }) : super(key: key); - final Map<_ScaffoldSlots, Widget> _children = new Map<_ScaffoldSlots, Widget>(); + final Widget body; + final Widget statusBar; + final Widget toolBar; + final Widget snackBar; + final Widget floatingActionButton; - _RenderScaffold createRenderObject() => new _RenderScaffold(); + Widget build(BuildContext context) { + double toolBarHeight = 0.0; + if (toolBar != null) + toolBarHeight = kToolBarHeight + sky.view.paddingTop; - _ScaffoldElement createElement() => new _ScaffoldElement(this); -} + double statusBarHeight = 0.0; + if (statusBar != null) + statusBarHeight = kStatusBarHeight; -class _ScaffoldElement extends RenderObjectElement { - _ScaffoldElement(Scaffold widget) : super(widget); + List children = []; - 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); + if (body != null) { + children.add(new Positioned( + top: toolBarHeight, right: 0.0, bottom: statusBarHeight, left: 0.0, + child: body + )); } - } - 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); + if (statusBar != null) { + children.add(new Positioned( + right: 0.0, bottom: 0.0, left: 0.0, + child: new SizedBox( + height: statusBarHeight, + child: statusBar + ) + )); } - } - 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)); + if (toolBar != null) { + children.add(new Positioned( + top: 0.0, right: 0.0, left: 0.0, + child: new SizedBox( + height: toolBarHeight, + child: toolBar + ) + )); } - } - void insertChildRenderObject(RenderObject child, _ScaffoldSlots slot) { - renderObject[slot] = child; - } + if (snackBar != null || floatingActionButton != null) { + List floatingChildren = []; - void moveChildRenderObject(RenderObject child, dynamic slot) { - removeChildRenderObject(child); - insertChildRenderObject(child, slot); - } + 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 + )); + } - void removeChildRenderObject(RenderObject child) { - assert(renderObject == child.parent); - renderObject.remove(child); + // TODO(jackson): On tablet/desktop, minWidth = 288, maxWidth = 568 + if (snackBar != null) { + floatingChildren.add(new ConstrainedBox( + constraints: const BoxConstraints(maxHeight: kSnackBarHeight), + child: snackBar + )); + } + + children.add(new Positioned( + right: 0.0, bottom: statusBarHeight, left: 0.0, + child: new Column(floatingChildren, alignItems: FlexAlignItems.end) + )); + } + + return new Stack(children); } } diff --git a/packages/flutter/lib/src/widgets/snack_bar.dart b/packages/flutter/lib/src/widgets/snack_bar.dart index cf2d55d24f..3db720df0f 100644 --- a/packages/flutter/lib/src/widgets/snack_bar.dart +++ b/packages/flutter/lib/src/widgets/snack_bar.dart @@ -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( 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, From 8b1effc544120a74564508800a22d35727da4b9d Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Tue, 6 Oct 2015 10:37:55 -0700 Subject: [PATCH 14/16] NetworkImage occasionally does not grab the image I haven't been able to reproduce this bug consistently, but my theory is that the ImageDecoder was being garbage collected before it called its completion callback. This patch prevents that by keeping a reference to the image decoder while the callback is in flight. Fixes #801 --- packages/flutter/lib/services.dart | 1 + .../lib/src/services/asset_bundle.dart | 11 ++++--- .../flutter/lib/src/services/image_cache.dart | 23 ++++++------- .../lib/src/services/image_decoder.dart | 32 +++++++++++++++++++ 4 files changed, 51 insertions(+), 16 deletions(-) create mode 100644 packages/flutter/lib/src/services/image_decoder.dart diff --git a/packages/flutter/lib/services.dart b/packages/flutter/lib/services.dart index c5bc1d3818..67387b1d3b 100644 --- a/packages/flutter/lib/services.dart +++ b/packages/flutter/lib/services.dart @@ -16,6 +16,7 @@ export 'src/services/asset_bundle.dart'; export 'src/services/embedder.dart'; export 'src/services/fetch.dart'; export 'src/services/image_cache.dart'; +export 'src/services/image_decoder.dart'; export 'src/services/image_resource.dart'; export 'src/services/keyboard.dart'; export 'src/services/shell.dart'; diff --git a/packages/flutter/lib/src/services/asset_bundle.dart b/packages/flutter/lib/src/services/asset_bundle.dart index aecc48a53b..0ecfa008bb 100644 --- a/packages/flutter/lib/src/services/asset_bundle.dart +++ b/packages/flutter/lib/src/services/asset_bundle.dart @@ -11,6 +11,7 @@ import 'package:mojo/core.dart' as core; import 'package:mojo_services/mojo/asset_bundle/asset_bundle.mojom.dart'; import 'package:sky/src/services/fetch.dart'; import 'package:sky/src/services/image_cache.dart'; +import 'package:sky/src/services/image_decoder.dart'; import 'package:sky/src/services/image_resource.dart'; import 'package:sky/src/services/shell.dart'; @@ -66,13 +67,13 @@ class MojoAssetBundle extends AssetBundle { _imageCache = null; } + Future _fetchImage(String key) async { + return await decodeImageFromDataPipe(await load(key)); + } + ImageResource loadImage(String key) { return _imageCache.putIfAbsent(key, () { - Completer completer = new Completer(); - load(key).then((assetData) { - new sky.ImageDecoder.consume(assetData.handle.h, completer.complete); - }); - return new ImageResource(completer.future); + return new ImageResource(_fetchImage(key)); }); } diff --git a/packages/flutter/lib/src/services/image_cache.dart b/packages/flutter/lib/src/services/image_cache.dart index fdc7e37bd5..81b3d3f730 100644 --- a/packages/flutter/lib/src/services/image_cache.dart +++ b/packages/flutter/lib/src/services/image_cache.dart @@ -7,8 +7,18 @@ import 'dart:collection'; import 'dart:sky' as sky; import 'package:mojo/mojo/url_response.mojom.dart'; -import 'package:sky/src/services/image_resource.dart'; import 'package:sky/src/services/fetch.dart'; +import 'package:sky/src/services/image_decoder.dart'; +import 'package:sky/src/services/image_resource.dart'; + +Future _fetchImage(String url) async { + UrlResponse response = await fetchUrl(url); + if (response.statusCode >= 400) { + print("Failed (${response.statusCode}) to load image ${url}"); + return null; + } + return await decodeImageFromDataPipe(response.body); +} class _ImageCache { _ImageCache._(); @@ -17,16 +27,7 @@ class _ImageCache { ImageResource load(String url) { return _cache.putIfAbsent(url, () { - Completer completer = new Completer(); - fetchUrl(url).then((UrlResponse response) { - if (response.statusCode >= 400) { - print("Failed (${response.statusCode}) to load image ${url}"); - completer.complete(null); - } else { - new sky.ImageDecoder.consume(response.body.handle.h, completer.complete); - } - }); - return new ImageResource(completer.future); + return new ImageResource(_fetchImage(url)); }); } } diff --git a/packages/flutter/lib/src/services/image_decoder.dart b/packages/flutter/lib/src/services/image_decoder.dart new file mode 100644 index 0000000000..375c31398e --- /dev/null +++ b/packages/flutter/lib/src/services/image_decoder.dart @@ -0,0 +1,32 @@ +// Copyright 2015 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 'dart:async'; +import 'dart:sky' show Image, ImageDecoder, ImageDecoderCallback; +import 'dart:typed_data'; + +import 'package:mojo/core.dart' show MojoDataPipeConsumer; + +final Set _activeDecoders = new Set(); + +typedef ImageDecoder _DecoderFactory(ImageDecoderCallback callback); + +Future _decode(_DecoderFactory createDecoder) { + Completer completer = new Completer(); + ImageDecoder decoder; + decoder = createDecoder((Image image) { + _activeDecoders.remove(decoder); + completer.complete(image); + }); + _activeDecoders.add(decoder); + return completer.future; +} + +Future decodeImageFromDataPipe(MojoDataPipeConsumer consumerHandle) { + return _decode((ImageDecoderCallback callback) => new ImageDecoder.consume(consumerHandle.handle.h, callback)); +} + +Future decodeImageFromList(Uint8List list) { + return _decode((ImageDecoderCallback callback) => new ImageDecoder.fromList(list, callback)); +} From b6e12ca9aa809a9f67633fee0367bd34c1114b0c Mon Sep 17 00:00:00 2001 From: Viktor Lidholt Date: Tue, 6 Oct 2015 10:39:26 -0700 Subject: [PATCH 15/16] Forwards physics body properties to box2d in sprite physics --- .../flutter_sprites/lib/physics_body.dart | 82 ++++++++++++++++--- 1 file changed, 72 insertions(+), 10 deletions(-) diff --git a/packages/flutter_sprites/lib/physics_body.dart b/packages/flutter_sprites/lib/physics_body.dart index dac140513a..0d12febef5 100644 --- a/packages/flutter_sprites/lib/physics_body.dart +++ b/packages/flutter_sprites/lib/physics_body.dart @@ -17,11 +17,11 @@ class PhysicsBody { double angularVelocity: 0.0, this.linearDampening: 0.0, double awakeangularDampening: 0.0, - this.allowSleep: true, - this.awake: true, - this.fixedRotation: false, - this.bullet: false, - this.active: true, + bool allowSleep: true, + bool awake: true, + bool fixedRotation: false, + bool bullet: false, + bool active: true, this.gravityScale: 1.0 }) { this.density = density; @@ -32,6 +32,12 @@ class PhysicsBody { this.linearVelocity = linearVelocity; this.angularVelocity = angularVelocity; this.angularDampening = angularDampening; + + this.allowSleep = allowSleep; + this.awake = awake; + this.fixedRotation = fixedRotation; + this.bullet = bullet; + this.active = active; } Object tag; @@ -151,15 +157,71 @@ class PhysicsBody { _body.angularDamping = angularDampening; } - bool allowSleep; + bool _allowSleep; - bool awake; + bool get allowSleep => _allowSleep; - bool fixedRotation; + set allowSleep(bool allowSleep) { + _allowSleep = allowSleep; - bool bullet; + if (_body != null) + _body.setSleepingAllowed(allowSleep); + } - bool active; + bool _awake; + + bool get awake { + if (_body != null) + return _body.isAwake(); + else + return _awake; + } + + set awake(bool awake) { + _awake = awake; + + if (_body != null) + _body.setAwake(awake); + } + + bool _fixedRotation; + + bool get fixedRotation => _fixedRotation; + + set fixedRotation(bool fixedRotation) { + _fixedRotation = fixedRotation; + + if (_body != null) + _body.setFixedRotation(fixedRotation); + } + + bool _bullet; + + bool get bullet => _bullet; + + set bullet(bool bullet) { + _bullet = bullet; + + if (_body != null) { + _body.setBullet(bullet); + } + } + + bool _active; + + bool get active { + if (_body != null) + return _body.isActive(); + else + return _active; + } + + set active(bool active) { + _active = active; + + if (_body != null) + _body.setActive(active); + } double gravityScale; From 56d9d85f2a6225fdeb844fffbb40f63bb79c417e Mon Sep 17 00:00:00 2001 From: Hans Muller Date: Tue, 6 Oct 2015 10:30:33 -0700 Subject: [PATCH 16/16] Remove PopupMenu margin The margins make using showMenu's menuPosition argument difficult and they're not really needed. I also made a few small gratuitous changes in navigator.dart. --- examples/stocks/lib/stock_menu.dart | 6 +- .../flutter/lib/src/widgets/navigator.dart | 12 +--- .../flutter/lib/src/widgets/popup_menu.dart | 58 +++++++++---------- 3 files changed, 34 insertions(+), 42 deletions(-) diff --git a/examples/stocks/lib/stock_menu.dart b/examples/stocks/lib/stock_menu.dart index cd1fa97226..d5c1f95884 100644 --- a/examples/stocks/lib/stock_menu.dart +++ b/examples/stocks/lib/stock_menu.dart @@ -6,12 +6,14 @@ part of stocks; enum _MenuItems { autorefresh, autorefreshCheckbox, add, remove } +const double _kMenuMargin = 16.0; // 24.0 on tablet + Future showStockMenu(NavigatorState navigator, { bool autorefresh, ValueChanged onAutorefreshChanged }) async { switch (await showMenu( navigator: navigator, position: new MenuPosition( - right: sky.view.paddingRight, - top: sky.view.paddingTop + right: sky.view.paddingRight + _kMenuMargin, + top: sky.view.paddingTop + _kMenuMargin ), builder: (NavigatorState navigator) { return [ diff --git a/packages/flutter/lib/src/widgets/navigator.dart b/packages/flutter/lib/src/widgets/navigator.dart index 7cd5167f83..112bf87808 100644 --- a/packages/flutter/lib/src/widgets/navigator.dart +++ b/packages/flutter/lib/src/widgets/navigator.dart @@ -63,16 +63,11 @@ class NavigatorState extends State { } void pushNamed(String name) { - RouteBuilder builder; - if (!config.routes.containsKey(name)) { + RouteBuilder generateRoute() { assert(config.onGenerateRoute != null); - builder = config.onGenerateRoute(name); - } else { - builder = config.routes[name]; + return config.onGenerateRoute(name); } - if (builder == null) - builder = config.onUnknownRoute; // 404! - assert(builder != null); // 404 getting your 404! + RouteBuilder builder = config.routes[name] ?? generateRoute() ?? config.onUnknownRoute; push(new PageRoute(builder)); } @@ -87,7 +82,6 @@ class NavigatorState extends State { assert(!_debugCurrentlyHaveRoute(route)); setState(() { while (currentRoute.ephemeral) { - assert(currentRoute.ephemeral); currentRoute.didPop(null); _currentPosition -= 1; } diff --git a/packages/flutter/lib/src/widgets/popup_menu.dart b/packages/flutter/lib/src/widgets/popup_menu.dart index 930b2513c9..e3cd1059ce 100644 --- a/packages/flutter/lib/src/widgets/popup_menu.dart +++ b/packages/flutter/lib/src/widgets/popup_menu.dart @@ -21,7 +21,6 @@ import 'package:sky/src/widgets/transitions.dart'; const Duration _kMenuDuration = const Duration(milliseconds: 300); const double _kMenuCloseIntervalEnd = 2.0 / 3.0; const double _kMenuWidthStep = 56.0; -const double _kMenuMargin = 16.0; // 24.0 on tablet const double _kMenuMinWidth = 2.0 * _kMenuWidthStep; const double _kMenuMaxWidth = 5.0 * _kMenuWidthStep; const double _kMenuHorizontalPadding = 16.0; @@ -75,39 +74,36 @@ class PopupMenu extends StatelessComponent { return new FadeTransition( performance: performance, opacity: new AnimatedValue(0.0, end: 1.0, curve: new Interval(0.0, 1.0 / 3.0)), - child: new Container( - margin: new EdgeDims.all(_kMenuMargin), - child: new BuilderTransition( - performance: performance, - variables: [width, height], - builder: (BuildContext context) { - return new CustomPaint( - callback: (sky.Canvas canvas, Size size) { - double widthValue = width.value * size.width; - double heightValue = height.value * size.height; - painter.paint(canvas, new Rect.fromLTWH(size.width - widthValue, 0.0, widthValue, heightValue)); - }, - child: new ConstrainedBox( - constraints: new BoxConstraints( - minWidth: _kMenuMinWidth, - maxWidth: _kMenuMaxWidth - ), - child: new IntrinsicWidth( - stepWidth: _kMenuWidthStep, - child: new ScrollableViewport( - child: new Container( - padding: const EdgeDims.symmetric( - horizontal: _kMenuHorizontalPadding, - vertical: _kMenuVerticalPadding - ), - child: new BlockBody(children) - ) + child: new BuilderTransition( + performance: performance, + variables: [width, height], + builder: (BuildContext context) { + return new CustomPaint( + callback: (sky.Canvas canvas, Size size) { + double widthValue = width.value * size.width; + double heightValue = height.value * size.height; + painter.paint(canvas, new Rect.fromLTWH(size.width - widthValue, 0.0, widthValue, heightValue)); + }, + child: new ConstrainedBox( + constraints: new BoxConstraints( + minWidth: _kMenuMinWidth, + maxWidth: _kMenuMaxWidth + ), + child: new IntrinsicWidth( + stepWidth: _kMenuWidthStep, + child: new ScrollableViewport( + child: new Container( + padding: const EdgeDims.symmetric( + horizontal: _kMenuHorizontalPadding, + vertical: _kMenuVerticalPadding + ), + child: new BlockBody(children) ) ) ) - ); - } - ) + ) + ); + } ) ); }