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
+    );
+  }
+}