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> {
return new Dialog(
title: new Text("What are you doing?"),
content: new Block(menuItems),
onDismiss: () {
Navigator.of(context).pop();
},
actions: <Widget>[
new FlatButton(
child: new Text('CANCEL'),
......
......@@ -70,9 +70,6 @@ class SettingsFragmentState extends State<SettingsFragment> {
keyboardType: KeyboardType.NUMBER,
onChanged: _handleGoalWeightChanged
),
onDismiss: () {
Navigator.of(context).pop();
},
actions: <Widget>[
new FlatButton(
child: new Text('CANCEL'),
......
......@@ -11,7 +11,7 @@ const double _kMenuMargin = 16.0; // 24.0 on tablet
Future showStockMenu({BuildContext context, bool autorefresh, ValueChanged<bool> onAutorefreshChanged }) async {
switch (await showMenu(
context: context,
position: new MenuPosition(
position: new ModalPosition(
right: ui.window.padding.right + _kMenuMargin,
top: ui.window.padding.top + _kMenuMargin
),
......
......@@ -21,6 +21,7 @@ class StockSettings extends StatefulComponent {
class StockSettingsState extends State<StockSettings> {
void _handleOptimismChanged(bool value) {
value ??= false;
sendUpdates(value ? StockMode.optimistic : StockMode.pessimistic, config.backup);
}
......@@ -39,9 +40,6 @@ class StockSettingsState extends State<StockSettings> {
child: new Dialog(
title: new Text("Change mode?"),
content: new Text("Optimistic mode means everything is awesome. Are you sure you can handle that?"),
onDismiss: () {
Navigator.of(context).pop(false);
},
actions: <Widget>[
new FlatButton(
child: new Text('NO THANKS'),
......
......@@ -68,43 +68,28 @@ class _BottomSheetState extends State<_BottomSheet> {
}
Widget build(BuildContext context) {
return new Focus(
key: new GlobalObjectKey(config.route),
autofocus: true,
child: new GestureDetector(
onTap: () { Navigator.of(context).pop(); },
child: new Stack(<Widget>[
// mask
new ColorTransition(
performance: config.route._performance,
color: new AnimatedColorValue(Colors.transparent, end: Colors.black54),
child: new Container()
),
new BuilderTransition(
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)
)
)
);
}
return new BuilderTransition(
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 }) {
_performance = new Performance(duration: transitionDuration, debugLabel: 'ModalBottomSheet');
}
......@@ -122,7 +107,8 @@ class _ModalBottomSheetRoute extends TransitionRoute {
return _performance;
}
List<Widget> createWidgets() => [ new _BottomSheet(route: this) ];
Color get barrierColor => Colors.black54;
Widget createModalWidget() => new _BottomSheet(route: this);
void didPop([dynamic result]) {
completer.complete(result);
......
......@@ -24,8 +24,7 @@ class Dialog extends StatelessComponent {
this.titlePadding,
this.content,
this.contentPadding,
this.actions,
this.onDismiss
this.actions
}) : super(key: key);
/// The (optional) title of the dialog is displayed in a large font at the top
......@@ -47,9 +46,6 @@ class Dialog extends StatelessComponent {
/// dialog.
final List<Widget> actions;
/// An (optional) callback that is called when the dialog is dismissed.
final VoidCallback onDismiss;
Color _getColor(BuildContext context) {
switch (Theme.of(context).brightness) {
case ThemeBrightness.light:
......@@ -100,38 +96,26 @@ class Dialog extends StatelessComponent {
));
}
// TODO(abarth): We should return the backdrop as a separate entry from createWidgets.
return new Stack(<Widget>[
new GestureDetector(
onTap: onDismiss,
child: new Container(
decoration: const BoxDecoration(
backgroundColor: const Color(0x7F000000)
)
)
),
new Center(
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)
)
return new Center(
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 });
final Completer completer;
......@@ -139,19 +123,14 @@ class _DialogRoute extends TransitionRoute {
bool get opaque => false;
Duration get transitionDuration => const Duration(milliseconds: 150);
List<Widget> createWidgets() {
return [
new Focus(
key: new GlobalObjectKey(this),
autofocus: true,
child: new FadeTransition(
performance: performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: Curves.easeOut),
child: child
)
)
];
Color get barrierColor => Colors.black54;
Widget createModalWidget() {
return new FadeTransition(
performance: performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: Curves.easeOut),
child: child
);
}
void didPop([dynamic result]) {
......
......@@ -21,8 +21,11 @@ const double _kMenuHorizontalPadding = 36.0;
const double _kBaselineOffsetFromBottom = 20.0;
const Border _kDropdownUnderline = const Border(bottom: const BorderSide(color: const Color(0xFFBDBDBD), width: 2.0));
class _DropdownMenu extends StatelessComponent {
_DropdownMenu({ Key key, this.route }) : super(key: key);
class _DropdownMenu extends StatusTransitionComponent {
_DropdownMenu({
Key key,
_MenuRoute route
}) : route = route, super(key: key, performance: route.performance);
final _MenuRoute route;
......@@ -92,7 +95,6 @@ class _DropdownMenu extends StatelessComponent {
left: menuRect.left - _kMenuHorizontalPadding,
child: new Focus(
key: new GlobalObjectKey(route),
autofocus: true,
child: new FadeTransition(
performance: route.performance,
opacity: menuOpacity,
......@@ -117,6 +119,7 @@ class _DropdownMenu extends StatelessComponent {
}
}
// TODO(abarth): This should use ModalRoute.
class _MenuRoute extends TransitionRoute {
_MenuRoute({
this.completer,
......
......@@ -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> height = new AnimatedValue<double>(0.0, end: 1.0, curve: new Interval(0.0, unit * route.items.length));
return new Positioned(
top: route.position?.top,
right: route.position?.right,
bottom: route.position?.bottom,
left: route.position?.left,
child: new Focus(
key: new GlobalObjectKey(route),
autofocus: true,
child: new FadeTransition(
performance: route.performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: new Interval(0.0, 1.0 / 3.0)),
child: new BuilderTransition(
performance: route.performance,
variables: <AnimatedValue<double>>[width, height],
builder: (BuildContext context) {
return new CustomPaint(
onPaint: (ui.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(
// TODO(abarth): Teach Block about padding.
padding: const EdgeDims.symmetric(
horizontal: _kMenuHorizontalPadding,
vertical: _kMenuVerticalPadding
),
child: new BlockBody(children)
)
)
return new BuilderTransition(
performance: route.performance,
variables: <AnimatedValue<double>>[opacity, width, height],
builder: (BuildContext context) {
return new Opacity(
opacity: opacity.value,
child: new CustomPaint(
onPaint: (ui.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(
// TODO(abarth): Teach Block about padding.
padding: const EdgeDims.symmetric(
horizontal: _kMenuHorizontalPadding,
vertical: _kMenuVerticalPadding
),
child: new BlockBody(children)
)
)
);
}
)
)
)
)
)
);
}
);
}
}
class MenuPosition {
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 {
class _MenuRoute extends ModalRoute {
_MenuRoute({ this.completer, this.position, this.items, this.level });
final Completer completer;
final MenuPosition position;
final ModalPosition position;
final List<PopupMenuItem> items;
final int level;
......@@ -132,10 +114,7 @@ class _MenuRoute extends TransitionRoute {
bool get opaque => false;
Duration get transitionDuration => _kMenuDuration;
List<Widget> createWidgets() => [
new ModalBarrier(),
new _PopupMenu(route: this),
];
Widget createModalWidget() => new _PopupMenu(route: this);
void didPop([dynamic result]) {
completer.complete(result);
......@@ -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();
Navigator.of(context).pushEphemeral(new _MenuRoute(
completer: completer,
......
......@@ -2,19 +2,124 @@
// 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 'basic.dart';
import 'focus.dart';
import 'framework.dart';
import 'navigator.dart';
import 'routes.dart';
import 'status_transitions.dart';
import 'transitions.dart';
const Color _kTransparent = const Color(0x00000000);
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 child;
if (color != null) {
child = new DecoratedBox(
decoration: new BoxDecoration(
backgroundColor: color
)
);
}
return new Listener(
onPointerDown: (_) {
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';
import 'basic.dart';
import 'framework.dart';
import 'modal_barrier.dart';
import 'navigator.dart';
import 'page_storage.dart';
import 'routes.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 {
_Page({
Key key,
......@@ -23,12 +53,6 @@ class _Page extends StatefulComponent {
}
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();
Widget build(BuildContext context) {
......@@ -41,17 +65,12 @@ class _PageState extends State<_Page> {
)
);
}
return new SlideTransition(
return new _PageTransition(
performance: config.route.performance,
position: _position,
child: new FadeTransition(
performance: config.route.performance,
opacity: _opacity,
child: new PageStorage(
key: _subtreeKey,
bucket: config.route._storageBucket,
child: _invokeBuilder()
)
child: new PageStorage(
key: _subtreeKey,
bucket: config.route._storageBucket,
child: _invokeBuilder()
)
);
}
......@@ -68,7 +87,7 @@ class _PageState extends State<_Page> {
}
}
class PageRoute extends TransitionRoute {
class PageRoute extends ModalRoute {
PageRoute({
this.builder,
this.settings: const NamedRouteSettings()
......@@ -85,9 +104,8 @@ class PageRoute extends TransitionRoute {
bool get opaque => true;
String get name => settings.name;
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();
......
// 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';
export 'src/widgets/routes.dart';
export 'src/widgets/scrollable.dart';
export 'src/widgets/statistics_overlay.dart';
export 'src/widgets/status_transitions.dart';
export 'src/widgets/transitions.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