From bc8ab63efd47b493dd48e449de6fb5722d41c7cb Mon Sep 17 00:00:00 2001 From: Collin Jackson Date: Wed, 17 Jun 2015 12:21:08 -0700 Subject: [PATCH] Refactor Navigator to put state in separate class, initial back button plumbing R=abarth@chromium.org, abarth Review URL: https://codereview.chromium.org/1195493002. --- examples/stocks2/lib/stock_app.dart | 29 +++++----- examples/widgets/navigation.dart | 4 +- sdk/lib/app/view.dart | 6 +++ sdk/lib/widgets/navigator.dart | 84 ++++++++++++++++++----------- sdk/lib/widgets/widget.dart | 7 +++ 5 files changed, 87 insertions(+), 43 deletions(-) diff --git a/examples/stocks2/lib/stock_app.dart b/examples/stocks2/lib/stock_app.dart index 1c450b02682..1586b173553 100644 --- a/examples/stocks2/lib/stock_app.dart +++ b/examples/stocks2/lib/stock_app.dart @@ -14,19 +14,24 @@ class StocksApp extends App { StocksApp({ RenderView renderViewOverride }) : super(renderViewOverride: renderViewOverride); + NavigationState _navState = new NavigationState([ + new Route(name: '/', builder: (navigator) => new StockHome(navigator)), + new Route(name: '/settings', builder: (navigator) => new StockSettings(navigator)), + ]); + + void onBack() { + if (_navState.hasPrevious()) { + setState(() { + _navState.pop(); + }); + return; + } + print ("Should exit app here"); + // TODO(jackson): Need a way to invoke default back behavior here + } + Widget build() { - return new Navigator( - routes: [ - new Route( - name: '/', - builder: (navigator) => new StockHome(navigator) - ), - new Route( - name: '/settings', - builder: (navigator) => new StockSettings(navigator) - ), - ] - ); + return new Navigator(_navState); } } diff --git a/examples/widgets/navigation.dart b/examples/widgets/navigation.dart index ca81a4fbd79..82e81f85020 100644 --- a/examples/widgets/navigation.dart +++ b/examples/widgets/navigation.dart @@ -65,8 +65,10 @@ List routes = [ ]; class NavigationExampleApp extends App { + NavigationState _navState = new NavigationState(routes); + Widget build() { - return new Flex([new Navigator(routes: routes)]); + return new Flex([new Navigator(_navState)]); } } diff --git a/sdk/lib/app/view.dart b/sdk/lib/app/view.dart index 4702ae4d03c..b6022d15661 100644 --- a/sdk/lib/app/view.dart +++ b/sdk/lib/app/view.dart @@ -50,6 +50,8 @@ class AppView { Function onFrame; + List eventListeners = new List(); + RenderBox get root => _renderView.child; void set root(RenderBox value) { _renderView.child = value; @@ -68,6 +70,10 @@ class AppView { HitTestResult result = new HitTestResult(); _renderView.hitTest(result, position: new Point(event.x, event.y)); dispatchEvent(event, result); + } else { + for (sky.EventListener listener in eventListeners) { + listener(event); + } } } diff --git a/sdk/lib/widgets/navigator.dart b/sdk/lib/widgets/navigator.dart index ea31c8bafed..74bf38077d9 100644 --- a/sdk/lib/widgets/navigator.dart +++ b/sdk/lib/widgets/navigator.dart @@ -18,28 +18,23 @@ class Route extends RouteBase { Widget build(Navigator navigator) => builder(navigator); } -class Navigator extends Component { - Navigator({ Object key, RouteBase defaultRoute, List routes }) - : super(key: key, stateful: true) { - if (routes != null) { - if (defaultRoute == null) - defaultRoute = routes[0]; - for (Route route in routes) { - if (route.name != null) - namedRoutes[route.name] = route; - } +class NavigationState { + + NavigationState(List routes) { + for (Route route in routes) { + if (route.name != null) + namedRoutes[route.name] = route; } - assert(defaultRoute != null); - _history.add(defaultRoute); + history.add(routes[0]); } - List _history = new List(); - int _historyIndex = 0; + List history = new List(); + int historyIndex = 0; Map namedRoutes = new Map(); - void syncFields(Navigator source) { - namedRoutes = source.namedRoutes; - } + RouteBase get currentRoute => history[historyIndex]; + bool hasPrevious() => historyIndex > 0; + bool hasNext() => history.length > historyIndex + 1; void pushNamed(String name) { Route route = namedRoutes[name]; @@ -48,38 +43,67 @@ class Navigator extends Component { } void push(RouteBase route) { + // Discard future history + history.removeRange(historyIndex + 1, history.length); + historyIndex = history.length; + history.add(route); + } + + void pop() { + if (historyIndex > 0) { + history.removeLast(); + historyIndex--; + } + } + + void back() { + if (historyIndex > 0) + historyIndex--; + } + + void forward() { + historyIndex++; + assert(historyIndex < history.length); + } +} + +class Navigator extends Component { + + Navigator(this.state, { String key }) : super(key: key); + + NavigationState state; + + void pushNamed(String name) { setState(() { - // Discard future history - _history.removeRange(_historyIndex + 1, _history.length); - _historyIndex = _history.length; - _history.add(route); + state.pushNamed(name); + }); + } + + void push(RouteBase route) { + setState(() { + state.push(route); }); } void pop() { setState(() { - if (_historyIndex > 0) { - _history.removeLast(); - _historyIndex--; - } + state.pop(); }); } void back() { setState(() { - if (_historyIndex > 0) - _historyIndex--; + state.back(); }); } void forward() { setState(() { - _historyIndex++; - assert(_historyIndex < _history.length); + state.forward(); }); } Widget build() { - return _history[_historyIndex].build(this); + return state.currentRoute.build(this); } } diff --git a/sdk/lib/widgets/widget.dart b/sdk/lib/widgets/widget.dart index 7d450c98b11..fcf6ebbceec 100644 --- a/sdk/lib/widgets/widget.dart +++ b/sdk/lib/widgets/widget.dart @@ -799,10 +799,17 @@ abstract class App extends AbstractWidgetRoot { if (root.parent == null) { // we haven't attached it yet WidgetAppView._appView.root = root; + WidgetAppView._appView.eventListeners.add((event) { + if (event.type == "back") + onBack(); + }); } assert(root.parent is RenderView); } + // Override this to handle back button behavior in your app + void onBack() { } + } typedef Widget Builder();