Merge pull request #1028 from sethladd/sethladd-patch-1

Slightly clarify contribution workflow
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 7fec2b7..55f9ae5 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -73,6 +73,8 @@
 Running the examples
 --------------------
 
+* Before running the examples, you'll need to set up your path to include the Dart SDK directory, like so (starting in the src directory of your code tree):
+ - ``$ export PATH=$PATH:`pwd`/third_party/dark-sdk/dart-sdk/bin``
 * You can find example code in subdirectories of the `examples` directory, for example `examples/stocks`.
 * Once you have a local build, run `pub get` from the example folder of your choice to make sure that you have all of the Dart dependencies.
 * Then, to run the current example locally, you can run:
diff --git a/examples/game/lib/game_demo.dart b/examples/game/lib/game_demo.dart
index 88ac116..c325613 100644
--- a/examples/game/lib/game_demo.dart
+++ b/examples/game/lib/game_demo.dart
@@ -4,9 +4,9 @@
 import 'dart:math' as math;
 import 'dart:sky' as sky;
 
-import 'package:sky/rendering/object.dart';
 import 'package:sky/painting/text_style.dart';
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/rendering/object.dart';
+import 'package:sky/src/widgets/framework.dart';
 import 'package:skysprites/skysprites.dart';
 import 'package:vector_math/vector_math.dart';
 
diff --git a/examples/game/lib/game_demo_node.dart b/examples/game/lib/game_demo_node.dart
index aa405d3..8ad670c 100644
--- a/examples/game/lib/game_demo_node.dart
+++ b/examples/game/lib/game_demo_node.dart
@@ -4,7 +4,7 @@
 double _gameSizeHeight = 320.0;
 
 final double _chunkSpacing = 640.0;
-final int _chunksPerLevel = 6;
+final int _chunksPerLevel = 9;
 
 final bool _drawDebug = false;
 
@@ -80,7 +80,6 @@
   PlayerState _playerState;
 
   // Game properties
-  double _scrollSpeed = 2.0;
   double _scroll = 0.0;
 
   int _framesToFire = 0;
@@ -95,11 +94,11 @@
 
   void update(double dt) {
     // Scroll the level
-    _scroll = _level.scroll(_scrollSpeed);
-    _starField.move(0.0, _scrollSpeed);
+    _scroll = _level.scroll(_playerState.scrollSpeed);
+    _starField.move(0.0, _playerState.scrollSpeed);
 
-    _background.move(_scrollSpeed * 0.1);
-    _nebula.move(_scrollSpeed);
+    _background.move(_playerState.scrollSpeed * 0.1);
+    _nebula.move(_playerState.scrollSpeed);
 
     // Add objects
     addObjects();
@@ -198,33 +197,41 @@
       lbl.position = new Point(0.0, yPos + _chunkSpacing / 2.0 - 150.0);
       _level.addChild(lbl);
     } else if (part == 1) {
-      _objectFactory.addAsteroids(10 + level * 4, yPos, 0.0 + (level * 0.2).clamp(0.0, 0.7));
+      _objectFactory.addAsteroids(10 + level * 2, yPos, 0.0 + (level * 0.2).clamp(0.0, 0.7));
     } else if (part == 2) {
       _objectFactory.addEnemyScoutSwarm(4 + level * 2, yPos);
     } else if (part == 3) {
-      _objectFactory.addAsteroids(10 + level * 4, yPos, 0.0 + (level * 0.2).clamp(0.0, 0.7));
+      _objectFactory.addAsteroids(10 + level * 2, yPos, 0.0 + (level * 0.2).clamp(0.0, 0.7));
     } else if (part == 4) {
       _objectFactory.addEnemyDestroyerSwarm(2 + level, yPos);
     } else if (part == 5) {
-      _objectFactory.addAsteroids(10 + level * 4, yPos, 0.0 + (level * 0.2).clamp(0.0, 0.7));
+      _objectFactory.addAsteroids(10 + level * 2, yPos, 0.0 + (level * 0.2).clamp(0.0, 0.7));
+    } else if (part == 6) {
+      _objectFactory.addEnemyScoutSwarm(4 + level * 2, yPos);
+    } else if (part == 7) {
+      _objectFactory.addAsteroids(10 + level * 2, yPos, 0.0 + (level * 0.2).clamp(0.0, 0.7));
+    } else if (part == 8) {
+      _objectFactory.addBossFight(level, yPos);
     }
   }
 
   void fire() {
-    Laser shot0 = new Laser(_objectFactory, -90.0);
+    int laserLevel = _objectFactory.playerState.laserLevel;
+
+    Laser shot0 = new Laser(_objectFactory, laserLevel, -90.0);
     shot0.position = _level.ship.position + new Offset(17.0, -10.0);
     _level.addChild(shot0);
 
-    Laser shot1 = new Laser(_objectFactory, -90.0);
+    Laser shot1 = new Laser(_objectFactory, laserLevel, -90.0);
     shot1.position = _level.ship.position + new Offset(-17.0, -10.0);
     _level.addChild(shot1);
 
     if (_playerState.sideLaserActive) {
-      Laser shot2 = new Laser(_objectFactory, 0.0);
+      Laser shot2 = new Laser(_objectFactory, laserLevel, -45.0);
       shot2.position = _level.ship.position + new Offset(17.0, -10.0);
       _level.addChild(shot2);
 
-      Laser shot3 = new Laser(_objectFactory, 180.0);
+      Laser shot3 = new Laser(_objectFactory, laserLevel, -135.0);
       shot3.position = _level.ship.position + new Offset(-17.0, -10.0);
       _level.addChild(shot3);
     }
diff --git a/examples/game/lib/game_object_factory.dart b/examples/game/lib/game_object_factory.dart
index db790a0..b4508c0 100644
--- a/examples/game/lib/game_object_factory.dart
+++ b/examples/game/lib/game_object_factory.dart
@@ -61,4 +61,13 @@
 
     level.addChild(obj);
   }
+
+  void addBossFight(int l, double yPos) {
+    EnemyBoss boss = new EnemyBoss(this);
+    boss.position = new Point(0.0, yPos + _chunkSpacing / 2.0);
+    boss.setupActions();
+
+    level.addChild(boss);
+    playerState.boss = boss;
+  }
 }
