Commit fc8d9f30 authored by Adam Barth's avatar Adam Barth

Merge pull request #1898 from abarth/interactive_routes

Routes shouldn't be interactive when animating out
parents eaf5e058 828775e9
...@@ -248,9 +248,6 @@ class AddItemDialogState extends State<AddItemDialog> { ...@@ -248,9 +248,6 @@ class AddItemDialogState extends State<AddItemDialog> {
return new Dialog( return new Dialog(
title: new Text("What are you doing?"), title: new Text("What are you doing?"),
content: new Block(menuItems), content: new Block(menuItems),
onDismiss: () {
Navigator.of(context).pop();
},
actions: <Widget>[ actions: <Widget>[
new FlatButton( new FlatButton(
child: new Text('CANCEL'), child: new Text('CANCEL'),
......
...@@ -70,9 +70,6 @@ class SettingsFragmentState extends State<SettingsFragment> { ...@@ -70,9 +70,6 @@ class SettingsFragmentState extends State<SettingsFragment> {
keyboardType: KeyboardType.NUMBER, keyboardType: KeyboardType.NUMBER,
onChanged: _handleGoalWeightChanged onChanged: _handleGoalWeightChanged
), ),
onDismiss: () {
Navigator.of(context).pop();
},
actions: <Widget>[ actions: <Widget>[
new FlatButton( new FlatButton(
child: new Text('CANCEL'), child: new Text('CANCEL'),
......
...@@ -11,7 +11,7 @@ const double _kMenuMargin = 16.0; // 24.0 on tablet ...@@ -11,7 +11,7 @@ const double _kMenuMargin = 16.0; // 24.0 on tablet
Future showStockMenu({BuildContext context, bool autorefresh, ValueChanged<bool> onAutorefreshChanged }) async { Future showStockMenu({BuildContext context, bool autorefresh, ValueChanged<bool> onAutorefreshChanged }) async {
switch (await showMenu( switch (await showMenu(
context: context, context: context,
position: new MenuPosition( position: new ModalPosition(
right: ui.window.padding.right + _kMenuMargin, right: ui.window.padding.right + _kMenuMargin,
top: ui.window.padding.top + _kMenuMargin top: ui.window.padding.top + _kMenuMargin
), ),
......
...@@ -21,6 +21,7 @@ class StockSettings extends StatefulComponent { ...@@ -21,6 +21,7 @@ class StockSettings extends StatefulComponent {
class StockSettingsState extends State<StockSettings> { class StockSettingsState extends State<StockSettings> {
void _handleOptimismChanged(bool value) { void _handleOptimismChanged(bool value) {
value ??= false;
sendUpdates(value ? StockMode.optimistic : StockMode.pessimistic, config.backup); sendUpdates(value ? StockMode.optimistic : StockMode.pessimistic, config.backup);
} }
...@@ -39,9 +40,6 @@ class StockSettingsState extends State<StockSettings> { ...@@ -39,9 +40,6 @@ class StockSettingsState extends State<StockSettings> {
child: new Dialog( child: new Dialog(
title: new Text("Change mode?"), title: new Text("Change mode?"),
content: new Text("Optimistic mode means everything is awesome. Are you sure you can handle that?"), content: new Text("Optimistic mode means everything is awesome. Are you sure you can handle that?"),
onDismiss: () {
Navigator.of(context).pop(false);
},
actions: <Widget>[ actions: <Widget>[
new FlatButton( new FlatButton(
child: new Text('NO THANKS'), child: new Text('NO THANKS'),
......
...@@ -68,43 +68,28 @@ class _BottomSheetState extends State<_BottomSheet> { ...@@ -68,43 +68,28 @@ class _BottomSheetState extends State<_BottomSheet> {
} }
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new Focus( return new BuilderTransition(
key: new GlobalObjectKey(config.route), performance: config.route._performance,
autofocus: true, variables: <AnimatedValue<double>>[_layout.childTop],
child: new GestureDetector( builder: (BuildContext context) {
onTap: () { Navigator.of(context).pop(); }, return new ClipRect(
child: new Stack(<Widget>[ child: new CustomOneChildLayout(
// mask delegate: _layout,
new ColorTransition( token: _layout.childTop.value,
performance: config.route._performance, child: new GestureDetector(
color: new AnimatedColorValue(Colors.transparent, end: Colors.black54), onVerticalDragStart: _handleDragStart,
child: new Container() onVerticalDragUpdate: _handleDragUpdate,
), onVerticalDragEnd: _handleDragEnd,
new BuilderTransition( child: new Material(child: config.route.child)
performance: config.route._performance, )
variables: <AnimatedValue<double>>[_layout.childTop],
builder: (BuildContext context) {
return new ClipRect(
child: new CustomOneChildLayout(
delegate: _layout,
token: _layout.childTop.value,
child: new GestureDetector(
onVerticalDragStart: _handleDragStart,
onVerticalDragUpdate: _handleDragUpdate,
onVerticalDragEnd: _handleDragEnd,
child: new Material(child: config.route.child)
)
)
);
}
) )
]) );
) }
); );
} }
} }
class _ModalBottomSheetRoute extends TransitionRoute { class _ModalBottomSheetRoute extends ModalRoute {
_ModalBottomSheetRoute({ this.completer, this.child }) { _ModalBottomSheetRoute({ this.completer, this.child }) {
_performance = new Performance(duration: transitionDuration, debugLabel: 'ModalBottomSheet'); _performance = new Performance(duration: transitionDuration, debugLabel: 'ModalBottomSheet');
} }
...@@ -122,7 +107,8 @@ class _ModalBottomSheetRoute extends TransitionRoute { ...@@ -122,7 +107,8 @@ class _ModalBottomSheetRoute extends TransitionRoute {
return _performance; return _performance;
} }
List<Widget> createWidgets() => [ new _BottomSheet(route: this) ]; Color get barrierColor => Colors.black54;
Widget createModalWidget() => new _BottomSheet(route: this);
void didPop([dynamic result]) { void didPop([dynamic result]) {
completer.complete(result); completer.complete(result);
......
...@@ -24,8 +24,7 @@ class Dialog extends StatelessComponent { ...@@ -24,8 +24,7 @@ class Dialog extends StatelessComponent {
this.titlePadding, this.titlePadding,
this.content, this.content,
this.contentPadding, this.contentPadding,
this.actions, this.actions
this.onDismiss
}) : super(key: key); }) : super(key: key);
/// The (optional) title of the dialog is displayed in a large font at the top /// The (optional) title of the dialog is displayed in a large font at the top
...@@ -47,9 +46,6 @@ class Dialog extends StatelessComponent { ...@@ -47,9 +46,6 @@ class Dialog extends StatelessComponent {
/// dialog. /// dialog.
final List<Widget> actions; final List<Widget> actions;
/// An (optional) callback that is called when the dialog is dismissed.
final VoidCallback onDismiss;
Color _getColor(BuildContext context) { Color _getColor(BuildContext context) {
switch (Theme.of(context).brightness) { switch (Theme.of(context).brightness) {
case ThemeBrightness.light: case ThemeBrightness.light:
...@@ -100,38 +96,26 @@ class Dialog extends StatelessComponent { ...@@ -100,38 +96,26 @@ class Dialog extends StatelessComponent {
)); ));
} }
// TODO(abarth): We should return the backdrop as a separate entry from createWidgets. return new Center(
return new Stack(<Widget>[ child: new Container(
new GestureDetector( margin: new EdgeDims.symmetric(horizontal: 40.0, vertical: 24.0),
onTap: onDismiss, child: new ConstrainedBox(
child: new Container( constraints: new BoxConstraints(minWidth: 280.0),
decoration: const BoxDecoration( child: new Material(
backgroundColor: const Color(0x7F000000) level: 4,
) color: _getColor(context),
) type: MaterialType.card,
), child: new IntrinsicWidth(
new Center( child: new Block(dialogBody)
child: new Container(
margin: new EdgeDims.symmetric(horizontal: 40.0, vertical: 24.0),
child: new ConstrainedBox(
constraints: new BoxConstraints(minWidth: 280.0),
child: new Material(
level: 4,
color: _getColor(context),
type: MaterialType.card,
child: new IntrinsicWidth(
child: new Block(dialogBody)
)
) )
) )
) )
) )
]); );
} }
} }
class _DialogRoute extends TransitionRoute { class _DialogRoute extends ModalRoute {
_DialogRoute({ this.completer, this.child }); _DialogRoute({ this.completer, this.child });
final Completer completer; final Completer completer;
...@@ -139,19 +123,14 @@ class _DialogRoute extends TransitionRoute { ...@@ -139,19 +123,14 @@ class _DialogRoute extends TransitionRoute {
bool get opaque => false; bool get opaque => false;
Duration get transitionDuration => const Duration(milliseconds: 150); Duration get transitionDuration => const Duration(milliseconds: 150);
Color get barrierColor => Colors.black54;
List<Widget> createWidgets() {
return [ Widget createModalWidget() {
new Focus( return new FadeTransition(
key: new GlobalObjectKey(this), performance: performance,
autofocus: true, opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: Curves.easeOut),
child: new FadeTransition( child: child
performance: performance, );
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: Curves.easeOut),
child: child
)
)
];
} }
void didPop([dynamic result]) { void didPop([dynamic result]) {
......
...@@ -21,8 +21,11 @@ const double _kMenuHorizontalPadding = 36.0; ...@@ -21,8 +21,11 @@ const double _kMenuHorizontalPadding = 36.0;
const double _kBaselineOffsetFromBottom = 20.0; const double _kBaselineOffsetFromBottom = 20.0;
const Border _kDropdownUnderline = const Border(bottom: const BorderSide(color: const Color(0xFFBDBDBD), width: 2.0)); const Border _kDropdownUnderline = const Border(bottom: const BorderSide(color: const Color(0xFFBDBDBD), width: 2.0));
class _DropdownMenu extends StatelessComponent { class _DropdownMenu extends StatusTransitionComponent {
_DropdownMenu({ Key key, this.route }) : super(key: key); _DropdownMenu({
Key key,
_MenuRoute route
}) : route = route, super(key: key, performance: route.performance);
final _MenuRoute route; final _MenuRoute route;
...@@ -92,7 +95,6 @@ class _DropdownMenu extends StatelessComponent { ...@@ -92,7 +95,6 @@ class _DropdownMenu extends StatelessComponent {
left: menuRect.left - _kMenuHorizontalPadding, left: menuRect.left - _kMenuHorizontalPadding,
child: new Focus( child: new Focus(
key: new GlobalObjectKey(route), key: new GlobalObjectKey(route),
autofocus: true,
child: new FadeTransition( child: new FadeTransition(
performance: route.performance, performance: route.performance,
opacity: menuOpacity, opacity: menuOpacity,
...@@ -117,6 +119,7 @@ class _DropdownMenu extends StatelessComponent { ...@@ -117,6 +119,7 @@ class _DropdownMenu extends StatelessComponent {
} }
} }
// TODO(abarth): This should use ModalRoute.
class _MenuRoute extends TransitionRoute { class _MenuRoute extends TransitionRoute {
_MenuRoute({ _MenuRoute({
this.completer, this.completer,
......
...@@ -53,71 +53,53 @@ class _PopupMenu extends StatelessComponent { ...@@ -53,71 +53,53 @@ class _PopupMenu extends StatelessComponent {
); );
} }
final AnimatedValue<double> opacity = new AnimatedValue<double>(0.0, end: 1.0, curve: new Interval(0.0, 1.0 / 3.0));
final AnimatedValue<double> width = new AnimatedValue<double>(0.0, end: 1.0, curve: new Interval(0.0, unit)); final AnimatedValue<double> width = new AnimatedValue<double>(0.0, end: 1.0, curve: new Interval(0.0, unit));
final AnimatedValue<double> height = new AnimatedValue<double>(0.0, end: 1.0, curve: new Interval(0.0, unit * route.items.length)); final AnimatedValue<double> height = new AnimatedValue<double>(0.0, end: 1.0, curve: new Interval(0.0, unit * route.items.length));
return new Positioned( return new BuilderTransition(
top: route.position?.top, performance: route.performance,
right: route.position?.right, variables: <AnimatedValue<double>>[opacity, width, height],
bottom: route.position?.bottom, builder: (BuildContext context) {
left: route.position?.left, return new Opacity(
child: new Focus( opacity: opacity.value,
key: new GlobalObjectKey(route), child: new CustomPaint(
autofocus: true, onPaint: (ui.Canvas canvas, Size size) {
child: new FadeTransition( double widthValue = width.value * size.width;
performance: route.performance, double heightValue = height.value * size.height;
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: new Interval(0.0, 1.0 / 3.0)), painter.paint(canvas, new Rect.fromLTWH(size.width - widthValue, 0.0, widthValue, heightValue));
child: new BuilderTransition( },
performance: route.performance, child: new ConstrainedBox(
variables: <AnimatedValue<double>>[width, height], constraints: new BoxConstraints(
builder: (BuildContext context) { minWidth: _kMenuMinWidth,
return new CustomPaint( maxWidth: _kMenuMaxWidth
onPaint: (ui.Canvas canvas, Size size) { ),
double widthValue = width.value * size.width; child: new IntrinsicWidth(
double heightValue = height.value * size.height; stepWidth: _kMenuWidthStep,
painter.paint(canvas, new Rect.fromLTWH(size.width - widthValue, 0.0, widthValue, heightValue)); child: new ScrollableViewport(
}, child: new Container(
child: new ConstrainedBox( // TODO(abarth): Teach Block about padding.
constraints: new BoxConstraints( padding: const EdgeDims.symmetric(
minWidth: _kMenuMinWidth, horizontal: _kMenuHorizontalPadding,
maxWidth: _kMenuMaxWidth vertical: _kMenuVerticalPadding
), ),
child: new IntrinsicWidth( child: new BlockBody(children)
stepWidth: _kMenuWidthStep,
child: new ScrollableViewport(
child: new Container(
// TODO(abarth): Teach Block about padding.
padding: const EdgeDims.symmetric(
horizontal: _kMenuHorizontalPadding,
vertical: _kMenuVerticalPadding
),
child: new BlockBody(children)
)
)
) )
) )
); )
} )
) )
) );
) }
); );
} }
} }
class MenuPosition { class _MenuRoute extends ModalRoute {
const MenuPosition({ this.top, this.right, this.bottom, this.left });
final double top;
final double right;
final double bottom;
final double left;
}
class _MenuRoute extends TransitionRoute {
_MenuRoute({ this.completer, this.position, this.items, this.level }); _MenuRoute({ this.completer, this.position, this.items, this.level });
final Completer completer; final Completer completer;
final MenuPosition position; final ModalPosition position;
final List<PopupMenuItem> items; final List<PopupMenuItem> items;
final int level; final int level;
...@@ -132,10 +114,7 @@ class _MenuRoute extends TransitionRoute { ...@@ -132,10 +114,7 @@ class _MenuRoute extends TransitionRoute {
bool get opaque => false; bool get opaque => false;
Duration get transitionDuration => _kMenuDuration; Duration get transitionDuration => _kMenuDuration;
List<Widget> createWidgets() => [ Widget createModalWidget() => new _PopupMenu(route: this);
new ModalBarrier(),
new _PopupMenu(route: this),
];
void didPop([dynamic result]) { void didPop([dynamic result]) {
completer.complete(result); completer.complete(result);
...@@ -143,7 +122,7 @@ class _MenuRoute extends TransitionRoute { ...@@ -143,7 +122,7 @@ class _MenuRoute extends TransitionRoute {
} }
} }
Future showMenu({ BuildContext context, MenuPosition position, List<PopupMenuItem> items, int level: 4 }) { Future showMenu({ BuildContext context, ModalPosition position, List<PopupMenuItem> items, int level: 4 }) {
Completer completer = new Completer(); Completer completer = new Completer();
Navigator.of(context).pushEphemeral(new _MenuRoute( Navigator.of(context).pushEphemeral(new _MenuRoute(
completer: completer, completer: completer,
......
...@@ -2,19 +2,124 @@ ...@@ -2,19 +2,124 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/animation.dart';
import 'basic.dart'; import 'basic.dart';
import 'focus.dart';
import 'framework.dart'; import 'framework.dart';
import 'navigator.dart'; import 'navigator.dart';
import 'routes.dart';
import 'status_transitions.dart';
import 'transitions.dart';
const Color _kTransparent = const Color(0x00000000);
class ModalBarrier extends StatelessComponent { class ModalBarrier extends StatelessComponent {
ModalBarrier({ Key key }) : super(key: key); ModalBarrier({
Key key,
this.color
}) : super(key: key);
final Color color;
Widget build(BuildContext context) { Widget build(BuildContext context) {
Widget child;
if (color != null) {
child = new DecoratedBox(
decoration: new BoxDecoration(
backgroundColor: color
)
);
}
return new Listener( return new Listener(
onPointerDown: (_) { onPointerDown: (_) {
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
child: new Container() child: new ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: child
)
);
}
}
class _AnimatedModalBarrier extends StatelessComponent {
_AnimatedModalBarrier({
Key key,
this.color,
this.performance
}) : super(key: key);
final AnimatedColorValue color;
final PerformanceView performance;
Widget build(BuildContext context) {
return new BuilderTransition(
performance: performance,
variables: <AnimatedColorValue>[color],
builder: (BuildContext context) {
return new IgnorePointer(
ignoring: performance.status == PerformanceStatus.reverse,
child: new ModalBarrier(color: color.value)
);
}
);
}
}
class _ModalScope extends StatusTransitionComponent {
_ModalScope({
Key key,
ModalRoute route,
this.child
}) : route = route, super(key: key, performance: route.performance);
final ModalRoute route;
final Widget child;
Widget build(BuildContext context) {
Widget focus = new Focus(
key: new GlobalObjectKey(route),
child: new IgnorePointer(
ignoring: route.performance.status == PerformanceStatus.reverse,
child: child
)
);
ModalPosition position = route.position;
if (position == null)
return focus;
return new Positioned(
top: position.top,
right: position.right,
bottom: position.bottom,
left: position.left,
child: focus
); );
} }
} }
class ModalPosition {
const ModalPosition({ this.top, this.right, this.bottom, this.left });
final double top;
final double right;
final double bottom;
final double left;
}
abstract class ModalRoute extends TransitionRoute {
ModalPosition get position => null;
Color get barrierColor => _kTransparent;
Widget createModalWidget();
List<Widget> createWidgets() {
return [
new _AnimatedModalBarrier(
color: new AnimatedColorValue(_kTransparent, end: barrierColor, curve: Curves.ease),
performance: performance
),
new _ModalScope(route: this, child: createModalWidget()),
];
}
}
...@@ -6,11 +6,41 @@ import 'package:flutter/animation.dart'; ...@@ -6,11 +6,41 @@ import 'package:flutter/animation.dart';
import 'basic.dart'; import 'basic.dart';
import 'framework.dart'; import 'framework.dart';
import 'modal_barrier.dart';
import 'navigator.dart'; import 'navigator.dart';
import 'page_storage.dart'; import 'page_storage.dart';
import 'routes.dart';
import 'transitions.dart'; import 'transitions.dart';
class _PageTransition extends TransitionWithChild {
_PageTransition({
Key key,
PerformanceView performance,
Widget child
}) : super(key: key,
performance: performance,
child: child);
final AnimatedValue<Point> _position =
new AnimatedValue<Point>(const Point(0.0, 75.0), end: Point.origin, curve: Curves.easeOut);
final AnimatedValue<double> _opacity =
new AnimatedValue<double>(0.0, end: 1.0, curve: Curves.easeOut);
Widget buildWithChild(BuildContext context, Widget child) {
performance.updateVariable(_position);
performance.updateVariable(_opacity);
Matrix4 transform = new Matrix4.identity()
..translate(_position.value.x, _position.value.y);
return new Transform(
transform: transform,
child: new Opacity(
opacity: _opacity.value,
child: child
)
);
}
}
class _Page extends StatefulComponent { class _Page extends StatefulComponent {
_Page({ _Page({
Key key, Key key,
...@@ -23,12 +53,6 @@ class _Page extends StatefulComponent { ...@@ -23,12 +53,6 @@ class _Page extends StatefulComponent {
} }
class _PageState extends State<_Page> { class _PageState extends State<_Page> {
final AnimatedValue<Point> _position =
new AnimatedValue<Point>(const Point(0.0, 75.0), end: Point.origin, curve: Curves.easeOut);
final AnimatedValue<double> _opacity =
new AnimatedValue<double>(0.0, end: 1.0, curve: Curves.easeOut);
final GlobalKey _subtreeKey = new GlobalKey(); final GlobalKey _subtreeKey = new GlobalKey();
Widget build(BuildContext context) { Widget build(BuildContext context) {
...@@ -41,17 +65,12 @@ class _PageState extends State<_Page> { ...@@ -41,17 +65,12 @@ class _PageState extends State<_Page> {
) )
); );
} }
return new SlideTransition( return new _PageTransition(
performance: config.route.performance, performance: config.route.performance,
position: _position, child: new PageStorage(
child: new FadeTransition( key: _subtreeKey,
performance: config.route.performance, bucket: config.route._storageBucket,
opacity: _opacity, child: _invokeBuilder()
child: new PageStorage(
key: _subtreeKey,
bucket: config.route._storageBucket,
child: _invokeBuilder()
)
) )
); );
} }
...@@ -68,7 +87,7 @@ class _PageState extends State<_Page> { ...@@ -68,7 +87,7 @@ class _PageState extends State<_Page> {
} }
} }
class PageRoute extends TransitionRoute { class PageRoute extends ModalRoute {
PageRoute({ PageRoute({
this.builder, this.builder,
this.settings: const NamedRouteSettings() this.settings: const NamedRouteSettings()
...@@ -85,9 +104,8 @@ class PageRoute extends TransitionRoute { ...@@ -85,9 +104,8 @@ class PageRoute extends TransitionRoute {
bool get opaque => true; bool get opaque => true;
String get name => settings.name; String get name => settings.name;
Duration get transitionDuration => const Duration(milliseconds: 150); Duration get transitionDuration => const Duration(milliseconds: 150);
List<Widget> createWidgets() => [ new _Page(key: pageKey, route: this) ]; Widget createModalWidget() => new _Page(key: pageKey, route: this);
final PageStorageBucket _storageBucket = new PageStorageBucket(); final PageStorageBucket _storageBucket = new PageStorageBucket();
......
// 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:flutter/animation.dart';
import 'framework.dart';
abstract class StatusTransitionComponent extends StatefulComponent {
StatusTransitionComponent({
Key key,
this.performance
}) : super(key: key) {
assert(performance != null);
}
final PerformanceView performance;
Widget build(BuildContext context);
_StatusTransitionState createState() => new _StatusTransitionState();
}
class _StatusTransitionState extends State<StatusTransitionComponent> {
void initState() {
super.initState();
config.performance.addStatusListener(_performanceStatusChanged);
}
void didUpdateConfig(StatusTransitionComponent oldConfig) {
if (config.performance != oldConfig.performance) {
oldConfig.performance.removeStatusListener(_performanceStatusChanged);
config.performance.addStatusListener(_performanceStatusChanged);
}
}
void dispose() {
config.performance.removeStatusListener(_performanceStatusChanged);
super.dispose();
}
void _performanceStatusChanged(PerformanceStatus status) {
setState(() {
// The performance's state is our build state, and it changed already.
});
}
Widget build(BuildContext context) {
return config.build(context);
}
}
...@@ -31,6 +31,7 @@ export 'src/widgets/placeholder.dart'; ...@@ -31,6 +31,7 @@ export 'src/widgets/placeholder.dart';
export 'src/widgets/routes.dart'; export 'src/widgets/routes.dart';
export 'src/widgets/scrollable.dart'; export 'src/widgets/scrollable.dart';
export 'src/widgets/statistics_overlay.dart'; export 'src/widgets/statistics_overlay.dart';
export 'src/widgets/status_transitions.dart';
export 'src/widgets/transitions.dart'; export 'src/widgets/transitions.dart';
export 'src/widgets/unique_component.dart'; export 'src/widgets/unique_component.dart';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment