blob: fb44db96032e0c8bf7db4bcb9fe9f84497266e73 [file] [log] [blame]
// 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 'package:flutter/rendering.dart';
import 'basic.dart';
import 'framework.dart';
import 'heroes.dart';
import 'navigator.dart';
import 'overlay.dart';
import 'page.dart';
class HeroPageRoute extends PageRoute {
HeroPageRoute({
WidgetBuilder builder,
NamedRouteSettings settings: const NamedRouteSettings(),
this.heroController
}) : super(builder: builder, settings: settings);
final HeroController heroController;
NavigatorState _navigator;
void didPush(OverlayState overlay, OverlayEntry insertionPoint) {
super.didPush(overlay, insertionPoint);
// TODO(abarth): Pass the NavigatorState explicitly.
if (overlay != null) {
_navigator = Navigator.of(overlay.context);
heroController?.didPush(_navigator, this);
}
}
void didPop(dynamic result) {
super.didPop(result);
if (_navigator != null) {
heroController?.didPop(_navigator, this);
_navigator = null;
}
}
}
class HeroController {
HeroController() {
_party = new HeroParty(onQuestFinished: _handleQuestFinished);
}
HeroParty _party;
PerformanceView _performance;
HeroPageRoute _from;
HeroPageRoute _to;
final List<OverlayEntry> _overlayEntries = new List<OverlayEntry>();
void didPush(NavigatorState navigator, HeroPageRoute route) {
assert(route != null);
assert(route.performance != null);
Route from = navigator.currentRoute;
if (from is HeroPageRoute)
_from = from;
_to = route;
_performance = route.performance;
_checkForHeroQuest();
}
void didPop(NavigatorState navigator, HeroPageRoute route) {
assert(route != null);
assert(route.performance != null);
Route to = navigator.currentRoute;
if (to is HeroPageRoute) {
_to = to;
_from = route;
_performance = route.performance;
_checkForHeroQuest();
}
}
void _checkForHeroQuest() {
if (_from != null && _to != null && _from != _to) {
_to.offstage = _to.performance.status != PerformanceStatus.completed;
scheduler.requestPostFrameCallback(_updateQuest);
}
}
void _handleQuestFinished() {
_removeHeroesFromOverlay();
_from = null;
_to = null;
_performance = null;
}
Rect _getAnimationArea(BuildContext context) {
RenderBox box = context.findRenderObject();
Point topLeft = box.localToGlobal(Point.origin);
Point bottomRight = box.localToGlobal(box.size.bottomRight(Point.origin));
return new Rect.fromLTRB(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);
}
void _removeHeroesFromOverlay() {
for (OverlayEntry entry in _overlayEntries)
entry.remove();
_overlayEntries.clear();
}
void _addHeroesToOverlay(Iterable<Widget> heroes, OverlayState overlay) {
for (Widget hero in heroes) {
OverlayEntry entry = new OverlayEntry(builder: (_) => hero);
overlay.insert(entry);
_overlayEntries.add(entry);
}
}
Set<Key> _getMostValuableKeys() {
Set<Key> result = new Set<Key>();
if (_from.settings.mostValuableKeys != null)
result.addAll(_from.settings.mostValuableKeys);
if (_to.settings.mostValuableKeys != null)
result.addAll(_to.settings.mostValuableKeys);
return result;
}
void _updateQuest(Duration timeStamp) {
Set<Key> mostValuableKeys = _getMostValuableKeys();
Map<Object, HeroHandle> heroesFrom = _party.isEmpty ?
Hero.of(_from.pageKey.currentContext, mostValuableKeys) : _party.getHeroesToAnimate();
BuildContext context = _to.pageKey.currentContext;
Map<Object, HeroHandle> heroesTo = Hero.of(context, mostValuableKeys);
_to.offstage = false;
PerformanceView performance = _performance;
Curve curve = Curves.ease;
if (performance.status == PerformanceStatus.reverse) {
performance = new ReversePerformance(performance);
curve = new Interval(performance.progress, 1.0, curve: curve);
}
NavigatorState navigator = Navigator.of(context);
_party.animate(heroesFrom, heroesTo, _getAnimationArea(navigator.context), curve);
_removeHeroesFromOverlay();
Iterable<Widget> heroes = _party.getWidgets(navigator.context, performance);
_addHeroesToOverlay(heroes, navigator.overlay);
}
}