Commit f538d3b7 authored by Adam Barth's avatar Adam Barth

Add liftToOverlay to Mimicable

This patch integrates Mimicable with Overlay such that you can tell a Mimicable
to lift up into the overlay and animate towards another widget identified by a
global key.
parent 853c42fc
// 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/material.dart';
const double _kHeight = 150.0;
const Duration _kEffectDuration = const Duration(seconds: 1);
class MimicDemo extends StatefulComponent {
_MimicDemoState createState() => new _MimicDemoState();
}
class _MimicDemoState extends State<MimicDemo> {
GlobalKey<MimicableState> _orange = new GlobalKey<MimicableState>();
GlobalKey _targetContainer = new GlobalKey();
bool _slotForOrangeOnTop = false;
bool _orangeOnTop = false;
void _handleTap() {
if (_slotForOrangeOnTop)
return;
setState(() {
_slotForOrangeOnTop = true;
});
MimicOverlayEntry entry = _orange.currentState.liftToOverlay();
entry.animateTo(targetKey: _targetContainer, duration: _kEffectDuration, curve: Curves.ease).then((_) {
setState(() {
_orangeOnTop = true;
});
entry.dispose();
});
}
void _reset() {
setState(() {
_slotForOrangeOnTop = false;
_orangeOnTop = false;
});
}
Widget _buildOrange() {
return new Mimicable(
key: _orange,
child: new Container(
height: _kHeight,
decoration: new BoxDecoration(
backgroundColor: Colors.deepOrange[500]
)
)
);
}
Widget build(BuildContext context) {
List<Widget> children = <Widget>[
new Container(
height: _kHeight,
decoration: new BoxDecoration(
backgroundColor: Colors.amber[500]
)
),
new AnimatedContainer(
key: _targetContainer,
height: _slotForOrangeOnTop ? _kHeight : 0.0,
duration: _kEffectDuration,
curve: Curves.ease,
child: _orangeOnTop ? _buildOrange() : null
),
new Container(
height: _kHeight,
decoration: new BoxDecoration(
backgroundColor: Colors.green[500]
)
),
new Container(
height: _kHeight,
decoration: new BoxDecoration(
backgroundColor: Colors.blue[500]
)
),
];
if (!_orangeOnTop)
children.add(_buildOrange());
return new GestureDetector(
onTap: _handleTap,
onLongPress: _reset,
child: new Block(children)
);
}
}
void main() {
runApp(new MaterialApp(
title: 'Mimic Demo',
routes: {
'/': (_) => new MimicDemo()
}
));
}
......@@ -136,6 +136,8 @@ class _AnimatedContainerState extends State<AnimatedContainer> {
}
bool _configVariable(AnimatedValue variable, dynamic targetValue) {
if (targetValue == variable.end)
return false;
dynamic currentValue = variable.value;
variable.end = targetValue;
variable.begin = currentValue;
......
......@@ -2,10 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:ui' as ui;
import 'package:flutter/animation.dart';
import 'package:flutter/rendering.dart';
import 'basic.dart';
import 'framework.dart';
import 'overlay.dart';
class MimicableKey {
MimicableKey._(this._state);
......@@ -19,6 +24,84 @@ class MimicableKey {
}
}
class MimicOverlayEntry {
MimicOverlayEntry._(this._key) {
_overlayEntry = new OverlayEntry(builder: _build);
_initialGlobalBounds = _key.globalBounds;
}
Rect _initialGlobalBounds;
MimicableKey _key;
OverlayEntry _overlayEntry;
// Animation state
GlobalKey _targetKey;
Curve _curve;
Performance _performance;
Future animateTo({
GlobalKey targetKey,
Duration duration,
Curve curve: Curves.linear
}) {
assert(_key != null);
assert(_overlayEntry != null);
assert(targetKey != null);
assert(duration != null);
assert(curve != null);
_targetKey = targetKey;
_curve = curve;
// TODO(abarth): Support changing the animation target when in flight.
assert(_performance == null);
_performance = new Performance(duration: duration)
..addListener(_overlayEntry.markNeedsBuild);
return _performance.play();
}
void dispose() {
_targetKey = null;
_curve = null;
_performance?.stop();
_performance = null;
_key.stopMimic();
_key = null;
_overlayEntry.remove();
_overlayEntry = null;
}
Widget _build(BuildContext context) {
assert(_key != null);
assert(_overlayEntry != null);
Rect globalBounds = _initialGlobalBounds;
Point globalPosition = globalBounds.topLeft;
if (_targetKey != null) {
assert(_performance != null);
assert(_curve != null);
RenderBox box = _targetKey.currentContext?.findRenderObject();
if (box != null) {
// TODO(abarth): Handle the case where the transform here isn't just a translation.
Point localPosition = box.localToGlobal(Point.origin);
double t = _curve.transform(_performance.progress);
// TODO(abarth): Add Point.lerp.
globalPosition = new Point(ui.lerpDouble(globalPosition.x, localPosition.x, t),
ui.lerpDouble(globalPosition.y, localPosition.y, t));
}
}
RenderBox stack = context.ancestorRenderObjectOfType(RenderStack);
// TODO(abarth): Handle the case where the transform here isn't just a translation.
Point localPosition = stack == null ? globalPosition: stack.globalToLocal(globalPosition);
return new Positioned(
left: localPosition.x,
top: localPosition.y,
width: globalBounds.width,
height: globalBounds.height,
child: new Mimic(original: _key)
);
}
}
class Mimic extends StatelessComponent {
Mimic({ Key key, this.original }) : super(key: key);
......@@ -52,6 +135,14 @@ class MimicableState extends State<Mimicable> {
return new MimicableKey._(this);
}
MimicOverlayEntry liftToOverlay() {
OverlayState overlay = Overlay.of(context);
assert(overlay != null); // You need an overlay to lift into.
MimicOverlayEntry entry = new MimicOverlayEntry._(startMimic());
overlay.insert(entry._overlayEntry);
return entry;
}
void _stopMimic() {
assert(_beingMimicked);
if (!mounted) {
......
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