Commit 9bed37b5 authored by Adam Barth's avatar Adam Barth

Add widgets for reparenting widgets

Wrap widgets you want to reparent in a Mimicable widget and assign the
Mimicable widget a global key. Then, given the same global key to a Mimic
widget to make it appear elsewhere in the view hierarchy.
parent 8722e76d
// 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/painting/box_painter.dart';
import 'package:sky/widgets.dart';
class Circle extends Component {
Circle({ this.child });
Widget child;
Widget build() {
return new Container(
height: 100.0,
margin: new EdgeDims.symmetric(horizontal: 20.0, vertical: 4.0),
decoration: new BoxDecoration(
backgroundColor: const Color(0xFF0000FF)
),
child: new Center(
child: child
)
);
}
}
class CircleData {
final GlobalKey key;
final String content;
CircleData({ this.key, this.content });
}
class ExampleApp extends App {
ExampleApp() {
for (int i = 0; i < 20; ++i) {
_data.add(new CircleData(
key: new GlobalKey(),
content: '$i'
));
}
}
final List<CircleData> _data = new List<CircleData>();
GlobalKey _keyToMimic;
Widget _buildCircle(CircleData circleData) {
return new Mimicable(
key: circleData.key,
child: new Listener(
child: new Circle(
child: new Text(circleData.content)
),
onGestureTap: (_) {
setState(() {
_keyToMimic = circleData.key;
});
}
)
);
}
Widget build() {
List<Widget> circles = new List<Widget>();
for (int i = 0; i < 20; ++i) {
circles.add(_buildCircle(_data[i]));
}
List<Widget> layers = new List<Widget>();
layers.add(new ScrollableBlock(circles));
if (_keyToMimic != null) {
layers.add(
new Positioned(
top: 50.0,
left: 50.0,
child: new Mimic(
original: _keyToMimic)
)
);
}
return new Stack(layers);
}
}
void main() {
runApp(new ExampleApp());
}
......@@ -21,11 +21,14 @@ export 'widgets/drawer_item.dart';
export 'widgets/flat_button.dart';
export 'widgets/floating_action_button.dart';
export 'widgets/focus.dart';
export 'widgets/icon_button.dart';
export 'widgets/framework.dart';
export 'widgets/icon.dart';
export 'widgets/icon_button.dart';
export 'widgets/ink_well.dart';
export 'widgets/material_button.dart';
export 'widgets/material.dart';
export 'widgets/material_button.dart';
export 'widgets/mimic.dart';
export 'widgets/mimic.dart';
export 'widgets/modal_overlay.dart';
export 'widgets/navigator.dart';
export 'widgets/popup_menu.dart';
......@@ -41,4 +44,3 @@ export 'widgets/task_description.dart';
export 'widgets/theme.dart';
export 'widgets/tool_bar.dart';
export 'widgets/transitions.dart';
export 'widgets/framework.dart';
......@@ -110,6 +110,11 @@ abstract class GlobalKey extends Key {
assert(removed);
}
static Widget getWidget(GlobalKey key) {
assert(key != null);
return _registry[key];
}
static void _notifyListeners() {
assert(!_inRenderDirtyComponents);
assert(!Widget._notifyingMountStatus);
......
// 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/widgets/basic.dart';
class Mimic extends StatefulComponent {
Mimic({ Key key, this.original }) : super(key: key);
GlobalKey original;
void initState() {
_requestToStartMimic();
}
void syncFields(Mimic source) {
if (original != source.original) {
_stopMimic();
original = source.original;
_requestToStartMimic();
}
}
void didMount() {
super.didMount();
// TODO(abarth): Why is didMount being called without a call to didUnmount?
if (_mimicable == null)
_requestToStartMimic();
}
void didUnmount() {
super.didUnmount();
_stopMimic();
}
Mimicable _mimicable;
Size _size;
void _requestToStartMimic() {
assert(_mimicable == null);
assert(_size == null);
if (original == null)
return;
_mimicable = GlobalKey.getWidget(original) as Mimicable;
assert(_mimicable != null);
_mimicable._requestToStartMimic(this);
}
void _startMimic(GlobalKey key, Size size) {
assert(key == original);
setState(() {
_size = size;
});
}
void _stopMimic() {
if (_mimicable != null)
_mimicable._didStopMimic(this);
_mimicable = null;
_size = null;
}
Widget build() {
if (_size == null || !_mimicable.mounted)
return new Container();
return new ConstrainedBox(
constraints: new BoxConstraints.tight(_size),
child: _mimicable.child
);
}
}
class Mimicable extends StatefulComponent {
Mimicable({ GlobalKey key, this.child }) : super(key: key);
Widget child;
Size _size;
Size get size => _size;
Mimic _mimic;
bool _didStartMimic = false;
void syncFields(Mimicable source) {
child = source.child;
}
void _requestToStartMimic(Mimic mimic) {
assert(mounted);
if (_mimic != null)
return;
setState(() {
_mimic = mimic;
_didStartMimic = false;
});
}
void _didStopMimic(Mimic mimic) {
assert(_mimic != null);
assert(mimic == _mimic);
setState(() {
_mimic = null;
_didStartMimic = false;
});
}
void _handleSizeChanged(Size size) {
setState(() {
_size = size;
});
}
void _startMimicIfNeeded() {
if (_didStartMimic)
return;
assert(_mimic != null);
_mimic._startMimic(key, _size);
_didStartMimic = true;
}
Widget build() {
if (_mimic != null) {
_startMimicIfNeeded();
return new ConstrainedBox(constraints: new BoxConstraints.tight(_size));
}
return new SizeObserver(
callback: _handleSizeChanged,
child: child
);
}
}
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