Add Mimic to fn3
diff --git a/packages/flutter/lib/src/fn3/framework.dart b/packages/flutter/lib/src/fn3/framework.dart
index 99d9d1d..7c6df96 100644
--- a/packages/flutter/lib/src/fn3/framework.dart
+++ b/packages/flutter/lib/src/fn3/framework.dart
@@ -298,6 +298,8 @@
/// The context in which this object will be built
BuildContext get context => _element;
+ bool get mounted => _element != null;
+
/// Called when this object is inserted into the tree. Override this function
/// to perform initialization that depends on the location at which this
/// object was inserted into the tree or on the widget configuration object.
diff --git a/packages/flutter/lib/src/fn3/mimic.dart b/packages/flutter/lib/src/fn3/mimic.dart
new file mode 100644
index 0000000..b331366
--- /dev/null
+++ b/packages/flutter/lib/src/fn3/mimic.dart
@@ -0,0 +1,87 @@
+// 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:sky/rendering.dart';
+import 'package:sky/src/fn3/basic.dart';
+import 'package:sky/src/fn3/framework.dart';
+
+class MimicableKey {
+ MimicableKey._(this._state);
+
+ final MimicableState _state;
+
+ Rect get globalBounds => _state._globalBounds;
+
+ void stopMimic() {
+ _state._stopMimic();
+ }
+}
+
+class Mimic extends StatelessComponent {
+ Mimic({ Key key, this.original }) : super(key: key);
+
+ final MimicableKey original;
+
+ Widget build(BuildContext context) {
+ if (original != null && original._state._beingMimicked)
+ return original._state.config.child;
+ return new Container();
+ }
+}
+
+class Mimicable extends StatefulComponent {
+ Mimicable({ Key key, this.child }) : super(key: key);
+
+ final Widget child;
+
+ MimicableState createState() => new MimicableState();
+}
+
+class MimicableState extends State<Mimicable> {
+ Size _size;
+ bool _beingMimicked = false;
+
+ MimicableKey startMimic() {
+ assert(!_beingMimicked);
+ assert(_size != null);
+ setState(() {
+ _beingMimicked = true;
+ });
+ return new MimicableKey._(this);
+ }
+
+ void _stopMimic() {
+ assert(!_beingMimicked);
+ if (!mounted) {
+ _beingMimicked = false;
+ return;
+ }
+ setState(() {
+ _beingMimicked = false;
+ });
+ }
+
+ Rect get _globalBounds {
+ RenderBox box = context.findRenderObject();
+ return box.localToGlobal(Point.origin) & box.size;
+ }
+
+ void _handleSizeChanged(Size size) {
+ setState(() {
+ _size = size;
+ });
+ }
+
+ Widget build(BuildContext context) {
+ if (_beingMimicked) {
+ return new ConstrainedBox(
+ constraints: new BoxConstraints.tight(_size)
+ );
+ }
+ return new SizeObserver(
+ callback: _handleSizeChanged,
+ child: config.child
+ );
+ }
+}