Commit c253ca62 authored by Collin Jackson's avatar Collin Jackson

Support for hero transitions underneath a PageRoute

parent fa31a4ad
// Copyright (c) 2015, the Flutter project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:ui' as ui;
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
void main() {
timeDilation = 8.0;
runApp(
new MaterialApp(
title: "Hero Under",
routes: {
'/': (RouteArguments args) => new HeroDemo()
}
)
);
}
const String kImageSrc = 'http://uploads0.wikiart.org/images/m-c-escher/crab-canon.jpg!Blog.jpg';
const String kText = """
Low-crab diets are dietary programs that restrict crustacean consumption, often for the treatment of obesity or diabetes. Foods high in easily digestible crustaceans (e.g., crab, lobster, shrimp) are limited or replaced with foods made from other animals (e.g., poultry, beef, pork) and other crustaceans that are hard to digest (e.g., barnacles), although krill are often allowed. The amount of crab allowed varies with different low-crab diets.
""";
class HeroImage extends StatelessComponent {
HeroImage({ this.size });
final Size size;
Widget build(BuildContext context) {
return new Hero(
child: new Container(
width: size.width,
height: size.height,
decoration: new BoxDecoration(
backgroundImage: new BackgroundImage(
fit: ImageFit.cover,
image: imageCache.load(kImageSrc)
)
)
),
tag: HeroImage
);
}
}
class HeroDemo extends StatelessComponent {
Widget build(BuildContext context) {
return new Scaffold(
toolBar: new ToolBar(
left: new IconButton(icon: "navigation/menu"),
center: new Text("Diets")
),
body: new Center(
child: new GestureDetector(
onTap: () => Navigator.push(context, new CrabRoute()),
child: new Card(
child: new Row(<Widget>[
new HeroImage(
size: const Size(100.0, 100.0)
),
new Flexible(
child: new Container(
padding: const EdgeDims.all(10.0),
child: new Text(
"Low Crab Diet",
style: Theme.of(context).text.title
)
)
)
])
)
)
)
);
}
}
class CrabRoute extends MaterialPageRoute {
CrabRoute() : super(builder: (BuildContext context) => new CrabPage());
void insertHeroOverlayEntry(OverlayEntry entry, Object tag, OverlayState overlay) {
overlay.insert(entry, above: overlayEntries.first);
}
}
class CrabPage extends StatelessComponent {
Widget build(BuildContext context) {
TextStyle titleStyle = Typography.white.display2.copyWith(color: Colors.white);
return new Material(
color: const Color(0x00000000),
child: new Block(
<Widget>[
new Stack(<Widget>[
new HeroImage(
size: new Size(ui.window.size.width, ui.window.size.width)
),
new ToolBar(
padding: new EdgeDims.only(top: ui.window.padding.top),
backgroundColor: const Color(0x00000000),
left: new IconButton(
icon: "navigation/arrow_back",
onPressed: () => Navigator.pop(context)
),
right: <Widget>[
new IconButton(icon: "navigation/more_vert")
]
),
new Positioned(
bottom: 10.0,
left: 10.0,
child: new Text("Low Crab Diet", style: titleStyle)
)
]),
new Material(
child: new Container(
padding: const EdgeDims.all(10.0),
child: new Column(<Widget>[
new Text(kText, style: Theme.of(context).text.body1),
new Container(height: 800.0),
], alignItems: FlexAlignItems.start)
)
)
]
)
);
}
}
......@@ -11,7 +11,6 @@ import 'framework.dart';
import 'navigator.dart';
import 'overlay.dart';
import 'pages.dart';
import 'routes.dart';
import 'transitions.dart';
// Heroes are the parts of an application's screen-to-screen transitions where a
......@@ -388,15 +387,13 @@ class HeroParty {
_currentPerformance = null;
}
Iterable<Widget> getWidgets(BuildContext context, PerformanceView performance) sync* {
void setPerformance(PerformanceView performance) {
assert(performance != null || _heroes.length == 0);
if (performance != _currentPerformance) {
_clearCurrentPerformance();
_currentPerformance = performance;
_currentPerformance?.addStatusListener(_handleUpdate);
}
for (_HeroQuestState hero in _heroes)
yield hero.build(context, performance);
}
void _handleUpdate(PerformanceStatus status) {
......@@ -425,8 +422,8 @@ class HeroController extends NavigatorObserver {
HeroParty _party;
PerformanceView _performance;
ModalRoute _from;
ModalRoute _to;
PageRoute _from;
PageRoute _to;
final List<OverlayEntry> _overlayEntries = new List<OverlayEntry>();
......@@ -484,12 +481,13 @@ class HeroController extends NavigatorObserver {
_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);
}
void _addHeroToOverlay(Widget hero, Object tag, OverlayState overlay) {
OverlayEntry entry = new OverlayEntry(builder: (_) => hero);
if (_performance.direction == AnimationDirection.forward)
_to.insertHeroOverlayEntry(entry, tag, overlay);
else
_from.insertHeroOverlayEntry(entry, tag, overlay);
_overlayEntries.add(entry);
}
Set<Key> _getMostValuableKeys() {
......@@ -505,7 +503,6 @@ class HeroController extends NavigatorObserver {
void _updateQuest(Duration timeStamp) {
Set<Key> mostValuableKeys = _getMostValuableKeys();
Map<Object, HeroHandle> heroesFrom = _party.isEmpty ?
Hero.of(_from.subtreeContext, mostValuableKeys) : _party.getHeroesToAnimate();
......@@ -521,7 +518,10 @@ class HeroController extends NavigatorObserver {
_party.animate(heroesFrom, heroesTo, _getAnimationArea(navigator.context), curve);
_removeHeroesFromOverlay();
Iterable<Widget> heroes = _party.getWidgets(navigator.context, performance);
_addHeroesToOverlay(heroes, navigator.overlay);
_party.setPerformance(performance);
for (_HeroQuestState hero in _party._heroes) {
Widget widget = hero.build(navigator.context, performance);
_addHeroToOverlay(widget, hero.tag, navigator.overlay);
}
}
}
......@@ -4,7 +4,6 @@
import 'dart:async';
import 'heroes.dart';
import 'navigator.dart';
import 'overlay.dart';
import 'routes.dart';
......@@ -19,4 +18,9 @@ abstract class PageRoute<T> extends ModalRoute<T> {
bool get barrierDismissable => false;
bool canTransitionTo(TransitionRoute nextRoute) => nextRoute is PageRoute;
bool canTransitionFrom(TransitionRoute nextRoute) => nextRoute is PageRoute;
// Subclasses can override this method to customize way heroes are inserted
void insertHeroOverlayEntry(OverlayEntry entry, Object tag, OverlayState overlay) {
overlay.insert(entry);
}
}
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