Commit 6e371875 authored by Hixie's avatar Hixie

Route refactor

- Removed the concept of ephemeral routes.
- Renamed the two _MenuRoutes to _PopupMenuRoute and _DropDownRoute.
- Added type arguments in various places:
  - DropDownMenu
  - _DropDownRoute
  - _ModalBottomSheetRoute
  - PopupMenuItem
  - _PopupMenu
  - _PopupMenuRoute
- Made _ModalBottomSheetRoute, the two ex _MenuRoutes, and _DialogRoute
  all inherit from ModalRoute, via PopupRoute.
- Change "Dropdown" and "DropDown" to "DropDown" consistently.
- Made MaterialPageRoute inherit from PageRoute.
- Made ModalBarrier not create a box if it's always transparent.
- Exposed the Futures on TransitionRoutes.
- Fixed that menus were no longer dismissable by tapping the modal
  barrier.
parent de772647
...@@ -4,24 +4,22 @@ ...@@ -4,24 +4,22 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class DropdownDemo extends StatefulComponent { class DropDownDemo extends StatefulComponent {
DropdownDemo(); DropDownDemoState createState() => new DropDownDemoState();
DropdownDemoState createState() => new DropdownDemoState();
} }
class DropdownDemoState extends State<DropdownDemo> { class DropDownDemoState extends State<DropDownDemo> {
String _value = "Free"; String _value = "Free";
List<DropdownMenuItem> _buildItems() { List<DropDownMenuItem<String>> _buildItems() {
return ["One", "Two", "Free", "Four"].map((String value) { return ["One", "Two", "Free", "Four"].map((String value) {
return new DropdownMenuItem<String>(value: value, child: new Text(value)); return new DropDownMenuItem<String>(value: value, child: new Text(value));
}) })
.toList(); .toList();
} }
Widget build(BuildContext context) { Widget build(BuildContext context) {
Widget dropdown = new DropdownButton<String>( Widget dropdown = new DropDownButton<String>(
items: _buildItems(), items: _buildItems(),
value: _value, value: _value,
onChanged: (String newValue) { onChanged: (String newValue) {
...@@ -33,7 +31,7 @@ class DropdownDemoState extends State<DropdownDemo> { ...@@ -33,7 +31,7 @@ class DropdownDemoState extends State<DropdownDemo> {
); );
return new Scaffold( return new Scaffold(
toolBar: new ToolBar(center: new Text('DropdownDemo Demo')), toolBar: new ToolBar(center: new Text('DropDownDemo Demo')),
body: new Container( body: new Container(
decoration: new BoxDecoration(backgroundColor: Theme.of(context).primarySwatch[50]), decoration: new BoxDecoration(backgroundColor: Theme.of(context).primarySwatch[50]),
child: new Center(child: dropdown) child: new Center(child: dropdown)
...@@ -44,14 +42,14 @@ class DropdownDemoState extends State<DropdownDemo> { ...@@ -44,14 +42,14 @@ class DropdownDemoState extends State<DropdownDemo> {
void main() { void main() {
runApp(new MaterialApp( runApp(new MaterialApp(
title: 'DropdownDemo', title: 'DropDownDemo',
theme: new ThemeData( theme: new ThemeData(
brightness: ThemeBrightness.light, brightness: ThemeBrightness.light,
primarySwatch: Colors.blue, primarySwatch: Colors.blue,
accentColor: Colors.redAccent[200] accentColor: Colors.redAccent[200]
), ),
routes: <String, RouteBuilder>{ routes: <String, RouteBuilder>{
'/': (RouteArguments args) => new DropdownDemo(), '/': (RouteArguments args) => new DropDownDemo(),
} }
)); ));
} }
...@@ -140,55 +140,32 @@ class _ModalBottomSheetState extends State<_ModalBottomSheet> { ...@@ -140,55 +140,32 @@ class _ModalBottomSheetState extends State<_ModalBottomSheet> {
} }
} }
class _ModalBottomSheetRoute extends OverlayRoute { class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
_ModalBottomSheetRoute({ this.completer, this.builder }); _ModalBottomSheetRoute({
Completer<T> completer,
this.builder
}) : super(completer: completer);
final Completer completer;
final WidgetBuilder builder; final WidgetBuilder builder;
Performance performance;
void didPush(OverlayState overlay, OverlayEntry insertionPoint) {
performance = BottomSheet.createPerformance()
..forward();
super.didPush(overlay, insertionPoint);
}
void didPop(dynamic result) { Duration get transitionDuration => _kBottomSheetDuration;
completer.complete(result); bool get barrierDismissable => true;
if (performance.isDismissed) Color get barrierColor => Colors.black54;
finished();
else
performance.reverse().then((_) { finished(); });
}
Widget _buildModalBarrier(BuildContext context) { Performance createPerformance() {
return new AnimatedModalBarrier( return BottomSheet.createPerformance();
color: new AnimatedColorValue(_kTransparent, end: _kBarrierColor, curve: Curves.ease),
performance: performance
);
} }
Widget _buildBottomSheet(BuildContext context) { Widget buildPage(BuildContext context) {
return new Focus( return new _ModalBottomSheet(route: this);
key: new GlobalObjectKey(this),
child: new _ModalBottomSheet(route: this)
);
} }
List<WidgetBuilder> get builders => <WidgetBuilder>[
_buildModalBarrier,
_buildBottomSheet,
];
String get debugLabel => '$runtimeType';
String toString() => '$runtimeType(performance: $performance)';
} }
Future showModalBottomSheet({ BuildContext context, WidgetBuilder builder }) { Future showModalBottomSheet({ BuildContext context, WidgetBuilder builder }) {
assert(context != null); assert(context != null);
assert(builder != null); assert(builder != null);
final Completer completer = new Completer(); final Completer completer = new Completer();
Navigator.of(context).pushEphemeral(new _ModalBottomSheetRoute( Navigator.of(context).push(new _ModalBottomSheetRoute(
completer: completer, completer: completer,
builder: builder builder: builder
)); ));
......
...@@ -115,13 +115,16 @@ class Dialog extends StatelessComponent { ...@@ -115,13 +115,16 @@ class Dialog extends StatelessComponent {
} }
} }
class _DialogRoute<T> extends ModalRoute<T> { class _DialogRoute<T> extends PopupRoute<T> {
_DialogRoute({ Completer<T> completer, this.child }) : super(completer: completer); _DialogRoute({
Completer<T> completer,
this.child
}) : super(completer: completer);
final Widget child; final Widget child;
bool get opaque => false;
Duration get transitionDuration => const Duration(milliseconds: 150); Duration get transitionDuration => const Duration(milliseconds: 150);
bool get barrierDismissable => true;
Color get barrierColor => Colors.black54; Color get barrierColor => Colors.black54;
Widget buildPage(BuildContext context) => child; Widget buildPage(BuildContext context) => child;
......
...@@ -14,14 +14,14 @@ import 'ink_well.dart'; ...@@ -14,14 +14,14 @@ import 'ink_well.dart';
import 'shadows.dart'; import 'shadows.dart';
import 'theme.dart'; import 'theme.dart';
const Duration _kMenuDuration = const Duration(milliseconds: 300); const Duration _kDropDownMenuDuration = const Duration(milliseconds: 300);
const double _kMenuItemHeight = 48.0; const double _kMenuItemHeight = 48.0;
const EdgeDims _kMenuHorizontalPadding = const EdgeDims.only(left: 36.0, right: 36.0); const EdgeDims _kMenuHorizontalPadding = const EdgeDims.only(left: 36.0, right: 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 _DropdownMenuPainter extends CustomPainter { class _DropDownMenuPainter extends CustomPainter {
const _DropdownMenuPainter({ const _DropDownMenuPainter({
this.color, this.color,
this.elevation, this.elevation,
this.menuTop, this.menuTop,
...@@ -47,7 +47,7 @@ class _DropdownMenuPainter extends CustomPainter { ...@@ -47,7 +47,7 @@ class _DropdownMenuPainter extends CustomPainter {
painter.paint(canvas, new Rect.fromLTRB(0.0, top, size.width, bottom)); painter.paint(canvas, new Rect.fromLTRB(0.0, top, size.width, bottom));
} }
bool shouldRepaint(_DropdownMenuPainter oldPainter) { bool shouldRepaint(_DropDownMenuPainter oldPainter) {
return oldPainter.color != color return oldPainter.color != color
|| oldPainter.elevation != elevation || oldPainter.elevation != elevation
|| oldPainter.menuTop != menuTop || oldPainter.menuTop != menuTop
...@@ -56,23 +56,23 @@ class _DropdownMenuPainter extends CustomPainter { ...@@ -56,23 +56,23 @@ class _DropdownMenuPainter extends CustomPainter {
} }
} }
class _DropdownMenu extends StatusTransitionComponent { class _DropDownMenu<T> extends StatusTransitionComponent {
_DropdownMenu({ _DropDownMenu({
Key key, Key key,
_MenuRoute route _DropDownRoute<T> route
}) : route = route, super(key: key, performance: route.performance); }) : route = route, super(key: key, performance: route.performance);
final _MenuRoute route; final _DropDownRoute<T> route;
Widget build(BuildContext context) { Widget build(BuildContext context) {
// The menu is shown in three stages (unit timing in brackets): // The menu is shown in three stages (unit timing in brackets):
// [0 - 0.25] - Fade in a rect-sized menu container with the selected item. // [0s - 0.25s] - Fade in a rect-sized menu container with the selected item.
// [0.25 - 0.5] - Grow the otherwise empty menu container from the center // [0.25s - 0.5s] - Grow the otherwise empty menu container from the center
// until it's big enough for as many items as we're going to show. // until it's big enough for as many items as we're going to show.
// [0.5 - 1.0] Fade in the remaining visible items from top to bottom. // [0.5s - 1.0s] Fade in the remaining visible items from top to bottom.
// //
// When the menu is dismissed we just fade the entire thing out // When the menu is dismissed we just fade the entire thing out
// in the first 0.25. // in the first 0.25s.
final double unit = 0.5 / (route.items.length + 1.5); final double unit = 0.5 / (route.items.length + 1.5);
final List<Widget> children = <Widget>[]; final List<Widget> children = <Widget>[];
...@@ -135,7 +135,7 @@ class _DropdownMenu extends StatusTransitionComponent { ...@@ -135,7 +135,7 @@ class _DropdownMenu extends StatusTransitionComponent {
variables: <AnimatedValue<double>>[menuTop, menuBottom], variables: <AnimatedValue<double>>[menuTop, menuBottom],
builder: (BuildContext context) { builder: (BuildContext context) {
return new CustomPaint( return new CustomPaint(
painter: new _DropdownMenuPainter( painter: new _DropDownMenuPainter(
color: Theme.of(context).canvasColor, color: Theme.of(context).canvasColor,
elevation: route.elevation, elevation: route.elevation,
menuTop: menuTop.value, menuTop: menuTop.value,
...@@ -152,38 +152,29 @@ class _DropdownMenu extends StatusTransitionComponent { ...@@ -152,38 +152,29 @@ class _DropdownMenu extends StatusTransitionComponent {
} }
} }
// TODO(abarth): This should use ModalRoute. class _DropDownRoute<T> extends PopupRoute<T> {
class _MenuRoute extends TransitionRoute { _DropDownRoute({
_MenuRoute({ Completer<T> completer,
this.completer,
this.items, this.items,
this.selectedIndex, this.selectedIndex,
this.rect, this.rect,
this.elevation: 8 this.elevation: 8
}); }) : super(completer: completer);
final Completer completer; final List<DropDownMenuItem<T>> items;
final int selectedIndex;
final Rect rect; final Rect rect;
final List<DropdownMenuItem> items;
final int elevation; final int elevation;
final int selectedIndex;
bool get opaque => false;
Duration get transitionDuration => _kMenuDuration;
Widget _buildModalBarrier(BuildContext context) => new ModalBarrier(); Duration get transitionDuration => _kDropDownMenuDuration;
Widget _buildDropDownMenu(BuildContext context) => new _DropdownMenu(route: this); bool get barrierDismissable => true;
Color get barrierColor => null;
List<WidgetBuilder> get builders => <WidgetBuilder>[ _buildModalBarrier, _buildDropDownMenu ]; Widget buildPage(BuildContext context) => new _DropDownMenu(route: this);
void didPop([dynamic result]) {
completer.complete(result);
super.didPop(result);
}
} }
class DropdownMenuItem<T> extends StatelessComponent { class DropDownMenuItem<T> extends StatelessComponent {
DropdownMenuItem({ DropDownMenuItem({
Key key, Key key,
this.value, this.value,
this.child this.child
...@@ -207,8 +198,8 @@ class DropdownMenuItem<T> extends StatelessComponent { ...@@ -207,8 +198,8 @@ class DropdownMenuItem<T> extends StatelessComponent {
} }
} }
class DropdownButton<T> extends StatelessComponent { class DropDownButton<T> extends StatelessComponent {
DropdownButton({ DropDownButton({
Key key, Key key,
this.items, this.items,
this.value, this.value,
...@@ -216,16 +207,16 @@ class DropdownButton<T> extends StatelessComponent { ...@@ -216,16 +207,16 @@ class DropdownButton<T> extends StatelessComponent {
this.elevation: 8 this.elevation: 8
}) : super(key: key); }) : super(key: key);
final List<DropdownMenuItem<T>> items; final List<DropDownMenuItem<T>> items;
final T value; final T value;
final ValueChanged<T> onChanged; final ValueChanged<T> onChanged;
final int elevation; final int elevation;
void _showDropdown(BuildContext context, int selectedIndex, GlobalKey indexedStackKey) { void _showDropDown(BuildContext context, int selectedIndex, GlobalKey indexedStackKey) {
final RenderBox renderBox = indexedStackKey.currentContext.findRenderObject(); final RenderBox renderBox = indexedStackKey.currentContext.findRenderObject();
final Rect rect = renderBox.localToGlobal(Point.origin) & renderBox.size; final Rect rect = renderBox.localToGlobal(Point.origin) & renderBox.size;
final Completer completer = new Completer(); final Completer completer = new Completer<T>();
Navigator.of(context).pushEphemeral(new _MenuRoute( Navigator.of(context).push(new _DropDownRoute<T>(
completer: completer, completer: completer,
items: items, items: items,
selectedIndex: selectedIndex, selectedIndex: selectedIndex,
...@@ -239,7 +230,7 @@ class DropdownButton<T> extends StatelessComponent { ...@@ -239,7 +230,7 @@ class DropdownButton<T> extends StatelessComponent {
} }
Widget build(BuildContext context) { Widget build(BuildContext context) {
GlobalKey indexedStackKey = new GlobalKey(debugLabel: 'DropdownButton.IndexedStack'); GlobalKey indexedStackKey = new GlobalKey(debugLabel: 'DropDownButton.IndexedStack');
int selectedIndex = 0; int selectedIndex = 0;
for (int itemIndex = 0; itemIndex < items.length; itemIndex++) { for (int itemIndex = 0; itemIndex < items.length; itemIndex++) {
if (items[itemIndex].value == value) { if (items[itemIndex].value == value) {
...@@ -247,12 +238,12 @@ class DropdownButton<T> extends StatelessComponent { ...@@ -247,12 +238,12 @@ class DropdownButton<T> extends StatelessComponent {
break; break;
} }
} }
return new GestureDetector( return new GestureDetector(
child: new Container( child: new Container(
decoration: new BoxDecoration(border: _kDropdownUnderline), decoration: new BoxDecoration(border: _kDropDownUnderline),
child: new Row(<Widget>[ child: new Row(<Widget>[
new IndexedStack(items, new IndexedStack(
items,
key: indexedStackKey, key: indexedStackKey,
index: selectedIndex, index: selectedIndex,
alignment: const FractionalOffset(0.5, 0.0) alignment: const FractionalOffset(0.5, 0.0)
...@@ -266,7 +257,7 @@ class DropdownButton<T> extends StatelessComponent { ...@@ -266,7 +257,7 @@ class DropdownButton<T> extends StatelessComponent {
) )
), ),
onTap: () { onTap: () {
_showDropdown(context, selectedIndex, indexedStackKey); _showDropDown(context, selectedIndex, indexedStackKey);
} }
); );
} }
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
import 'package:flutter/animation.dart'; import 'package:flutter/animation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'colors.dart';
class _MaterialPageTransition extends TransitionWithChild { class _MaterialPageTransition extends TransitionWithChild {
_MaterialPageTransition({ _MaterialPageTransition({
Key key, Key key,
...@@ -36,7 +38,7 @@ class _MaterialPageTransition extends TransitionWithChild { ...@@ -36,7 +38,7 @@ class _MaterialPageTransition extends TransitionWithChild {
} }
} }
class MaterialPageRoute<T> extends ModalRoute<T> { class MaterialPageRoute<T> extends PageRoute<T> {
MaterialPageRoute({ MaterialPageRoute({
this.builder, this.builder,
NamedRouteSettings settings: const NamedRouteSettings() NamedRouteSettings settings: const NamedRouteSettings()
...@@ -48,8 +50,8 @@ class MaterialPageRoute<T> extends ModalRoute<T> { ...@@ -48,8 +50,8 @@ class MaterialPageRoute<T> extends ModalRoute<T> {
final WidgetBuilder builder; final WidgetBuilder builder;
Duration get transitionDuration => const Duration(milliseconds: 150); Duration get transitionDuration => const Duration(milliseconds: 150);
bool get barrierDismissable => false;
bool get opaque => true; Color get barrierColor => Colors.black54;
Widget buildPage(BuildContext context) { Widget buildPage(BuildContext context) {
Widget result = builder(context); Widget result = builder(context);
......
...@@ -53,13 +53,13 @@ class _PopupMenuPainter extends CustomPainter { ...@@ -53,13 +53,13 @@ class _PopupMenuPainter extends CustomPainter {
} }
} }
class _PopupMenu extends StatelessComponent { class _PopupMenu<T> extends StatelessComponent {
_PopupMenu({ _PopupMenu({
Key key, Key key,
this.route this.route
}) : super(key: key); }) : super(key: key);
final _MenuRoute route; final _PopupMenuRoute<T> route;
Widget build(BuildContext context) { Widget build(BuildContext context) {
double unit = 1.0 / (route.items.length + 1.5); // 1.0 for the width and 0.5 for the last item's fade. double unit = 1.0 / (route.items.length + 1.5); // 1.0 for the width and 0.5 for the last item's fade.
...@@ -118,11 +118,16 @@ class _PopupMenu extends StatelessComponent { ...@@ -118,11 +118,16 @@ class _PopupMenu extends StatelessComponent {
} }
} }
class _MenuRoute extends ModalRoute { class _PopupMenuRoute<T> extends PopupRoute<T> {
_MenuRoute({ Completer completer, this.position, this.items, this.elevation }) : super(completer: completer); _PopupMenuRoute({
Completer<T> completer,
this.position,
this.items,
this.elevation
}) : super(completer: completer);
final ModalPosition position; final ModalPosition position;
final List<PopupMenuItem> items; final List<PopupMenuItem<T>> items;
final int elevation; final int elevation;
Performance createPerformance() { Performance createPerformance() {
...@@ -133,15 +138,16 @@ class _MenuRoute extends ModalRoute { ...@@ -133,15 +138,16 @@ class _MenuRoute extends ModalRoute {
return result; return result;
} }
bool get opaque => false;
Duration get transitionDuration => _kMenuDuration; Duration get transitionDuration => _kMenuDuration;
bool get barrierDismissable => true;
Color get barrierColor => null;
Widget buildPage(BuildContext context) => new _PopupMenu(route: this); Widget buildPage(BuildContext context) => new _PopupMenu(route: this);
} }
Future showMenu({ BuildContext context, ModalPosition position, List<PopupMenuItem> items, int elevation: 8 }) { Future showMenu({ BuildContext context, ModalPosition position, List<PopupMenuItem> items, int elevation: 8 }) {
Completer completer = new Completer(); Completer completer = new Completer();
Navigator.of(context).pushEphemeral(new _MenuRoute( Navigator.of(context).push(new _PopupMenuRoute(
completer: completer, completer: completer,
position: position, position: position,
items: items, items: items,
......
...@@ -9,7 +9,7 @@ import 'theme.dart'; ...@@ -9,7 +9,7 @@ import 'theme.dart';
const double _kMenuItemHeight = 48.0; const double _kMenuItemHeight = 48.0;
const double _kBaselineOffsetFromBottom = 20.0; const double _kBaselineOffsetFromBottom = 20.0;
class PopupMenuItem extends StatelessComponent { class PopupMenuItem<T> extends StatelessComponent {
PopupMenuItem({ PopupMenuItem({
Key key, Key key,
this.value, this.value,
...@@ -17,7 +17,7 @@ class PopupMenuItem extends StatelessComponent { ...@@ -17,7 +17,7 @@ class PopupMenuItem extends StatelessComponent {
}) : super(key: key); }) : super(key: key);
final Widget child; final Widget child;
final dynamic value; final T value;
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new Container( return new Container(
......
...@@ -12,7 +12,7 @@ import 'transitions.dart'; ...@@ -12,7 +12,7 @@ import 'transitions.dart';
// Heroes are the parts of an application's screen-to-screen transitions where a // Heroes are the parts of an application's screen-to-screen transitions where a
// component from one screen shifts to a position on the other. For example, // component from one screen shifts to a position on the other. For example,
// album art from a list of albums growing to become the centerpiece of the // album art from a list of albums growing to become the centerpiece of the
// album's details view. In this context, a screen is a navigator Route. // album's details view. In this context, a screen is a navigator ModalRoute.
// To get this effect, all you have to do is wrap each hero on each route with a // To get this effect, all you have to do is wrap each hero on each route with a
// Hero widget, and give each hero a tag. Tag must either be unique within the // Hero widget, and give each hero a tag. Tag must either be unique within the
...@@ -50,7 +50,7 @@ import 'transitions.dart'; ...@@ -50,7 +50,7 @@ import 'transitions.dart';
// TODO(ianh): If the widgets use Inherited properties, they are taken from the // TODO(ianh): If the widgets use Inherited properties, they are taken from the
// Navigator's position in the widget hierarchy, not the source or target. We // Navigator's position in the widget hierarchy, not the source or target. We
// should interpolate the inherited properties from their value at the source to // should interpolate the inherited properties from their value at the source to
// their value at the target. See: https://github.com/flutter/engine/issues/1698 // their value at the target. See: https://github.com/flutter/flutter/issues/213
final Object centerOfAttentionHeroTag = new Object(); final Object centerOfAttentionHeroTag = new Object();
......
...@@ -9,12 +9,10 @@ import 'framework.dart'; ...@@ -9,12 +9,10 @@ import 'framework.dart';
import 'navigator.dart'; import 'navigator.dart';
import 'transitions.dart'; import 'transitions.dart';
const Color kTransparent = const Color(0x00000000);
class ModalBarrier extends StatelessComponent { class ModalBarrier extends StatelessComponent {
ModalBarrier({ ModalBarrier({
Key key, Key key,
this.color: kTransparent, this.color,
this.dismissable: true this.dismissable: true
}) : super(key: key); }) : super(key: key);
...@@ -27,9 +25,10 @@ class ModalBarrier extends StatelessComponent { ...@@ -27,9 +25,10 @@ class ModalBarrier extends StatelessComponent {
if (dismissable) if (dismissable)
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
behavior: HitTestBehavior.opaque,
child: new ConstrainedBox( child: new ConstrainedBox(
constraints: const BoxConstraints.expand(), constraints: const BoxConstraints.expand(),
child: new DecoratedBox( child: color == null ? null : new DecoratedBox(
decoration: new BoxDecoration( decoration: new BoxDecoration(
backgroundColor: color backgroundColor: color
) )
......
...@@ -61,7 +61,7 @@ class Navigator extends StatefulComponent { ...@@ -61,7 +61,7 @@ class Navigator extends StatefulComponent {
class NavigatorState extends State<Navigator> { class NavigatorState extends State<Navigator> {
final GlobalKey<OverlayState> _overlayKey = new GlobalKey<OverlayState>(); final GlobalKey<OverlayState> _overlayKey = new GlobalKey<OverlayState>();
final List<Route> _ephemeral = new List<Route>(); // TODO(ianh): Rename _modal to _history or some such
final List<Route> _modal = new List<Route>(); final List<Route> _modal = new List<Route>();
void initState() { void initState() {
...@@ -88,10 +88,6 @@ class NavigatorState extends State<Navigator> { ...@@ -88,10 +88,6 @@ class NavigatorState extends State<Navigator> {
OverlayState get overlay => _overlayKey.currentState; OverlayState get overlay => _overlayKey.currentState;
OverlayEntry get _currentOverlay { OverlayEntry get _currentOverlay {
for (Route route in _ephemeral.reversed) {
if (route.overlayEntries.isNotEmpty)
return route.overlayEntries.last;
}
for (Route route in _modal.reversed) { for (Route route in _modal.reversed) {
if (route.overlayEntries.isNotEmpty) if (route.overlayEntries.isNotEmpty)
return route.overlayEntries.last; return route.overlayEntries.last;
...@@ -110,7 +106,6 @@ class NavigatorState extends State<Navigator> { ...@@ -110,7 +106,6 @@ class NavigatorState extends State<Navigator> {
void push(Route route, { Set<Key> mostValuableKeys }) { void push(Route route, { Set<Key> mostValuableKeys }) {
setState(() { setState(() {
_popAllEphemeralRoutes();
int index = _modal.length-1; int index = _modal.length-1;
while (index >= 0 && _modal[index].willPushNext(route)) while (index >= 0 && _modal[index].willPushNext(route))
index -= 1; index -= 1;
...@@ -120,19 +115,6 @@ class NavigatorState extends State<Navigator> { ...@@ -120,19 +115,6 @@ class NavigatorState extends State<Navigator> {
}); });
} }
void pushEphemeral(Route route) {
route.didPush(overlay, _currentOverlay);
_ephemeral.add(route);
}
void _popAllEphemeralRoutes() {
List<Route> localEphemeral = new List<Route>.from(_ephemeral);
_ephemeral.clear();
for (Route route in localEphemeral)
route.didPop(null);
assert(_ephemeral.isEmpty);
}
/// Pops the given route, if it's the current route. If it's not the current /// Pops the given route, if it's the current route. If it's not the current
/// route, removes it from the list of active routes without notifying any /// route, removes it from the list of active routes without notifying any
/// observers or adjacent routes. /// observers or adjacent routes.
...@@ -167,9 +149,6 @@ class NavigatorState extends State<Navigator> { ...@@ -167,9 +149,6 @@ class NavigatorState extends State<Navigator> {
// We use setState to guarantee that we'll rebuild, since the routes can't // We use setState to guarantee that we'll rebuild, since the routes can't
// do that for themselves, even if they have changed their own state (e.g. // do that for themselves, even if they have changed their own state (e.g.
// ModalScope.isCurrent). // ModalScope.isCurrent).
if (_ephemeral.isNotEmpty) {
_ephemeral.removeLast().didPop(result);
} else {
assert(_modal.length > 1); assert(_modal.length > 1);
Route route = _modal.removeLast(); Route route = _modal.removeLast();
route.didPop(result); route.didPop(result);
...@@ -177,11 +156,11 @@ class NavigatorState extends State<Navigator> { ...@@ -177,11 +156,11 @@ class NavigatorState extends State<Navigator> {
while (index >= 0 && _modal[index].didPopNext(route)) while (index >= 0 && _modal[index].didPopNext(route))
index -= 1; index -= 1;
config.observer?.didPopModal(route, index >= 0 ? _modal[index] : null); config.observer?.didPopModal(route, index >= 0 ? _modal[index] : null);
}
}); });
} }
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(_modal.isNotEmpty);
return new Overlay( return new Overlay(
key: _overlayKey, key: _overlayKey,
initialEntries: _modal.first.overlayEntries initialEntries: _modal.first.overlayEntries
......
...@@ -15,6 +15,8 @@ import 'overlay.dart'; ...@@ -15,6 +15,8 @@ import 'overlay.dart';
import 'page_storage.dart'; import 'page_storage.dart';
import 'status_transitions.dart'; import 'status_transitions.dart';
const _kTransparent = const Color(0x00000000);
class StateRoute extends Route { class StateRoute extends Route {
StateRoute({ this.onPop }); StateRoute({ this.onPop });
...@@ -72,7 +74,13 @@ abstract class TransitionRoute<T> extends OverlayRoute<T> { ...@@ -72,7 +74,13 @@ abstract class TransitionRoute<T> extends OverlayRoute<T> {
}) : _popCompleter = popCompleter, }) : _popCompleter = popCompleter,
_transitionCompleter = transitionCompleter; _transitionCompleter = transitionCompleter;
/// This future completes once the animation has been dismissed. For
/// ModalRoutes, this will be after the completer that's passed in, since that
/// one completes before the animation even starts, as soon as the route is
/// popped.
Future<T> get popped => _popCompleter?.future;
final Completer<T> _popCompleter; final Completer<T> _popCompleter;
Future<T> get completed => _transitionCompleter?.future;
final Completer<T> _transitionCompleter; final Completer<T> _transitionCompleter;
Duration get transitionDuration; Duration get transitionDuration;
...@@ -101,7 +109,9 @@ abstract class TransitionRoute<T> extends OverlayRoute<T> { ...@@ -101,7 +109,9 @@ abstract class TransitionRoute<T> extends OverlayRoute<T> {
overlayEntries.first.opaque = false; overlayEntries.first.opaque = false;
break; break;
case PerformanceStatus.dismissed: case PerformanceStatus.dismissed:
super.finished(); // clear the overlays assert(!overlayEntries.first.opaque);
finished(); // clear the overlays
assert(overlayEntries.isEmpty);
break; break;
} }
} }
...@@ -239,9 +249,14 @@ abstract class ModalRoute<T> extends TransitionRoute<T> { ...@@ -239,9 +249,14 @@ abstract class ModalRoute<T> extends TransitionRoute<T> {
return child; return child;
} }
// The API for subclasses to override - used by this class // The API for subclasses to override - used by this class
Color get barrierColor => kTransparent; /// Whether you can dismiss this route by tapping the modal barrier.
bool get barrierDismissable;
/// The color to use for the modal barrier. If this is null, the barrier will
/// be transparent.
Color get barrierColor;
// The API for _ModalScope and HeroController // The API for _ModalScope and HeroController
...@@ -300,11 +315,16 @@ abstract class ModalRoute<T> extends TransitionRoute<T> { ...@@ -300,11 +315,16 @@ abstract class ModalRoute<T> extends TransitionRoute<T> {
final PageStorageBucket _storageBucket = new PageStorageBucket(); final PageStorageBucket _storageBucket = new PageStorageBucket();
Widget _buildModalBarrier(BuildContext context) { Widget _buildModalBarrier(BuildContext context) {
if (barrierColor != null) {
assert(barrierColor != _kTransparent);
return new AnimatedModalBarrier( return new AnimatedModalBarrier(
color: new AnimatedColorValue(kTransparent, end: barrierColor, curve: Curves.ease), color: new AnimatedColorValue(_kTransparent, end: barrierColor, curve: Curves.ease),
performance: performance, performance: performance,
dismissable: false dismissable: barrierDismissable
); );
} else {
return new ModalBarrier(dismissable: barrierDismissable);
}
} }
Widget _buildModalScope(BuildContext context) { Widget _buildModalScope(BuildContext context) {
...@@ -315,6 +335,7 @@ abstract class ModalRoute<T> extends TransitionRoute<T> { ...@@ -315,6 +335,7 @@ abstract class ModalRoute<T> extends TransitionRoute<T> {
performance: performance, performance: performance,
current: isCurrent, current: isCurrent,
route: this route: this
// calls buildTransition() and buildPage(), defined above
); );
} }
...@@ -324,3 +345,18 @@ abstract class ModalRoute<T> extends TransitionRoute<T> { ...@@ -324,3 +345,18 @@ abstract class ModalRoute<T> extends TransitionRoute<T> {
]; ];
} }
/// A modal route that overlays a widget over the current route.
abstract class PopupRoute<T> extends ModalRoute<T> {
PopupRoute({ Completer<T> completer }) : super(completer: completer);
bool get opaque => false;
}
/// A modal route that replaces the entire screen.
abstract class PageRoute<T> extends ModalRoute<T> {
PageRoute({
Completer<T> completer,
NamedRouteSettings settings: const NamedRouteSettings()
}) : super(completer: completer, settings: settings);
bool get opaque => true;
}
\ No newline at end of file
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