diff --git a/examples/game/lib/game_objects.dart b/examples/game/lib/game_objects.dart
index 185e548..dbe8657 100644
--- a/examples/game/lib/game_objects.dart
+++ b/examples/game/lib/game_objects.dart
@@ -92,7 +92,7 @@
     canBeDamaged = false;
 
     Label lbl = new Label(
-      "LEVEL $level",
+      "L E V E L $level",
       new TextStyle(
         textAlign: TextAlign.center,
         color:new Color(0xffffffff),
@@ -156,27 +156,54 @@
 }
 
 class Laser extends GameObject {
-  double impact = 1.0;
+  double impact = 0.0;
 
-  Laser(GameObjectFactory f, double r) : super(f) {
-    // Add sprite
-    _sprt = new Sprite(f.sheet["explosion_particle.png"]);
-    _sprt.scale = 0.5;
-    _sprt.colorOverlay = new Color(0xff95f4fb);
-    _sprt.transferMode = sky.TransferMode.plus;
-    _sprt.rotation = r + 90.0;
-    addChild(_sprt);
+  final List<Color> laserColors = [
+    new Color(0xff95f4fb),
+    new Color(0xff5bff35),
+    new Color(0xffff886c),
+    new Color(0xffffd012),
+    new Color(0xfffd7fff)
+  ];
+
+  Laser(GameObjectFactory f, int level, double r) : super(f) {
+    // Game object properties
     radius = 10.0;
     removeLimit = 640.0;
-
-
     canDamageShip = false;
     canBeDamaged = false;
+    impact = 1.0 + level * 0.5;
 
+    // Offset for movement
     _offset = new Offset(math.cos(radians(r)) * 10.0, math.sin(radians(r)) * 10.0);
+
+    // Drawing properties
+    rotation = r + 90.0;
+    int numLasers = level % 3 + 1;
+    Color laserColor = laserColors[(level ~/ 3) % laserColors.length];
+
+    // Add sprites
+    List<Sprite> sprites = [];
+    for (int i = 0; i < numLasers; i++) {
+      Sprite sprt = new Sprite(f.sheet["explosion_particle.png"]);
+      sprt.scale = 0.5;
+      sprt.colorOverlay = laserColor;
+      sprt.transferMode = sky.TransferMode.plus;
+      addChild(sprt);
+      sprites.add(sprt);
+    }
+
+    // Position the individual sprites
+    if (numLasers == 2) {
+      sprites[0].position = new Point(-3.0, 0.0);
+      sprites[1].position = new Point(3.0, 0.0);
+    } else if (numLasers == 3) {
+      sprites[0].position = new Point(-4.0, 0.0);
+      sprites[1].position = new Point(4.0, 0.0);
+      sprites[2].position = new Point(0.0, -2.0);
+    }
   }
 
-  Sprite _sprt;
   Offset _offset;
 
   void move() {
@@ -350,8 +377,6 @@
   void update(double dt) {
     _countDown -= 1;
     if (_countDown <= 0) {
-      print("SHOOT!!");
-
       // Shoot at player
       EnemyLaser laser = new EnemyLaser(f, rotation, 5.0, new Color(0xffffe38e));
       laser.position = position;
@@ -382,8 +407,6 @@
 
     double rad = radians(rotation);
     _movement = new Offset(math.cos(rad) * speed, math.sin(rad) * speed);
-
-    print("LASER!!");
   }
 
   Sprite _sprt;
@@ -394,6 +417,39 @@
   }
 }
 
+class EnemyBoss extends Obstacle {
+  EnemyBoss(GameObjectFactory f) : super(f) {
+    radius = 48.0;
+    _sprt = new Sprite(f.sheet["enemy_destroyer_1.png"]);
+    _sprt.scale = 0.64;
+    addChild(_sprt);
+    maxDamage = 40.0;
+
+    constraints = [new ConstraintRotationToNode(f.level.ship, dampening: 0.05)];
+  }
+
+  Sprite _sprt;
+
+  int _countDown = randomInt(120) + 240;
+
+  void update(double dt) {
+    _countDown -= 1;
+    if (_countDown <= 0) {
+      // Shoot at player
+      EnemyLaser laser = new EnemyLaser(f, rotation, 5.0, new Color(0xffffe38e));
+      laser.position = position;
+      f.level.addChild(laser);
+
+      _countDown = 60 + randomInt(120);
+    }
+  }
+
+  void destroy() {
+    f.playerState.boss = null;
+    super.destroy();
+  }
+}
+
 class Collectable extends GameObject {
   Collectable(GameObjectFactory f) : super(f) {
     canDamageShip = false;
@@ -435,6 +491,7 @@
   shield,
   speedLaser,
   sideLaser,
+  speedBoost,
 }
 
 List<PowerUpType> _powerUpTypes = new List.from(PowerUpType.values);
diff --git a/examples/game/lib/main.dart b/examples/game/lib/main.dart
index 6570eb0..6ba4765 100644
--- a/examples/game/lib/main.dart
+++ b/examples/game/lib/main.dart
@@ -5,15 +5,15 @@
 import 'dart:async';
 
 import 'package:sky/mojo/asset_bundle.dart';
-import 'package:sky/theme/colors.dart' as colors;
-import 'package:sky/rendering/object.dart';
 import 'package:sky/painting/text_style.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/button_base.dart';
-import 'package:sky/widgets/navigator.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/title.dart';
-import 'package:sky/widgets/theme.dart';
+import 'package:sky/src/rendering/object.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/button_base.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/navigator.dart';
+import 'package:sky/src/widgets/theme.dart';
+import 'package:sky/src/widgets/title.dart';
+import 'package:sky/theme/colors.dart' as colors;
 import 'package:skysprites/skysprites.dart';
 
 import 'game_demo.dart';
diff --git a/examples/game/lib/player_state.dart b/examples/game/lib/player_state.dart
index 607f60d..70f4370 100644
--- a/examples/game/lib/player_state.dart
+++ b/examples/game/lib/player_state.dart
@@ -28,6 +28,16 @@
   final SpriteSheet _sheetUI;
   final SpriteSheet _sheetGame;
 
+  int laserLevel = 0;
+
+  static const double normalScrollSpeed = 2.0;
+
+  double scrollSpeed = normalScrollSpeed;
+
+  double _scrollSpeedTarget = normalScrollSpeed;
+
+  EnemyBoss boss;
+
   Sprite _sprtBgScore;
   ScoreDisplay _scoreDisplay;
   Sprite _sprtBgCoins;
@@ -78,12 +88,15 @@
       _sideLaserFrames += 300;
     } else if (type == PowerUpType.speedLaser) {
       _speedLaserFrames += 300;
+    } else if (type == PowerUpType.speedBoost) {
+      _speedBoostFrames += 150;
     }
   }
 
   int _shieldFrames = 0;
-  bool get shieldActive => _shieldFrames > 0;
-  bool get shieldDeactivating => _shieldFrames > 0 && _shieldFrames < 60;
+  bool get shieldActive => _shieldFrames > 0 || _speedBoostFrames > 0;
+  bool get shieldDeactivating =>
+    math.max(_shieldFrames, _speedBoostFrames) > 0 && math.max(_shieldFrames, _speedBoostFrames) < 60;
 
   int _sideLaserFrames = 0;
   bool get sideLaserActive => _sideLaserFrames > 0;
@@ -91,6 +104,9 @@
   int _speedLaserFrames = 0;
   bool get speedLaserActive => _speedLaserFrames > 0;
 
+  int _speedBoostFrames = 0;
+  bool get speedBoostActive => _speedBoostFrames > 0;
+
   void flashBgSprite(Sprite sprt) {
     sprt.actions.stopAll();
     ActionTween flash = new ActionTween(
@@ -105,6 +121,23 @@
     if (_shieldFrames > 0) _shieldFrames--;
     if (_sideLaserFrames > 0) _sideLaserFrames--;
     if (_speedLaserFrames > 0) _speedLaserFrames--;
+    if (_speedBoostFrames > 0) _speedBoostFrames--;
+
+    // Update speed
+    if (boss != null) {
+      Point globalBossPos = boss.convertPointToBoxSpace(Point.origin);
+      if (globalBossPos.y > (_gameSizeHeight - 400.0))
+        _scrollSpeedTarget = 0.0;
+      else
+        _scrollSpeedTarget = normalScrollSpeed;
+    } else {
+      if (speedBoostActive)
+        _scrollSpeedTarget = normalScrollSpeed * 6.0;
+      else
+        _scrollSpeedTarget = normalScrollSpeed;
+    }
+
+    scrollSpeed = GameMath.filter(scrollSpeed, _scrollSpeedTarget, 0.1);
   }
 }
 
diff --git a/examples/game/test_drawatlas.dart b/examples/game/test_drawatlas.dart
index 56b70d0..c6c80619 100644
--- a/examples/game/test_drawatlas.dart
+++ b/examples/game/test_drawatlas.dart
@@ -1,9 +1,8 @@
 import 'dart:sky';
 
 import 'package:sky/mojo/asset_bundle.dart';
-import 'package:sky/rendering/object.dart';
+import 'package:sky/rendering.dart';
 import 'package:sky/theme/colors.dart' as colors;
-import 'package:sky/widgets/basic.dart';
 import 'package:sky/widgets.dart';
 import 'package:skysprites/skysprites.dart';
 
diff --git a/examples/game/test_performance.dart b/examples/game/test_performance.dart
index c65e08d..d7f79cd 100644
--- a/examples/game/test_performance.dart
+++ b/examples/game/test_performance.dart
@@ -2,7 +2,7 @@
 import 'dart:math' as math;
 
 import 'package:sky/mojo/asset_bundle.dart';
-import 'package:sky/rendering/object.dart';
+import 'package:sky/rendering.dart';
 import 'package:sky/theme/colors.dart' as colors;
 import 'package:sky/widgets.dart';
 import 'package:skysprites/skysprites.dart';
diff --git a/examples/raw/baseline.dart b/examples/raw/baseline.dart
index 0e1e866..8e0eb10 100644
--- a/examples/raw/baseline.dart
+++ b/examples/raw/baseline.dart
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 import 'dart:sky' as sky;
+import 'dart:typed_data';
 
 void drawText(sky.Canvas canvas, String lh) {
   sky.Paint paint = new sky.Paint();
@@ -44,14 +45,10 @@
   layoutRoot.paint(canvas);
 }
 
-void main() {
-  // prepare the rendering
+sky.Picture paint(sky.Rect paintBounds) {
   sky.PictureRecorder recorder = new sky.PictureRecorder();
-  final double devicePixelRatio = sky.view.devicePixelRatio;
-  sky.Canvas canvas = new sky.Canvas(recorder, new sky.Rect.fromLTWH(0.0, 0.0, sky.view.width * devicePixelRatio, sky.view.height * devicePixelRatio));
-  canvas.scale(devicePixelRatio, devicePixelRatio);
+  sky.Canvas canvas = new sky.Canvas(recorder, paintBounds);
 
-  // background
   sky.Paint paint = new sky.Paint();
   paint.color = const sky.Color(0xFFFFFFFF);
   paint.setStyle(sky.PaintingStyle.fill);
@@ -61,7 +58,30 @@
   drawText(canvas, '1.0');
   drawText(canvas, 'lh');
 
-  // put it on the screen
-  sky.view.picture = recorder.endRecording();
+  return recorder.endRecording();
+}
+
+sky.Scene composite(sky.Picture picture, sky.Rect paintBounds) {
+  final double devicePixelRatio = sky.view.devicePixelRatio;
+  sky.Rect sceneBounds = new sky.Rect.fromLTWH(0.0, 0.0, sky.view.width * devicePixelRatio, sky.view.height * devicePixelRatio);
+  Float32List deviceTransform = new Float32List(16)
+    ..[0] = devicePixelRatio
+    ..[5] = devicePixelRatio;
+  sky.SceneBuilder sceneBuilder = new sky.SceneBuilder(sceneBounds)
+    ..pushTransform(deviceTransform)
+    ..addPicture(sky.Offset.zero, picture, paintBounds)
+    ..pop();
+  return sceneBuilder.build();
+}
+
+void beginFrame(double timeStamp) {
+  sky.Rect paintBounds = new sky.Rect.fromLTWH(0.0, 0.0, sky.view.width, sky.view.height);
+  sky.Picture picture = paint(paintBounds);
+  sky.Scene scene = composite(picture, paintBounds);
+  sky.view.scene = scene;
+}
+
+void main() {
+  sky.view.setFrameCallback(beginFrame);
   sky.view.scheduleFrame();
 }
diff --git a/examples/raw/hello_world.dart b/examples/raw/hello_world.dart
index ee4669c..87810f8 100644
--- a/examples/raw/hello_world.dart
+++ b/examples/raw/hello_world.dart
@@ -2,37 +2,59 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'dart:sky';
+import 'dart:sky' as sky;
+import 'dart:typed_data';
 
-Picture draw(int a, int r, int g, int b) {
-  Size size = new Size(view.width, view.height);
+sky.Color color;
 
-  PictureRecorder recorder = new PictureRecorder();
-  final double devicePixelRatio = view.devicePixelRatio;
-  Canvas canvas = new Canvas(recorder, Point.origin & (size * devicePixelRatio));
-  canvas.scale(devicePixelRatio, devicePixelRatio);
+sky.Picture paint(sky.Rect paintBounds) {
+  sky.PictureRecorder recorder = new sky.PictureRecorder();
+  sky.Canvas canvas = new sky.Canvas(recorder, paintBounds);
+  sky.Size size = paintBounds.size;
+
   double radius = size.shortestSide * 0.45;
+  sky.Paint paint = new sky.Paint()
+    ..color = color;
+  canvas.drawCircle(size.center(sky.Point.origin), radius, paint);
 
-  Paint paint = new Paint()..color = new Color.fromARGB(a, r, g, b);
-  canvas.drawCircle(size.center(Point.origin), radius, paint);
   return recorder.endRecording();
 }
 
-bool handleEvent(Event event) {
-  if (event.type == "pointerdown") {
-    view.picture = draw(255, 0, 0, 255);
-    view.scheduleFrame();
+sky.Scene composite(sky.Picture picture, sky.Rect paintBounds) {
+  final double devicePixelRatio = sky.view.devicePixelRatio;
+  sky.Rect sceneBounds = new sky.Rect.fromLTWH(0.0, 0.0, sky.view.width * devicePixelRatio, sky.view.height * devicePixelRatio);
+  Float32List deviceTransform = new Float32List(16)
+    ..[0] = devicePixelRatio
+    ..[5] = devicePixelRatio;
+  sky.SceneBuilder sceneBuilder = new sky.SceneBuilder(sceneBounds)
+    ..pushTransform(deviceTransform)
+    ..addPicture(sky.Offset.zero, picture, paintBounds)
+    ..pop();
+  return sceneBuilder.build();
+}
+
+void beginFrame(double timeStamp) {
+  sky.Rect paintBounds = new sky.Rect.fromLTWH(0.0, 0.0, sky.view.width, sky.view.height);
+  sky.Picture picture = paint(paintBounds);
+  sky.Scene scene = composite(picture, paintBounds);
+  sky.view.scene = scene;
+}
+
+bool handleEvent(sky.Event event) {
+  if (event.type == 'pointerdown') {
+    color = new sky.Color.fromARGB(255, 0, 0, 255);
+    sky.view.scheduleFrame();
     return true;
   }
 
-  if (event.type == "pointerup") {
-    view.picture = draw(255, 0, 255, 0);
-    view.scheduleFrame();
+  if (event.type == 'pointerup') {
+    color = new sky.Color.fromARGB(255, 0, 255, 0);
+    sky.view.scheduleFrame();
     return true;
   }
 
-  if (event.type == "back") {
-    print("Pressed back button.");
+  if (event.type == 'back') {
+    print('Pressed back button.');
     return true;
   }
 
@@ -40,9 +62,9 @@
 }
 
 void main() {
-  print("Hello, world");
-  view.picture = draw(255, 0, 255, 0);
-  view.scheduleFrame();
-
-  view.setEventCallback(handleEvent);
+  print('Hello, world');
+  color = new sky.Color.fromARGB(255, 0, 255, 0);
+  sky.view.setFrameCallback(beginFrame);
+  sky.view.setEventCallback(handleEvent);
+  sky.view.scheduleFrame();
 }
diff --git a/examples/raw/mutating-dom.dart b/examples/raw/mutating-dom.dart
index 8ec6ec8..18fe4c6 100644
--- a/examples/raw/mutating-dom.dart
+++ b/examples/raw/mutating-dom.dart
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import "dart:math" as math;
+import 'dart:math' as math;
 import 'dart:sky' as sky;
+import 'dart:typed_data';
 
 const kMaxIterations = 100;
 int peakCount = 1000; // this is the number that must be reached for us to start reporting the peak number of nodes in the tree each frame
@@ -32,7 +33,7 @@
   return 'rgba(${color.red}, ${color.green}, ${color.blue}, ${color.alpha / 255.0})';
 }
 
-void doFrame(double timeStamp) {
+void mutate(sky.Canvas canvas) {
   // mutate the DOM randomly
   int iterationsLeft = kMaxIterations;
   sky.Node node = root;
@@ -201,16 +202,37 @@
 
   // draw the result
   report("recording...");
-  sky.PictureRecorder recorder = new sky.PictureRecorder();
-  final double devicePixelRatio = sky.view.devicePixelRatio;
-  sky.Canvas canvas = new sky.Canvas(recorder, new sky.Rect.fromLTWH(0.0, 0.0, sky.view.width * devicePixelRatio, sky.view.height * devicePixelRatio));
-  canvas.scale(devicePixelRatio, devicePixelRatio);
   layoutRoot.maxWidth = sky.view.width;
   layoutRoot.layout();
   layoutRoot.paint(canvas);
   report("painting...");
-  sky.view.picture = recorder.endRecording();
-  sky.view.scheduleFrame();
+}
+
+sky.Picture paint(sky.Rect paintBounds) {
+  sky.PictureRecorder recorder = new sky.PictureRecorder();
+  sky.Canvas canvas = new sky.Canvas(recorder, paintBounds);
+  mutate(canvas);
+  return recorder.endRecording();
+}
+
+sky.Scene composite(sky.Picture picture, sky.Rect paintBounds) {
+  final double devicePixelRatio = sky.view.devicePixelRatio;
+  sky.Rect sceneBounds = new sky.Rect.fromLTWH(0.0, 0.0, sky.view.width * devicePixelRatio, sky.view.height * devicePixelRatio);
+  Float32List deviceTransform = new Float32List(16)
+    ..[0] = devicePixelRatio
+    ..[5] = devicePixelRatio;
+  sky.SceneBuilder sceneBuilder = new sky.SceneBuilder(sceneBounds)
+    ..pushTransform(deviceTransform)
+    ..addPicture(sky.Offset.zero, picture, paintBounds)
+    ..pop();
+  return sceneBuilder.build();
+}
+
+void beginFrame(double timeStamp) {
+  sky.Rect paintBounds = new sky.Rect.fromLTWH(0.0, 0.0, sky.view.width, sky.view.height);
+  sky.Picture picture = paint(paintBounds);
+  sky.Scene scene = composite(picture, paintBounds);
+  sky.view.scene = scene;
 }
 
 void main() {
@@ -218,6 +240,6 @@
   root.style['display'] = 'paragraph';
   root.style['color'] = '#FFFFFF';
   layoutRoot.rootElement = root;
-  sky.view.setFrameCallback(doFrame);
+  sky.view.setFrameCallback(beginFrame);
   sky.view.scheduleFrame();
 }
diff --git a/examples/raw/painting.dart b/examples/raw/painting.dart
index 55963b1..7ffa87e 100644
--- a/examples/raw/painting.dart
+++ b/examples/raw/painting.dart
@@ -6,21 +6,17 @@
 import 'dart:math' as math;
 import 'dart:typed_data';
 
-void beginFrame(double timeStamp) {
-  sky.Size size = new sky.Size(sky.view.width, sky.view.height);
+sky.Picture paint(sky.Rect paintBounds) {
   sky.PictureRecorder recorder = new sky.PictureRecorder();
-  final double devicePixelRatio = sky.view.devicePixelRatio;
-  sky.Canvas canvas = new sky.Canvas(recorder, sky.Point.origin & (size * devicePixelRatio));
-  canvas.scale(devicePixelRatio, devicePixelRatio);
+  sky.Canvas canvas = new sky.Canvas(recorder, paintBounds);
+  sky.Size size = paintBounds.size;
 
   sky.Paint paint = new sky.Paint();
   sky.Point mid = size.center(sky.Point.origin);
   double radius = size.shortestSide / 2.0;
-
   canvas.drawPaint(new sky.Paint()..color = const sky.Color(0xFFFFFFFF));
 
   canvas.save();
-
   canvas.translate(-mid.x/2.0, sky.view.height*2.0);
   canvas.clipRect(
       new sky.Rect.fromLTRB(0.0, -sky.view.height, sky.view.width, radius));
@@ -39,7 +35,7 @@
   var scaleMatrix = new Float32List.fromList([
       0.5, 0.0, 0.0, 0.0,
       0.0, 0.5, 0.0, 0.0,
-      0.0, 0.0, 0.0, 0.0,
+      0.0, 0.0, 1.0, 0.0,
       0.0, 0.0, 0.0, 1.0,
   ]);
   canvas.concat(scaleMatrix);
@@ -81,7 +77,27 @@
   paint.setDrawLooper(builder.build());
   canvas.drawCircle(sky.Point.origin, radius, paint);
 
-  sky.view.picture = recorder.endRecording();
+  return recorder.endRecording();
+}
+
+sky.Scene composite(sky.Picture picture, sky.Rect paintBounds) {
+  final double devicePixelRatio = sky.view.devicePixelRatio;
+  sky.Rect sceneBounds = new sky.Rect.fromLTWH(0.0, 0.0, sky.view.width * devicePixelRatio, sky.view.height * devicePixelRatio);
+  Float32List deviceTransform = new Float32List(16)
+    ..[0] = devicePixelRatio
+    ..[5] = devicePixelRatio;
+  sky.SceneBuilder sceneBuilder = new sky.SceneBuilder(sceneBounds)
+    ..pushTransform(deviceTransform)
+    ..addPicture(sky.Offset.zero, picture, paintBounds)
+    ..pop();
+  return sceneBuilder.build();
+}
+
+void beginFrame(double timeStamp) {
+  sky.Rect paintBounds = new sky.Rect.fromLTWH(0.0, 0.0, sky.view.width, sky.view.height);
+  sky.Picture picture = paint(paintBounds);
+  sky.Scene scene = composite(picture, paintBounds);
+  sky.view.scene = scene;
 }
 
 void main() {
diff --git a/examples/raw/shadow.dart b/examples/raw/shadow.dart
index c301beb..9da0b15 100644
--- a/examples/raw/shadow.dart
+++ b/examples/raw/shadow.dart
@@ -2,41 +2,62 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'dart:sky';
+import 'dart:sky' as sky;
+import 'dart:typed_data';
 
-void beginFrame(double timeStamp) {
+sky.Picture paint(sky.Rect paintBounds) {
+  sky.PictureRecorder recorder = new sky.PictureRecorder();
+  sky.Canvas canvas = new sky.Canvas(recorder, paintBounds);
+
   double size = 100.0;
-  PictureRecorder recorder = new PictureRecorder();
-  final double devicePixelRatio = view.devicePixelRatio;
-  Canvas canvas = new Canvas(recorder, new Rect.fromLTWH(0.0, 0.0, view.width * devicePixelRatio, view.height * devicePixelRatio));
-  canvas.scale(devicePixelRatio, devicePixelRatio);
   canvas.translate(size + 10.0, size + 10.0);
 
-  Paint paint = new Paint();
-  paint.color = const Color.fromARGB(255, 0, 255, 0);
-  var builder = new LayerDrawLooperBuilder()
+  sky.Paint paint = new sky.Paint();
+  paint.color = const sky.Color.fromARGB(255, 0, 255, 0);
+  var builder = new sky.LayerDrawLooperBuilder()
     // Shadow layer.
     ..addLayerOnTop(
-        new DrawLooperLayerInfo()
-          ..setPaintBits(PaintBits.all)
-          ..setOffset(const Offset(5.0, 5.0))
-          ..setColorMode(TransferMode.src),
-        new Paint()
-          ..color = const Color.fromARGB(128, 55, 55, 55)
+        new sky.DrawLooperLayerInfo()
+          ..setPaintBits(sky.PaintBits.all)
+          ..setOffset(const sky.Offset(5.0, 5.0))
+          ..setColorMode(sky.TransferMode.src),
+        new sky.Paint()
+          ..color = const sky.Color.fromARGB(128, 55, 55, 55)
           ..setMaskFilter(
-            new MaskFilter.blur(BlurStyle.normal, 5.0, highQuality: true))
+            new sky.MaskFilter.blur(sky.BlurStyle.normal, 5.0))
     )
     // Main layer.
-    ..addLayerOnTop(new DrawLooperLayerInfo(), new Paint());
+    ..addLayerOnTop(new sky.DrawLooperLayerInfo(), new sky.Paint());
   paint.setDrawLooper(builder.build());
 
   canvas.drawPaint(
-      new Paint()..color = const Color.fromARGB(255, 255, 255, 255));
-  canvas.drawRect(new Rect.fromLTRB(-size, -size, size, size), paint);
-  view.picture = recorder.endRecording();
+      new sky.Paint()..color = const sky.Color.fromARGB(255, 255, 255, 255));
+  canvas.drawRect(new sky.Rect.fromLTRB(-size, -size, size, size), paint);
+
+  return recorder.endRecording();
+}
+
+sky.Scene composite(sky.Picture picture, sky.Rect paintBounds) {
+  final double devicePixelRatio = sky.view.devicePixelRatio;
+  sky.Rect sceneBounds = new sky.Rect.fromLTWH(0.0, 0.0, sky.view.width * devicePixelRatio, sky.view.height * devicePixelRatio);
+  Float32List deviceTransform = new Float32List(16)
+    ..[0] = devicePixelRatio
+    ..[5] = devicePixelRatio;
+  sky.SceneBuilder sceneBuilder = new sky.SceneBuilder(sceneBounds)
+    ..pushTransform(deviceTransform)
+    ..addPicture(sky.Offset.zero, picture, paintBounds)
+    ..pop();
+  return sceneBuilder.build();
+}
+
+void beginFrame(double timeStamp) {
+  sky.Rect paintBounds = new sky.Rect.fromLTWH(0.0, 0.0, sky.view.width, sky.view.height);
+  sky.Picture picture = paint(paintBounds);
+  sky.Scene scene = composite(picture, paintBounds);
+  sky.view.scene = scene;
 }
 
 void main() {
-  view.setFrameCallback(beginFrame);
-  view.scheduleFrame();
+  sky.view.setFrameCallback(beginFrame);
+  sky.view.scheduleFrame();
 }
diff --git a/examples/raw/spinning_arabic.dart b/examples/raw/spinning_arabic.dart
index 5fef68d..66562b2 100644
--- a/examples/raw/spinning_arabic.dart
+++ b/examples/raw/spinning_arabic.dart
@@ -2,24 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import "dart:math" as math;
-import 'dart:sky';
+import 'dart:math' as math;
+import 'dart:sky' as sky;
+import 'dart:typed_data';
 
 double timeBase = null;
-LayoutRoot layoutRoot = new LayoutRoot();
+sky.LayoutRoot layoutRoot = new sky.LayoutRoot();
 
-void beginFrame(double timeStamp) {
-  if (timeBase == null)
-    timeBase = timeStamp;
-  double delta = timeStamp - timeBase;
-  PictureRecorder recorder = new PictureRecorder();
-  final double devicePixelRatio = view.devicePixelRatio;
-  Canvas canvas = new Canvas(recorder, new Rect.fromLTWH(0.0, 0.0, view.width * devicePixelRatio, view.height * devicePixelRatio));
-  canvas.scale(devicePixelRatio, devicePixelRatio);
-  canvas.translate(view.width / 2.0, view.height / 2.0);
+sky.Picture paint(sky.Rect paintBounds, double delta) {
+  sky.PictureRecorder recorder = new sky.PictureRecorder();
+  sky.Canvas canvas = new sky.Canvas(recorder, paintBounds);
+
+  canvas.translate(sky.view.width / 2.0, sky.view.height / 2.0);
   canvas.rotate(math.PI * delta / 1800);
-  canvas.drawRect(new Rect.fromLTRB(-100.0, -100.0, 100.0, 100.0),
-                  new Paint()..color = const Color.fromARGB(255, 0, 255, 0));
+  canvas.drawRect(new sky.Rect.fromLTRB(-100.0, -100.0, 100.0, 100.0),
+                  new sky.Paint()..color = const sky.Color.fromARGB(255, 0, 255, 0));
 
   double sin = math.sin(delta / 200);
   layoutRoot.maxWidth = 150.0 + (50 * sin);
@@ -28,12 +25,34 @@
   canvas.translate(layoutRoot.maxWidth / -2.0, (layoutRoot.maxWidth / 2.0) - 125);
   layoutRoot.paint(canvas);
 
-  view.picture = recorder.endRecording();
-  view.scheduleFrame();
+  return recorder.endRecording();
+}
+
+sky.Scene composite(sky.Picture picture, sky.Rect paintBounds) {
+  final double devicePixelRatio = sky.view.devicePixelRatio;
+  sky.Rect sceneBounds = new sky.Rect.fromLTWH(0.0, 0.0, sky.view.width * devicePixelRatio, sky.view.height * devicePixelRatio);
+  Float32List deviceTransform = new Float32List(16)
+    ..[0] = devicePixelRatio
+    ..[5] = devicePixelRatio;
+  sky.SceneBuilder sceneBuilder = new sky.SceneBuilder(sceneBounds)
+    ..pushTransform(deviceTransform)
+    ..addPicture(sky.Offset.zero, picture, paintBounds)
+    ..pop();
+  return sceneBuilder.build();
+}
+
+void beginFrame(double timeStamp) {
+  if (timeBase == null)
+    timeBase = timeStamp;
+  double delta = timeStamp - timeBase;
+  sky.Rect paintBounds = new sky.Rect.fromLTWH(0.0, 0.0, sky.view.width, sky.view.height);
+  sky.Picture picture = paint(paintBounds, delta);
+  sky.Scene scene = composite(picture, paintBounds);
+  sky.view.scene = scene;
 }
 
 void main() {
-  var document = new Document();
+  var document = new sky.Document();
   var arabic = document.createText("هذا هو قليلا طويلة من النص الذي يجب التفاف .");
   var more = document.createText(" و أكثر قليلا لجعله أطول. ");
   var block = document.createElement('p');
@@ -45,6 +64,6 @@
 
   layoutRoot.rootElement = block;
 
-  view.setFrameCallback(beginFrame);
-  view.scheduleFrame();
+  sky.view.setFrameCallback(beginFrame);
+  sky.view.scheduleFrame();
 }
diff --git a/examples/raw/spinning_image.dart b/examples/raw/spinning_image.dart
index d4a2239..0c3a6fa 100644
--- a/examples/raw/spinning_image.dart
+++ b/examples/raw/spinning_image.dart
@@ -3,32 +3,29 @@
 // found in the LICENSE file.
 
 import 'dart:math' as math;
-import 'dart:sky';
+import 'dart:sky' as sky;
+import 'dart:typed_data';
 
 import 'package:sky/mojo/net/image_cache.dart' as image_cache;
 
 double timeBase = null;
 
-Image image = null;
+sky.Image image = null;
 String url1 = "https://www.dartlang.org/logos/dart-logo.png";
 String url2 = "http://i2.kym-cdn.com/photos/images/facebook/000/581/296/c09.jpg";
 
-void beginFrame(double timeStamp) {
-  if (timeBase == null)
-    timeBase = timeStamp;
-  double delta = timeStamp - timeBase;
-  PictureRecorder recorder = new PictureRecorder();
-  final double devicePixelRatio = view.devicePixelRatio;
-  Canvas canvas = new Canvas(recorder, Point.origin & new Size(view.width * devicePixelRatio, view.height * devicePixelRatio));
-  canvas.scale(devicePixelRatio, devicePixelRatio);
-  canvas.translate(view.width / 2.0, view.height / 2.0);
+sky.Picture paint(sky.Rect paintBounds, double delta) {
+  sky.PictureRecorder recorder = new sky.PictureRecorder();
+  sky.Canvas canvas = new sky.Canvas(recorder, paintBounds);
+
+  canvas.translate(paintBounds.width / 2.0, paintBounds.height / 2.0);
   canvas.rotate(math.PI * delta / 1800);
   canvas.scale(0.2, 0.2);
-  Paint paint = new Paint()..color = const Color.fromARGB(255, 0, 255, 0);
+  sky.Paint paint = new sky.Paint()..color = const sky.Color.fromARGB(255, 0, 255, 0);
 
   // Draw image
   if (image != null)
-    canvas.drawImage(image, new Point(-image.width / 2.0, -image.height / 2.0), paint);
+    canvas.drawImage(image, new sky.Point(-image.width / 2.0, -image.height / 2.0), paint);
 
   // Draw cut out of image
   canvas.rotate(math.PI * delta / 1800);
@@ -36,26 +33,49 @@
     var w = image.width.toDouble();
     var h = image.width.toDouble();
     canvas.drawImageRect(image,
-      new Rect.fromLTRB(w * 0.25, h * 0.25, w * 0.75, h * 0.75),
-      new Rect.fromLTRB(-w / 4.0, -h / 4.0, w / 4.0, h / 4.0),
+      new sky.Rect.fromLTRB(w * 0.25, h * 0.25, w * 0.75, h * 0.75),
+      new sky.Rect.fromLTRB(-w / 4.0, -h / 4.0, w / 4.0, h / 4.0),
       paint);
   }
 
-  view.picture = recorder.endRecording();
-  view.scheduleFrame();
+  return recorder.endRecording();
 }
 
+sky.Scene composite(sky.Picture picture, sky.Rect paintBounds) {
+  final double devicePixelRatio = sky.view.devicePixelRatio;
+  sky.Rect sceneBounds = new sky.Rect.fromLTWH(0.0, 0.0, sky.view.width * devicePixelRatio, sky.view.height * devicePixelRatio);
+  Float32List deviceTransform = new Float32List(16)
+    ..[0] = devicePixelRatio
+    ..[5] = devicePixelRatio;
+  sky.SceneBuilder sceneBuilder = new sky.SceneBuilder(sceneBounds)
+    ..pushTransform(deviceTransform)
+    ..addPicture(sky.Offset.zero, picture, paintBounds)
+    ..pop();
+  return sceneBuilder.build();
+}
+
+void beginFrame(double timeStamp) {
+  if (timeBase == null)
+    timeBase = timeStamp;
+  double delta = timeStamp - timeBase;
+  sky.Rect paintBounds = new sky.Rect.fromLTWH(0.0, 0.0, sky.view.width, sky.view.height);
+  sky.Picture picture = paint(paintBounds, delta);
+  sky.Scene scene = composite(picture, paintBounds);
+  sky.view.scene = scene;
+}
+
+
 void handleImageLoad(result) {
   if (result != image) {
     print("${result.width}x${result.width} image loaded!");
     image = result;
-    view.scheduleFrame();
+    sky.view.scheduleFrame();
   } else {
     print("Existing image was loaded again");
   }
 }
 
-bool handleEvent(Event event) {
+bool handleEvent(sky.Event event) {
   if (event.type == "pointerdown") {
     return true;
   }
@@ -70,6 +90,6 @@
 
 void main() {
   image_cache.load(url1).first.then(handleImageLoad);
-  view.setEventCallback(handleEvent);
-  view.setFrameCallback(beginFrame);
+  sky.view.setEventCallback(handleEvent);
+  sky.view.setFrameCallback(beginFrame);
 }
diff --git a/examples/raw/spinning_square.dart b/examples/raw/spinning_square.dart
index 9119f3a..cc6d168 100644
--- a/examples/raw/spinning_square.dart
+++ b/examples/raw/spinning_square.dart
@@ -2,30 +2,44 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'dart:sky';
 import 'dart:math' as math;
+import 'dart:sky' as sky;
+import 'dart:typed_data';
 
 double timeBase = null;
 
 void beginFrame(double timeStamp) {
-  tracing.begin('beginFrame');
+  sky.tracing.begin('beginFrame');
   if (timeBase == null)
     timeBase = timeStamp;
   double delta = timeStamp - timeBase;
-  PictureRecorder recorder = new PictureRecorder();
-  final double devicePixelRatio = view.devicePixelRatio;
-  Canvas canvas = new Canvas(recorder, new Rect.fromLTWH(0.0, 0.0, view.width * devicePixelRatio, view.height * devicePixelRatio));
-  canvas.scale(devicePixelRatio, devicePixelRatio);
-  canvas.translate(view.width / 2.0, view.height / 2.0);
+
+  // paint
+  sky.Rect paintBounds = new sky.Rect.fromLTWH(0.0, 0.0, sky.view.width, sky.view.height);
+  sky.PictureRecorder recorder = new sky.PictureRecorder();
+  sky.Canvas canvas = new sky.Canvas(recorder, paintBounds);
+  canvas.translate(paintBounds.width / 2.0, paintBounds.height / 2.0);
   canvas.rotate(math.PI * delta / 1800);
-  canvas.drawRect(new Rect.fromLTRB(-100.0, -100.0, 100.0, 100.0),
-                  new Paint()..color = const Color.fromARGB(255, 0, 255, 0));
-  view.picture = recorder.endRecording();
-  view.scheduleFrame();
-  tracing.end('beginFrame');
+  canvas.drawRect(new sky.Rect.fromLTRB(-100.0, -100.0, 100.0, 100.0),
+                  new sky.Paint()..color = const sky.Color.fromARGB(255, 0, 255, 0));
+  sky.Picture picture = recorder.endRecording();
+
+  // composite
+  final double devicePixelRatio = sky.view.devicePixelRatio;
+  sky.Rect sceneBounds = new sky.Rect.fromLTWH(0.0, 0.0, sky.view.width * devicePixelRatio, sky.view.height * devicePixelRatio);
+  Float32List deviceTransform = new Float32List(16)
+    ..[0] = devicePixelRatio
+    ..[5] = devicePixelRatio;
+  sky.SceneBuilder sceneBuilder = new sky.SceneBuilder(sceneBounds)
+    ..pushTransform(deviceTransform)
+    ..addPicture(sky.Offset.zero, picture, paintBounds)
+    ..pop();
+  sky.view.scene = sceneBuilder.build();
+
+  sky.tracing.end('beginFrame');
 }
 
 void main() {
-  view.setFrameCallback(beginFrame);
-  view.scheduleFrame();
+  sky.view.setFrameCallback(beginFrame);
+  sky.view.scheduleFrame();
 }
diff --git a/examples/widgets/pageable_list.dart b/examples/widgets/pageable_list.dart
index eac188c..3f2085e 100644
--- a/examples/widgets/pageable_list.dart
+++ b/examples/widgets/pageable_list.dart
@@ -23,6 +23,7 @@
   List<CardModel> cardModels;
   Size pageSize = new Size(200.0, 200.0);
   ScrollDirection scrollDirection = ScrollDirection.horizontal;
+  bool itemsWrap = false;
 
   void initState() {
     List<Size> cardSizes = [
@@ -67,13 +68,18 @@
     );
   }
 
-  EventDisposition switchScrollDirection() {
+  void switchScrollDirection() {
     setState(() {
       scrollDirection = (scrollDirection == ScrollDirection.vertical)
         ? ScrollDirection.horizontal
         : ScrollDirection.vertical;
     });
-    return EventDisposition.processed;
+  }
+
+  void toggleItemsWrap() {
+    setState(() {
+      itemsWrap = !itemsWrap;
+    });
   }
 
   bool _drawerShowing = false;
@@ -113,6 +119,13 @@
           selected: scrollDirection == ScrollDirection.vertical,
           child: new Text('Vertical Layout'),
           onPressed: switchScrollDirection
+        ),
+        new DrawerItem(
+          onPressed: toggleItemsWrap,
+          child: new Row([
+            new Flexible(child: new Text('Scrolling wraps around')),
+            new Checkbox(value: itemsWrap)
+          ])
         )
       ]
     );
@@ -132,7 +145,7 @@
   Widget buildBody() {
     Widget list = new PageableList<CardModel>(
       items: cardModels,
-      itemsWrap: true,
+      itemsWrap: itemsWrap,
       itemBuilder: buildCard,
       scrollDirection: scrollDirection,
       itemExtent: (scrollDirection == ScrollDirection.vertical)
diff --git a/services/sky/compositor/BUILD.gn b/services/sky/compositor/BUILD.gn
index eb8793f..7bba856 100644
--- a/services/sky/compositor/BUILD.gn
+++ b/services/sky/compositor/BUILD.gn
@@ -4,8 +4,6 @@
 
 source_set("compositor") {
   sources = [
-    "layer.cc",
-    "layer.h",
     "layer_client.cc",
     "layer_client.h",
     "layer_host.cc",
@@ -28,6 +26,8 @@
     "surface_holder.h",
     "texture_cache.cc",
     "texture_cache.h",
+    "texture_layer.cc",
+    "texture_layer.h",
   ]
 
   deps = [
diff --git a/services/sky/compositor/layer_host.cc b/services/sky/compositor/layer_host.cc
index 9760555..f8bfb11 100644
--- a/services/sky/compositor/layer_host.cc
+++ b/services/sky/compositor/layer_host.cc
@@ -10,7 +10,7 @@
 #include "mojo/gpu/gl_context.h"
 #include "mojo/services/surfaces/public/cpp/surfaces_utils.h"
 #include "mojo/skia/ganesh_context.h"
-#include "services/sky/compositor/layer.h"
+#include "services/sky/compositor/texture_layer.h"
 
 namespace sky {
 
@@ -36,7 +36,7 @@
     BeginFrameSoon();
 }
 
-void LayerHost::SetRootLayer(scoped_refptr<Layer> layer) {
+void LayerHost::SetRootLayer(scoped_refptr<TextureLayer> layer) {
   DCHECK(!root_layer_.get());
   root_layer_ = layer;
 }
@@ -86,7 +86,7 @@
   Upload(root_layer_.get());
 }
 
-void LayerHost::Upload(Layer* layer) {
+void LayerHost::Upload(TextureLayer* layer) {
   TRACE_EVENT0("sky", "LayerHost::Upload");
 
   gfx::Size size = layer->size();
diff --git a/services/sky/compositor/layer_host.h b/services/sky/compositor/layer_host.h
index 377eb45..8527fb3 100644
--- a/services/sky/compositor/layer_host.h
+++ b/services/sky/compositor/layer_host.h
@@ -16,7 +16,7 @@
 
 namespace sky {
 class ResourceManager;
-class Layer;
+class TextureLayer;
 class LayerHostClient;
 
 class LayerHost : public SurfaceHolder::Client {
@@ -39,7 +39,7 @@
   }
 
   void SetNeedsAnimate();
-  void SetRootLayer(scoped_refptr<Layer> layer);
+  void SetRootLayer(scoped_refptr<TextureLayer> layer);
 
  private:
   enum State {
@@ -55,7 +55,7 @@
   void BeginFrameSoon();
   void BeginFrame();
 
-  void Upload(Layer* layer);
+  void Upload(TextureLayer* layer);
   void DidCompleteFrame();
 
   LayerHostClient* client_;
@@ -65,7 +65,7 @@
   mojo::GLContextOwner gl_context_owner_;
   mojo::GaneshContext ganesh_context_;
   ResourceManager resource_manager_;
-  scoped_refptr<Layer> root_layer_;
+  scoped_refptr<TextureLayer> root_layer_;
 
   base::WeakPtrFactory<LayerHost> weak_factory_;
 
diff --git a/services/sky/compositor/resource_manager.cc b/services/sky/compositor/resource_manager.cc
index 9fe69b2..4223f52 100644
--- a/services/sky/compositor/resource_manager.cc
+++ b/services/sky/compositor/resource_manager.cc
@@ -15,7 +15,7 @@
 #include "mojo/converters/geometry/geometry_type_converters.h"
 #include "mojo/gpu/gl_context.h"
 #include "mojo/gpu/gl_texture.h"
-#include "services/sky/compositor/layer.h"
+#include "services/sky/compositor/texture_layer.h"
 
 namespace sky {
 
@@ -39,7 +39,7 @@
 }
 
 mojo::TransferableResourcePtr ResourceManager::CreateTransferableResource(
-    Layer* layer) {
+    TextureLayer* layer) {
   scoped_ptr<mojo::GLTexture> texture = layer->GetTexture();
   mojo::Size size = texture->size();
 
diff --git a/services/sky/compositor/resource_manager.h b/services/sky/compositor/resource_manager.h
index 0f7bcb2..ad709dd 100644
--- a/services/sky/compositor/resource_manager.h
+++ b/services/sky/compositor/resource_manager.h
@@ -22,8 +22,8 @@
 }
 
 namespace sky {
-class Layer;
 class LayerHost;
+class TextureLayer;
 
 class ResourceManager {
  public:
@@ -32,7 +32,7 @@
 
   scoped_ptr<mojo::GLTexture> CreateTexture(const gfx::Size& size);
 
-  mojo::TransferableResourcePtr CreateTransferableResource(Layer* layer);
+  mojo::TransferableResourcePtr CreateTransferableResource(TextureLayer* layer);
   void ReturnResources(mojo::Array<mojo::ReturnedResourcePtr> resources);
 
  private:
diff --git a/services/sky/compositor/layer.cc b/services/sky/compositor/texture_layer.cc
similarity index 78%
rename from services/sky/compositor/layer.cc
rename to services/sky/compositor/texture_layer.cc
index 0523b23..ead0c3d 100644
--- a/services/sky/compositor/layer.cc
+++ b/services/sky/compositor/texture_layer.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/sky/compositor/layer.h"
+#include "services/sky/compositor/texture_layer.h"
 
 #include "base/trace_event/trace_event.h"
 #include "services/sky/compositor/layer_host.h"
@@ -15,17 +15,17 @@
 
 namespace sky {
 
-Layer::Layer(LayerClient* client) : client_(client) {
+TextureLayer::TextureLayer(LayerClient* client) : client_(client) {
 }
 
-Layer::~Layer() {
+TextureLayer::~TextureLayer() {
 }
 
-void Layer::SetSize(const gfx::Size& size) {
+void TextureLayer::SetSize(const gfx::Size& size) {
   size_ = size;
 }
 
-void Layer::Display() {
+void TextureLayer::Display() {
   TRACE_EVENT0("sky", "Layer::Display");
   DCHECK(rasterizer_);
   RefPtr<SkPicture> picture = RecordPicture();
@@ -38,7 +38,7 @@
   texture_ = rasterizer_->Rasterize(picture.get());
 }
 
-PassRefPtr<SkPicture> Layer::RecordPicture() {
+PassRefPtr<SkPicture> TextureLayer::RecordPicture() {
   TRACE_EVENT0("sky", "Layer::RecordPicture");
 
   SkRTreeFactory factory;
@@ -52,7 +52,7 @@
   return adoptRef(recorder.endRecordingAsPicture());
 }
 
-scoped_ptr<mojo::GLTexture> Layer::GetTexture() {
+scoped_ptr<mojo::GLTexture> TextureLayer::GetTexture() {
   return texture_.Pass();
 }
 
diff --git a/services/sky/compositor/layer.h b/services/sky/compositor/texture_layer.h
similarity index 72%
rename from services/sky/compositor/layer.h
rename to services/sky/compositor/texture_layer.h
index 3bf540c..0bee97c 100644
--- a/services/sky/compositor/layer.h
+++ b/services/sky/compositor/texture_layer.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SKY_VIEWER_COMPOSITOR_LAYER_H_
-#define SKY_VIEWER_COMPOSITOR_LAYER_H_
+#ifndef SKY_VIEWER_COMPOSITOR_TEXTURE_LAYER_H_
+#define SKY_VIEWER_COMPOSITOR_TEXTURE_LAYER_H_
 
 #include "base/memory/ref_counted.h"
 #include "mojo/gpu/gl_texture.h"
@@ -17,9 +17,9 @@
 
 class LayerHost;
 
-class Layer : public base::RefCounted<Layer> {
+class TextureLayer : public base::RefCounted<TextureLayer> {
  public:
-  explicit Layer(LayerClient* client);
+  explicit TextureLayer(LayerClient* client);
 
   void SetSize(const gfx::Size& size);
   void Display();
@@ -33,8 +33,8 @@
   }
 
  private:
-  friend class base::RefCounted<Layer>;
-  ~Layer();
+  friend class base::RefCounted<TextureLayer>;
+  ~TextureLayer();
 
   PassRefPtr<SkPicture> RecordPicture();
 
@@ -43,9 +43,9 @@
   scoped_ptr<mojo::GLTexture> texture_;
   scoped_ptr<Rasterizer> rasterizer_;
 
-  DISALLOW_COPY_AND_ASSIGN(Layer);
+  DISALLOW_COPY_AND_ASSIGN(TextureLayer);
 };
 
 }  // namespace sky
 
-#endif  // SKY_VIEWER_COMPOSITOR_LAYER_H_
+#endif  // SKY_VIEWER_COMPOSITOR_TEXTURE_LAYER_H_
diff --git a/services/sky/document_view.cc b/services/sky/document_view.cc
index 15787e7..8ce55d3 100644
--- a/services/sky/document_view.cc
+++ b/services/sky/document_view.cc
@@ -19,10 +19,10 @@
 #include "mojo/services/view_manager/public/cpp/view_manager.h"
 #include "mojo/services/view_manager/public/interfaces/view_manager.mojom.h"
 #include "services/asset_bundle/asset_unpacker_job.h"
-#include "services/sky/compositor/layer.h"
 #include "services/sky/compositor/layer_host.h"
 #include "services/sky/compositor/rasterizer_bitmap.h"
 #include "services/sky/compositor/rasterizer_ganesh.h"
+#include "services/sky/compositor/texture_layer.h"
 #include "services/sky/converters/input_event_types.h"
 #include "services/sky/dart_library_provider_impl.h"
 #include "services/sky/internals.h"
@@ -149,7 +149,7 @@
 void DocumentView::Load(mojo::URLResponsePtr response) {
   sky_view_ = blink::SkyView::Create(this);
   layer_host_.reset(new LayerHost(this));
-  root_layer_ = make_scoped_refptr(new Layer(this));
+  root_layer_ = make_scoped_refptr(new TextureLayer(this));
   root_layer_->set_rasterizer(CreateRasterizer());
   layer_host_->SetRootLayer(root_layer_);
 
diff --git a/services/sky/document_view.h b/services/sky/document_view.h
index c6c89a1..e67faa9 100644
--- a/services/sky/document_view.h
+++ b/services/sky/document_view.h
@@ -33,11 +33,11 @@
 }
 
 namespace sky {
+class DartLibraryProviderImpl;
+class LayerHost;
 class Rasterizer;
 class RasterizerBitmap;
-class Layer;
-class LayerHost;
-class DartLibraryProviderImpl;
+class TextureLayer;
 
 class DocumentView : public blink::ServiceProvider,
                      public blink::SkyViewClient,
@@ -127,7 +127,7 @@
   mojo::ViewManagerClientFactory view_manager_client_factory_;
   scoped_ptr<DartLibraryProviderImpl> library_provider_;
   scoped_ptr<LayerHost> layer_host_;
-  scoped_refptr<Layer> root_layer_;
+  scoped_refptr<TextureLayer> root_layer_;
   RasterizerBitmap* bitmap_rasterizer_;  // Used for pixel tests.
   mojo::ServiceRegistryPtr service_registry_;
   scoped_ptr<mojo::StrongBinding<mojo::ServiceProvider>>
diff --git a/sky/compositor/BUILD.gn b/sky/compositor/BUILD.gn
new file mode 100644
index 0000000..ffce6c0
--- /dev/null
+++ b/sky/compositor/BUILD.gn
@@ -0,0 +1,16 @@
+# 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.
+
+source_set("compositor") {
+  sources = [
+    "layer.cc",
+    "layer.h",
+  ]
+
+  deps = [
+    "//base",
+    "//skia",
+    "//sky/engine/wtf",
+  ]
+}
diff --git a/sky/compositor/layer.cc b/sky/compositor/layer.cc
new file mode 100644
index 0000000..d43bbcd
--- /dev/null
+++ b/sky/compositor/layer.cc
@@ -0,0 +1,130 @@
+// 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.
+
+#include "sky/compositor/layer.h"
+
+#include "third_party/skia/include/core/SkColorFilter.h"
+
+namespace sky {
+
+Layer::Layer() {
+}
+
+Layer::~Layer() {
+}
+
+PictureLayer::PictureLayer() {
+}
+
+PictureLayer::~PictureLayer() {
+}
+
+void PictureLayer::Paint(SkCanvas* canvas) {
+  canvas->save();
+  canvas->translate(offset_.x(), offset_.y());
+  canvas->drawPicture(picture_.get());
+  canvas->restore();
+}
+
+ContainerLayer::ContainerLayer() {
+}
+
+ContainerLayer::~ContainerLayer() {
+}
+
+void ContainerLayer::Add(std::unique_ptr<Layer> layer) {
+  layer->set_parent(this);
+  layers_.push_back(std::move(layer));
+}
+
+void ContainerLayer::PaintChildren(SkCanvas* canvas) {
+  for (auto& layer : layers_)
+    layer->Paint(canvas);
+}
+
+TransformLayer::TransformLayer() {
+}
+
+TransformLayer::~TransformLayer() {
+}
+
+void TransformLayer::Paint(SkCanvas* canvas) {
+  canvas->save();
+  canvas->concat(transform_);
+  PaintChildren(canvas);
+  canvas->restore();
+}
+
+ClipRectLayer::ClipRectLayer() {
+}
+
+ClipRectLayer::~ClipRectLayer() {
+}
+
+void ClipRectLayer::Paint(SkCanvas* canvas) {
+  canvas->save();
+  canvas->clipRect(clip_rect_);
+  PaintChildren(canvas);
+  canvas->restore();
+}
+
+ClipRRectLayer::ClipRRectLayer() {
+}
+
+ClipRRectLayer::~ClipRRectLayer() {
+}
+
+void ClipRRectLayer::Paint(SkCanvas* canvas) {
+  canvas->saveLayer(&clip_rrect_.getBounds(), nullptr);
+  canvas->clipRRect(clip_rrect_);
+  PaintChildren(canvas);
+  canvas->restore();
+}
+
+ClipPathLayer::ClipPathLayer() {
+}
+
+ClipPathLayer::~ClipPathLayer() {
+}
+
+void ClipPathLayer::Paint(SkCanvas* canvas) {
+  canvas->saveLayer(&clip_path_.getBounds(), nullptr);
+  canvas->clipPath(clip_path_);
+  PaintChildren(canvas);
+  canvas->restore();
+}
+
+OpacityLayer::OpacityLayer() {
+}
+
+OpacityLayer::~OpacityLayer() {
+}
+
+void OpacityLayer::Paint(SkCanvas* canvas) {
+  SkColor color = SkColorSetARGB(alpha_, 0, 0, 0);
+  RefPtr<SkColorFilter> colorFilter = adoptRef(SkColorFilter::CreateModeFilter(color, SkXfermode::kSrcOver_Mode));
+  SkPaint paint;
+  paint.setColorFilter(colorFilter.get());
+  canvas->saveLayer(&paint_bounds(), &paint);
+  PaintChildren(canvas);
+  canvas->restore();
+}
+
+ColorFilterLayer::ColorFilterLayer() {
+}
+
+ColorFilterLayer::~ColorFilterLayer() {
+}
+
+void ColorFilterLayer::Paint(SkCanvas* canvas) {
+  RefPtr<SkColorFilter> color_filter =
+      adoptRef(SkColorFilter::CreateModeFilter(color_, transfer_mode_));
+  SkPaint paint;
+  paint.setColorFilter(color_filter.get());
+  canvas->saveLayer(&paint_bounds(), &paint);
+  PaintChildren(canvas);
+  canvas->restore();
+}
+
+}  // namespace sky
diff --git a/sky/compositor/layer.h b/sky/compositor/layer.h
new file mode 100644
index 0000000..6dcd36e
--- /dev/null
+++ b/sky/compositor/layer.h
@@ -0,0 +1,173 @@
+// 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.
+
+#ifndef SKY_COMPOSITOR_H_
+#define SKY_COMPOSITOR_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "sky/engine/wtf/PassRefPtr.h"
+#include "sky/engine/wtf/RefPtr.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkColorFilter.h"
+#include "third_party/skia/include/core/SkMatrix.h"
+#include "third_party/skia/include/core/SkPath.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "third_party/skia/include/core/SkRRect.h"
+#include "third_party/skia/include/core/SkXfermode.h"
+
+namespace sky {
+class ContainerLayer;
+
+class Layer {
+ public:
+  Layer();
+  virtual ~Layer();
+
+  virtual void Paint(SkCanvas* canvas) = 0;
+
+  ContainerLayer* parent() const { return parent_; }
+  void set_parent(ContainerLayer* parent) { parent_ = parent; }
+
+  const SkRect& paint_bounds() const { return paint_bounds_; }
+  void set_paint_bounds(const SkRect& paint_bounds) { paint_bounds_ = paint_bounds; }
+
+ private:
+  ContainerLayer* parent_;
+  SkRect paint_bounds_;
+
+  DISALLOW_COPY_AND_ASSIGN(Layer);
+};
+
+class PictureLayer : public Layer {
+ public:
+  PictureLayer();
+  ~PictureLayer() override;
+
+  void Paint(SkCanvas* canvas) override;
+
+  void set_offset(const SkPoint& offset) { offset_ = offset; }
+  void set_picture(PassRefPtr<SkPicture> picture) { picture_ = picture; }
+
+ private:
+  SkPoint offset_;
+  RefPtr<SkPicture> picture_;
+
+  DISALLOW_COPY_AND_ASSIGN(PictureLayer);
+};
+
+class ContainerLayer : public Layer {
+ public:
+  ContainerLayer();
+  ~ContainerLayer() override;
+
+  void Add(std::unique_ptr<Layer> layer);
+
+  void PaintChildren(SkCanvas* canvas);
+
+ private:
+  std::vector<std::unique_ptr<Layer>> layers_;
+
+  DISALLOW_COPY_AND_ASSIGN(ContainerLayer);
+};
+
+class TransformLayer : public ContainerLayer {
+ public:
+  TransformLayer();
+  ~TransformLayer() override;
+
+  void Paint(SkCanvas* canvas) override;
+
+  void set_transform(const SkMatrix& transform) { transform_ = transform; }
+
+ private:
+  SkMatrix transform_;
+
+  DISALLOW_COPY_AND_ASSIGN(TransformLayer);
+};
+
+class ClipRectLayer : public ContainerLayer {
+ public:
+  ClipRectLayer();
+  ~ClipRectLayer() override;
+
+  void Paint(SkCanvas* canvas) override;
+
+  void set_clip_rect(const SkRect& clip_rect) { clip_rect_ = clip_rect; }
+
+ private:
+  SkRect clip_rect_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClipRectLayer);
+};
+
+class ClipRRectLayer : public ContainerLayer {
+ public:
+  ClipRRectLayer();
+  ~ClipRRectLayer() override;
+
+  void Paint(SkCanvas* canvas) override;
+
+  void set_clip_rrect(const SkRRect& clip_rrect) { clip_rrect_ = clip_rrect; }
+
+ private:
+  SkRRect clip_rrect_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClipRRectLayer);
+};
+
+class ClipPathLayer : public ContainerLayer {
+ public:
+  ClipPathLayer();
+  ~ClipPathLayer() override;
+
+  void Paint(SkCanvas* canvas) override;
+
+  void set_clip_path(const SkPath& clip_path) { clip_path_ = clip_path; }
+
+ private:
+  SkPath clip_path_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClipPathLayer);
+};
+
+class OpacityLayer : public ContainerLayer {
+ public:
+  OpacityLayer();
+  ~OpacityLayer() override;
+
+  void Paint(SkCanvas* canvas) override;
+
+  void set_alpha(int alpha) { alpha_ = alpha; }
+
+ private:
+  int alpha_;
+
+  DISALLOW_COPY_AND_ASSIGN(OpacityLayer);
+};
+
+class ColorFilterLayer : public ContainerLayer {
+ public:
+  ColorFilterLayer();
+  ~ColorFilterLayer() override;
+
+  void Paint(SkCanvas* canvas) override;
+
+  void set_color(SkColor color) { color_ = color; }
+  void set_transfer_mode(SkXfermode::Mode transfer_mode) { transfer_mode_ = transfer_mode; }
+
+ private:
+  SkColor color_;
+  SkXfermode::Mode transfer_mode_;
+
+  DISALLOW_COPY_AND_ASSIGN(ColorFilterLayer);
+};
+
+}  // namespace sky
+
+#endif  // SKY_COMPOSITOR_H_
diff --git a/sky/engine/core/BUILD.gn b/sky/engine/core/BUILD.gn
index 45103f2..7922cb1 100644
--- a/sky/engine/core/BUILD.gn
+++ b/sky/engine/core/BUILD.gn
@@ -21,6 +21,7 @@
     "//mojo/public/cpp/utility",
     "//mojo/public/interfaces/application",
     "//skia",
+    "//sky/compositor",
     "//sky/engine/tonic:tonic",
     "//sky/engine/wtf",
     "//third_party/iccjpeg",
diff --git a/sky/engine/core/compositing/Scene.cpp b/sky/engine/core/compositing/Scene.cpp
index 0845867..d58883f 100644
--- a/sky/engine/core/compositing/Scene.cpp
+++ b/sky/engine/core/compositing/Scene.cpp
@@ -4,16 +4,19 @@
 
 #include "sky/engine/core/compositing/Scene.h"
 
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkPictureRecorder.h"
+
 namespace blink {
 
-PassRefPtr<Scene> Scene::create(PassRefPtr<SkPicture> picture)
+PassRefPtr<Scene> Scene::create(std::unique_ptr<sky::Layer> rootLayer)
 {
-    ASSERT(picture);
-    return adoptRef(new Scene(picture));
+    ASSERT(rootLayer);
+    return adoptRef(new Scene(std::move(rootLayer)));
 }
 
-Scene::Scene(PassRefPtr<SkPicture> picture)
-    : m_picture(picture)
+Scene::Scene(std::unique_ptr<sky::Layer> rootLayer)
+    : m_rootLayer(std::move(rootLayer))
 {
 }
 
@@ -21,4 +24,14 @@
 {
 }
 
+PassRefPtr<SkPicture> Scene::createPicture() const
+{
+    SkRTreeFactory rtreeFactory;
+    SkPictureRecorder pictureRecorder;
+    SkCanvas* canvas = pictureRecorder.beginRecording(m_rootLayer->paint_bounds(),
+        &rtreeFactory, SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag);
+    m_rootLayer->Paint(canvas);
+    return adoptRef(pictureRecorder.endRecording());
+}
+
 } // namespace blink
diff --git a/sky/engine/core/compositing/Scene.h b/sky/engine/core/compositing/Scene.h
index 8d3a5ac..af2347f 100644
--- a/sky/engine/core/compositing/Scene.h
+++ b/sky/engine/core/compositing/Scene.h
@@ -5,6 +5,9 @@
 #ifndef SKY_ENGINE_CORE_COMPOSITING_SCENE_H_
 #define SKY_ENGINE_CORE_COMPOSITING_SCENE_H_
 
+#include <memory>
+
+#include "sky/compositor/layer.h"
 #include "sky/engine/tonic/dart_wrappable.h"
 #include "sky/engine/wtf/PassRefPtr.h"
 #include "sky/engine/wtf/RefCounted.h"
@@ -16,16 +19,16 @@
     DEFINE_WRAPPERTYPEINFO();
 public:
     ~Scene() override;
-    static PassRefPtr<Scene> create(PassRefPtr<SkPicture> skPicture);
-
-    SkPicture* toSkia() const { return m_picture.get(); }
-
-private:
-    explicit Scene(PassRefPtr<SkPicture> skPicture);
+    static PassRefPtr<Scene> create(std::unique_ptr<sky::Layer> rootLayer);
 
     // While bootstraping the compositing system, we use an SkPicture instead
     // of a layer tree to back the scene.
-    RefPtr<SkPicture> m_picture;
+    PassRefPtr<SkPicture> createPicture() const;
+
+private:
+    explicit Scene(std::unique_ptr<sky::Layer> rootLayer);
+
+    std::unique_ptr<sky::Layer> m_rootLayer;
 };
 
 } // namespace blink
diff --git a/sky/engine/core/compositing/SceneBuilder.cpp b/sky/engine/core/compositing/SceneBuilder.cpp
index 7e10ca3..82def61 100644
--- a/sky/engine/core/compositing/SceneBuilder.cpp
+++ b/sky/engine/core/compositing/SceneBuilder.cpp
@@ -10,9 +10,9 @@
 namespace blink {
 
 SceneBuilder::SceneBuilder(const Rect& bounds)
+    : m_rootPaintBounds(bounds.sk_rect)
+    , m_currentLayer(nullptr)
 {
-    m_canvas = m_pictureRecorder.beginRecording(bounds.sk_rect,
-        &m_rtreeFactory, SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag);
 }
 
 SceneBuilder::~SceneBuilder()
@@ -24,77 +24,88 @@
     SkMatrix sk_matrix = toSkMatrix(matrix4, es);
     if (es.had_exception())
         return;
-    m_canvas->save();
-    m_canvas->concat(sk_matrix);
+    std::unique_ptr<sky::TransformLayer> layer(new sky::TransformLayer());
+    layer->set_transform(sk_matrix);
+    addLayer(std::move(layer));
 }
 
 void SceneBuilder::pushClipRect(const Rect& rect)
 {
-    if (!m_canvas)
-        return;
-    m_canvas->save();
-    m_canvas->clipRect(rect.sk_rect);
+    std::unique_ptr<sky::ClipRectLayer> layer(new sky::ClipRectLayer());
+    layer->set_clip_rect(rect.sk_rect);
+    addLayer(std::move(layer));
 }
 
 void SceneBuilder::pushClipRRect(const RRect* rrect, const Rect& bounds)
 {
-    if (!m_canvas)
-        return;
-    m_canvas->saveLayer(&bounds.sk_rect, nullptr);
-    m_canvas->clipRRect(rrect->rrect());
+    std::unique_ptr<sky::ClipRRectLayer> layer(new sky::ClipRRectLayer());
+    layer->set_clip_rrect(rrect->rrect());
+    addLayer(std::move(layer));
 }
 
 void SceneBuilder::pushClipPath(const CanvasPath* path, const Rect& bounds)
 {
-    if (!m_canvas)
-        return;
-    m_canvas->saveLayer(&bounds.sk_rect, nullptr);
-    m_canvas->clipPath(path->path());
+    std::unique_ptr<sky::ClipPathLayer> layer(new sky::ClipPathLayer());
+    layer->set_clip_path(path->path());
+    addLayer(std::move(layer));
 }
 
 void SceneBuilder::pushOpacity(int alpha, const Rect& bounds)
 {
-    if (!m_canvas)
-        return;
-    SkColor color = SkColorSetARGB(alpha, 0, 0, 0);
-    RefPtr<SkColorFilter> colorFilter = adoptRef(SkColorFilter::CreateModeFilter(color, SkXfermode::kSrcOver_Mode));
-    SkPaint paint;
-    paint.setColorFilter(colorFilter.get());
-    m_canvas->saveLayer(&bounds.sk_rect, &paint);
+    std::unique_ptr<sky::OpacityLayer> layer(new sky::OpacityLayer());
+    layer->set_paint_bounds(bounds.sk_rect);
+    layer->set_alpha(alpha);
+    addLayer(std::move(layer));
 }
 
 void SceneBuilder::pushColorFilter(SkColor color, SkXfermode::Mode transferMode, const Rect& bounds)
 {
-    if (!m_canvas)
+    std::unique_ptr<sky::ColorFilterLayer> layer(new sky::ColorFilterLayer());
+    layer->set_paint_bounds(bounds.sk_rect);
+    layer->set_color(color);
+    layer->set_transfer_mode(transferMode);
+    addLayer(std::move(layer));
+}
+
+void SceneBuilder::addLayer(std::unique_ptr<sky::ContainerLayer> layer)
+{
+    DCHECK(layer);
+    if (!m_rootLayer) {
+        DCHECK(!m_currentLayer);
+        m_rootLayer = std::move(layer);
+        m_rootLayer->set_paint_bounds(m_rootPaintBounds);
+        m_currentLayer = m_rootLayer.get();
         return;
-    RefPtr<SkColorFilter> colorFilter = adoptRef(SkColorFilter::CreateModeFilter(color, transferMode));
-    SkPaint paint;
-    paint.setColorFilter(colorFilter.get());
-    m_canvas->saveLayer(&bounds.sk_rect, &paint);
+    }
+    if (!m_currentLayer)
+        return;
+    sky::ContainerLayer* newLayer = layer.get();
+    m_currentLayer->Add(std::move(layer));
+    m_currentLayer = newLayer;
 }
 
 void SceneBuilder::pop()
 {
-    if (!m_canvas)
+    if (!m_currentLayer)
         return;
-    m_canvas->restore();
+    m_currentLayer = m_currentLayer->parent();
 }
 
 void SceneBuilder::addPicture(const Offset& offset, Picture* picture, const Rect& paintBounds)
 {
-    if (!m_canvas)
+    if (!m_currentLayer)
         return;
-    m_canvas->save();
-    m_canvas->translate(offset.sk_size.width(), offset.sk_size.height());
-    m_canvas->drawPicture(picture->toSkia());
-    m_canvas->restore();
+    std::unique_ptr<sky::PictureLayer> layer(new sky::PictureLayer());
+    layer->set_offset(SkPoint::Make(offset.sk_size.width(), offset.sk_size.height()));
+    layer->set_picture(picture->toSkia());
+    layer->set_paint_bounds(paintBounds.sk_rect);
+    m_currentLayer->Add(std::move(layer));
 }
 
 PassRefPtr<Scene> SceneBuilder::build()
 {
-    RefPtr<Scene> scene = Scene::create(adoptRef(m_pictureRecorder.endRecording()));
-    m_canvas = nullptr;
-    return scene.release();
+    m_currentLayer = nullptr;
+    return Scene::create(std::move(m_rootLayer));
 }
 
 } // namespace blink
diff --git a/sky/engine/core/compositing/SceneBuilder.h b/sky/engine/core/compositing/SceneBuilder.h
index d8facf6..099d1f5 100644
--- a/sky/engine/core/compositing/SceneBuilder.h
+++ b/sky/engine/core/compositing/SceneBuilder.h
@@ -5,6 +5,9 @@
 #ifndef SKY_ENGINE_CORE_COMPOSITING_SCENEBUILDER_H_
 #define SKY_ENGINE_CORE_COMPOSITING_SCENEBUILDER_H_
 
+#include <memory>
+
+#include "sky/compositor/layer.h"
 #include "sky/engine/bindings/exception_state.h"
 #include "sky/engine/core/compositing/Scene.h"
 #include "sky/engine/core/painting/CanvasPath.h"
@@ -19,8 +22,6 @@
 #include "sky/engine/tonic/float32_list.h"
 #include "sky/engine/wtf/PassRefPtr.h"
 #include "sky/engine/wtf/RefCounted.h"
-#include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkPictureRecorder.h"
 
 namespace blink {
 
@@ -47,9 +48,11 @@
 private:
     explicit SceneBuilder(const Rect& bounds);
 
-    SkRTreeFactory m_rtreeFactory;
-    SkPictureRecorder m_pictureRecorder;
-    SkCanvas* m_canvas;
+    void addLayer(std::unique_ptr<sky::ContainerLayer> layer);
+
+    SkRect m_rootPaintBounds;
+    std::unique_ptr<sky::ContainerLayer> m_rootLayer;
+    sky::ContainerLayer* m_currentLayer;
 };
 
 } // namespace blink
diff --git a/sky/engine/public/sky/sky_view.cc b/sky/engine/public/sky/sky_view.cc
index 3122ce4..de5cc2d 100644
--- a/sky/engine/public/sky/sky_view.cc
+++ b/sky/engine/public/sky/sky_view.cc
@@ -72,7 +72,7 @@
 
 PassRefPtr<SkPicture> SkyView::Paint() {
   if (Scene* scene = view_->scene())
-    return scene->toSkia();
+    return scene->createPicture();
   if (Picture* picture = view_->picture())
     return picture->toSkia();
   return nullptr;
diff --git a/sky/packages/sky/README.md b/sky/packages/sky/README.md
index 5deef2d..f28ac8a 100644
--- a/sky/packages/sky/README.md
+++ b/sky/packages/sky/README.md
@@ -4,7 +4,7 @@
 Sky apps are written in Dart. To get started, we need to set up Dart SDK:
 
  - Install the [Dart SDK](https://www.dartlang.org/downloads/):
-   - Mac: `brew tap dart-lang/dart && brew install dart`
+   - Mac: `brew tap dart-lang/dart && brew install dart --devel`
    - Linux: See [https://www.dartlang.org/downloads/linux.html](https://www.dartlang.org/downloads/linux.html)
  - Ensure that `$DART_SDK` is set to the path of your Dart SDK and that the
    `dart` and `pub` executables are on your `$PATH`.
@@ -57,6 +57,8 @@
  - Install the `adb` tool from the [Android SDK](https://developer.android.com/sdk/installing/index.html?pkg=tools):
   - Mac: `brew install android-platform-tools`
   - Linux: `sudo apt-get install android-tools-adb`
+    - If the version of `adb` provided by your Linux distribution is too old,
+      you might need to [install the Android SDK manually](https://developer.android.com/sdk/installing/index.html?pkg=tools]).
 
  - Enable developer mode on your device by visiting `Settings > About phone`
    and tapping the `Build number` field five times.
diff --git a/sky/packages/sky/lib/editing/editable_text.dart b/sky/packages/sky/lib/editing/editable_text.dart
index 2fb6dee..3a2da19 100644
--- a/sky/packages/sky/lib/editing/editable_text.dart
+++ b/sky/packages/sky/lib/editing/editable_text.dart
@@ -7,9 +7,9 @@
 
 import 'package:sky/editing/editable_string.dart';
 import 'package:sky/painting/text_style.dart';
-import 'package:sky/rendering/object.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/rendering/object.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/framework.dart';
 
 const _kCursorBlinkPeriod = 500; // milliseconds
 const _kCursorGap = 1.0;
diff --git a/sky/packages/sky/lib/editing/input.dart b/sky/packages/sky/lib/editing/input.dart
index 632e467..a253c1a 100644
--- a/sky/packages/sky/lib/editing/input.dart
+++ b/sky/packages/sky/lib/editing/input.dart
@@ -6,10 +6,10 @@
 import 'package:sky/editing/editable_text.dart';
 import 'package:sky/mojo/keyboard.dart';
 import 'package:sky/painting/text_style.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/focus.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/theme.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/focus.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/theme.dart';
 
 export 'package:sky/mojo/keyboard.dart' show KeyboardType_TEXT, KeyboardType_NUMBER, KeyboardType_PHONE, KeyboardType_DATETIME;
 
diff --git a/sky/packages/sky/lib/mojo/activity.dart b/sky/packages/sky/lib/mojo/activity.dart
index f193638..1e591d0 100644
--- a/sky/packages/sky/lib/mojo/activity.dart
+++ b/sky/packages/sky/lib/mojo/activity.dart
@@ -50,7 +50,7 @@
 
   TaskDescription description = new TaskDescription()
     ..label = label
-    ..primaryColor = (color != null ? color.value : null);
+    ..primaryColor = color?.value;
 
   _activityProxy.ptr.setTaskDescription(description);
 }
diff --git a/sky/packages/sky/lib/rendering.dart b/sky/packages/sky/lib/rendering.dart
index 4755544..52db6c6 100644
--- a/sky/packages/sky/lib/rendering.dart
+++ b/sky/packages/sky/lib/rendering.dart
@@ -7,21 +7,21 @@
 /// The Sky render tree
 library rendering;
 
-export 'package:sky/rendering/auto_layout.dart';
-export 'package:sky/rendering/block.dart';
-export 'package:sky/rendering/box.dart';
-export 'package:sky/rendering/flex.dart';
-export 'package:sky/rendering/grid.dart';
-export 'package:sky/rendering/image.dart';
-export 'package:sky/rendering/layer.dart';
-export 'package:sky/rendering/object.dart';
-export 'package:sky/rendering/paragraph.dart';
-export 'package:sky/rendering/proxy_box.dart';
-export 'package:sky/rendering/shifted_box.dart';
-export 'package:sky/rendering/sky_binding.dart';
-export 'package:sky/rendering/stack.dart';
-export 'package:sky/rendering/toggleable.dart';
-export 'package:sky/rendering/view.dart';
-export 'package:sky/rendering/viewport.dart';
+export 'package:sky/src/rendering/auto_layout.dart';
+export 'package:sky/src/rendering/block.dart';
+export 'package:sky/src/rendering/box.dart';
+export 'package:sky/src/rendering/flex.dart';
+export 'package:sky/src/rendering/grid.dart';
+export 'package:sky/src/rendering/image.dart';
+export 'package:sky/src/rendering/layer.dart';
+export 'package:sky/src/rendering/object.dart';
+export 'package:sky/src/rendering/paragraph.dart';
+export 'package:sky/src/rendering/proxy_box.dart';
+export 'package:sky/src/rendering/shifted_box.dart';
+export 'package:sky/src/rendering/sky_binding.dart';
+export 'package:sky/src/rendering/stack.dart';
+export 'package:sky/src/rendering/toggleable.dart';
+export 'package:sky/src/rendering/view.dart';
+export 'package:sky/src/rendering/viewport.dart';
 
 export 'package:vector_math/vector_math.dart' show Matrix4;
diff --git a/sky/packages/sky/lib/sky_tool b/sky/packages/sky/lib/sky_tool
index 1e78345..54c7d4e 100755
--- a/sky/packages/sky/lib/sky_tool
+++ b/sky/packages/sky/lib/sky_tool
@@ -9,6 +9,7 @@
 import hashlib
 import json
 import logging
+import multiprocessing
 import os
 import platform
 import random
@@ -146,6 +147,46 @@
     return 'http://localhost:%s/%s' % (port, relative_path)
 
 
+class SkyLogs(object):
+    def add_subparser(self, subparsers):
+        logs_parser = subparsers.add_parser('logs',
+            help='Show logs for running Sky apps')
+        logs_parser.set_defaults(func=self.run)
+
+    def run(self, args, pids):
+        android_log_reader = None
+        ios_dev_log_reader = None
+        ios_sim_log_reader = None
+
+        android = AndroidDevice()
+        if android.is_connected():
+            android_log_reader = android.logs()
+
+        if IOSDevice.is_connected():
+            ios_dev_log_reader = IOSDevice.logs()
+
+        if IOSSimulator.is_connected():
+            ios_sim_log_reader = IOSSimulator.logs()
+
+        if android_log_reader is not None:
+            try:
+                android_log_reader.join()
+            except KeyboardInterrupt:
+                pass
+
+        if ios_dev_log_reader is not None:
+            try:
+                ios_dev_log_reader.join()
+            except KeyboardInterrupt:
+                pass
+
+        if ios_sim_log_reader is not None:
+            try:
+                ios_sim_log_reader.join()
+            except KeyboardInterrupt:
+                pass
+
+
 class InstallSky(object):
     def add_subparser(self, subparsers):
         install_parser = subparsers.add_parser('install',
@@ -447,6 +488,27 @@
         logging.info(' '.join(cmd))
         subprocess.check_output(cmd)
 
+    def logs(self):
+        def do_logs():
+            cmd = [
+                self.adb_path,
+                'logcat',
+                '-s',
+                'sky',
+                'chromium',
+            ]
+            logging.info(' '.join(cmd))
+            log_process = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE)
+            while True:
+                try:
+                    sys.stdout.write('ANDROID: ' + log_process.stdout.readline())
+                    sys.stdout.flush()
+                except KeyboardInterrupt:
+                    break
+        log_reader = multiprocessing.Process(target=do_logs)
+        log_reader.daemon = True
+        log_reader.start()
+        return log_reader
 
 
 class IOSDevice(object):
@@ -540,6 +602,38 @@
         except subprocess.CalledProcessError:
             pass
 
+    @classmethod
+    def logs(cls):
+        try:
+            cmd = [
+                'which',
+                'idevicesyslog'
+            ]
+            logging.info(' '.join(cmd))
+            subprocess.check_call(cmd)
+        except subprocess.CalledProcessError:
+            logging.error('"log" command only works with iOS devices if you have installed idevicesyslog. Run "brew install libimobiledevice" to install it with homebrew.')
+            return None
+
+        def do_logs():
+            cmd = [
+                'idevicesyslog',
+            ]
+            logging.info(' '.join(cmd))
+            log_process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+            while True:
+                try:
+                    log_line = log_process.stdout.readline()
+                    if re.match(r'.*SkyShell.*', log_line) is not None:
+                        sys.stdout.write('IOS DEV: ' + log_line)
+                        sys.stdout.flush()
+                except KeyboardInterrupt:
+                    break
+        log_reader = multiprocessing.Process(target=do_logs)
+        log_reader.daemon = True
+        log_reader.start()
+        return log_reader
+
 
 class IOSSimulator(object):
     @classmethod
@@ -548,6 +642,10 @@
             return False
         return cls.get_simulator_device_id() is not None
 
+    @classmethod
+    def is_connected(cls):
+        return cls.is_booted()
+
     _device_id = None
     @classmethod
     def get_simulator_device_id(cls):
@@ -636,6 +734,29 @@
         return cls.is_booted()
 
     @classmethod
+    def logs(cls):
+        def do_logs():
+            cmd = [
+                'tail',
+                '-f',
+                os.path.expanduser('~/Library/Logs/CoreSimulator/' + cls.get_simulator_device_id() + '/system.log'),
+            ]
+            logging.info(' '.join(cmd))
+            log_process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+            while True:
+                try:
+                    log_line = log_process.stdout.readline()
+                    if re.match(r'.*SkyShell.*', log_line) is not None:
+                        sys.stdout.write('IOS SIM: ' + log_line)
+                        sys.stdout.flush()
+                except KeyboardInterrupt:
+                    break
+        log_reader = multiprocessing.Process(target=do_logs)
+        log_reader.daemon = True
+        log_reader.start()
+        return log_reader
+
+    @classmethod
     def fork_install_app(cls, ios_app_path):
         cmd = [
             os.path.abspath(__file__),
@@ -1045,7 +1166,7 @@
 
         subparsers = parser.add_subparsers(help='sub-command help')
 
-        for command in [InstallSky(), StartSky(), StopSky(), StartListening(), StartTracing(), StopTracing(), IOSSimulator()]:
+        for command in [SkyLogs(), InstallSky(), StartSky(), StopSky(), StartListening(), StartTracing(), StopTracing(), IOSSimulator()]:
             command.add_subparser(subparsers)
 
         args = parser.parse_args()
diff --git a/sky/packages/sky/lib/rendering/README.md b/sky/packages/sky/lib/src/rendering/README.md
similarity index 99%
rename from sky/packages/sky/lib/rendering/README.md
rename to sky/packages/sky/lib/src/rendering/README.md
index d14a48a..91ba8a2 100644
--- a/sky/packages/sky/lib/rendering/README.md
+++ b/sky/packages/sky/lib/src/rendering/README.md
@@ -372,7 +372,7 @@
 working with the render tree.
 
 ```dart
-import 'package:sky/rendering/sky_binding.dart';
+import 'package:sky/src/rendering/sky_binding.dart';
 import 'package:sky/base/scheduler.dart' as scheduler;
 
 scheduler.addPersistentFrameCallback((_) {
diff --git a/sky/packages/sky/lib/rendering/auto_layout.dart b/sky/packages/sky/lib/src/rendering/auto_layout.dart
similarity index 98%
rename from sky/packages/sky/lib/rendering/auto_layout.dart
rename to sky/packages/sky/lib/src/rendering/auto_layout.dart
index 82ea255..150f7f4 100644
--- a/sky/packages/sky/lib/rendering/auto_layout.dart
+++ b/sky/packages/sky/lib/src/rendering/auto_layout.dart
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 import 'package:cassowary/cassowary.dart' as al;
-import 'package:sky/rendering/box.dart';
-import 'package:sky/rendering/object.dart';
+import 'package:sky/src/rendering/box.dart';
+import 'package:sky/src/rendering/object.dart';
 
 /// Hosts the edge parameters and vends useful methods to construct expressions
 /// for constraints. Also sets up and manages implicit constraints and edit
diff --git a/sky/packages/sky/lib/rendering/block.dart b/sky/packages/sky/lib/src/rendering/block.dart
similarity index 99%
rename from sky/packages/sky/lib/rendering/block.dart
rename to sky/packages/sky/lib/src/rendering/block.dart
index bb113f6..994c81d 100644
--- a/sky/packages/sky/lib/rendering/block.dart
+++ b/sky/packages/sky/lib/src/rendering/block.dart
@@ -4,8 +4,8 @@
 
 import 'dart:math' as math;
 
-import 'package:sky/rendering/box.dart';
-import 'package:sky/rendering/object.dart';
+import 'package:sky/src/rendering/box.dart';
+import 'package:sky/src/rendering/object.dart';
 import 'package:vector_math/vector_math.dart';
 
 class BlockParentData extends BoxParentData with ContainerParentDataMixin<RenderBox> { }
diff --git a/sky/packages/sky/lib/rendering/box.dart b/sky/packages/sky/lib/src/rendering/box.dart
similarity index 99%
rename from sky/packages/sky/lib/rendering/box.dart
rename to sky/packages/sky/lib/src/rendering/box.dart
index 713dd7f..2af20e1 100644
--- a/sky/packages/sky/lib/rendering/box.dart
+++ b/sky/packages/sky/lib/src/rendering/box.dart
@@ -8,7 +8,7 @@
 import 'package:sky/base/debug.dart';
 import 'package:sky/painting/box_painter.dart';
 import 'package:sky/painting/text_style.dart';
-import 'package:sky/rendering/object.dart';
+import 'package:sky/src/rendering/object.dart';
 import 'package:vector_math/vector_math.dart';
 
 export 'package:sky/painting/text_style.dart' show TextBaseline;
diff --git a/sky/packages/sky/lib/rendering/flex.dart b/sky/packages/sky/lib/src/rendering/flex.dart
similarity index 98%
rename from sky/packages/sky/lib/rendering/flex.dart
rename to sky/packages/sky/lib/src/rendering/flex.dart
index a6236e4..fa3fa34 100644
--- a/sky/packages/sky/lib/rendering/flex.dart
+++ b/sky/packages/sky/lib/src/rendering/flex.dart
@@ -4,10 +4,10 @@
 
 import 'dart:math' as math;
 
-import 'package:sky/rendering/box.dart';
-import 'package:sky/rendering/object.dart';
+import 'package:sky/src/rendering/box.dart';
+import 'package:sky/src/rendering/object.dart';
 
-export 'package:sky/rendering/object.dart' show EventDisposition;
+export 'package:sky/src/rendering/object.dart' show EventDisposition;
 
 class FlexParentData extends BoxParentData with ContainerParentDataMixin<RenderBox> {
   int flex;
diff --git a/sky/packages/sky/lib/rendering/grid.dart b/sky/packages/sky/lib/src/rendering/grid.dart
similarity index 98%
rename from sky/packages/sky/lib/rendering/grid.dart
rename to sky/packages/sky/lib/src/rendering/grid.dart
index 37186d9..4431e0f 100644
--- a/sky/packages/sky/lib/rendering/grid.dart
+++ b/sky/packages/sky/lib/src/rendering/grid.dart
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'package:sky/rendering/box.dart';
-import 'package:sky/rendering/object.dart';
+import 'package:sky/src/rendering/box.dart';
+import 'package:sky/src/rendering/object.dart';
 
 class GridParentData extends BoxParentData with ContainerParentDataMixin<RenderBox> {}
 
diff --git a/sky/packages/sky/lib/rendering/image.dart b/sky/packages/sky/lib/src/rendering/image.dart
similarity index 97%
rename from sky/packages/sky/lib/rendering/image.dart
rename to sky/packages/sky/lib/src/rendering/image.dart
index 0d1f518..e3eb3a1 100644
--- a/sky/packages/sky/lib/rendering/image.dart
+++ b/sky/packages/sky/lib/src/rendering/image.dart
@@ -5,8 +5,8 @@
 import 'dart:sky' as sky;
 
 import 'package:sky/painting/box_painter.dart';
-import 'package:sky/rendering/object.dart';
-import 'package:sky/rendering/box.dart';
+import 'package:sky/src/rendering/object.dart';
+import 'package:sky/src/rendering/box.dart';
 
 class RenderImage extends RenderBox {
   RenderImage({
diff --git a/sky/packages/sky/lib/rendering/layer.dart b/sky/packages/sky/lib/src/rendering/layer.dart
similarity index 98%
rename from sky/packages/sky/lib/rendering/layer.dart
rename to sky/packages/sky/lib/src/rendering/layer.dart
index adcbe8c..ea4a755 100644
--- a/sky/packages/sky/lib/rendering/layer.dart
+++ b/sky/packages/sky/lib/src/rendering/layer.dart
@@ -242,7 +242,7 @@
   int alpha;
 
   void addToScene(sky.SceneBuilder builder, Offset layerOffset) {
-    builder.pushOpacity(alpha, bounds == null ? null : bounds.shift(layerOffset));
+    builder.pushOpacity(alpha, bounds?.shift(layerOffset));
     addChildrenToScene(builder, offset + layerOffset);
     builder.pop();
   }
diff --git a/sky/packages/sky/lib/rendering/object.dart b/sky/packages/sky/lib/src/rendering/object.dart
similarity index 99%
rename from sky/packages/sky/lib/rendering/object.dart
rename to sky/packages/sky/lib/src/rendering/object.dart
index 6b5d797..0977c1f 100644
--- a/sky/packages/sky/lib/rendering/object.dart
+++ b/sky/packages/sky/lib/src/rendering/object.dart
@@ -10,7 +10,7 @@
 import 'package:sky/base/hit_test.dart';
 import 'package:sky/base/node.dart';
 import 'package:sky/base/scheduler.dart' as scheduler;
-import 'package:sky/rendering/layer.dart';
+import 'package:sky/src/rendering/layer.dart';
 import 'package:vector_math/vector_math.dart';
 
 export 'dart:sky' show Point, Offset, Size, Rect, Color, Paint, Path;
diff --git a/sky/packages/sky/lib/rendering/paragraph.dart b/sky/packages/sky/lib/src/rendering/paragraph.dart
similarity index 97%
rename from sky/packages/sky/lib/rendering/paragraph.dart
rename to sky/packages/sky/lib/src/rendering/paragraph.dart
index 1f7f124..dd00721 100644
--- a/sky/packages/sky/lib/rendering/paragraph.dart
+++ b/sky/packages/sky/lib/src/rendering/paragraph.dart
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 import 'package:sky/painting/text_painter.dart';
-import 'package:sky/rendering/box.dart';
-import 'package:sky/rendering/object.dart';
+import 'package:sky/src/rendering/box.dart';
+import 'package:sky/src/rendering/object.dart';
 
 export 'package:sky/painting/text_painter.dart';
 
diff --git a/sky/packages/sky/lib/rendering/proxy_box.dart b/sky/packages/sky/lib/src/rendering/proxy_box.dart
similarity index 99%
rename from sky/packages/sky/lib/rendering/proxy_box.dart
rename to sky/packages/sky/lib/src/rendering/proxy_box.dart
index 6dec1df..1632250 100644
--- a/sky/packages/sky/lib/rendering/proxy_box.dart
+++ b/sky/packages/sky/lib/src/rendering/proxy_box.dart
@@ -6,8 +6,8 @@
 
 import 'package:sky/painting/box_painter.dart';
 import 'package:sky/painting/text_style.dart';
-import 'package:sky/rendering/object.dart';
-import 'package:sky/rendering/box.dart';
+import 'package:sky/src/rendering/object.dart';
+import 'package:sky/src/rendering/box.dart';
 import 'package:vector_math/vector_math.dart';
 
 export 'package:sky/painting/box_painter.dart';
diff --git a/sky/packages/sky/lib/rendering/shifted_box.dart b/sky/packages/sky/lib/src/rendering/shifted_box.dart
similarity index 98%
rename from sky/packages/sky/lib/rendering/shifted_box.dart
rename to sky/packages/sky/lib/src/rendering/shifted_box.dart
index 22d8f4b..a7c32f6 100644
--- a/sky/packages/sky/lib/rendering/shifted_box.dart
+++ b/sky/packages/sky/lib/src/rendering/shifted_box.dart
@@ -4,8 +4,8 @@
 
 import 'package:sky/painting/box_painter.dart';
 import 'package:sky/painting/text_style.dart';
-import 'package:sky/rendering/object.dart';
-import 'package:sky/rendering/box.dart';
+import 'package:sky/src/rendering/object.dart';
+import 'package:sky/src/rendering/box.dart';
 
 abstract class RenderShiftedBox extends RenderBox with RenderObjectWithChildMixin<RenderBox> {
 
diff --git a/sky/packages/sky/lib/rendering/sky_binding.dart b/sky/packages/sky/lib/src/rendering/sky_binding.dart
similarity index 97%
rename from sky/packages/sky/lib/rendering/sky_binding.dart
rename to sky/packages/sky/lib/src/rendering/sky_binding.dart
index 59f761c..4afacd3 100644
--- a/sky/packages/sky/lib/rendering/sky_binding.dart
+++ b/sky/packages/sky/lib/src/rendering/sky_binding.dart
@@ -8,9 +8,9 @@
 import 'package:sky/base/hit_test.dart';
 import 'package:sky/base/scheduler.dart' as scheduler;
 import 'package:sky/gestures/arena.dart';
-import 'package:sky/rendering/box.dart';
-import 'package:sky/rendering/object.dart';
-import 'package:sky/rendering/view.dart';
+import 'package:sky/src/rendering/box.dart';
+import 'package:sky/src/rendering/object.dart';
+import 'package:sky/src/rendering/view.dart';
 
 int _hammingWeight(int value) {
   if (value == 0)
diff --git a/sky/packages/sky/lib/rendering/stack.dart b/sky/packages/sky/lib/src/rendering/stack.dart
similarity index 98%
rename from sky/packages/sky/lib/rendering/stack.dart
rename to sky/packages/sky/lib/src/rendering/stack.dart
index 0d850a6..4ee6eb5 100644
--- a/sky/packages/sky/lib/rendering/stack.dart
+++ b/sky/packages/sky/lib/src/rendering/stack.dart
@@ -4,8 +4,8 @@
 
 import 'dart:math' as math;
 
-import 'package:sky/rendering/box.dart';
-import 'package:sky/rendering/object.dart';
+import 'package:sky/src/rendering/box.dart';
+import 'package:sky/src/rendering/object.dart';
 
 class StackParentData extends BoxParentData with ContainerParentDataMixin<RenderBox> {
   double top;
diff --git a/sky/packages/sky/lib/rendering/toggleable.dart b/sky/packages/sky/lib/src/rendering/toggleable.dart
similarity index 93%
rename from sky/packages/sky/lib/rendering/toggleable.dart
rename to sky/packages/sky/lib/src/rendering/toggleable.dart
index 796f49c..c42d1d8 100644
--- a/sky/packages/sky/lib/rendering/toggleable.dart
+++ b/sky/packages/sky/lib/src/rendering/toggleable.dart
@@ -7,9 +7,9 @@
 import 'package:sky/animation/animated_value.dart';
 import 'package:sky/animation/animation_performance.dart';
 import 'package:sky/animation/curves.dart';
-import 'package:sky/rendering/box.dart';
-import 'package:sky/rendering/object.dart';
-import 'package:sky/rendering/proxy_box.dart';
+import 'package:sky/src/rendering/box.dart';
+import 'package:sky/src/rendering/object.dart';
+import 'package:sky/src/rendering/proxy_box.dart';
 
 typedef void ValueChanged(bool value);
 
diff --git a/sky/packages/sky/lib/rendering/view.dart b/sky/packages/sky/lib/src/rendering/view.dart
similarity index 95%
rename from sky/packages/sky/lib/rendering/view.dart
rename to sky/packages/sky/lib/src/rendering/view.dart
index e835ce3..f5c4637 100644
--- a/sky/packages/sky/lib/rendering/view.dart
+++ b/sky/packages/sky/lib/src/rendering/view.dart
@@ -5,9 +5,9 @@
 import 'dart:sky' as sky;
 
 import 'package:sky/base/scheduler.dart' as scheduler;
-import 'package:sky/rendering/layer.dart';
-import 'package:sky/rendering/object.dart';
-import 'package:sky/rendering/box.dart';
+import 'package:sky/src/rendering/layer.dart';
+import 'package:sky/src/rendering/object.dart';
+import 'package:sky/src/rendering/box.dart';
 import 'package:vector_math/vector_math.dart';
 
 class ViewConstraints {
diff --git a/sky/packages/sky/lib/rendering/viewport.dart b/sky/packages/sky/lib/src/rendering/viewport.dart
similarity index 97%
rename from sky/packages/sky/lib/rendering/viewport.dart
rename to sky/packages/sky/lib/src/rendering/viewport.dart
index 7b76645..96cc073 100644
--- a/sky/packages/sky/lib/rendering/viewport.dart
+++ b/sky/packages/sky/lib/src/rendering/viewport.dart
@@ -4,8 +4,8 @@
 
 import 'dart:sky' as sky;
 
-import 'package:sky/rendering/object.dart';
-import 'package:sky/rendering/box.dart';
+import 'package:sky/src/rendering/object.dart';
+import 'package:sky/src/rendering/box.dart';
 import 'package:vector_math/vector_math.dart';
 
 enum ScrollDirection { horizontal, vertical, both }
diff --git a/sky/packages/sky/lib/widgets/README.md b/sky/packages/sky/lib/src/widgets/README.md
similarity index 100%
rename from sky/packages/sky/lib/widgets/README.md
rename to sky/packages/sky/lib/src/widgets/README.md
diff --git a/sky/packages/sky/lib/widgets/animated_component.dart b/sky/packages/sky/lib/src/widgets/animated_component.dart
similarity index 96%
rename from sky/packages/sky/lib/widgets/animated_component.dart
rename to sky/packages/sky/lib/src/widgets/animated_component.dart
index 7913410..2088a4d 100644
--- a/sky/packages/sky/lib/widgets/animated_component.dart
+++ b/sky/packages/sky/lib/src/widgets/animated_component.dart
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import 'package:sky/animation/animation_performance.dart';
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/widgets/framework.dart';
 
 abstract class AnimatedComponent extends StatefulComponent {
 
diff --git a/sky/packages/sky/lib/widgets/animated_container.dart b/sky/packages/sky/lib/src/widgets/animated_container.dart
similarity index 97%
rename from sky/packages/sky/lib/widgets/animated_container.dart
rename to sky/packages/sky/lib/src/widgets/animated_container.dart
index 4e53beb..0eb19b1 100644
--- a/sky/packages/sky/lib/widgets/animated_container.dart
+++ b/sky/packages/sky/lib/src/widgets/animated_container.dart
@@ -9,9 +9,9 @@
 import 'package:sky/animation/curves.dart';
 import 'package:sky/base/lerp.dart';
 import 'package:sky/painting/box_painter.dart';
-import 'package:sky/widgets/animated_component.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/widgets/animated_component.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/framework.dart';
 
 class AnimatedBoxConstraintsValue extends AnimatedValue<BoxConstraints> {
   AnimatedBoxConstraintsValue(BoxConstraints begin, { BoxConstraints end, Curve curve: linear })
diff --git a/sky/packages/sky/lib/widgets/basic.dart b/sky/packages/sky/lib/src/widgets/basic.dart
similarity index 93%
rename from sky/packages/sky/lib/widgets/basic.dart
rename to sky/packages/sky/lib/src/widgets/basic.dart
index e297864..af66756 100644
--- a/sky/packages/sky/lib/widgets/basic.dart
+++ b/sky/packages/sky/lib/src/widgets/basic.dart
@@ -11,29 +11,29 @@
 import 'package:sky/mojo/net/image_cache.dart' as image_cache;
 import 'package:sky/painting/text_painter.dart';
 import 'package:sky/painting/text_style.dart';
-import 'package:sky/rendering/block.dart';
-import 'package:sky/rendering/box.dart';
-import 'package:sky/rendering/flex.dart';
-import 'package:sky/rendering/grid.dart';
-import 'package:sky/rendering/image.dart';
-import 'package:sky/rendering/object.dart';
-import 'package:sky/rendering/paragraph.dart';
-import 'package:sky/rendering/proxy_box.dart';
-import 'package:sky/rendering/shifted_box.dart';
-import 'package:sky/rendering/stack.dart';
-import 'package:sky/rendering/viewport.dart';
-import 'package:sky/widgets/default_text_style.dart';
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/rendering/block.dart';
+import 'package:sky/src/rendering/box.dart';
+import 'package:sky/src/rendering/flex.dart';
+import 'package:sky/src/rendering/grid.dart';
+import 'package:sky/src/rendering/image.dart';
+import 'package:sky/src/rendering/object.dart';
+import 'package:sky/src/rendering/paragraph.dart';
+import 'package:sky/src/rendering/proxy_box.dart';
+import 'package:sky/src/rendering/shifted_box.dart';
+import 'package:sky/src/rendering/stack.dart';
+import 'package:sky/src/rendering/viewport.dart';
+import 'package:sky/src/widgets/default_text_style.dart';
+import 'package:sky/src/widgets/framework.dart';
 
 export 'package:sky/base/hit_test.dart' show EventDisposition, combineEventDispositions;
 export 'package:sky/painting/text_style.dart';
-export 'package:sky/rendering/block.dart' show BlockDirection;
-export 'package:sky/rendering/box.dart' show BoxConstraints;
-export 'package:sky/rendering/flex.dart' show FlexJustifyContent, FlexAlignItems, FlexDirection;
-export 'package:sky/rendering/object.dart' show Point, Offset, Size, Rect, Color, Paint, Path;
-export 'package:sky/rendering/proxy_box.dart' show BackgroundImage, BoxDecoration, BoxDecorationPosition, BoxShadow, Border, BorderSide, EdgeDims, Shape;
-export 'package:sky/rendering/toggleable.dart' show ValueChanged;
-export 'package:sky/rendering/viewport.dart' show ScrollDirection;
+export 'package:sky/src/rendering/block.dart' show BlockDirection;
+export 'package:sky/src/rendering/box.dart' show BoxConstraints;
+export 'package:sky/src/rendering/flex.dart' show FlexJustifyContent, FlexAlignItems, FlexDirection;
+export 'package:sky/src/rendering/object.dart' show Point, Offset, Size, Rect, Color, Paint, Path;
+export 'package:sky/src/rendering/proxy_box.dart' show BackgroundImage, BoxDecoration, BoxDecorationPosition, BoxShadow, Border, BorderSide, EdgeDims, Shape;
+export 'package:sky/src/rendering/toggleable.dart' show ValueChanged;
+export 'package:sky/src/rendering/viewport.dart' show ScrollDirection;
 
 // PAINTING NODES
 
@@ -487,10 +487,7 @@
   RenderStack get renderObject => super.renderObject;
 
   void updateParentData(RenderObject child, Positioned positioned) {
-    if (positioned == null)
-      _updateParentDataWithValues(child, null, null, null, null);
-    else
-      _updateParentDataWithValues(child, positioned.top, positioned.right, positioned.bottom, positioned.left);
+    _updateParentDataWithValues(child, positioned?.top, positioned?.right, positioned?.bottom, positioned?.left);
   }
 
   void _updateParentDataWithValues(RenderObject child, double top, double right, double bottom, double left) {
@@ -592,7 +589,7 @@
   }
 
   void updateParentData(RenderObject child, Flexible flexible) {
-    _updateParentDataWithValues(child, flexible == null ? null : flexible.flex);
+    _updateParentDataWithValues(child, flexible?.flex);
   }
 
   void _updateParentDataWithValues(RenderObject child, int flex) {
diff --git a/sky/packages/sky/lib/widgets/button_base.dart b/sky/packages/sky/lib/src/widgets/button_base.dart
similarity index 95%
rename from sky/packages/sky/lib/widgets/button_base.dart
rename to sky/packages/sky/lib/src/widgets/button_base.dart
index bf0829f..7fd9b93 100644
--- a/sky/packages/sky/lib/widgets/button_base.dart
+++ b/sky/packages/sky/lib/src/widgets/button_base.dart
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/widgets/framework.dart';
 
 abstract class ButtonBase extends StatefulComponent {
 
diff --git a/sky/packages/sky/lib/widgets/card.dart b/sky/packages/sky/lib/src/widgets/card.dart
similarity index 82%
rename from sky/packages/sky/lib/widgets/card.dart
rename to sky/packages/sky/lib/src/widgets/card.dart
index 3f86a78..db059fb 100644
--- a/sky/packages/sky/lib/widgets/card.dart
+++ b/sky/packages/sky/lib/src/widgets/card.dart
@@ -2,9 +2,9 @@
 // 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';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/material.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/material.dart';
 
 const EdgeDims _kCardMargins = const EdgeDims.all(4.0);
 
diff --git a/sky/packages/sky/lib/widgets/checkbox.dart b/sky/packages/sky/lib/src/widgets/checkbox.dart
similarity index 94%
rename from sky/packages/sky/lib/widgets/checkbox.dart
rename to sky/packages/sky/lib/src/widgets/checkbox.dart
index 6e615d0..f4dc443 100644
--- a/sky/packages/sky/lib/widgets/checkbox.dart
+++ b/sky/packages/sky/lib/src/widgets/checkbox.dart
@@ -4,13 +4,13 @@
 
 import 'dart:sky' as sky;
 
-import 'package:sky/rendering/object.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/theme.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/rendering/toggleable.dart';
+import 'package:sky/src/rendering/object.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/theme.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/rendering/toggleable.dart';
 
-export 'package:sky/rendering/toggleable.dart' show ValueChanged;
+export 'package:sky/src/rendering/toggleable.dart' show ValueChanged;
 
 const double _kMidpoint = 0.5;
 const sky.Color _kLightUncheckedColor = const sky.Color(0x8A000000);
diff --git a/sky/packages/sky/lib/widgets/date_picker.dart b/sky/packages/sky/lib/src/widgets/date_picker.dart
similarity index 97%
rename from sky/packages/sky/lib/widgets/date_picker.dart
rename to sky/packages/sky/lib/src/widgets/date_picker.dart
index b6747be..101a809 100644
--- a/sky/packages/sky/lib/widgets/date_picker.dart
+++ b/sky/packages/sky/lib/src/widgets/date_picker.dart
@@ -9,12 +9,12 @@
 import 'package:sky/mojo/activity.dart';
 import 'package:sky/theme/colors.dart' as colors;
 import 'package:sky/theme/typography.dart' as typography;
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/gesture_detector.dart';
-import 'package:sky/widgets/ink_well.dart';
-import 'package:sky/widgets/scrollable.dart';
-import 'package:sky/widgets/theme.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/gesture_detector.dart';
+import 'package:sky/src/widgets/ink_well.dart';
+import 'package:sky/src/widgets/scrollable.dart';
+import 'package:sky/src/widgets/theme.dart';
 
 typedef void DatePickerValueChanged(DateTime dateTime);
 
diff --git a/sky/packages/sky/lib/widgets/default_text_style.dart b/sky/packages/sky/lib/src/widgets/default_text_style.dart
similarity index 82%
rename from sky/packages/sky/lib/widgets/default_text_style.dart
rename to sky/packages/sky/lib/src/widgets/default_text_style.dart
index c4a763d..6339904 100644
--- a/sky/packages/sky/lib/widgets/default_text_style.dart
+++ b/sky/packages/sky/lib/src/widgets/default_text_style.dart
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 import 'package:sky/painting/text_style.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/framework.dart';
 
 class DefaultTextStyle extends Inherited {
 
@@ -21,7 +21,7 @@
 
   static TextStyle of(Widget widget) {
     DefaultTextStyle result = widget.inheritedOfType(DefaultTextStyle);
-    return result == null ? null : result.style;
+    return result?.style;
   }
 
   bool syncShouldNotify(DefaultTextStyle old) => style != old.style;
diff --git a/sky/packages/sky/lib/widgets/dialog.dart b/sky/packages/sky/lib/src/widgets/dialog.dart
similarity index 90%
rename from sky/packages/sky/lib/widgets/dialog.dart
rename to sky/packages/sky/lib/src/widgets/dialog.dart
index 979b777..7ab5b32 100644
--- a/sky/packages/sky/lib/widgets/dialog.dart
+++ b/sky/packages/sky/lib/src/widgets/dialog.dart
@@ -7,16 +7,16 @@
 import 'package:sky/animation/animated_value.dart';
 import 'package:sky/animation/curves.dart';
 import 'package:sky/theme/colors.dart' as colors;
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/default_text_style.dart';
-import 'package:sky/widgets/focus.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/gesture_detector.dart';
-import 'package:sky/widgets/material.dart';
-import 'package:sky/widgets/navigator.dart';
-import 'package:sky/widgets/scrollable.dart';
-import 'package:sky/widgets/theme.dart';
-import 'package:sky/widgets/transitions.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/default_text_style.dart';
+import 'package:sky/src/widgets/focus.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/gesture_detector.dart';
+import 'package:sky/src/widgets/material.dart';
+import 'package:sky/src/widgets/navigator.dart';
+import 'package:sky/src/widgets/scrollable.dart';
+import 'package:sky/src/widgets/theme.dart';
+import 'package:sky/src/widgets/transitions.dart';
 
 typedef Widget DialogBuilder(Navigator navigator);
 
diff --git a/sky/packages/sky/lib/widgets/dismissable.dart b/sky/packages/sky/lib/src/widgets/dismissable.dart
similarity index 96%
rename from sky/packages/sky/lib/widgets/dismissable.dart
rename to sky/packages/sky/lib/src/widgets/dismissable.dart
index c33b24e..321fc1c 100644
--- a/sky/packages/sky/lib/widgets/dismissable.dart
+++ b/sky/packages/sky/lib/src/widgets/dismissable.dart
@@ -7,10 +7,10 @@
 import 'package:sky/animation/animated_value.dart';
 import 'package:sky/animation/animation_performance.dart';
 import 'package:sky/animation/curves.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/transitions.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/gesture_detector.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/transitions.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/gesture_detector.dart';
 
 const Duration _kCardDismissFadeout = const Duration(milliseconds: 200);
 const Duration _kCardDismissResize = const Duration(milliseconds: 300);
diff --git a/sky/packages/sky/lib/widgets/drag_target.dart b/sky/packages/sky/lib/src/widgets/drag_target.dart
similarity index 93%
rename from sky/packages/sky/lib/widgets/drag_target.dart
rename to sky/packages/sky/lib/src/widgets/drag_target.dart
index 63e8c1e..3b9b2d5 100644
--- a/sky/packages/sky/lib/widgets/drag_target.dart
+++ b/sky/packages/sky/lib/src/widgets/drag_target.dart
@@ -5,10 +5,10 @@
 import 'dart:collection';
 
 import 'package:sky/base/hit_test.dart';
-import 'package:sky/rendering/object.dart';
-import 'package:sky/rendering/sky_binding.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/rendering/object.dart';
+import 'package:sky/src/rendering/sky_binding.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/framework.dart';
 
 typedef bool DragTargetWillAccept<T>(T data);
 typedef void DragTargetAccept<T>(T data);
diff --git a/sky/packages/sky/lib/widgets/drawer.dart b/sky/packages/sky/lib/src/widgets/drawer.dart
similarity index 92%
rename from sky/packages/sky/lib/widgets/drawer.dart
rename to sky/packages/sky/lib/src/widgets/drawer.dart
index 1822e39..feb2a02 100644
--- a/sky/packages/sky/lib/widgets/drawer.dart
+++ b/sky/packages/sky/lib/src/widgets/drawer.dart
@@ -10,14 +10,14 @@
 import 'package:sky/animation/forces.dart';
 import 'package:sky/theme/colors.dart' as colors;
 import 'package:sky/theme/shadows.dart';
-import 'package:sky/widgets/animated_container.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/gesture_detector.dart';
-import 'package:sky/widgets/navigator.dart';
-import 'package:sky/widgets/scrollable.dart';
-import 'package:sky/widgets/theme.dart';
-import 'package:sky/widgets/transitions.dart';
+import 'package:sky/src/widgets/animated_container.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/gesture_detector.dart';
+import 'package:sky/src/widgets/navigator.dart';
+import 'package:sky/src/widgets/scrollable.dart';
+import 'package:sky/src/widgets/theme.dart';
+import 'package:sky/src/widgets/transitions.dart';
 
 export 'package:sky/animation/animation_performance.dart' show AnimationStatus;
 
diff --git a/sky/packages/sky/lib/widgets/drawer_divider.dart b/sky/packages/sky/lib/src/widgets/drawer_divider.dart
similarity index 80%
rename from sky/packages/sky/lib/widgets/drawer_divider.dart
rename to sky/packages/sky/lib/src/widgets/drawer_divider.dart
index e29dd95..a73fbde 100644
--- a/sky/packages/sky/lib/widgets/drawer_divider.dart
+++ b/sky/packages/sky/lib/src/widgets/drawer_divider.dart
@@ -2,9 +2,9 @@
 // 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';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/theme.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/theme.dart';
 
 class DrawerDivider extends Component {
   DrawerDivider({ Key key }) : super(key: key);
diff --git a/sky/packages/sky/lib/widgets/drawer_header.dart b/sky/packages/sky/lib/src/widgets/drawer_header.dart
similarity index 85%
rename from sky/packages/sky/lib/widgets/drawer_header.dart
rename to sky/packages/sky/lib/src/widgets/drawer_header.dart
index f2c7caf..64c393e 100644
--- a/sky/packages/sky/lib/widgets/drawer_header.dart
+++ b/sky/packages/sky/lib/src/widgets/drawer_header.dart
@@ -3,10 +3,10 @@
 // found in the LICENSE file.
 
 import 'package:sky/theme/view_configuration.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/default_text_style.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/theme.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/default_text_style.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/theme.dart';
 
 // TODO(jackson): This class should usually render the user's
 // preferred banner image rather than a solid background
diff --git a/sky/packages/sky/lib/widgets/drawer_item.dart b/sky/packages/sky/lib/src/widgets/drawer_item.dart
similarity index 84%
rename from sky/packages/sky/lib/widgets/drawer_item.dart
rename to sky/packages/sky/lib/src/widgets/drawer_item.dart
index 84b8787..b81edf1 100644
--- a/sky/packages/sky/lib/widgets/drawer_item.dart
+++ b/sky/packages/sky/lib/src/widgets/drawer_item.dart
@@ -6,16 +6,16 @@
 
 import 'package:sky/painting/text_style.dart';
 import 'package:sky/theme/colors.dart' as colors;
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/button_base.dart';
-import 'package:sky/widgets/default_text_style.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/gesture_detector.dart';
-import 'package:sky/widgets/icon.dart';
-import 'package:sky/widgets/ink_well.dart';
-import 'package:sky/widgets/theme.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/button_base.dart';
+import 'package:sky/src/widgets/default_text_style.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/gesture_detector.dart';
+import 'package:sky/src/widgets/icon.dart';
+import 'package:sky/src/widgets/ink_well.dart';
+import 'package:sky/src/widgets/theme.dart';
 
-typedef EventDisposition OnPressedFunction();
+typedef void OnPressedFunction();
 
 class DrawerItem extends ButtonBase {
   DrawerItem({ Key key, this.icon, this.child, this.onPressed, this.selected: false })
diff --git a/sky/packages/sky/lib/widgets/flat_button.dart b/sky/packages/sky/lib/src/widgets/flat_button.dart
similarity index 82%
rename from sky/packages/sky/lib/widgets/flat_button.dart
rename to sky/packages/sky/lib/src/widgets/flat_button.dart
index f61e95d..d7bfe26 100644
--- a/sky/packages/sky/lib/widgets/flat_button.dart
+++ b/sky/packages/sky/lib/src/widgets/flat_button.dart
@@ -3,9 +3,9 @@
 // found in the LICENSE file.
 
 import 'package:sky/theme/colors.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/material_button.dart';
-import 'package:sky/widgets/theme.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/material_button.dart';
+import 'package:sky/src/widgets/theme.dart';
 
 class FlatButton extends MaterialButton {
   FlatButton({
diff --git a/sky/packages/sky/lib/widgets/floating_action_button.dart b/sky/packages/sky/lib/src/widgets/floating_action_button.dart
similarity index 82%
rename from sky/packages/sky/lib/widgets/floating_action_button.dart
rename to sky/packages/sky/lib/src/widgets/floating_action_button.dart
index e6efe33..ddc2eb0 100644
--- a/sky/packages/sky/lib/widgets/floating_action_button.dart
+++ b/sky/packages/sky/lib/src/widgets/floating_action_button.dart
@@ -2,14 +2,14 @@
 // 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';
-import 'package:sky/widgets/button_base.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/gesture_detector.dart';
-import 'package:sky/widgets/icon.dart';
-import 'package:sky/widgets/ink_well.dart';
-import 'package:sky/widgets/material.dart';
-import 'package:sky/widgets/theme.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/button_base.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/gesture_detector.dart';
+import 'package:sky/src/widgets/icon.dart';
+import 'package:sky/src/widgets/ink_well.dart';
+import 'package:sky/src/widgets/material.dart';
+import 'package:sky/src/widgets/theme.dart';
 
 // TODO(eseidel): This needs to change based on device size?
 // http://www.google.com/design/spec/layout/metrics-keylines.html#metrics-keylines-keylines-spacing
diff --git a/sky/packages/sky/lib/widgets/focus.dart b/sky/packages/sky/lib/src/widgets/focus.dart
similarity index 99%
rename from sky/packages/sky/lib/widgets/focus.dart
rename to sky/packages/sky/lib/src/widgets/focus.dart
index 44f8aaa..139a8cb 100644
--- a/sky/packages/sky/lib/widgets/focus.dart
+++ b/sky/packages/sky/lib/src/widgets/focus.dart
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/widgets/framework.dart';
 
 typedef void FocusChanged(GlobalKey key);
 
diff --git a/sky/packages/sky/lib/widgets/framework.dart b/sky/packages/sky/lib/src/widgets/framework.dart
similarity index 98%
rename from sky/packages/sky/lib/widgets/framework.dart
rename to sky/packages/sky/lib/src/widgets/framework.dart
index b29384b..bbd1253 100644
--- a/sky/packages/sky/lib/widgets/framework.dart
+++ b/sky/packages/sky/lib/src/widgets/framework.dart
@@ -9,14 +9,14 @@
 import 'package:sky/base/hit_test.dart';
 import 'package:sky/base/scheduler.dart' as scheduler;
 import 'package:sky/mojo/activity.dart';
-import 'package:sky/rendering/box.dart';
-import 'package:sky/rendering/object.dart';
-import 'package:sky/rendering/sky_binding.dart';
-import 'package:sky/rendering/view.dart';
+import 'package:sky/src/rendering/box.dart';
+import 'package:sky/src/rendering/object.dart';
+import 'package:sky/src/rendering/sky_binding.dart';
+import 'package:sky/src/rendering/view.dart';
 
 export 'package:sky/base/hit_test.dart' show EventDisposition, combineEventDispositions;
-export 'package:sky/rendering/box.dart' show BoxConstraints, BoxDecoration, Border, BorderSide, EdgeDims;
-export 'package:sky/rendering/object.dart' show Point, Offset, Size, Rect, Color, Paint, Path;
+export 'package:sky/src/rendering/box.dart' show BoxConstraints, BoxDecoration, Border, BorderSide, EdgeDims;
+export 'package:sky/src/rendering/object.dart' show Point, Offset, Size, Rect, Color, Paint, Path;
 
 final bool _shouldLogRenderDuration = false; // see also 'enableProfilingLoop' argument to runApp()
 
@@ -498,7 +498,7 @@
   }
 
   void _sync(Widget old, dynamic slot) {
-    Widget oldChild = old == null ? null : (old as TagNode).child;
+    Widget oldChild = (old as TagNode)?.child;
     child = syncChild(child, oldChild, slot);
     if (child != null) {
       assert(child.parent == this);
@@ -1259,7 +1259,7 @@
 
   void syncRenderObject(RenderObjectWrapper old) {
     super.syncRenderObject(old);
-    Widget oldChild = old == null ? null : (old as OneChildRenderObjectWrapper).child;
+    Widget oldChild = (old as OneChildRenderObjectWrapper)?.child;
     Widget newChild = child;
     _child = syncChild(newChild, oldChild, null);
     assert((newChild == null && child == null) || (newChild != null && child.parent == this));
@@ -1304,7 +1304,7 @@
 
   void insertChildRenderObject(RenderObjectWrapper child, Widget slot) {
     final renderObject = this.renderObject; // TODO(ianh): Remove this once the analyzer is cleverer
-    RenderObject nextSibling = slot != null ? slot.renderObject : null;
+    RenderObject nextSibling = slot?.renderObject;
     assert(nextSibling == null || nextSibling is RenderObject);
     assert(renderObject is ContainerRenderObjectMixin);
     renderObject.add(child.renderObject, before: nextSibling);
diff --git a/sky/packages/sky/lib/widgets/gesture_detector.dart b/sky/packages/sky/lib/src/widgets/gesture_detector.dart
similarity index 92%
rename from sky/packages/sky/lib/widgets/gesture_detector.dart
rename to sky/packages/sky/lib/src/widgets/gesture_detector.dart
index 768dad3..847e0b2 100644
--- a/sky/packages/sky/lib/widgets/gesture_detector.dart
+++ b/sky/packages/sky/lib/src/widgets/gesture_detector.dart
@@ -9,8 +9,8 @@
 import 'package:sky/gestures/scroll.dart';
 import 'package:sky/gestures/show_press.dart';
 import 'package:sky/gestures/tap.dart';
-import 'package:sky/rendering/sky_binding.dart';
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/rendering/sky_binding.dart';
+import 'package:sky/src/widgets/framework.dart';
 
 class GestureDetector extends StatefulComponent {
   GestureDetector({
@@ -52,12 +52,12 @@
     onTap = source.onTap;
     onShowPress = source.onShowPress;
     onLongPress = source.onLongPress;
-    onVerticalScrollStart = onVerticalScrollStart;
-    onVerticalScrollUpdate = onVerticalScrollUpdate;
-    onVerticalScrollEnd = onVerticalScrollEnd;
-    onHorizontalScrollStart = onHorizontalScrollStart;
-    onHorizontalScrollUpdate = onHorizontalScrollUpdate;
-    onHorizontalScrollEnd = onHorizontalScrollEnd;
+    onVerticalScrollStart = source.onVerticalScrollStart;
+    onVerticalScrollUpdate = source.onVerticalScrollUpdate;
+    onVerticalScrollEnd = source.onVerticalScrollEnd;
+    onHorizontalScrollStart = source.onHorizontalScrollStart;
+    onHorizontalScrollUpdate = source.onHorizontalScrollUpdate;
+    onHorizontalScrollEnd = source.onHorizontalScrollEnd;
     onPanStart = source.onPanStart;
     onPanUpdate = source.onPanUpdate;
     onPanEnd = source.onPanEnd;
diff --git a/sky/packages/sky/lib/widgets/homogeneous_viewport.dart b/sky/packages/sky/lib/src/widgets/homogeneous_viewport.dart
similarity index 93%
rename from sky/packages/sky/lib/widgets/homogeneous_viewport.dart
rename to sky/packages/sky/lib/src/widgets/homogeneous_viewport.dart
index e613112..c6c95e8 100644
--- a/sky/packages/sky/lib/widgets/homogeneous_viewport.dart
+++ b/sky/packages/sky/lib/src/widgets/homogeneous_viewport.dart
@@ -4,11 +4,11 @@
 
 import 'dart:math' as math;
 
-import 'package:sky/rendering/block.dart';
-import 'package:sky/rendering/box.dart';
-import 'package:sky/rendering/object.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/basic.dart';
+import 'package:sky/src/rendering/block.dart';
+import 'package:sky/src/rendering/box.dart';
+import 'package:sky/src/rendering/object.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/basic.dart';
 
 typedef List<Widget> ListBuilder(int startIndex, int count);
 
@@ -65,7 +65,7 @@
   }
 
   void insertChildRenderObject(RenderObjectWrapper child, Widget slot) {
-    RenderObject nextSibling = slot != null ? slot.renderObject : null;
+    RenderObject nextSibling = slot?.renderObject;
     renderObject.add(child.renderObject, before: nextSibling);
   }
 
@@ -83,6 +83,10 @@
       _layoutDirty = true;
       itemCount = newNode.itemCount;
     }
+    if (itemsWrap != newNode.itemsWrap) {
+      _layoutDirty = true;
+      itemsWrap = newNode.itemsWrap;
+    }
     if (itemExtent != newNode.itemExtent) {
       _layoutDirty = true;
       itemExtent = newNode.itemExtent;
diff --git a/sky/packages/sky/lib/widgets/icon.dart b/sky/packages/sky/lib/src/widgets/icon.dart
similarity index 93%
rename from sky/packages/sky/lib/widgets/icon.dart
rename to sky/packages/sky/lib/src/widgets/icon.dart
index 768cfc2..7aafe22 100644
--- a/sky/packages/sky/lib/widgets/icon.dart
+++ b/sky/packages/sky/lib/src/widgets/icon.dart
@@ -5,9 +5,9 @@
 import 'dart:sky' as sky;
 
 import 'package:sky/mojo/asset_bundle.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/theme.dart';
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/theme.dart';
+import 'package:sky/src/widgets/framework.dart';
 
 enum IconThemeColor { white, black }
 
@@ -31,7 +31,7 @@
 
   static IconThemeData of(Component component) {
     IconTheme result = component.inheritedOfType(IconTheme);
-    return result == null ? null : result.data;
+    return result?.data;
   }
 
   bool syncShouldNotify(IconTheme old) => data != old.data;
diff --git a/sky/packages/sky/lib/widgets/icon_button.dart b/sky/packages/sky/lib/src/widgets/icon_button.dart
similarity index 79%
rename from sky/packages/sky/lib/widgets/icon_button.dart
rename to sky/packages/sky/lib/src/widgets/icon_button.dart
index af32206..714f1ae 100644
--- a/sky/packages/sky/lib/widgets/icon_button.dart
+++ b/sky/packages/sky/lib/src/widgets/icon_button.dart
@@ -4,10 +4,10 @@
 
 import 'dart:sky' as sky;
 
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/icon.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/gesture_detector.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/icon.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/gesture_detector.dart';
 
 class IconButton extends Component {
 
diff --git a/sky/packages/sky/lib/widgets/ink_well.dart b/sky/packages/sky/lib/src/widgets/ink_well.dart
similarity index 94%
rename from sky/packages/sky/lib/widgets/ink_well.dart
rename to sky/packages/sky/lib/src/widgets/ink_well.dart
index 6e2e22e..5844912 100644
--- a/sky/packages/sky/lib/widgets/ink_well.dart
+++ b/sky/packages/sky/lib/src/widgets/ink_well.dart
@@ -8,11 +8,11 @@
 import 'package:sky/animation/animated_value.dart';
 import 'package:sky/animation/animation_performance.dart';
 import 'package:sky/animation/curves.dart';
-import 'package:sky/rendering/box.dart';
-import 'package:sky/rendering/object.dart';
-import 'package:sky/rendering/proxy_box.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/rendering/box.dart';
+import 'package:sky/src/rendering/object.dart';
+import 'package:sky/src/rendering/proxy_box.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/framework.dart';
 
 const int _kSplashInitialOpacity = 0x30;
 const double _kSplashCancelledVelocity = 0.7;
diff --git a/sky/packages/sky/lib/widgets/material.dart b/sky/packages/sky/lib/src/widgets/material.dart
similarity index 87%
rename from sky/packages/sky/lib/widgets/material.dart
rename to sky/packages/sky/lib/src/widgets/material.dart
index 5a03e6b..129f39d 100644
--- a/sky/packages/sky/lib/widgets/material.dart
+++ b/sky/packages/sky/lib/src/widgets/material.dart
@@ -4,11 +4,11 @@
 
 import 'package:sky/painting/box_painter.dart';
 import 'package:sky/theme/shadows.dart';
-import 'package:sky/widgets/animated_container.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/default_text_style.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/theme.dart';
+import 'package:sky/src/widgets/animated_container.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/default_text_style.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/theme.dart';
 
 enum MaterialType { canvas, card, circle, button }
 
diff --git a/sky/packages/sky/lib/widgets/material_button.dart b/sky/packages/sky/lib/src/widgets/material_button.dart
similarity index 82%
rename from sky/packages/sky/lib/widgets/material_button.dart
rename to sky/packages/sky/lib/src/widgets/material_button.dart
index ca13b90..747c218 100644
--- a/sky/packages/sky/lib/widgets/material_button.dart
+++ b/sky/packages/sky/lib/src/widgets/material_button.dart
@@ -2,12 +2,12 @@
 // 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';
-import 'package:sky/widgets/button_base.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/gesture_detector.dart';
-import 'package:sky/widgets/ink_well.dart';
-import 'package:sky/widgets/material.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/button_base.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/gesture_detector.dart';
+import 'package:sky/src/widgets/ink_well.dart';
+import 'package:sky/src/widgets/material.dart';
 
 // Rather than using this class directly, please use FlatButton or RaisedButton.
 abstract class MaterialButton extends ButtonBase {
diff --git a/sky/packages/sky/lib/widgets/mimic.dart b/sky/packages/sky/lib/src/widgets/mimic.dart
similarity index 97%
rename from sky/packages/sky/lib/widgets/mimic.dart
rename to sky/packages/sky/lib/src/widgets/mimic.dart
index b87028b..6d5f29a 100644
--- a/sky/packages/sky/lib/widgets/mimic.dart
+++ b/sky/packages/sky/lib/src/widgets/mimic.dart
@@ -2,8 +2,8 @@
 // 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';
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/framework.dart';
 
 abstract class GlobalKeyWatcher extends StatefulComponent {
   GlobalKeyWatcher({
diff --git a/sky/packages/sky/lib/widgets/mimic_overlay.dart b/sky/packages/sky/lib/src/widgets/mimic_overlay.dart
similarity index 94%
rename from sky/packages/sky/lib/widgets/mimic_overlay.dart
rename to sky/packages/sky/lib/src/widgets/mimic_overlay.dart
index ef09885..f4958d6 100644
--- a/sky/packages/sky/lib/widgets/mimic_overlay.dart
+++ b/sky/packages/sky/lib/src/widgets/mimic_overlay.dart
@@ -5,10 +5,10 @@
 import 'package:sky/animation/animated_value.dart';
 import 'package:sky/animation/animation_performance.dart';
 import 'package:sky/animation/curves.dart';
-import 'package:sky/widgets/animated_component.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/mimic.dart';
+import 'package:sky/src/widgets/animated_component.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/mimic.dart';
 
 class MimicOverlay extends AnimatedComponent {
   MimicOverlay({
diff --git a/sky/packages/sky/lib/widgets/mixed_viewport.dart b/sky/packages/sky/lib/src/widgets/mixed_viewport.dart
similarity index 98%
rename from sky/packages/sky/lib/widgets/mixed_viewport.dart
rename to sky/packages/sky/lib/src/widgets/mixed_viewport.dart
index 5bd9dcf..dd2b420 100644
--- a/sky/packages/sky/lib/widgets/mixed_viewport.dart
+++ b/sky/packages/sky/lib/src/widgets/mixed_viewport.dart
@@ -4,11 +4,11 @@
 
 import 'dart:collection';
 
-import 'package:sky/rendering/block.dart';
-import 'package:sky/rendering/box.dart';
-import 'package:sky/rendering/object.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/basic.dart';
+import 'package:sky/src/rendering/block.dart';
+import 'package:sky/src/rendering/box.dart';
+import 'package:sky/src/rendering/object.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/basic.dart';
 
 // return null if index is greater than index of last entry
 typedef Widget IndexedBuilder(int index);
diff --git a/sky/packages/sky/lib/widgets/modal_overlay.dart b/sky/packages/sky/lib/src/widgets/modal_overlay.dart
similarity index 75%
rename from sky/packages/sky/lib/widgets/modal_overlay.dart
rename to sky/packages/sky/lib/src/widgets/modal_overlay.dart
index 35595a8..16ed10f 100644
--- a/sky/packages/sky/lib/widgets/modal_overlay.dart
+++ b/sky/packages/sky/lib/src/widgets/modal_overlay.dart
@@ -2,9 +2,9 @@
 // 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';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/gesture_detector.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/gesture_detector.dart';
 
 class ModalOverlay extends Component {
 
diff --git a/sky/packages/sky/lib/widgets/navigator.dart b/sky/packages/sky/lib/src/widgets/navigator.dart
similarity index 96%
rename from sky/packages/sky/lib/widgets/navigator.dart
rename to sky/packages/sky/lib/src/widgets/navigator.dart
index b848a97..8fa3a11 100644
--- a/sky/packages/sky/lib/widgets/navigator.dart
+++ b/sky/packages/sky/lib/src/widgets/navigator.dart
@@ -5,10 +5,10 @@
 import 'package:sky/animation/animated_value.dart';
 import 'package:sky/animation/animation_performance.dart';
 import 'package:sky/animation/curves.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/focus.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/transitions.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/focus.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/transitions.dart';
 
 typedef Widget RouteBuilder(Navigator navigator, RouteBase route);
 
diff --git a/sky/packages/sky/lib/widgets/popup_menu.dart b/sky/packages/sky/lib/src/widgets/popup_menu.dart
similarity index 94%
rename from sky/packages/sky/lib/widgets/popup_menu.dart
rename to sky/packages/sky/lib/src/widgets/popup_menu.dart
index 89a9248..f08d7b7 100644
--- a/sky/packages/sky/lib/widgets/popup_menu.dart
+++ b/sky/packages/sky/lib/src/widgets/popup_menu.dart
@@ -9,12 +9,12 @@
 import 'package:sky/painting/box_painter.dart';
 import 'package:sky/theme/colors.dart';
 import 'package:sky/theme/shadows.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/navigator.dart';
-import 'package:sky/widgets/popup_menu_item.dart';
-import 'package:sky/widgets/scrollable.dart';
-import 'package:sky/widgets/transitions.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/navigator.dart';
+import 'package:sky/src/widgets/popup_menu_item.dart';
+import 'package:sky/src/widgets/scrollable.dart';
+import 'package:sky/src/widgets/transitions.dart';
 
 export 'package:sky/animation/animation_performance.dart' show AnimationStatus;
 
diff --git a/sky/packages/sky/lib/widgets/popup_menu_item.dart b/sky/packages/sky/lib/src/widgets/popup_menu_item.dart
similarity index 76%
rename from sky/packages/sky/lib/widgets/popup_menu_item.dart
rename to sky/packages/sky/lib/src/widgets/popup_menu_item.dart
index 1de7169..01cefe0 100644
--- a/sky/packages/sky/lib/widgets/popup_menu_item.dart
+++ b/sky/packages/sky/lib/src/widgets/popup_menu_item.dart
@@ -3,12 +3,12 @@
 // found in the LICENSE file.
 
 import 'package:sky/painting/text_style.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/default_text_style.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/gesture_detector.dart';
-import 'package:sky/widgets/ink_well.dart';
-import 'package:sky/widgets/theme.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/default_text_style.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/gesture_detector.dart';
+import 'package:sky/src/widgets/ink_well.dart';
+import 'package:sky/src/widgets/theme.dart';
 
 const double _kMenuItemHeight = 48.0;
 const double _kBaselineOffsetFromBottom = 20.0;
diff --git a/sky/packages/sky/lib/widgets/progress_indicator.dart b/sky/packages/sky/lib/src/widgets/progress_indicator.dart
similarity index 95%
rename from sky/packages/sky/lib/widgets/progress_indicator.dart
rename to sky/packages/sky/lib/src/widgets/progress_indicator.dart
index 0aa56df..3688e43 100644
--- a/sky/packages/sky/lib/widgets/progress_indicator.dart
+++ b/sky/packages/sky/lib/src/widgets/progress_indicator.dart
@@ -8,10 +8,10 @@
 import 'package:sky/animation/animation_performance.dart';
 import 'package:sky/animation/animated_value.dart';
 import 'package:sky/animation/curves.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/theme.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/transitions.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/theme.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/transitions.dart';
 
 const double _kLinearProgressIndicatorHeight = 6.0;
 const double _kMinCircularProgressIndicatorSize = 15.0;
diff --git a/sky/packages/sky/lib/widgets/radio.dart b/sky/packages/sky/lib/src/widgets/radio.dart
similarity index 87%
rename from sky/packages/sky/lib/widgets/radio.dart
rename to sky/packages/sky/lib/src/widgets/radio.dart
index 034a0cb..6592ee9 100644
--- a/sky/packages/sky/lib/widgets/radio.dart
+++ b/sky/packages/sky/lib/src/widgets/radio.dart
@@ -4,12 +4,12 @@
 
 import 'dart:sky' as sky;
 
-import 'package:sky/rendering/object.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/button_base.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/gesture_detector.dart';
-import 'package:sky/widgets/theme.dart';
+import 'package:sky/src/rendering/object.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/button_base.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/gesture_detector.dart';
+import 'package:sky/src/widgets/theme.dart';
 
 const sky.Color _kLightOffColor = const sky.Color(0x8A000000);
 const sky.Color _kDarkOffColor = const sky.Color(0xB2FFFFFF);
diff --git a/sky/packages/sky/lib/widgets/raised_button.dart b/sky/packages/sky/lib/src/widgets/raised_button.dart
similarity index 84%
rename from sky/packages/sky/lib/widgets/raised_button.dart
rename to sky/packages/sky/lib/src/widgets/raised_button.dart
index dffc878..23a8e25 100644
--- a/sky/packages/sky/lib/widgets/raised_button.dart
+++ b/sky/packages/sky/lib/src/widgets/raised_button.dart
@@ -3,10 +3,10 @@
 // found in the LICENSE file.
 
 import 'package:sky/theme/colors.dart' as colors;
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/material_button.dart';
-import 'package:sky/widgets/theme.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/material_button.dart';
+import 'package:sky/src/widgets/theme.dart';
 
 class RaisedButton extends MaterialButton {
 
diff --git a/sky/packages/sky/lib/widgets/scaffold.dart b/sky/packages/sky/lib/src/widgets/scaffold.dart
similarity index 97%
rename from sky/packages/sky/lib/widgets/scaffold.dart
rename to sky/packages/sky/lib/src/widgets/scaffold.dart
index c87c79c..ed3c29c 100644
--- a/sky/packages/sky/lib/widgets/scaffold.dart
+++ b/sky/packages/sky/lib/src/widgets/scaffold.dart
@@ -4,10 +4,10 @@
 
 import 'dart:sky' as sky;
 
-import 'package:sky/rendering/box.dart';
-import 'package:sky/rendering/object.dart';
+import 'package:sky/src/rendering/box.dart';
+import 'package:sky/src/rendering/object.dart';
 import 'package:sky/theme/view_configuration.dart';
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/widgets/framework.dart';
 
 // Slots are painted in this order and hit tested in reverse of this order
 enum ScaffoldSlots {
@@ -208,7 +208,7 @@
   }
 
   void insertChildRenderObject(RenderObjectWrapper child, ScaffoldSlots slot) {
-    renderObject[slot] = child != null ? child.renderObject : null;
+    renderObject[slot] = child?.renderObject;
   }
 
   void detachChildRenderObject(RenderObjectWrapper child) {
diff --git a/sky/packages/sky/lib/widgets/scrollable.dart b/sky/packages/sky/lib/src/widgets/scrollable.dart
similarity index 97%
rename from sky/packages/sky/lib/widgets/scrollable.dart
rename to sky/packages/sky/lib/src/widgets/scrollable.dart
index bdd8b80..253ab48 100644
--- a/sky/packages/sky/lib/widgets/scrollable.dart
+++ b/sky/packages/sky/lib/src/widgets/scrollable.dart
@@ -13,15 +13,15 @@
 import 'package:sky/animation/curves.dart';
 import 'package:sky/animation/scroll_behavior.dart';
 import 'package:sky/gestures/constants.dart';
-import 'package:sky/rendering/box.dart';
-import 'package:sky/rendering/viewport.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/gesture_detector.dart';
-import 'package:sky/widgets/homogeneous_viewport.dart';
-import 'package:sky/widgets/mixed_viewport.dart';
+import 'package:sky/src/rendering/box.dart';
+import 'package:sky/src/rendering/viewport.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/gesture_detector.dart';
+import 'package:sky/src/widgets/homogeneous_viewport.dart';
+import 'package:sky/src/widgets/mixed_viewport.dart';
 
-export 'package:sky/widgets/mixed_viewport.dart' show MixedViewportLayoutState;
+export 'package:sky/src/widgets/mixed_viewport.dart' show MixedViewportLayoutState;
 
 // The GestureEvent velocity properties are pixels/second, config min,max limits are pixels/ms
 const double _kMillisecondsPerSecond = 1000.0;
@@ -371,7 +371,13 @@
       itemExtent != source.itemExtent ||
       scrollDirection != source.scrollDirection;
 
+    if (itemsWrap != source.itemsWrap) {
+      _scrollBehavior = null;
+      scrollBehaviorUpdateNeeded = true;
+    }
+
     padding = source.padding;
+    itemsWrap = source.itemsWrap;
     itemExtent = source.itemExtent;
     super.syncConstructorArguments(source); // update scrollDirection
 
@@ -492,7 +498,6 @@
   void syncConstructorArguments(ScrollableList<T> source) {
     items = source.items;
     itemBuilder = source.itemBuilder;
-    itemsWrap = source.itemsWrap;
     super.syncConstructorArguments(source);
   }
 
@@ -558,10 +563,6 @@
       .clamp(scrollBehavior.minScrollOffset, scrollBehavior.maxScrollOffset);
   }
 
-  EventDisposition _handlePointerDown(_) {
-    return EventDisposition.ignored;
-  }
-
   EventDisposition _handleFlingStart(sky.GestureEvent event) {
     double velocity = _eventVelocity(event);
     double newScrollOffset = _snapScrollOffset(scrollOffset + velocity.sign * itemExtent)
diff --git a/sky/packages/sky/lib/widgets/sizing.md b/sky/packages/sky/lib/src/widgets/sizing.md
similarity index 100%
rename from sky/packages/sky/lib/widgets/sizing.md
rename to sky/packages/sky/lib/src/widgets/sizing.md
diff --git a/sky/packages/sky/lib/widgets/snack_bar.dart b/sky/packages/sky/lib/src/widgets/snack_bar.dart
similarity index 87%
rename from sky/packages/sky/lib/widgets/snack_bar.dart
rename to sky/packages/sky/lib/src/widgets/snack_bar.dart
index 4603fc8..2e5a521 100644
--- a/sky/packages/sky/lib/widgets/snack_bar.dart
+++ b/sky/packages/sky/lib/src/widgets/snack_bar.dart
@@ -8,13 +8,13 @@
 import 'package:sky/animation/curves.dart';
 import 'package:sky/painting/text_style.dart';
 import 'package:sky/theme/typography.dart' as typography;
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/default_text_style.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/gesture_detector.dart';
-import 'package:sky/widgets/material.dart';
-import 'package:sky/widgets/theme.dart';
-import 'package:sky/widgets/transitions.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/default_text_style.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/gesture_detector.dart';
+import 'package:sky/src/widgets/material.dart';
+import 'package:sky/src/widgets/theme.dart';
+import 'package:sky/src/widgets/transitions.dart';
 
 export 'package:sky/animation/animation_performance.dart' show AnimationStatus;
 
diff --git a/sky/packages/sky/lib/widgets/switch.dart b/sky/packages/sky/lib/src/widgets/switch.dart
similarity index 92%
rename from sky/packages/sky/lib/widgets/switch.dart
rename to sky/packages/sky/lib/src/widgets/switch.dart
index 573716d..50614dd 100644
--- a/sky/packages/sky/lib/widgets/switch.dart
+++ b/sky/packages/sky/lib/src/widgets/switch.dart
@@ -7,15 +7,15 @@
 
 import 'package:sky/painting/radial_reaction.dart';
 import 'package:sky/painting/shadows.dart';
-import 'package:sky/rendering/box.dart';
-import 'package:sky/rendering/object.dart';
+import 'package:sky/src/rendering/box.dart';
+import 'package:sky/src/rendering/object.dart';
 import 'package:sky/theme/shadows.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/theme.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/rendering/toggleable.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/theme.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/rendering/toggleable.dart';
 
-export 'package:sky/rendering/toggleable.dart' show ValueChanged;
+export 'package:sky/src/rendering/toggleable.dart' show ValueChanged;
 
 const sky.Color _kThumbOffColor = const sky.Color(0xFFFAFAFA);
 const sky.Color _kTrackOffColor = const sky.Color(0x42000000);
diff --git a/sky/packages/sky/lib/widgets/tabs.dart b/sky/packages/sky/lib/src/widgets/tabs.dart
similarity index 96%
rename from sky/packages/sky/lib/widgets/tabs.dart
rename to sky/packages/sky/lib/src/widgets/tabs.dart
index 0b78f85..804efe3 100644
--- a/sky/packages/sky/lib/widgets/tabs.dart
+++ b/sky/packages/sky/lib/src/widgets/tabs.dart
@@ -11,20 +11,20 @@
 import 'package:sky/animation/curves.dart';
 import 'package:sky/animation/scroll_behavior.dart';
 import 'package:sky/painting/text_style.dart';
-import 'package:sky/rendering/box.dart';
-import 'package:sky/rendering/object.dart';
-import 'package:sky/rendering/viewport.dart';
+import 'package:sky/src/rendering/box.dart';
+import 'package:sky/src/rendering/object.dart';
+import 'package:sky/src/rendering/viewport.dart';
 import 'package:sky/theme/colors.dart' as colors;
 import 'package:sky/theme/typography.dart' as typography;
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/default_text_style.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/gesture_detector.dart';
-import 'package:sky/widgets/icon.dart';
-import 'package:sky/widgets/ink_well.dart';
-import 'package:sky/widgets/scrollable.dart';
-import 'package:sky/widgets/theme.dart';
-import 'package:sky/widgets/transitions.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/default_text_style.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/gesture_detector.dart';
+import 'package:sky/src/widgets/icon.dart';
+import 'package:sky/src/widgets/ink_well.dart';
+import 'package:sky/src/widgets/scrollable.dart';
+import 'package:sky/src/widgets/theme.dart';
+import 'package:sky/src/widgets/transitions.dart';
 
 typedef void SelectedIndexChanged(int selectedIndex);
 typedef void LayoutChanged(Size size, List<double> widths);
diff --git a/sky/packages/sky/lib/widgets/theme.dart b/sky/packages/sky/lib/src/widgets/theme.dart
similarity index 93%
rename from sky/packages/sky/lib/widgets/theme.dart
rename to sky/packages/sky/lib/src/widgets/theme.dart
index eb568f9..36a1ac7 100644
--- a/sky/packages/sky/lib/widgets/theme.dart
+++ b/sky/packages/sky/lib/src/widgets/theme.dart
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import 'package:sky/theme/theme_data.dart';
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/widgets/framework.dart';
 
 export 'package:sky/theme/theme_data.dart' show ThemeData, ThemeBrightness;
 
diff --git a/sky/packages/sky/lib/widgets/title.dart b/sky/packages/sky/lib/src/widgets/title.dart
similarity index 81%
rename from sky/packages/sky/lib/widgets/title.dart
rename to sky/packages/sky/lib/src/widgets/title.dart
index f775529..be99b68 100644
--- a/sky/packages/sky/lib/widgets/title.dart
+++ b/sky/packages/sky/lib/src/widgets/title.dart
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 import 'package:sky/mojo/activity.dart';
-import 'package:sky/widgets/theme.dart';
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/widgets/theme.dart';
+import 'package:sky/src/widgets/framework.dart';
 
 class Title extends Component {
 
diff --git a/sky/packages/sky/lib/widgets/tool_bar.dart b/sky/packages/sky/lib/src/widgets/tool_bar.dart
similarity index 89%
rename from sky/packages/sky/lib/widgets/tool_bar.dart
rename to sky/packages/sky/lib/src/widgets/tool_bar.dart
index 9ac4821..8eff10e 100644
--- a/sky/packages/sky/lib/widgets/tool_bar.dart
+++ b/sky/packages/sky/lib/src/widgets/tool_bar.dart
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'package:sky/widgets/theme.dart';
+import 'package:sky/src/widgets/theme.dart';
 
 import 'package:sky/painting/text_style.dart';
-import 'package:sky/rendering/flex.dart';
+import 'package:sky/src/rendering/flex.dart';
 import 'package:sky/theme/shadows.dart';
 import 'package:sky/theme/typography.dart' as typography;
 import 'package:sky/theme/view_configuration.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/default_text_style.dart';
-import 'package:sky/widgets/framework.dart';
-import 'package:sky/widgets/icon.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/default_text_style.dart';
+import 'package:sky/src/widgets/framework.dart';
+import 'package:sky/src/widgets/icon.dart';
 
 class ToolBar extends Component {
 
diff --git a/sky/packages/sky/lib/widgets/transitions.dart b/sky/packages/sky/lib/src/widgets/transitions.dart
similarity index 96%
rename from sky/packages/sky/lib/widgets/transitions.dart
rename to sky/packages/sky/lib/src/widgets/transitions.dart
index 8144f83..d94a6b7 100644
--- a/sky/packages/sky/lib/widgets/transitions.dart
+++ b/sky/packages/sky/lib/src/widgets/transitions.dart
@@ -4,15 +4,13 @@
 
 import 'package:sky/animation/animated_value.dart';
 import 'package:sky/animation/animation_performance.dart';
-import 'package:sky/widgets/animated_component.dart';
-import 'package:sky/widgets/basic.dart';
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/widgets/animated_component.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/framework.dart';
 import 'package:vector_math/vector_math.dart';
 
 export 'package:sky/animation/direction.dart' show Direction;
 
-dynamic _maybe(AnimatedValue x) => x != null ? x.value : null;
-
 // A helper class to anchor widgets to one another. Pass an instance of this to
 // a Transition, then use the build() method to create a child with the same
 // transition applied.
@@ -270,7 +268,7 @@
       performance.updateVariable(width);
     if (height != null)
       performance.updateVariable(height);
-    return new SizedBox(width: _maybe(width), height: _maybe(height), child: child);
+    return new SizedBox(width: width?.value, height: height?.value, child: child);
   }
 }
 
diff --git a/sky/packages/sky/lib/widgets/widgets.md b/sky/packages/sky/lib/src/widgets/widgets.md
similarity index 100%
rename from sky/packages/sky/lib/widgets/widgets.md
rename to sky/packages/sky/lib/src/widgets/widgets.md
diff --git a/sky/packages/sky/lib/widgets.dart b/sky/packages/sky/lib/widgets.dart
index f38b6f9..44f290e 100644
--- a/sky/packages/sky/lib/widgets.dart
+++ b/sky/packages/sky/lib/widgets.dart
@@ -7,50 +7,50 @@
 /// The Sky widget framework
 library widgets;
 
-export 'package:sky/widgets/animated_component.dart';
-export 'package:sky/widgets/animated_container.dart';
-export 'package:sky/widgets/basic.dart';
-export 'package:sky/widgets/button_base.dart';
-export 'package:sky/widgets/card.dart';
-export 'package:sky/widgets/checkbox.dart';
-export 'package:sky/widgets/date_picker.dart';
-export 'package:sky/widgets/default_text_style.dart';
-export 'package:sky/widgets/dialog.dart';
-export 'package:sky/widgets/dismissable.dart';
-export 'package:sky/widgets/drag_target.dart';
-export 'package:sky/widgets/drawer.dart';
-export 'package:sky/widgets/drawer_divider.dart';
-export 'package:sky/widgets/drawer_header.dart';
-export 'package:sky/widgets/drawer_item.dart';
-export 'package:sky/widgets/flat_button.dart';
-export 'package:sky/widgets/floating_action_button.dart';
-export 'package:sky/widgets/focus.dart';
-export 'package:sky/widgets/framework.dart';
-export 'package:sky/widgets/gesture_detector.dart';
-export 'package:sky/widgets/homogeneous_viewport.dart';
-export 'package:sky/widgets/icon.dart';
-export 'package:sky/widgets/icon_button.dart';
-export 'package:sky/widgets/ink_well.dart';
-export 'package:sky/widgets/material.dart';
-export 'package:sky/widgets/material_button.dart';
-export 'package:sky/widgets/mimic.dart';
-export 'package:sky/widgets/mimic_overlay.dart';
-export 'package:sky/widgets/mixed_viewport.dart';
-export 'package:sky/widgets/modal_overlay.dart';
-export 'package:sky/widgets/navigator.dart';
-export 'package:sky/widgets/popup_menu.dart';
-export 'package:sky/widgets/popup_menu_item.dart';
-export 'package:sky/widgets/progress_indicator.dart';
-export 'package:sky/widgets/radio.dart';
-export 'package:sky/widgets/raised_button.dart';
-export 'package:sky/widgets/scaffold.dart';
-export 'package:sky/widgets/scrollable.dart';
-export 'package:sky/widgets/snack_bar.dart';
-export 'package:sky/widgets/switch.dart';
-export 'package:sky/widgets/tabs.dart';
-export 'package:sky/widgets/theme.dart';
-export 'package:sky/widgets/title.dart';
-export 'package:sky/widgets/tool_bar.dart';
-export 'package:sky/widgets/transitions.dart';
+export 'package:sky/src/widgets/animated_component.dart';
+export 'package:sky/src/widgets/animated_container.dart';
+export 'package:sky/src/widgets/basic.dart';
+export 'package:sky/src/widgets/button_base.dart';
+export 'package:sky/src/widgets/card.dart';
+export 'package:sky/src/widgets/checkbox.dart';
+export 'package:sky/src/widgets/date_picker.dart';
+export 'package:sky/src/widgets/default_text_style.dart';
+export 'package:sky/src/widgets/dialog.dart';
+export 'package:sky/src/widgets/dismissable.dart';
+export 'package:sky/src/widgets/drag_target.dart';
+export 'package:sky/src/widgets/drawer.dart';
+export 'package:sky/src/widgets/drawer_divider.dart';
+export 'package:sky/src/widgets/drawer_header.dart';
+export 'package:sky/src/widgets/drawer_item.dart';
+export 'package:sky/src/widgets/flat_button.dart';
+export 'package:sky/src/widgets/floating_action_button.dart';
+export 'package:sky/src/widgets/focus.dart';
+export 'package:sky/src/widgets/framework.dart';
+export 'package:sky/src/widgets/gesture_detector.dart';
+export 'package:sky/src/widgets/homogeneous_viewport.dart';
+export 'package:sky/src/widgets/icon.dart';
+export 'package:sky/src/widgets/icon_button.dart';
+export 'package:sky/src/widgets/ink_well.dart';
+export 'package:sky/src/widgets/material.dart';
+export 'package:sky/src/widgets/material_button.dart';
+export 'package:sky/src/widgets/mimic.dart';
+export 'package:sky/src/widgets/mimic_overlay.dart';
+export 'package:sky/src/widgets/mixed_viewport.dart';
+export 'package:sky/src/widgets/modal_overlay.dart';
+export 'package:sky/src/widgets/navigator.dart';
+export 'package:sky/src/widgets/popup_menu.dart';
+export 'package:sky/src/widgets/popup_menu_item.dart';
+export 'package:sky/src/widgets/progress_indicator.dart';
+export 'package:sky/src/widgets/radio.dart';
+export 'package:sky/src/widgets/raised_button.dart';
+export 'package:sky/src/widgets/scaffold.dart';
+export 'package:sky/src/widgets/scrollable.dart';
+export 'package:sky/src/widgets/snack_bar.dart';
+export 'package:sky/src/widgets/switch.dart';
+export 'package:sky/src/widgets/tabs.dart';
+export 'package:sky/src/widgets/theme.dart';
+export 'package:sky/src/widgets/title.dart';
+export 'package:sky/src/widgets/tool_bar.dart';
+export 'package:sky/src/widgets/transitions.dart';
 
 export 'package:vector_math/vector_math.dart' show Matrix4;
diff --git a/sky/packages/sky/pubspec.yaml b/sky/packages/sky/pubspec.yaml
index 9dcd96d..cc50dc4 100644
--- a/sky/packages/sky/pubspec.yaml
+++ b/sky/packages/sky/pubspec.yaml
@@ -5,7 +5,7 @@
 homepage: https://github.com/domokit/sky_engine/tree/master/sky/packages/sky
 dependencies:
   cassowary: ^0.1.7
-  material_design_icons: ^0.0.2
+  material_design_icons: ^0.0.3
   mojo_services: 0.0.23
   mojo: 0.0.23
   newton: ^0.1.2
@@ -15,4 +15,4 @@
   vector_math: ^1.4.3
   intl: ^0.12.4+2
 environment:
-  sdk: '>=1.8.0 <2.0.0'
+  sdk: '>=1.12.0 <2.0.0'
diff --git a/sky/shell/ios/sky_surface.h b/sky/shell/ios/sky_surface.h
index fea4f12..abed8ea 100644
--- a/sky/shell/ios/sky_surface.h
+++ b/sky/shell/ios/sky_surface.h
@@ -10,3 +10,7 @@
 -(instancetype) initWithShellView:(sky::shell::ShellView *) shellView;
 
 @end
+
+extern "C" {
+void SaveFrameToSkPicture();
+}
diff --git a/sky/shell/ios/sky_surface.mm b/sky/shell/ios/sky_surface.mm
index dc7d1ed..a0c78d6 100644
--- a/sky/shell/ios/sky_surface.mm
+++ b/sky/shell/ios/sky_surface.mm
@@ -385,3 +385,27 @@
 }
 
 @end
+
+void SaveFrameToSkPicture() {
+  @autoreleasepool {
+    NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
+                                                         NSUserDomainMask, YES);
+    NSString* basePath = paths.firstObject;
+
+    if (basePath.length == 0) {
+      return;
+    }
+
+    NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
+    [formatter setDateFormat:@"HH_mm_ss"];
+    NSString* dateString = [formatter stringFromDate:[NSDate date]];
+    [formatter release];
+
+    NSString* path =
+        [NSString stringWithFormat:@"%@/%@.trace.skp", basePath, dateString];
+
+    base::FilePath filePath(path.UTF8String);
+    sky::shell::Shell::Shared().tracing_controller().SaveFrameToSkPicture(
+        filePath);
+  }
+}
diff --git a/sky/shell/shell_view.cc b/sky/shell/shell_view.cc
index 1a8e1b3..09942f2 100644
--- a/sky/shell/shell_view.cc
+++ b/sky/shell/shell_view.cc
@@ -63,5 +63,11 @@
                             base::Passed(&producer)));
 }
 
+void ShellView::SaveFrameToSkPicture(base::FilePath& destination) {
+  shell_.ui_task_runner()->PostTask(
+      FROM_HERE, base::Bind(&Engine::SaveFrameToSkPicture,
+                            engine_->GetWeakPtr(), destination));
+}
+
 }  // namespace shell
 }  // namespace sky
diff --git a/sky/shell/shell_view.h b/sky/shell/shell_view.h
index e924971..ac3472f 100644
--- a/sky/shell/shell_view.h
+++ b/sky/shell/shell_view.h
@@ -5,6 +5,7 @@
 #ifndef SKY_SHELL_SHELL_VIEW_H_
 #define SKY_SHELL_SHELL_VIEW_H_
 
+#include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/memory/scoped_ptr.h"
 #include "mojo/public/cpp/system/data_pipe.h"
@@ -26,6 +27,8 @@
   void StartDartTracing();
   void StopDartTracing(mojo::ScopedDataPipeProducerHandle producer);
 
+  void SaveFrameToSkPicture(base::FilePath& destination);
+
  private:
   void CreateEngine();
   void CreatePlatformView();
diff --git a/sky/shell/tracing_controller.cc b/sky/shell/tracing_controller.cc
index b0696b3..84bcf16 100644
--- a/sky/shell/tracing_controller.cc
+++ b/sky/shell/tracing_controller.cc
@@ -108,6 +108,12 @@
   }
 }
 
+void TracingController::SaveFrameToSkPicture(base::FilePath& destination) {
+  if (view_ != nullptr) {
+    view_->SaveFrameToSkPicture(destination);
+  }
+}
+
 void TracingController::RegisterShellView(ShellView* view) {
   view_ = view;
 }
diff --git a/sky/shell/tracing_controller.h b/sky/shell/tracing_controller.h
index 4514cc8..e0a591d 100644
--- a/sky/shell/tracing_controller.h
+++ b/sky/shell/tracing_controller.h
@@ -35,6 +35,8 @@
   // be merged before viewing in the trace viewer
   void StopTracing(const base::FilePath& path);
 
+  void SaveFrameToSkPicture(base::FilePath& destination);
+
  private:
   std::unique_ptr<mojo::common::DataPipeDrainer> drainer_;
   std::unique_ptr<base::File> trace_file_;
diff --git a/sky/shell/ui/engine.cc b/sky/shell/ui/engine.cc
index b3a4fa8..b9a3efd 100644
--- a/sky/shell/ui/engine.cc
+++ b/sky/shell/ui/engine.cc
@@ -12,6 +12,7 @@
 #include "mojo/data_pipe_utils/data_pipe_utils.h"
 #include "mojo/public/cpp/application/connect.h"
 #include "services/asset_bundle/asset_unpacker_job.h"
+#include "sky/shell/gpu/picture_serializer.h"
 #include "sky/engine/public/platform/WebInputEvent.h"
 #include "sky/engine/public/platform/sky_display_metrics.h"
 #include "sky/engine/public/platform/sky_display_metrics.h"
@@ -270,5 +271,10 @@
   sky_view_->StopDartTracing(producer.Pass());
 }
 
+void Engine::SaveFrameToSkPicture(const base::FilePath& destination) {
+  PassRefPtr<SkPicture> picture = Paint();
+  SerializePicture(destination.AsUTF8Unsafe().c_str(), picture.get());
+}
+
 }  // namespace shell
 }  // namespace sky
diff --git a/sky/shell/ui/engine.h b/sky/shell/ui/engine.h
index 765a6e7..8b68230 100644
--- a/sky/shell/ui/engine.h
+++ b/sky/shell/ui/engine.h
@@ -5,6 +5,7 @@
 #ifndef SKY_SHELL_UI_ENGINE_H_
 #define SKY_SHELL_UI_ENGINE_H_
 
+#include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
@@ -61,6 +62,8 @@
   void StartDartTracing();
   void StopDartTracing(mojo::ScopedDataPipeProducerHandle producer);
 
+  void SaveFrameToSkPicture(const base::FilePath& destination);
+
  private:
   // UIDelegate implementation:
   void ConnectToEngine(mojo::InterfaceRequest<SkyEngine> request) override;
diff --git a/sky/tests/examples/card_collection.dart b/sky/tests/examples/card_collection.dart
index 5e00d0f..fdb4481 100644
--- a/sky/tests/examples/card_collection.dart
+++ b/sky/tests/examples/card_collection.dart
@@ -4,7 +4,7 @@
 
 import 'dart:async';
 
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/widgets/framework.dart';
 
 import '../../../examples/widgets/card_collection.dart';
 import '../resources/display_list.dart';
diff --git a/sky/tests/examples/overlay_geometry.dart b/sky/tests/examples/overlay_geometry.dart
index f88b11b..f0025e7 100644
--- a/sky/tests/examples/overlay_geometry.dart
+++ b/sky/tests/examples/overlay_geometry.dart
@@ -4,7 +4,7 @@
 
 import 'dart:async';
 
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/widgets/framework.dart';
 
 import '../../../examples/widgets/overlay_geometry.dart';
 import '../resources/display_list.dart';
diff --git a/sky/tests/examples/sector.dart b/sky/tests/examples/sector.dart
index 1c7af60..caabef1 100644
--- a/sky/tests/examples/sector.dart
+++ b/sky/tests/examples/sector.dart
@@ -4,7 +4,7 @@
 
 import 'dart:async';
 
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/widgets/framework.dart';
 
 import '../../../examples/widgets/sector.dart';
 import '../resources/display_list.dart';
diff --git a/sky/tests/examples/stocks.dart b/sky/tests/examples/stocks.dart
index 1e83a02..8af52f4 100644
--- a/sky/tests/examples/stocks.dart
+++ b/sky/tests/examples/stocks.dart
@@ -4,7 +4,7 @@
 
 import 'dart:async';
 
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/widgets/framework.dart';
 
 import '../../../examples/stocks/lib/main.dart';
 import '../../../examples/stocks/lib/stock_data.dart';
diff --git a/sky/tests/examples/styled_text.dart b/sky/tests/examples/styled_text.dart
index c0b0a3e..8d9f744 100644
--- a/sky/tests/examples/styled_text.dart
+++ b/sky/tests/examples/styled_text.dart
@@ -4,7 +4,7 @@
 
 import 'dart:async';
 
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/widgets/framework.dart';
 
 import '../../../examples/widgets/styled_text.dart';
 import '../resources/display_list.dart';
diff --git a/sky/tests/examples/tabs.dart b/sky/tests/examples/tabs.dart
index cdd1220..464bb06 100644
--- a/sky/tests/examples/tabs.dart
+++ b/sky/tests/examples/tabs.dart
@@ -4,7 +4,7 @@
 
 import 'dart:async';
 
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/widgets/framework.dart';
 
 import '../../../examples/widgets/tabs.dart';
 import '../resources/display_list.dart';
diff --git a/sky/tests/raw/render_flex.dart b/sky/tests/raw/render_flex.dart
index 41091f2..c6c7dc3 100644
--- a/sky/tests/raw/render_flex.dart
+++ b/sky/tests/raw/render_flex.dart
@@ -4,7 +4,7 @@
 
 import 'dart:sky' as sky;
 
-import 'package:sky/rendering/box.dart';
+import 'package:sky/rendering.dart';
 
 import '../../../examples/rendering/flex.dart';
 import '../resources/display_list.dart';
diff --git a/sky/tests/widgets/buttons.dart b/sky/tests/widgets/buttons.dart
index 767d6c1..7dde6d3 100644
--- a/sky/tests/widgets/buttons.dart
+++ b/sky/tests/widgets/buttons.dart
@@ -2,10 +2,10 @@
 // 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';
-import 'package:sky/widgets/flat_button.dart';
-import 'package:sky/widgets/floating_action_button.dart';
-import 'package:sky/widgets/raised_button.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/flat_button.dart';
+import 'package:sky/src/widgets/floating_action_button.dart';
+import 'package:sky/src/widgets/raised_button.dart';
 
 import '../resources/display_list.dart';
 
diff --git a/sky/tests/widgets/dialog.dart b/sky/tests/widgets/dialog.dart
index 38f4dca..8291b85 100644
--- a/sky/tests/widgets/dialog.dart
+++ b/sky/tests/widgets/dialog.dart
@@ -2,8 +2,8 @@
 // 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';
-import 'package:sky/widgets/dialog.dart';
+import 'package:sky/src/widgets/basic.dart';
+import 'package:sky/src/widgets/dialog.dart';
 
 import '../resources/display_list.dart';
 
diff --git a/sky/tests/widgets/rounded_border.dart b/sky/tests/widgets/rounded_border.dart
index c091d13..6261291 100644
--- a/sky/tests/widgets/rounded_border.dart
+++ b/sky/tests/widgets/rounded_border.dart
@@ -2,7 +2,7 @@
 // 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';
+import 'package:sky/src/widgets/basic.dart';
 
 import '../resources/display_list.dart';
 
diff --git a/sky/tests/widgets/stack_relayout_from_parent_data.dart b/sky/tests/widgets/stack_relayout_from_parent_data.dart
index f680cdd..eb72d43 100644
--- a/sky/tests/widgets/stack_relayout_from_parent_data.dart
+++ b/sky/tests/widgets/stack_relayout_from_parent_data.dart
@@ -2,7 +2,7 @@
 // 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';
+import 'package:sky/src/widgets/basic.dart';
 
 import '../resources/display_list.dart';
 
diff --git a/sky/tools/skyx/bin/skyx.dart b/sky/tools/skyx/bin/skyx.dart
index 897e11c..9239a27 100644
--- a/sky/tools/skyx/bin/skyx.dart
+++ b/sky/tools/skyx/bin/skyx.dart
@@ -61,7 +61,7 @@
     currentAssetDescriptor['density'] = density;
     for (String theme in generateValues(assetDescriptor, 'theme', kThemes)) {
       currentAssetDescriptor['theme'] = theme;
-      for (String size in generateValues(assetDescriptor, 'size', kSizes)) {
+      for (int size in generateValues(assetDescriptor, 'size', kSizes)) {
         currentAssetDescriptor['size'] = size;
         yield new MaterialAsset(currentAssetDescriptor);
       }
diff --git a/sky/tools/skyx/pubspec.yaml b/sky/tools/skyx/pubspec.yaml
index 2475627..ea305a0 100644
--- a/sky/tools/skyx/pubspec.yaml
+++ b/sky/tools/skyx/pubspec.yaml
@@ -4,6 +4,6 @@
   args: '>=0.13.0 <1.0.0'
   yaml: '>=2.1.3 <3.0.0'
 description: Developer tool for packaging Sky applications
-homepage: https://github.com/domokit/mojo/tree/master/sky/tools/skyx
+homepage: https://github.com/domokit/sky_engine/tree/master/sky/tools/skyx
 name: skyx
-version: 0.0.2
+version: 0.0.3
diff --git a/sky/unit/test/widget/pageable_list_test.dart b/sky/unit/test/widget/pageable_list_test.dart
index deeeafd..1886245 100644
--- a/sky/unit/test/widget/pageable_list_test.dart
+++ b/sky/unit/test/widget/pageable_list_test.dart
@@ -7,6 +7,7 @@
 const Size pageSize = const Size(800.0, 600.0);
 const List<int> pages = const <int>[0, 1, 2, 3, 4, 5];
 int currentPage = null;
+bool itemsWrap = false;
 
 Widget buildPage(int page) {
   return new Container(
@@ -17,7 +18,7 @@
   );
 }
 
-Widget buildFrame({ bool itemsWrap: false }) {
+Widget buildFrame() {
   // The test framework forces the frame (and so the PageableList)
   // to be 800x600. The pageSize constant reflects as much.
   return new PageableList<int>(
@@ -55,6 +56,7 @@
   test('Scroll left from page 0 to page 1', () {
     WidgetTester tester = new WidgetTester();
     currentPage = null;
+    itemsWrap = false;
     tester.pumpFrame(buildFrame);
     expect(currentPage, isNull);
     pageLeft(tester);
@@ -64,6 +66,7 @@
   test('Underscroll (scroll right), return to page 0', () {
     WidgetTester tester = new WidgetTester();
     currentPage = null;
+    itemsWrap = false;
     tester.pumpFrame(buildFrame);
     expect(currentPage, isNull);
     pageRight(tester);
@@ -72,10 +75,13 @@
 
   // PageableList with itemsWrap: true
 
+  itemsWrap = true;
+
   test('Scroll left page 0 to page 1, itemsWrap: true', () {
     WidgetTester tester = new WidgetTester();
     currentPage = null;
-    tester.pumpFrame(() { return buildFrame(itemsWrap: true); });
+    itemsWrap = true;
+    tester.pumpFrame(buildFrame);
     expect(currentPage, isNull);
     pageLeft(tester);
     expect(currentPage, equals(1));
@@ -84,7 +90,8 @@
   test('Scroll right from page 0 to page 5, itemsWrap: true', () {
     WidgetTester tester = new WidgetTester();
     currentPage = null;
-    tester.pumpFrame(() { return buildFrame(itemsWrap: true); });
+    itemsWrap = true;
+    tester.pumpFrame(buildFrame);
     expect(currentPage, isNull);
     pageRight(tester);
     expect(currentPage, equals(5));
diff --git a/skysprites/lib/skysprites.dart b/skysprites/lib/skysprites.dart
index e079cda..3bb57b1 100644
--- a/skysprites/lib/skysprites.dart
+++ b/skysprites/lib/skysprites.dart
@@ -15,10 +15,10 @@
 import 'package:sky/base/scheduler.dart' as scheduler;
 import 'package:sky/mojo/asset_bundle.dart';
 import 'package:sky/mojo/shell.dart' as shell;
-import 'package:sky/rendering/box.dart';
-import 'package:sky/rendering/object.dart';
 import 'package:sky/painting/text_painter.dart';
-import 'package:sky/widgets/framework.dart';
+import 'package:sky/src/rendering/box.dart';
+import 'package:sky/src/rendering/object.dart';
+import 'package:sky/src/widgets/framework.dart';
 import 'package:sky_services/media/media.mojom.dart';
 import 'package:vector_math/vector_math.dart';