croupier: Flutter Mega Upgrade

Many renames and API changes occurred to Flutter recently.
We have also been forced to move to Dart 1.15.0 (from Dart 1.14.0).

This means that we have these changes:
- A lot of annotations (@override), constructors before properties, and
  ${var} --> $var.
- Flutter-related renames (MainAxis, CrossAxis, StatelessWidget, StatefulWidget)
- Icons accessed via Icons.
- Image providers are ImageInfo now.
- Access the shell through a different filename.

- Shortcuts are made to work again, by depending on the local flutter checkout.
  FLUTTER_ROOT has become more important, and its "engine" artifacts are found
  there.

This will probably fail presubmit unless I update the Flutter manifest
to the new Flutter version.
March 31st: 4df24abb336ff5120eba3617d82b0022bbb77b0f

Change-Id: Ie8105d6886c03c779482f54374ab1e31349db4ca
diff --git a/FLUTTER_VERSION b/FLUTTER_VERSION
index f2d5799..cd75950 100644
--- a/FLUTTER_VERSION
+++ b/FLUTTER_VERSION
@@ -1 +1 @@
-1b75de1b164a3c5f668933a7603aa9094c181aae
+4df24abb336ff5120eba3617d82b0022bbb77b0f
diff --git a/Makefile b/Makefile
index 444e4c7..1ece130 100644
--- a/Makefile
+++ b/Makefile
@@ -5,9 +5,14 @@
 # Add Dart SDK to path.
 PATH := $(shell jiri profile env --profiles=v23:dart DART_SDK=)/bin:$(PATH)
 
+# Point to where FLUTTER_ROOT lives.
+export FLUTTER_ROOT := $(JIRI_ROOT)/flutter
+
 # This section is used to setup the environment for running with mojo_shell.
-CROUPIER_DIR := $(shell pwd)
-DISCOVERY_DIR := $(JIRI_ROOT)/release/mojo/discovery
+ifdef ANDROID
+	MOJO_DEVTOOLS := $(shell jiri profile env --profiles=v23:mojo --target=arm-android MOJO_DEVTOOLS=)
+	MOJO_SHELL := $(shell jiri profile env --profiles=v23:mojo --target=arm-android MOJO_SHELL=)
+endif
 SHELL := /bin/bash -euo pipefail
 
 # Flags for Syncbase service running as Mojo service.
@@ -33,7 +38,7 @@
 ifdef ANDROID
 	MOJO_ANDROID_FLAGS := --android
 	SYNCBASE_MOJO_BIN_DIR := packages/syncbase/mojo_services/android
-	DISCOVERY_MOJO_BIN_DIR := packages/v23discovery/mojo_services/android
+	DISCOVERY_MOJO_BIN_DIR := packages/v23discovery/mojo_services/arm_android
 
 	# Name to mount under.
 	SYNCBASE_NAME_FLAG := --name=$(MOUNTTABLE)/croupier-$(DEVICE_ID)
@@ -64,7 +69,7 @@
 export SYNCBASE_SERVER_URL := https://mojo.v.io/syncbase_server.mojo
 export DISCOVERY_SERVER_URL := https://mojo2.v.io/discovery.mojo
 MOJO_SHELL_FLAGS := --enable-multiprocess \
-	--map-origin="https://mojo2.v.io=$(DISCOVERY_MOJO_BIN_DIR)" --args-for="$(DISCOVERY_SERVER_URL) host$(DEVICE_ID)" \
+	--map-origin="https://mojo2.v.io=$(DISCOVERY_MOJO_BIN_DIR)" \
 	--map-origin="https://mojo.v.io=$(SYNCBASE_MOJO_BIN_DIR)" --args-for="$(SYNCBASE_SERVER_URL) $(SYNCBASE_FLAGS)"
 
 ifdef ANDROID
@@ -77,7 +82,7 @@
 	pub run flutter_tools -v run_mojo \
 	--app $1 \
 	$(MOJO_ANDROID_FLAGS) \
-	--mojo-path $(MOJO_DIR)/src \
+	--devtools-path $(MOJO_DEVTOOLS)/mojo_run \
 	--checked \
 	--mojo-debug \
 	-- $(TARGET_DEVICE_FLAG) \
@@ -93,6 +98,7 @@
 # Only `pub upgrade` can escape such a thing.
 packages: pubspec.yaml
 	cd $(JIRI_ROOT)/flutter && git show
+	cd $(JIRI_ROOT)/flutter && bin/flutter --version
 	pub upgrade
 
 # Builds mounttabled and principal.
@@ -112,14 +118,14 @@
 
 .PHONY: lint
 lint: packages
-	dartanalyzer lib/main.dart | grep -v "\[warning\] The imported libraries"
-	dartanalyzer $(DART_TEST_FILES) | grep -v "\[warning\] The imported libraries"
+	dartanalyzer lib/main.dart | grep -vE "(\[warning\]\ The\ imported\ libraries|TODO\(alexfandrianto\):)"
+	dartanalyzer $(DART_TEST_FILES) | grep -vE "(\[warning\]\ The\ imported\ libraries|TODO\(alexfandrianto\):)"
 
 .PHONY: build
 build: croupier.flx
 
 croupier.flx: packages $(DART_LIB_FILES_ALL)
-	pub run flutter_tools -v build --manifest manifest.yaml --output-file $@
+	pub run flutter_tools -v build flx --manifest manifest.yaml --output-file $@
 
 SETTINGS_FILE := /sdcard/croupier_settings.json
 SETTINGS_JSON := {\"deviceID\": \"$(DEVICE_ID)\", \"mounttable\": \"$(MOUNTTABLE)\"}
@@ -132,7 +138,7 @@
 # Starts the app on the specified ANDROID device.
 # Don't forget to make creds first if they are not present.
 .PHONY: start
-start: croupier.flx env-check packages push-settings
+start: croupier.flx install-shell env-check packages push-settings
 ifdef ANDROID
 	# Make creds dir if it does not exist.
 	mkdir -p creds
@@ -159,19 +165,15 @@
 # This file is developed separately from Croupier.
 .PHONY: deploy
 deploy: build
-	wget https://storage.googleapis.com/mojo_infra/flutter/9ee9721b1b0379da1dae7ed27243da7635d55a3a/android-arm/artifacts.zip
-	unzip artifacts.zip -d artifacts
 	gsutil cp $(APP_ICON) $(GS_BUCKET_PATH)/croupier
 	gsutil cp $(APP_FLX_FILE) $(GS_BUCKET_PATH)/croupier
-	gsutil cp $(PWD)/artifacts/flutter.mojo $(GS_BUCKET_PATH)/flutter
+	gsutil cp $(FLUTTER_ROOT)/bin/cache/artifacts/engine/android-arm/flutter.mojo $(GS_BUCKET_PATH)/flutter
 	gsutil cp -r $(SYNCBASE_MOJO_DIR) $(GS_BUCKET_PATH)/syncbase
 	gsutil cp -r $(DISCOVERY_MOJO_DIR) $(GS_BUCKET_PATH)/v23discovery
 	gsutil -m acl set -R -a public-read $(GS_BUCKET_PATH)/croupier
 	gsutil -m acl set -R -a public-read $(GS_BUCKET_PATH)/flutter
 	gsutil -m acl set -R -a public-read $(GS_BUCKET_PATH)/syncbase
 	gsutil -m acl set -R -a public-read $(GS_BUCKET_PATH)/v23discovery
-	rm artifacts.zip
-	rm -r artifacts
 
 CROUPIER_SHORTCUT_NAME := Croupier
 CROUPIER_URL := mojo://storage.googleapis.com/mojo_services/croupier/croupier.flx
@@ -185,7 +187,7 @@
 	shortcut_template > shortcut_commands
 endef
 
-shortcut: env-check push-settings
+shortcut: install-shell env-check push-settings
 ifdef ANDROID
 	# Create the shortcut file.
 	$(call GENERATE_SHORTCUT_FILE,$(DEVICE_ID),$(SYNCBASE_NAME_FLAG))
@@ -205,6 +207,18 @@
 	adb -s $(DEVICE_ID) shell rm -f $(MOJO_SHELL_CMD_PATH)
 endif
 
+.PHONY: install-shell
+install-shell: shortcut-remove
+ifdef ANDROID
+	adb -s $(DEVICE_ID) install $(MOJO_SHELL)
+endif
+
+.PHONY: uninstall-shell
+uninstall-shell: clear-shortcut
+ifdef ANDROID
+	adb -s $(DEVICE_ID) uninstall org.chromium.mojo.shell
+endif
+
 .PHONY: mock
 mock:
 	mv lib/src/syncbase/log_writer.dart lib/src/syncbase/log_writer.dart.backup
diff --git a/lib/components/board.dart b/lib/components/board.dart
index dc80c64..1bfd2eb 100644
--- a/lib/components/board.dart
+++ b/lib/components/board.dart
@@ -28,7 +28,14 @@
 /// While other Widgets may be drawn to accomodate space, a Board is meant to
 /// consume a specific amount of space on the screen, which allows for more
 /// control when positioning elements within the Board's area.
-abstract class Board extends StatefulComponent {
+abstract class Board extends StatefulWidget {
+  Board(this.game,
+      {double height, double width, double cardHeight, double cardWidth})
+      : _height = height,
+        _width = width,
+        _cardHeight = cardHeight,
+        _cardWidth = cardWidth;
+
   final Game game;
   final double _height;
   final double _width;
@@ -39,27 +46,11 @@
   double get width => _width ?? defaultBoardWidth;
   double get cardHeight => _cardHeight ?? defaultCardHeight;
   double get cardWidth => _cardWidth ?? defaultCardWidth;
-
-  Board(this.game,
-      {double height, double width, double cardHeight, double cardWidth})
-      : _height = height,
-        _width = width,
-        _cardHeight = cardHeight,
-        _cardWidth = cardWidth;
 }
 
 /// The HeartsBoard represents the Hearts table view, which shows the number of
 /// cards each player has, and the cards they are currently playing.
 class HeartsBoard extends Board {
-  final Croupier croupier;
-  final SoundAssets sounds;
-  final bool isMini;
-  final AcceptCb gameAcceptCallback;
-  final VoidCallback setGameStateCallback;
-  final List<logic_card.Card> bufferedPlay;
-
-  HeartsGame get game => super.game;
-
   HeartsBoard(Croupier croupier, this.sounds,
       {double height,
       double width,
@@ -69,15 +60,26 @@
       this.gameAcceptCallback,
       this.setGameStateCallback,
       this.bufferedPlay})
-      : super(croupier.game,
+      : croupier = croupier,
+        super(croupier.game,
             height: height,
             width: width,
             cardHeight: cardHeight,
-            cardWidth: cardWidth),
-        croupier = croupier {
+            cardWidth: cardWidth) {
     assert(this.game is HeartsGame);
   }
 
+  final Croupier croupier;
+  final SoundAssets sounds;
+  final bool isMini;
+  final AcceptCb gameAcceptCallback;
+  final VoidCallback setGameStateCallback;
+  final List<logic_card.Card> bufferedPlay;
+
+  @override
+  HeartsGame get game => super.game;
+
+  @override
   HeartsBoardState createState() => new HeartsBoardState();
 }
 
@@ -162,6 +164,7 @@
     }
   }
 
+  @override
   Widget build(BuildContext context) {
     double offscreenDelta = config.isMini ? 5.0 : 1.5;
 
@@ -290,33 +293,34 @@
         width: config.width,
         child: new Column(
             children: [
-          new Flexible(child: _playerProfile(2, PROFILE_SIZE), flex: 0),
-          new Flexible(child: _getPass(2), flex: 0),
-          new Flexible(
-              child: new Row(
-                  children: [
-                new Flexible(child: _playerProfile(1, PROFILE_SIZE), flex: 0),
-                new Flexible(child: _getPass(1), flex: 0),
-                new Flexible(child: new Block(children: []), flex: 1),
-                new Flexible(child: _getPass(3), flex: 0),
-                new Flexible(child: _playerProfile(3, PROFILE_SIZE), flex: 0)
-              ],
-                  alignItems: FlexAlignItems.center,
-                  justifyContent: FlexJustifyContent.spaceAround),
-              flex: 1),
-          new Flexible(child: _getPass(0), flex: 0),
-          new Flexible(child: _playerProfile(0, PROFILE_SIZE), flex: 0)
-        ],
-            alignItems: FlexAlignItems.center,
-            justifyContent: FlexJustifyContent.spaceAround));
+              new Flexible(child: _playerProfile(2, PROFILE_SIZE), flex: 0),
+              new Flexible(child: _getPass(2), flex: 0),
+              new Flexible(
+                  child: new Row(
+                      children: [
+                        new Flexible(
+                            child: _playerProfile(1, PROFILE_SIZE), flex: 0),
+                        new Flexible(child: _getPass(1), flex: 0),
+                        new Flexible(child: new Block(children: []), flex: 1),
+                        new Flexible(child: _getPass(3), flex: 0),
+                        new Flexible(
+                            child: _playerProfile(3, PROFILE_SIZE), flex: 0)
+                      ],
+                      crossAxisAlignment: CrossAxisAlignment.center,
+                      mainAxisAlignment: MainAxisAlignment.spaceAround),
+                  flex: 1),
+              new Flexible(child: _getPass(0), flex: 0),
+              new Flexible(child: _playerProfile(0, PROFILE_SIZE), flex: 0)
+            ],
+            crossAxisAlignment: CrossAxisAlignment.center,
+            mainAxisAlignment: MainAxisAlignment.spaceAround));
   }
 
   Widget _buildMiniBoardLayout() {
     return new Container(
         height: config.height,
         width: config.width,
-        child: new Center(
-            child: new Row(children: [
+        child: new Center(child: new Row(children: [
           new Flexible(
               flex: 1,
               child: new Center(
@@ -371,12 +375,10 @@
       items.add(new Positioned(
           top: 0.0,
           left: 0.0,
-          child: new IgnorePointer(
-              child: new CroupierProfileComponent.mini(
-                  settings:
-                      config.croupier.settingsFromPlayerNumber(playerNumber),
-                  height: config.cardHeight,
-                  width: config.cardWidth))));
+          child: new IgnorePointer(child: new CroupierProfileComponent.mini(
+              settings: config.croupier.settingsFromPlayerNumber(playerNumber),
+              height: config.cardHeight,
+              width: config.cardWidth))));
     }
 
     return new Container(
@@ -394,7 +396,7 @@
 
     String s = numTricks != 1 ? "s" : "";
 
-    return _rotate(new Text("${numTricks} trick${s}"), pNum);
+    return _rotate(new Text("$numTricks trick$s"), pNum);
   }
 
   void _handleLocalAskingReset() {
@@ -442,52 +444,53 @@
         child: new Container(
             height: config.height,
             width: config.width,
-            decoration: new BoxDecoration(
-                border: new Border(
-                    top: new BorderSide(
-                        color: activePlayer == 2
-                            ? style.theme.accentColor
-                            : style.transparentColor,
-                        width: 5.0),
-                    right: new BorderSide(
-                        color: activePlayer == 3
-                            ? style.theme.accentColor
-                            : style.transparentColor,
-                        width: 5.0),
-                    left: new BorderSide(
-                        color: activePlayer == 1
-                            ? style.theme.accentColor
-                            : style.transparentColor,
-                        width: 5.0),
-                    bottom: new BorderSide(
-                        color: activePlayer == 0
-                            ? style.theme.accentColor
-                            : style.transparentColor,
-                        width: 5.0))),
+            decoration: new BoxDecoration(border: new Border(
+                top: new BorderSide(
+                    color: activePlayer == 2
+                        ? style.theme.accentColor
+                        : style.transparentColor,
+                    width: 5.0),
+                right: new BorderSide(
+                    color: activePlayer == 3
+                        ? style.theme.accentColor
+                        : style.transparentColor,
+                    width: 5.0),
+                left: new BorderSide(
+                    color: activePlayer == 1
+                        ? style.theme.accentColor
+                        : style.transparentColor,
+                    width: 5.0),
+                bottom: new BorderSide(
+                    color: activePlayer == 0
+                        ? style.theme.accentColor
+                        : style.transparentColor,
+                    width: 5.0))),
             child: new Column(
                 children: [
-              new Flexible(child: _playerProfile(2, PROFILE_SIZE), flex: 0),
-              new Flexible(child: _showTrickText(2), flex: 0),
-              new Flexible(
-                  child: new Row(
-                      children: [
-                    new Flexible(
-                        child: _playerProfile(1, PROFILE_SIZE), flex: 0),
-                    new Flexible(child: _showTrickText(1), flex: 0),
-                    new Flexible(
-                        child: new Center(child: _buildCenterCards()), flex: 1),
-                    new Flexible(child: _showTrickText(3), flex: 0),
-                    new Flexible(
-                        child: _playerProfile(3, PROFILE_SIZE), flex: 0)
-                  ],
-                      alignItems: FlexAlignItems.center,
-                      justifyContent: FlexJustifyContent.spaceAround),
-                  flex: 1),
-              new Flexible(child: _showTrickText(0), flex: 0),
-              new Flexible(child: _playerProfile(0, PROFILE_SIZE), flex: 0)
-            ],
-                alignItems: FlexAlignItems.center,
-                justifyContent: FlexJustifyContent.spaceAround)));
+                  new Flexible(child: _playerProfile(2, PROFILE_SIZE), flex: 0),
+                  new Flexible(child: _showTrickText(2), flex: 0),
+                  new Flexible(
+                      child: new Row(
+                          children: [
+                            new Flexible(
+                                child: _playerProfile(1, PROFILE_SIZE),
+                                flex: 0),
+                            new Flexible(child: _showTrickText(1), flex: 0),
+                            new Flexible(
+                                child: new Center(child: _buildCenterCards()),
+                                flex: 1),
+                            new Flexible(child: _showTrickText(3), flex: 0),
+                            new Flexible(
+                                child: _playerProfile(3, PROFILE_SIZE), flex: 0)
+                          ],
+                          crossAxisAlignment: CrossAxisAlignment.center,
+                          mainAxisAlignment: MainAxisAlignment.spaceAround),
+                      flex: 1),
+                  new Flexible(child: _showTrickText(0), flex: 0),
+                  new Flexible(child: _playerProfile(0, PROFILE_SIZE), flex: 0)
+                ],
+                crossAxisAlignment: CrossAxisAlignment.center,
+                mainAxisAlignment: MainAxisAlignment.spaceAround)));
   }
 
   Widget _buildCenterCards() {
@@ -517,40 +520,36 @@
 
     return new Column(
         children: [
-      new Flexible(
-          child: new Row(
+          new Flexible(child: new Row(
               children: [
-        new Flexible(child: new Block(children: [])),
-        new Flexible(child: new Center(child: _buildCenterCard(2))),
-        new Flexible(child: new Block(children: [])),
-      ],
-              alignItems: FlexAlignItems.center,
-              justifyContent: FlexJustifyContent.center)),
-      new Flexible(
-          child: new Row(
+                new Flexible(child: new Block(children: [])),
+                new Flexible(child: new Center(child: _buildCenterCard(2))),
+                new Flexible(child: new Block(children: [])),
+              ],
+              crossAxisAlignment: CrossAxisAlignment.center,
+              mainAxisAlignment: MainAxisAlignment.center)),
+          new Flexible(child: new Row(
               children: [
-        new Flexible(child: new Center(child: _buildCenterCard(1))),
-        new Flexible(
-            child: new Row(
-                children: [centerPiece],
-                alignItems: FlexAlignItems.center,
-                justifyContent: FlexJustifyContent.center)),
-        new Flexible(child: new Center(child: _buildCenterCard(3))),
-      ],
-              alignItems: FlexAlignItems.center,
-              justifyContent: FlexJustifyContent.center)),
-      new Flexible(
-          child: new Row(
+                new Flexible(child: new Center(child: _buildCenterCard(1))),
+                new Flexible(child: new Row(
+                    children: [centerPiece],
+                    crossAxisAlignment: CrossAxisAlignment.center,
+                    mainAxisAlignment: MainAxisAlignment.center)),
+                new Flexible(child: new Center(child: _buildCenterCard(3))),
+              ],
+              crossAxisAlignment: CrossAxisAlignment.center,
+              mainAxisAlignment: MainAxisAlignment.center)),
+          new Flexible(child: new Row(
               children: [
-        new Flexible(child: new Block(children: [])),
-        new Flexible(child: new Center(child: _buildCenterCard(0))),
-        new Flexible(child: new Block(children: [])),
-      ],
-              alignItems: FlexAlignItems.center,
-              justifyContent: FlexJustifyContent.center))
-    ],
-        alignItems: FlexAlignItems.center,
-        justifyContent: FlexJustifyContent.center);
+                new Flexible(child: new Block(children: [])),
+                new Flexible(child: new Center(child: _buildCenterCard(0))),
+                new Flexible(child: new Block(children: [])),
+              ],
+              crossAxisAlignment: CrossAxisAlignment.center,
+              mainAxisAlignment: MainAxisAlignment.center))
+        ],
+        crossAxisAlignment: CrossAxisAlignment.center,
+        mainAxisAlignment: MainAxisAlignment.center);
   }
 
   double get _centerScaleFactor {
diff --git a/lib/components/card.dart b/lib/components/card.dart
index afaddbf..0edbf0d 100644
--- a/lib/components/card.dart
+++ b/lib/components/card.dart
@@ -21,6 +21,7 @@
 
   GlobalCardKey(this.card, this.type) : super.constructor();
 
+  @override
   bool operator ==(Object other) {
     if (other is! GlobalCardKey) {
       return false;
@@ -29,12 +30,13 @@
     return k.card == card && k.type == type;
   }
 
+  @override
   int get hashCode {
     return 17 * card.hashCode + 33 * type.hashCode;
   }
 }
 
-class ZCard extends widgets.StatefulComponent {
+class ZCard extends widgets.StatefulWidget {
   final logic_card.Card card;
   final bool faceUp;
   final double width;
@@ -48,19 +50,20 @@
   final Point endingPosition;
 
   ZCard(Card dataComponent, this.startingPosition, this.endingPosition)
-      : super(key: new GlobalCardKey(dataComponent.card, CardUIType.zCard)),
-        card = dataComponent.card,
+      : card = dataComponent.card,
         faceUp = dataComponent.faceUp,
         width = dataComponent.width ?? 40.0,
         height = dataComponent.height ?? 40.0,
         rotation = dataComponent.rotation ?? 0.0,
         animationType = dataComponent.animationType,
-        z = dataComponent.z;
+        z = dataComponent.z,
+        super(key: new GlobalCardKey(dataComponent.card, CardUIType.zCard));
 
+  @override
   ZCardState createState() => new ZCardState();
 }
 
-class Card extends widgets.StatefulComponent {
+class Card extends widgets.StatefulWidget {
   final logic_card.Card card;
   final bool faceUp;
   final double width;
@@ -116,6 +119,7 @@
         c.z == z;
   }
 
+  @override
   CardState createState() => new CardState();
 }
 
@@ -125,6 +129,7 @@
     return box.localToGlobal(Point.origin);
   }
 
+  @override
   widgets.Widget build(widgets.BuildContext context) {
     widgets.Widget image = new widgets.GestureDetector(
         onTap: config.tapCallback != null
@@ -154,8 +159,8 @@
 class ZCardState extends widgets.State<ZCard> {
   Tween<Point> _positionTween;
   AnimationController _animationController;
-  List<
-      Point> _pointQueue; // at least 1 longer than the current animation index.
+  List<Point>
+      _pointQueue; // at least 1 longer than the current animation index.
   int _animationIndex;
 
   @override
@@ -244,12 +249,12 @@
       Point endingLocation = _pointQueue[_animationIndex + 1];
       _positionTween =
           new Tween<Point>(begin: startingLocation, end: endingLocation);
-      _animationController.value = 0.0;
       _animationController.duration = this.animationDuration;
-      _animationController.play(AnimationDirection.forward);
+      _animationController.forward(from: 0.0);
     }
   }
 
+  @override
   widgets.Widget build(widgets.BuildContext context) {
     widgets.Widget image = new widgets.Transform(
         child: _imageFromCard(
@@ -261,10 +266,11 @@
     widgets.Widget retWidget = new widgets.AnimatedBuilder(
         animation: _animationController,
         builder: (widgets.BuildContext c, widgets.Widget child) {
-      Matrix4 transform = new Matrix4.identity()
-        ..translate(localPosition.x, localPosition.y);
-      return new widgets.Transform(transform: transform, child: child);
-    }, child: image);
+          Matrix4 transform = new Matrix4.identity()
+            ..translate(localPosition.x, localPosition.y);
+          return new widgets.Transform(transform: transform, child: child);
+        },
+        child: image);
 
     return retWidget;
   }
diff --git a/lib/components/card_collection.dart b/lib/components/card_collection.dart
index 2227b2e..6d969ef 100644
--- a/lib/components/card_collection.dart
+++ b/lib/components/card_collection.dart
@@ -28,29 +28,7 @@
 const double WHITE_LINE_HEIGHT = 2.0; // white
 const double WHITE_LINE_MARGIN = 4.0; // each side
 
-class CardCollectionComponent extends StatefulComponent {
-  final List<logic_card.Card> cards;
-  final CardCollectionOrientation orientation;
-  final bool faceUp;
-  final AcceptCb acceptCallback;
-  final bool dragChildren;
-  final component_card.TapCallback cardTapCallback;
-  final DropType _acceptType;
-  final Comparator<logic_card.Card> comparator;
-  final double width;
-  final double height;
-  final double widthCard;
-  final double heightCard;
-  final Color _backgroundColor;
-  final Color _altColor;
-  final double rotation; // This angle is in radians.
-  final bool useKeys; // If set, every Card created in this collection will be keyed.
-  final component_card.CardAnimationType animationType;
-
-  DropType get acceptType => _acceptType ?? DropType.none;
-  Color get backgroundColor => _backgroundColor ?? Colors.grey[500];
-  Color get altColor => _altColor ?? Colors.grey[500];
-
+class CardCollectionComponent extends StatefulWidget {
   CardCollectionComponent(this.cards, this.faceUp, this.orientation,
       {this.dragChildren: false,
       this.cardTapCallback: null,
@@ -70,8 +48,33 @@
         _backgroundColor = backgroundColor,
         _altColor = altColor;
 
-  CardCollectionComponentState createState() =>
-      new CardCollectionComponentState();
+  final List<logic_card.Card> cards;
+  final CardCollectionOrientation orientation;
+  final bool faceUp;
+  final AcceptCb acceptCallback;
+  final bool dragChildren;
+  final component_card.TapCallback cardTapCallback;
+  final DropType _acceptType;
+  final Comparator<logic_card.Card> comparator;
+  final double width;
+  final double height;
+  final double widthCard;
+  final double heightCard;
+  final Color _backgroundColor;
+  final Color _altColor;
+  final double rotation; // This angle is in radians.
+  final bool
+      useKeys; // If set, every Card created in this collection will be keyed.
+  final component_card.CardAnimationType animationType;
+
+  DropType get acceptType => _acceptType ?? DropType.none;
+  Color get backgroundColor => _backgroundColor ?? Colors.grey[500];
+  Color get altColor => _altColor ?? Colors.grey[500];
+
+  @override
+  CardCollectionComponentState createState() {
+    return new CardCollectionComponentState();
+  }
 }
 
 class CardCollectionComponentState extends State<CardCollectionComponent> {
@@ -193,15 +196,14 @@
               new BoxDecoration(backgroundColor: config.backgroundColor),
           height: _produceRowHeight,
           width: config.width,
-          child: new Center(
-              child: new Opacity(
-                  opacity: 0.45,
-                  child: emptyBackgroundImage == ""
-                      ? null
-                      : new AssetImage(
-                          name: emptyBackgroundImage,
-                          fit: ImageFit.scaleDown,
-                          height: config.heightCard))));
+          child: new Center(child: new Opacity(
+              opacity: 0.45,
+              child: emptyBackgroundImage == ""
+                  ? null
+                  : new AssetImage(
+                      name: emptyBackgroundImage,
+                      fit: ImageFit.scaleDown,
+                      height: config.heightCard))));
     }
 
     double w = config.width ?? config.widthCard * 5;
@@ -300,9 +302,8 @@
     }
   }
 
-  Widget build(BuildContext context) {
-    return _buildCollection();
-  }
+  @override
+  Widget build(BuildContext context) => _buildCollection();
 
   Widget _buildCollection() {
     List<component_card.Card> cardComponents = new List<component_card.Card>();
@@ -336,29 +337,31 @@
             child: wrapCards(cardComponents));
       case DropType.card:
         return new DragTarget<component_card.Card>(
-            onWillAccept: _handleWillAccept, onAccept: _handleAccept,
+            onWillAccept: _handleWillAccept,
+            onAccept: _handleAccept,
             builder: (BuildContext context, List<component_card.Card> data, _) {
-          return new Container(
-              decoration: new BoxDecoration(
-                  backgroundColor:
-                      data.isEmpty ? config.backgroundColor : config.altColor),
-              height: this.desiredHeight,
-              width: this.desiredWidth,
-              child: wrapCards(cardComponents));
-        });
+              return new Container(
+                  decoration: new BoxDecoration(backgroundColor: data.isEmpty
+                      ? config.backgroundColor
+                      : config.altColor),
+                  height: this.desiredHeight,
+                  width: this.desiredWidth,
+                  child: wrapCards(cardComponents));
+            });
       case DropType.cardCollection:
         return new DragTarget<CardCollectionComponent>(
             onWillAccept: _handleWillAcceptMultiple,
-            onAccept: _handleAcceptMultiple, builder:
+            onAccept: _handleAcceptMultiple,
+            builder:
                 (BuildContext context, List<CardCollectionComponent> data, _) {
-          return new Container(
-              decoration: new BoxDecoration(
-                  backgroundColor:
-                      data.isEmpty ? config.backgroundColor : config.altColor),
-              height: this.desiredHeight,
-              width: this.desiredWidth,
-              child: wrapCards(cardComponents));
-        });
+              return new Container(
+                  decoration: new BoxDecoration(backgroundColor: data.isEmpty
+                      ? config.backgroundColor
+                      : config.altColor),
+                  height: this.desiredHeight,
+                  width: this.desiredWidth,
+                  child: wrapCards(cardComponents));
+            });
       default:
         assert(false);
         return null;
diff --git a/lib/components/croupier.dart b/lib/components/croupier.dart
index 9b46dda..f6152f8 100644
--- a/lib/components/croupier.dart
+++ b/lib/components/croupier.dart
@@ -19,12 +19,13 @@
 GlobalObjectKey _gameKey = new GlobalObjectKey("CroupierGameKey");
 GlobalObjectKey _gameArrangeKey = new GlobalObjectKey("CroupierGameArrangeKey");
 
-class CroupierComponent extends StatefulComponent {
+class CroupierComponent extends StatefulWidget {
   final logic_croupier.Croupier croupier;
   final SoundAssets sounds;
 
   CroupierComponent(this.croupier, this.sounds);
 
+  @override
   CroupierComponentState createState() => new CroupierComponentState();
 }
 
@@ -36,12 +37,13 @@
         });
   }
 
+  @override
   Widget build(BuildContext context) {
     switch (config.croupier.state) {
       case logic_croupier.CroupierState.welcome:
         // in which we show them a UI to start a new game, join a game, or change some settings.
         return new Container(
-            padding: new EdgeDims.only(top: ui.window.padding.top),
+            padding: new EdgeInsets.only(top: ui.window.padding.top),
             child: new Column(children: [
               new FlatButton(
                   child: new Text('Create Game', style: style.Text.titleStyle),
@@ -66,7 +68,7 @@
       case logic_croupier.CroupierState.chooseGame:
         // in which we let them pick a game out of the many possible games... There aren't that many.
         return new Container(
-            padding: new EdgeDims.only(top: ui.window.padding.top),
+            padding: new EdgeInsets.only(top: ui.window.padding.top),
             child: new Column(children: [
               new FlatButton(
                   child: new Text('Proto', style: style.Text.titleStyle),
@@ -93,7 +95,7 @@
       case logic_croupier.CroupierState.joinGame:
         // A stateful view, showing the game ads discovered.
         List<Widget> gameAdWidgets = new List<Widget>();
-        if (config.croupier.games_found.length == 0) {
+        if (config.croupier.gamesFound.length == 0) {
           gameAdWidgets.add(
               new Text("Looking for Games...", style: style.Text.titleStyle));
         } else {
@@ -101,9 +103,9 @@
               .add(new Text("Available Games", style: style.Text.titleStyle));
         }
 
-        config.croupier.games_found
+        config.croupier.gamesFound
             .forEach((String _, logic_game.GameStartData gsd) {
-          CroupierSettings cs = config.croupier.settings_everyone[gsd.ownerID];
+          CroupierSettings cs = config.croupier.settingsEveryone[gsd.ownerID];
           gameAdWidgets.add(new CroupierGameAdvertisementComponent(gsd,
               onTap: makeSetStateCallback(
                   logic_croupier.CroupierState.arrangePlayers, gsd),
@@ -117,15 +119,15 @@
 
         // in which players wait for game invitations to arrive.
         return new Container(
-            padding: new EdgeDims.only(top: ui.window.padding.top),
+            padding: new EdgeInsets.only(top: ui.window.padding.top),
             child: new Column(children: gameAdWidgets));
       case logic_croupier.CroupierState.arrangePlayers:
         return new Container(
-            padding: new EdgeDims.only(top: ui.window.padding.top),
+            padding: new EdgeInsets.only(top: ui.window.padding.top),
             child: _buildArrangePlayers());
       case logic_croupier.CroupierState.playGame:
         return new Container(
-            padding: new EdgeDims.only(top: ui.window.padding.top),
+            padding: new EdgeInsets.only(top: ui.window.padding.top),
             child: component_game.createGameComponent(
                 config.croupier,
                 config.sounds,
@@ -136,7 +138,7 @@
 
       case logic_croupier.CroupierState.resumeGame:
         return new Container(
-            padding: new EdgeDims.only(top: ui.window.padding.top),
+            padding: new EdgeInsets.only(top: ui.window.padding.top),
             child: new Text("Resuming Game...", style: style.Text.titleStyle),
             width: ui.window.size.width,
             height: ui.window.size.height - ui.window.padding.top);
@@ -150,9 +152,9 @@
   // shown if the person has not sat down yet.
   Widget _buildPlayerProfiles(bool needsArrangement) {
     List<Widget> profileWidgets = new List<Widget>();
-    config.croupier.players_found.forEach((int userID, int playerNumber) {
+    config.croupier.playersFound.forEach((int userID, int playerNumber) {
       // Note: Even if cs is null, a placeholder will be shown instead.
-      CroupierSettings cs = config.croupier.settings_everyone[userID];
+      CroupierSettings cs = config.croupier.settingsEveryone[userID];
       bool isMe = config.croupier.settings.userID == userID;
       Widget cpc = new Container(
           decoration: isMe ? style.Box.liveNow : null,
@@ -176,21 +178,24 @@
           scrollDirection: Axis.horizontal);
     }
     return new MaxTileWidthGrid(
-        children: profileWidgets, maxTileWidth: style.Size.settingsWidth);
+        children: profileWidgets,
+        maxTileWidth: style.Size.settingsWidth,
+        columnSpacing: 0.0,
+        rowSpacing: 0.0);
   }
 
   Widget _buildArrangePlayers() {
     List<Widget> allWidgets = new List<Widget>();
 
     logic_game.GameArrangeData gad = config.croupier.game.gameArrangeData;
-    Iterable<int> playerNumbers = config.croupier.players_found.values;
+    Iterable<int> playerNumbers = config.croupier.playersFound.values;
 
     allWidgets.add(new Flexible(
         flex: 0,
         child: new Row(children: [
           new Text("${config.croupier.game.gameTypeName}",
               style: style.Text.hugeStyle)
-        ], justifyContent: FlexJustifyContent.spaceAround)));
+        ], mainAxisAlignment: MainAxisAlignment.spaceAround)));
 
     // Then show the profile widgets of those who have joined the game.
     allWidgets.add(new Flexible(flex: 0, child: new Text("Player List")));
@@ -210,7 +215,7 @@
     VoidCallback startCb;
     if (gad.canStart(playerNumbers) || config.croupier.debugMode) {
       startCb = () {
-        config.croupier.settings_manager
+        config.croupier.settingsManager
             .setGameStatus(config.croupier.game.gameID, "RUNNING");
 
         // Since playerNumber starts out as null, we should set it to -1 if
@@ -228,7 +233,7 @@
                 child: new Text("Start Game", style: style.Text.hugeStyle),
                 onPressed: startCb,
                 color: style.theme.accentColor)
-          ], justifyContent: FlexJustifyContent.spaceAround)));
+          ], mainAxisAlignment: MainAxisAlignment.spaceAround)));
     }
     allWidgets.add(new Flexible(
         flex: 0,
diff --git a/lib/components/croupier_game_advertisement.dart b/lib/components/croupier_game_advertisement.dart
index 2042d3f..ae29569 100644
--- a/lib/components/croupier_game_advertisement.dart
+++ b/lib/components/croupier_game_advertisement.dart
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+import 'dart:ui' show VoidCallback;
 import 'package:flutter/material.dart';
 
 import 'croupier_profile.dart';
@@ -9,7 +10,7 @@
 import '../logic/game/game.dart' as game;
 import '../styles/common.dart' as style;
 
-class CroupierGameAdvertisementComponent extends StatelessComponent {
+class CroupierGameAdvertisementComponent extends StatelessWidget {
   final CroupierSettings settings;
   final game.GameStartData gameStartData;
   final VoidCallback onTap;
@@ -18,18 +19,15 @@
       {CroupierSettings settings, this.onTap})
       : settings = settings ?? new CroupierSettings.placeholder();
 
-  Widget build(BuildContext context) {
-    return new GestureDetector(
-        child: new Card(
-            child: new Row(children: [
-          new Card(
-              child: new CroupierProfileComponent(
-                  settings: settings,
-                  height: style.Size.settingsHeight,
-                  width: style.Size.settingsWidth)),
-          new Text(game.gameTypeToString(gameStartData.gameType),
-              style: style.Text.hugeStyle),
-        ])),
-        onTap: onTap);
-  }
+  @override
+  Widget build(BuildContext context) => new GestureDetector(
+      child: new Card(child: new Row(children: [
+        new Card(child: new CroupierProfileComponent(
+            settings: settings,
+            height: style.Size.settingsHeight,
+            width: style.Size.settingsWidth)),
+        new Text(game.gameTypeToString(gameStartData.gameType),
+            style: style.Text.hugeStyle),
+      ])),
+      onTap: onTap);
 }
diff --git a/lib/components/croupier_profile.dart b/lib/components/croupier_profile.dart
index e25f0ab..fb2852f 100644
--- a/lib/components/croupier_profile.dart
+++ b/lib/components/croupier_profile.dart
@@ -14,7 +14,7 @@
   textOnly
 }
 
-class CroupierProfileComponent extends StatelessComponent {
+class CroupierProfileComponent extends StatelessWidget {
   final CroupierSettings settings;
   final double height;
   final double width;
@@ -47,51 +47,52 @@
             settings: settings,
             orientation: CroupierProfileComponentOrientation.textOnly);
 
+  @override
   Widget build(BuildContext context) {
     switch (orientation) {
       case CroupierProfileComponentOrientation.standard:
         return new Container(
             height: this.height,
             width: this.width,
-            padding: const EdgeDims.all(padAmount),
+            padding: const EdgeInsets.all(padAmount),
             child: new Card(
                 color: new Color(settings.color),
                 child: new Column(children: [
                   new AssetImage(
                       name: CroupierSettings.makeAvatarUrl(settings.avatar)),
                   new Text(settings.name, style: style.Text.largeStyle)
-                ], justifyContent: FlexJustifyContent.spaceAround)));
+                ], mainAxisAlignment: MainAxisAlignment.spaceAround)));
       case CroupierProfileComponentOrientation.mini:
         return new Container(
             width: this.width,
             height: this.height,
-            padding: const EdgeDims.all(padAmount),
+            padding: const EdgeInsets.all(padAmount),
             child: new Card(
                 color: new Color(settings.color),
                 child: new Row(children: [
                   new AssetImage(
                       name: CroupierSettings.makeAvatarUrl(settings.avatar),
                       fit: ImageFit.scaleDown)
-                ], justifyContent: FlexJustifyContent.spaceAround)));
+                ], mainAxisAlignment: MainAxisAlignment.spaceAround)));
       case CroupierProfileComponentOrientation.horizontal:
         return new Card(
             color: new Color(settings.color),
             child: new Container(
-                padding: const EdgeDims.all(padAmount),
+                padding: const EdgeInsets.all(padAmount),
                 child: new Row(children: [
                   new AssetImage(
                       name: CroupierSettings.makeAvatarUrl(settings.avatar),
                       fit: ImageFit.scaleDown),
                   new Text(settings.name, style: style.Text.hugeStyle)
-                ], justifyContent: FlexJustifyContent.collapse)));
+                ], mainAxisAlignment: MainAxisAlignment.collapse)));
       case CroupierProfileComponentOrientation.textOnly:
         return new Card(
             color: new Color(settings.color),
             child: new Container(
-                padding: const EdgeDims.all(padAmount),
+                padding: const EdgeInsets.all(padAmount),
                 child: new Row(children: [
                   new Text(settings.name, style: style.Text.largeStyle)
-                ], justifyContent: FlexJustifyContent.collapse)));
+                ], mainAxisAlignment: MainAxisAlignment.collapse)));
     }
   }
 }
diff --git a/lib/components/croupier_settings.dart b/lib/components/croupier_settings.dart
index 4739e68..106dc1f 100644
--- a/lib/components/croupier_settings.dart
+++ b/lib/components/croupier_settings.dart
@@ -27,12 +27,13 @@
   avatarKey: DialogType.ImagePicker
 };
 
-class CroupierSettingsComponent extends StatefulComponent {
+class CroupierSettingsComponent extends StatefulWidget {
   final CroupierSettings settings;
   final SaveDataCb saveDataCb;
 
   CroupierSettingsComponent(this.settings, this.saveDataCb);
 
+  @override
   CroupierSettingsComponentState createState() =>
       new CroupierSettingsComponentState();
 }
@@ -40,6 +41,7 @@
 class CroupierSettingsComponentState extends State<CroupierSettingsComponent> {
   Map<String, String> _tempData = new Map<String, String>();
 
+  @override
   void initState() {
     super.initState();
 
@@ -64,17 +66,17 @@
         onPressed: cb);
   }
 
+  @override
   Widget build(BuildContext context) {
     return new Scaffold(
-        toolBar: _buildToolBar(), body: _buildSettingsPane(context));
+        appBar: _buildAppBar(), body: _buildSettingsPane(context));
   }
 
-  Widget _buildToolBar() {
-    return new ToolBar(
-        left: new IconButton(
-            icon: "navigation/arrow_back",
-            onPressed: () => Navigator.pop(context)),
-        center: new Text("Settings"));
+  Widget _buildAppBar() {
+    return new AppBar(
+        leading: new IconButton(
+            icon: Icons.arrow_back, onPressed: () => Navigator.pop(context)),
+        title: new Text("Settings"));
   }
 
   Widget _buildSettingsPane(BuildContext context) {
@@ -97,9 +99,10 @@
         child: new Row(children: [
           new Flexible(
               flex: 1,
-              child: new Text(capType, style: Theme.of(context).text.subhead)),
+              child: new Text(capType,
+                  style: Theme.of(context).textTheme.subhead)),
           new Flexible(flex: 3, child: child)
-        ], justifyContent: FlexJustifyContent.start));
+        ], mainAxisAlignment: MainAxisAlignment.start));
   }
 
   void _handlePressed(String type) {
@@ -114,16 +117,21 @@
             content: new Input(
                 key: globalKeys[type],
                 hintText: capType,
-                initialValue: config.settings.getStringValue(type),
+                value: new InputValue(text: _tempData[type] ??
+                    config.settings.getStringValue(type)),
                 keyboardType: KeyboardType.text,
                 onChanged: _makeHandleChanged(type)),
             actions: [
-              new FlatButton(child: new Text('CANCEL'), onPressed: () {
-                Navigator.pop(context);
-              }),
-              new FlatButton(child: new Text('SAVE'), onPressed: () {
-                Navigator.pop(context, _tempData[type]);
-              }),
+              new FlatButton(
+                  child: new Text('CANCEL'),
+                  onPressed: () {
+                    Navigator.pop(context);
+                  }),
+              new FlatButton(
+                  child: new Text('SAVE'),
+                  onPressed: () {
+                    Navigator.pop(context, _tempData[type]);
+                  }),
             ]);
         break;
       case DialogType.ColorPicker:
@@ -152,9 +160,11 @@
             content:
                 new MaxTileWidthGrid(children: flexColors, maxTileWidth: 75.0),
             actions: [
-              new FlatButton(child: new Text('CANCEL'), onPressed: () {
-                Navigator.pop(context);
-              })
+              new FlatButton(
+                  child: new Text('CANCEL'),
+                  onPressed: () {
+                    Navigator.pop(context);
+                  })
             ]);
         break;
       case DialogType.ImagePicker:
@@ -171,9 +181,11 @@
             content:
                 new MaxTileWidthGrid(children: flexAvatars, maxTileWidth: 75.0),
             actions: [
-              new FlatButton(child: new Text('CANCEL'), onPressed: () {
-                Navigator.pop(context);
-              })
+              new FlatButton(
+                  child: new Text('CANCEL'),
+                  onPressed: () {
+                    Navigator.pop(context);
+                  })
             ]);
         break;
       default:
@@ -197,11 +209,11 @@
 
   String _capitalize(String s) => s[0].toUpperCase() + s.substring(1);
 
-  OneStringCb _makeHandleChanged(String type) {
-    return (String data) {
+  ValueChanged<InputValue> _makeHandleChanged(String type) {
+    return (InputValue iv) {
       setState(() {
-        print("Updating ${type} with ${data}");
-        _tempData[type] = data;
+        print("Updating $type with ${iv.text}");
+        _tempData[type] = iv.text;
       });
     };
   }
diff --git a/lib/components/game.dart b/lib/components/game.dart
index c5f3ff1..392c9b0 100644
--- a/lib/components/game.dart
+++ b/lib/components/game.dart
@@ -29,22 +29,23 @@
 part 'proto/proto.part.dart';
 part 'solitaire/solitaire.part.dart';
 
-abstract class GameComponent extends StatefulComponent {
+abstract class GameComponent extends StatefulWidget {
+  GameComponent(this.croupier, this.sounds, this.gameEndCallback,
+      {Key key, this.width, this.height})
+      : super(key: key);
+
   final Croupier croupier;
   final SoundAssets sounds;
   Game get game => croupier.game;
   final VoidCallback gameEndCallback;
   final double width;
   final double height;
-
-  GameComponent(this.croupier, this.sounds, this.gameEndCallback,
-      {Key key, this.width, this.height})
-      : super(key: key);
 }
 
 abstract class GameComponentState<T extends GameComponent> extends State<T> {
   Map<logic_card.Card, CardAnimationData> cardLevelMap;
 
+  @override
   void initState() {
     super.initState();
 
@@ -109,12 +110,12 @@
       setState(() {
         cardLevelMap[logicCard] = new CardAnimationData(c, cad?.newPoint, p, z);
       });
-    } else if (!cad.comp_card.isMatchWith(c)) {
+    } else if (!cad.compCard.isMatchWith(c)) {
       // Even if the position or z index didn't change, we can still update the
       // card itself. This can help during screen rotations, since the top-left
       // card likely not change positions or z-index.
       setState(() {
-        cad.comp_card = c;
+        cad.compCard = c;
       });
     }
   }
@@ -188,7 +189,7 @@
               0.0, // must pass x and y or else it expands to the maximum Stack size.
           left:
               0.0, // must pass x and y or else it expands to the maximum Stack size.
-          child: new component_card.ZCard(data.comp_card, localOld, localNew)));
+          child: new component_card.ZCard(data.compCard, localOld, localNew)));
     });
 
     return new IgnorePointer(
@@ -220,7 +221,7 @@
   }
 }
 
-abstract class GameArrangeComponent extends StatefulComponent {
+abstract class GameArrangeComponent extends StatefulWidget {
   final Croupier croupier;
   final double width;
   final double height;
@@ -245,10 +246,10 @@
 /// It uses the comp_card's properties, the oldPoint, newPoint, and z-index to
 /// determine how it needs to animate.
 class CardAnimationData {
-  component_card.Card comp_card;
+  component_card.Card compCard;
   Point oldPoint;
   Point newPoint;
   double z;
 
-  CardAnimationData(this.comp_card, this.oldPoint, this.newPoint, this.z);
+  CardAnimationData(this.compCard, this.oldPoint, this.newPoint, this.z);
 }
diff --git a/lib/components/hearts/hearts.part.dart b/lib/components/hearts/hearts.part.dart
index 1324d43..3c8ad74 100644
--- a/lib/components/hearts/hearts.part.dart
+++ b/lib/components/hearts/hearts.part.dart
@@ -9,8 +9,12 @@
       {Key key, double width, double height})
       : super(croupier, sounds, cb, key: key, width: width, height: height);
 
-  HeartsGame get game => super.game;
+  @override
+  HeartsGame get game {
+    return super.game;
+  }
 
+  @override
   HeartsGameComponentState createState() => new HeartsGameComponentState();
 }
 
@@ -36,7 +40,7 @@
     _reset();
   }
 
-  bool get _isBoardPresent => config.croupier.players_found.values.contains(4);
+  bool get _isBoardPresent => config.croupier.playersFound.values.contains(4);
 
   @override
   void _reset() {
@@ -264,7 +268,7 @@
           game.canPlay(game.playerNumber, card, lenient: isBufferAttempt);
       if (reason == null) {
         if (isBufferAttempt) {
-          print("Buffering ${card}...");
+          print("Buffering $card...");
           _clearBufferedPlay();
           bufferedPlay.add(card);
         } else {
@@ -280,7 +284,7 @@
         }
         game.debugString = null;
       } else {
-        print("You can't do that! ${reason}");
+        print("You can't do that! $reason");
         game.debugString = reason;
       }
     });
@@ -330,7 +334,7 @@
             decoration: new BoxDecoration(
                 border: new Border.all(width: 1.0, color: borderColor),
                 backgroundColor: backgroundColor),
-            padding: new EdgeDims.all(10.0),
+            padding: new EdgeInsets.all(10.0),
             child: new Text(text)),
         onPressed: inactive ? null : callback);
   }
@@ -374,7 +378,7 @@
     }
     kids.add(_makeDebugButtons());
     return new Column(
-        children: kids, justifyContent: FlexJustifyContent.spaceBetween);
+        children: kids, mainAxisAlignment: MainAxisAlignment.spaceBetween);
   }
 
   Widget showBoard() {
@@ -400,14 +404,14 @@
         // Who's turn is it?
         String name = _getName(game.whoseTurn) ?? "Player ${game.whoseTurn}";
         isPlayer = game.whoseTurn == game.playerNumber;
-        status = isPlayer ? "Your turn" : "${name}'s turn";
+        status = isPlayer ? "Your turn" : "$name's turn";
 
         // Override if someone is taking a trick.
         if (game.allPlayed) {
           int winner = game.determineTrickWinner();
-          String trickTaker = _getName(winner) ?? "Player ${winner}";
+          String trickTaker = _getName(winner) ?? "Player $winner";
           isPlayer = winner == game.playerNumber;
-          status = isPlayer ? "Your trick" : "${trickTaker}'s trick";
+          status = isPlayer ? "Your trick" : "$trickTaker's trick";
         }
         break;
       case HeartsPhase.pass:
@@ -416,7 +420,7 @@
         } else {
           String name =
               _getName(game.passTarget) ?? "Player ${game.passTarget}";
-          status = "Pass to ${name}";
+          status = "Pass to $name";
           isPlayer = true;
         }
         break;
@@ -426,7 +430,7 @@
         } else {
           String name =
               _getName(game.takeTarget) ?? "Player ${game.takeTarget}";
-          status = "Take from ${name}";
+          status = "Take from $name";
           isPlayer = true;
         }
         break;
@@ -450,14 +454,30 @@
         game.cardCollections[HeartsGame.offsetTrick + game.playerNumber].length;
     int numTricks = numTrickCards ~/ 4;
 
-    String iconName = "image/filter_9_plus";
+    IconData iconData = Icons.filter_9_plus;
     if (numTricks == 0) {
-      iconName = "image/filter_none";
-    } else if (numTricks <= 9) {
-      iconName = "image/filter_${numTricks}";
+      iconData = Icons.filter_none;
+    } else if (numTricks == 1) {
+      iconData = Icons.filter_1;
+    } else if (numTricks == 2) {
+      iconData = Icons.filter_2;
+    } else if (numTricks == 3) {
+      iconData = Icons.filter_3;
+    } else if (numTricks == 4) {
+      iconData = Icons.filter_4;
+    } else if (numTricks == 5) {
+      iconData = Icons.filter_5;
+    } else if (numTricks == 6) {
+      iconData = Icons.filter_6;
+    } else if (numTricks == 7) {
+      iconData = Icons.filter_7;
+    } else if (numTricks == 8) {
+      iconData = Icons.filter_8;
+    } else if (numTricks == 9) {
+      iconData = Icons.filter_9;
     }
 
-    return new Icon(icon: iconName);
+    return new Icon(icon: iconData);
   }
 
   Widget _buildStatusBar() {
@@ -485,12 +505,13 @@
                           style: style.Text.largeStyle)))));
         }
         statusBarWidgets.add(_buildNumTrickIcon());
-        statusBarWidgets
-            .add(new IconButton(icon: "action/swap_vert", onPressed: () {
-          setState(() {
-            _showSplitView = !_showSplitView;
-          });
-        }));
+        statusBarWidgets.add(new IconButton(
+            icon: Icons.swap_vert,
+            onPressed: () {
+              setState(() {
+                _showSplitView = !_showSplitView;
+              });
+            }));
         break;
       case HeartsPhase.pass:
       case HeartsPhase.take:
@@ -513,7 +534,7 @@
             transform:
                 new vector_math.Matrix4.identity().rotateZ(rotationAngle),
             alignment: new FractionalOffset(0.5, 0.5),
-            child: new Icon(icon: "navigation/arrow_forward")));
+            child: new Icon(icon: Icons.arrow_forward)));
         break;
       default:
         break;
@@ -528,11 +549,11 @@
     }
 
     return new Container(
-        padding: new EdgeDims.all(10.0),
+        padding: new EdgeInsets.all(10.0),
         decoration: decoration,
         child: new Row(
             children: statusBarWidgets,
-            justifyContent: FlexJustifyContent.spaceBetween));
+            mainAxisAlignment: MainAxisAlignment.spaceBetween));
   }
 
   Widget _buildFullMiniBoard() {
@@ -570,19 +591,17 @@
       Widget playArea = new Container(
           decoration: new BoxDecoration(backgroundColor: Colors.teal[500]),
           width: config.width,
-          child: new Center(
-              child: new CardCollectionComponent(
-                  playOrBuffer, true, CardCollectionOrientation.show1,
-                  useKeys: true,
-                  acceptCallback: _makeGameMoveCallback,
-                  acceptType: p == game.whoseTurn || this._canBuffer
-                      ? DropType.card
-                      : DropType.none,
-                  backgroundColor:
-                      p == game.whoseTurn ? Colors.white : Colors.grey[500],
-                  altColor: p == game.whoseTurn
-                      ? Colors.grey[200]
-                      : Colors.grey[600])));
+          child: new Center(child: new CardCollectionComponent(
+              playOrBuffer, true, CardCollectionOrientation.show1,
+              useKeys: true,
+              acceptCallback: _makeGameMoveCallback,
+              acceptType: p == game.whoseTurn || this._canBuffer
+                  ? DropType.card
+                  : DropType.none,
+              backgroundColor:
+                  p == game.whoseTurn ? Colors.white : Colors.grey[500],
+              altColor:
+                  p == game.whoseTurn ? Colors.grey[200] : Colors.grey[600])));
 
       cardCollections.add(new Container(
           decoration: style.Box.background,
@@ -616,7 +635,7 @@
 
     return new Column(
         children: cardCollections,
-        justifyContent: FlexJustifyContent.spaceBetween);
+        mainAxisAlignment: MainAxisAlignment.spaceBetween);
   }
 
   Widget showScore() {
@@ -663,21 +682,24 @@
       TextStyle scoreStyle = isMaxOverall ? bigRedStyle : bigStyle;
 
       scores.add(new Flexible(
-          child: new Flex(children: [
-            new Flexible(
-                child: new CroupierProfileComponent(
-                    settings: config.croupier.settingsFromPlayerNumber(i)),
-                flex: 1),
-            new Flexible(
-                child: new Center(
-                    child:
-                        new Text("${game.deltaScores[i]}", style: deltaStyle)),
-                flex: 1),
-            new Flexible(
-                child: new Center(
-                    child: new Text("${game.scores[i]}", style: scoreStyle)),
-                flex: 1)
-          ], direction: crossDirection, alignItems: FlexAlignItems.stretch),
+          child: new Flex(
+              children: [
+                new Flexible(
+                    child: new CroupierProfileComponent(
+                        settings: config.croupier.settingsFromPlayerNumber(i)),
+                    flex: 1),
+                new Flexible(
+                    child: new Center(child: new Text("${game.deltaScores[i]}",
+                        style: deltaStyle)),
+                    flex: 1),
+                new Flexible(
+                    child: new Center(
+                        child:
+                            new Text("${game.scores[i]}", style: scoreStyle)),
+                    flex: 1)
+              ],
+              direction: crossDirection,
+              crossAxisAlignment: CrossAxisAlignment.stretch),
           flex: 2));
     }
     return new Column(children: [
@@ -695,7 +717,7 @@
           new Text('Player ${config.game.playerNumber}'),
           new Text('Waiting for Deal...'),
           _makeDebugButtons()
-        ], justifyContent: FlexJustifyContent.spaceBetween));
+        ], mainAxisAlignment: MainAxisAlignment.spaceBetween));
   }
 
   Widget _helpPassTake(
@@ -722,11 +744,11 @@
 
     Widget topArea = new Container(
         decoration: new BoxDecoration(backgroundColor: bgColor),
-        padding: new EdgeDims.all(10.0),
+        padding: new EdgeInsets.all(10.0),
         width: config.width,
         child: new Row(
             children: topCardWidgets,
-            justifyContent: FlexJustifyContent.spaceBetween));
+            mainAxisAlignment: MainAxisAlignment.spaceBetween));
     Widget combinedTopArea = new BlockBody(children: [statusBar, topArea]);
 
     List<logic_card.Card> emptyC;
@@ -756,7 +778,7 @@
 
     return new Column(
         children: <Widget>[combinedTopArea, combinedBottomArea],
-        justifyContent: FlexJustifyContent.spaceBetween);
+        mainAxisAlignment: MainAxisAlignment.spaceBetween);
   }
 
   Widget _topCardWidget(List<logic_card.Card> cards, AcceptCb cb) {
@@ -838,17 +860,19 @@
       {double width, double height, Key key})
       : super(croupier, width: width, height: height, key: key);
 
+  @override
   HeartsArrangeComponentState createState() =>
       new HeartsArrangeComponentState();
 }
 
 class HeartsArrangeComponentState extends State<HeartsArrangeComponent> {
-  bool isTable = null; // Must first ask what kind of device this is.
+  bool isTable; // Must first ask what kind of device this is. Starts unset.
   bool fallback = false; // Set to true to switch to the old arrange players.
 
-  static final String personIcon = "social/person_outline";
-  static final String tableIcon = "hardware/tablet";
+  static final IconData personIcon = Icons.person_outline;
+  static final IconData tableIcon = Icons.tablet;
 
+  @override
   Widget build(BuildContext context) {
     if (isTable == null) {
       return _buildAskDeviceType();
@@ -871,46 +895,48 @@
           new Text("Play Hearts as a...", style: style.Text.hugeStyle),
           new FlatButton(
               child: new Row(children: [
-                new Icon(size: IconSize.s48, icon: personIcon),
+                new Icon(size: 48.0, icon: personIcon),
                 new Text("Player", style: style.Text.largeStyle)
-              ], justifyContent: FlexJustifyContent.collapse),
-              color: style.secondaryTextColor, onPressed: () {
-            setState(() {
-              isTable = false;
-            });
-          }),
+              ], mainAxisAlignment: MainAxisAlignment.collapse),
+              color: style.secondaryTextColor,
+              onPressed: () {
+                setState(() {
+                  isTable = false;
+                });
+              }),
           new FlatButton(
               child: new Row(children: [
-                new Icon(size: IconSize.s48, icon: tableIcon),
+                new Icon(size: 48.0, icon: tableIcon),
                 new Text("Table", style: style.Text.largeStyle)
-              ], justifyContent: FlexJustifyContent.collapse),
-              color: style.secondaryTextColor, onPressed: () {
-            setState(() {
-              isTable = true;
-            });
-            // Also sit at the table.
-            config.croupier.settings_manager.setPlayerNumber(
-                config.croupier.game.gameID,
-                config.croupier.settings.userID,
-                4);
-          })
-        ], alignItems: FlexAlignItems.center));
+              ], mainAxisAlignment: MainAxisAlignment.collapse),
+              color: style.secondaryTextColor,
+              onPressed: () {
+                setState(() {
+                  isTable = true;
+                });
+                // Also sit at the table.
+                config.croupier.settingsManager.setPlayerNumber(
+                    config.croupier.game.gameID,
+                    config.croupier.settings.userID,
+                    4);
+              })
+        ], crossAxisAlignment: CrossAxisAlignment.center));
   }
 
-  int get numSitting => config.croupier.players_found.values.where((int index) {
+  int get numSitting => config.croupier.playersFound.values.where((int index) {
         return index != null && index >= 0 && index < 4;
       }).length;
 
   Widget _buildTableArrangePlayers() {
-    int arrangeID = null;
+    int arrangeID; // Starts unset.
     String status = "";
     bool canStart = false;
     if (numSitting < 4) {
       // We still need people to sit.
       // Only include non-table and non-sitting devices in this search.
       // Note that this means it's possible arrangeID is null.
-      arrangeID = config.croupier.players_found.keys.firstWhere((int playerID) {
-        int index = config.croupier.players_found[playerID];
+      arrangeID = config.croupier.playersFound.keys.firstWhere((int playerID) {
+        int index = config.croupier.playersFound[playerID];
         return index == null || index < 0;
       }, orElse: () => null);
       status = arrangeID != null ? "Tap to place" : "Waiting for players...";
@@ -923,7 +949,7 @@
     // Also add the player's name (using a placeholder if that's not possible).
     if (arrangeID != null) {
       children.add(new CroupierProfileComponent.textOnly(
-          settings: config.croupier.settings_everyone[arrangeID]));
+          settings: config.croupier.settingsEveryone[arrangeID]));
     }
 
     // You will need to show a Start Game button if the table isn't the creator.
@@ -932,14 +958,15 @@
       children = [
         new FlatButton(
             child: new Text(status, style: style.Text.hugeStyle),
-            color: style.theme.accentColor, onPressed: () {
-          config.croupier.settings_manager
-              .setGameStatus(config.croupier.game.gameID, "RUNNING");
-        })
+            color: style.theme.accentColor,
+            onPressed: () {
+              config.croupier.settingsManager
+                  .setGameStatus(config.croupier.game.gameID, "RUNNING");
+            })
       ];
     }
     Widget firstChild = new Row(
-        children: children, justifyContent: FlexJustifyContent.collapse);
+        children: children, mainAxisAlignment: MainAxisAlignment.collapse);
 
     return new Container(
         decoration: style.Box.liveNow,
@@ -948,7 +975,7 @@
         child: new Column(children: [
           firstChild,
           new Flexible(child: _buildArrangeTable(activeID: arrangeID))
-        ], alignItems: FlexAlignItems.center));
+        ], crossAxisAlignment: CrossAxisAlignment.center));
   }
 
   Widget _buildPlayerArrangePlayers() {
@@ -958,19 +985,20 @@
     if (config.croupier.game.isCreator) {
       children.add(new FlatButton(
           child: new Text("Manual Setup", style: style.Text.largeStyle),
-          color: style.theme.accentColor, onPressed: () {
-        setState(() {
-          fallback = true;
-        });
-      }));
+          color: style.theme.accentColor,
+          onPressed: () {
+            setState(() {
+              fallback = true;
+            });
+          }));
     }
 
     return new Container(
         decoration: style.Box.liveNow,
         height: config.height,
         width: config.width,
-        child:
-            new Column(children: children, alignItems: FlexAlignItems.center));
+        child: new Column(
+            children: children, crossAxisAlignment: CrossAxisAlignment.center));
   }
 
   Widget _buildFallbackArrangePlayers() {
@@ -982,66 +1010,66 @@
   }
 
   Widget _buildArrangeTable({int activeID: null, bool canDragTo: false}) {
-    int numAtTable = config.croupier.players_found.values
+    int numAtTable = config.croupier.playersFound.values
         .where((int playerNumber) => playerNumber == 4)
         .length;
     return new Column(
         children: [
-      new Flexible(
-          flex: 1,
-          child: new Row(
-              children: [
-            _buildEmptySlot(),
-            _buildSlot(personIcon, 2, activeID, canDragTo),
-            _buildEmptySlot()
-          ],
-              justifyContent: FlexJustifyContent.spaceAround,
-              alignItems: FlexAlignItems.stretch)),
-      new Flexible(
-          flex: 1,
-          child: new Row(
-              children: [
-            _buildSlot(personIcon, 1, activeID, canDragTo),
-            _buildSlot(tableIcon, 4, activeID, canDragTo,
-                extra: "x${numAtTable}"),
-            _buildSlot(personIcon, 3, activeID, canDragTo)
-          ],
-              justifyContent: FlexJustifyContent.spaceAround,
-              alignItems: FlexAlignItems.stretch)),
-      new Flexible(
-          flex: 1,
-          child: new Row(
-              children: [
-            _buildEmptySlot(),
-            _buildSlot(personIcon, 0, activeID, canDragTo),
-            _buildEmptySlot()
-          ],
-              justifyContent: FlexJustifyContent.spaceAround,
-              alignItems: FlexAlignItems.stretch))
-    ],
-        justifyContent: FlexJustifyContent.spaceAround,
-        alignItems: FlexAlignItems.stretch);
+          new Flexible(
+              flex: 1,
+              child: new Row(
+                  children: [
+                    _buildEmptySlot(),
+                    _buildSlot(personIcon, 2, activeID, canDragTo),
+                    _buildEmptySlot()
+                  ],
+                  mainAxisAlignment: MainAxisAlignment.spaceAround,
+                  crossAxisAlignment: CrossAxisAlignment.stretch)),
+          new Flexible(
+              flex: 1,
+              child: new Row(
+                  children: [
+                    _buildSlot(personIcon, 1, activeID, canDragTo),
+                    _buildSlot(tableIcon, 4, activeID, canDragTo,
+                        extra: "x$numAtTable"),
+                    _buildSlot(personIcon, 3, activeID, canDragTo)
+                  ],
+                  mainAxisAlignment: MainAxisAlignment.spaceAround,
+                  crossAxisAlignment: CrossAxisAlignment.stretch)),
+          new Flexible(
+              flex: 1,
+              child: new Row(
+                  children: [
+                    _buildEmptySlot(),
+                    _buildSlot(personIcon, 0, activeID, canDragTo),
+                    _buildEmptySlot()
+                  ],
+                  mainAxisAlignment: MainAxisAlignment.spaceAround,
+                  crossAxisAlignment: CrossAxisAlignment.stretch))
+        ],
+        mainAxisAlignment: MainAxisAlignment.spaceAround,
+        crossAxisAlignment: CrossAxisAlignment.stretch);
   }
 
   Widget _buildEmptySlot() {
     return new Flexible(flex: 1, child: new Text(""));
   }
 
-  Widget _buildSlot(String name, int index, int activeID, bool canDragTo,
+  Widget _buildSlot(IconData iconData, int index, int activeID, bool canDragTo,
       {String extra: ""}) {
     Widget slotWidget = new Row(
         children: [
-      new Icon(size: IconSize.s48, icon: name),
-      new Text(extra, style: style.Text.largeStyle)
-    ],
-        alignItems: FlexAlignItems.center,
-        justifyContent: FlexJustifyContent.center);
+          new Icon(size: 48.0, icon: iconData),
+          new Text(extra, style: style.Text.largeStyle)
+        ],
+        crossAxisAlignment: CrossAxisAlignment.center,
+        mainAxisAlignment: MainAxisAlignment.center);
 
     bool isMe = config.croupier.game.playerNumber == index;
     bool isPlayerIndex = index >= 0 && index < 4;
     bool isTableIndex = index == 4;
     bool seatTaken = (isPlayerIndex || (isTableIndex && isMe)) &&
-        config.croupier.players_found.containsValue(index);
+        config.croupier.playersFound.containsValue(index);
     if (seatTaken) {
       // Note: If more than 1 person is in the seat, it may no longer show you.
       CroupierSettings cs = config.croupier.settingsFromPlayerNumber(index);
@@ -1053,20 +1081,24 @@
 
     Widget dragTarget = new DragTarget<CroupierSettings>(
         builder: (BuildContext context, List<CroupierSettings> data, _) {
-      return new GestureDetector(onTap: () {
-        if (activeID != null) {
-          config.croupier.settings_manager
-              .setPlayerNumber(config.croupier.game.gameID, activeID, index);
-        }
-      },
-          child: new Container(
-              constraints: const BoxConstraints.expand(),
-              decoration: isMe ? style.Box.liveBackground : style.Box.border,
-              child: new Center(child: slotWidget)));
-    }, onAccept: (CroupierSettings cs) {
-      config.croupier.settings_manager
-          .setPlayerNumber(config.croupier.game.gameID, cs.userID, index);
-    }, onWillAccept: (_) => canDragTo);
+          return new GestureDetector(
+              onTap: () {
+                if (activeID != null) {
+                  config.croupier.settingsManager.setPlayerNumber(
+                      config.croupier.game.gameID, activeID, index);
+                }
+              },
+              child: new Container(
+                  constraints: const BoxConstraints.expand(),
+                  decoration:
+                      isMe ? style.Box.liveBackground : style.Box.border,
+                  child: new Center(child: slotWidget)));
+        },
+        onAccept: (CroupierSettings cs) {
+          config.croupier.settingsManager
+              .setPlayerNumber(config.croupier.game.gameID, cs.userID, index);
+        },
+        onWillAccept: (_) => canDragTo);
 
     return new Flexible(flex: 1, child: dragTarget);
   }
diff --git a/lib/components/main_route.dart b/lib/components/main_route.dart
index f85a86e..80b01ce 100644
--- a/lib/components/main_route.dart
+++ b/lib/components/main_route.dart
@@ -12,12 +12,13 @@
 
 final GlobalKey _scaffoldKey = new GlobalKey();
 
-class MainRoute extends StatefulComponent {
+class MainRoute extends StatefulWidget {
   final Croupier croupier;
   final SoundAssets sounds;
 
   MainRoute(this.croupier, this.sounds);
 
+  @override
   MainRouteState createState() => new MainRouteState();
 }
 
@@ -35,17 +36,18 @@
     }
   }
 
+  @override
   Widget build(BuildContext context) {
     if (config.croupier.settings == null) {
       return _buildSplashScreen();
     }
     return new Scaffold(
         key: _scaffoldKey,
-        toolBar: new ToolBar(
-            left: new IconButton(
-                icon: "navigation/menu",
+        appBar: new AppBar(
+            leading: new IconButton(
+                icon: Icons.menu,
                 onPressed: () => _scaffoldKey.currentState?.openDrawer()),
-            center: new Text('Croupier')),
+            title: new Text('Croupier')),
         body: new Material(
             child: new CroupierComponent(config.croupier, config.sounds)),
         drawer: _buildDrawer());
@@ -59,23 +61,22 @@
             name: 'images/splash/flutter.png', width: style.Size.splashLogo),
         new AssetImage(
             name: 'images/splash/vanadium.png', width: style.Size.splashLogo)
-      ], justifyContent: FlexJustifyContent.center),
+      ], mainAxisAlignment: MainAxisAlignment.center),
       new Container(
           child: new Row(
-              children:
-                  [new Text('Loading Croupier...', style: style.Text.splash)],
-              alignItems: FlexAlignItems.end,
-              justifyContent: FlexJustifyContent.center),
+              children: [
+                new Text('Loading Croupier...', style: style.Text.splash)
+              ],
+              crossAxisAlignment: CrossAxisAlignment.end,
+              mainAxisAlignment: MainAxisAlignment.center),
           padding: style.Spacing.normalPadding)
     ]);
     return stack;
   }
 
   Widget _buildDrawer() {
-    return new Drawer(
-        child: new Block(children: <Widget>[
-      new DrawerHeader(
-          child: new BlockBody(children: [
+    return new Drawer(child: new Block(children: <Widget>[
+      new DrawerHeader(child: new BlockBody(children: [
         new CroupierProfileComponent(
             settings: config.croupier.settings,
             width: style.Size.settingsWidth,
@@ -83,7 +84,7 @@
         new Text('Croupier', style: style.Text.titleStyle)
       ])),
       new DrawerItem(
-          icon: 'action/settings',
+          icon: Icons.settings,
           // TODO(alexfandrianto): Fix the Splash Screen, and we won't need
           // to check if settings is null here.
           // https://github.com/vanadium/issues/issues/958
@@ -91,13 +92,13 @@
               config.croupier.settings != null ? _handleShowSettings : null,
           child: new Text('Settings')),
       new DrawerItem(
-          icon: 'action/build',
+          icon: Icons.build,
           child: new Row(children: [
             new Text('Debug Mode'),
             new Switch(
                 value: config.croupier.debugMode, onChanged: _handleDebugMode)
-          ], justifyContent: FlexJustifyContent.spaceBetween)),
-      new DrawerItem(icon: 'action/help', child: new Text('Help & Feedback'))
+          ], mainAxisAlignment: MainAxisAlignment.spaceBetween)),
+      new DrawerItem(icon: Icons.help, child: new Text('Help & Feedback'))
     ]));
   }
 
diff --git a/lib/components/proto/proto.part.dart b/lib/components/proto/proto.part.dart
index 9071709..f014007 100644
--- a/lib/components/proto/proto.part.dart
+++ b/lib/components/proto/proto.part.dart
@@ -9,7 +9,10 @@
       {Key key, double width, double height})
       : super(croupier, sounds, cb, key: key, width: width, height: height);
 
-  ProtoGameComponentState createState() => new ProtoGameComponentState();
+  @override
+  ProtoGameComponentState createState() {
+    return new ProtoGameComponentState();
+  }
 }
 
 class ProtoGameComponentState extends GameComponentState<ProtoGameComponent> {
diff --git a/lib/components/settings_route.dart b/lib/components/settings_route.dart
index ab133bd..44685d3 100644
--- a/lib/components/settings_route.dart
+++ b/lib/components/settings_route.dart
@@ -7,13 +7,12 @@
 import '../logic/croupier.dart' show Croupier;
 import 'croupier_settings.dart' show CroupierSettingsComponent;
 
-class SettingsRoute extends StatelessComponent {
+class SettingsRoute extends StatelessWidget {
   final Croupier croupier;
 
   SettingsRoute(this.croupier);
 
-  Widget build(BuildContext context) {
-    return new CroupierSettingsComponent(
-        croupier.settings, croupier.settings_manager.save);
-  }
+  @override
+  Widget build(BuildContext context) => new CroupierSettingsComponent(
+      croupier.settings, croupier.settingsManager.save);
 }
diff --git a/lib/components/solitaire/solitaire.part.dart b/lib/components/solitaire/solitaire.part.dart
index 9bda63c..febea74 100644
--- a/lib/components/solitaire/solitaire.part.dart
+++ b/lib/components/solitaire/solitaire.part.dart
@@ -9,6 +9,7 @@
       {Key key, double width, double height})
       : super(croupier, sounds, cb, key: key, width: width, height: height);
 
+  @override
   SolitaireGameComponentState createState() =>
       new SolitaireGameComponentState();
 }
@@ -83,7 +84,7 @@
             decoration: new BoxDecoration(
                 border: new Border.all(width: 1.0, color: borderColor),
                 backgroundColor: backgroundColor),
-            padding: new EdgeDims.all(10.0),
+            padding: new EdgeInsets.all(10.0),
             child: new Text(text)),
         onPressed: inactive ? null : callback);
   }
@@ -118,7 +119,7 @@
       if (reason == null) {
         game.move(card, collection);
       } else {
-        print("You can't do that! ${reason}");
+        print("You can't do that! $reason");
         game.debugString = reason;
       }
     });
@@ -194,9 +195,12 @@
     }
 
     return new Column(children: [
-      new Row(children: row1, justifyContent: FlexJustifyContent.spaceBetween),
-      new Row(children: row2, justifyContent: FlexJustifyContent.spaceBetween),
-      new Row(children: row3, justifyContent: FlexJustifyContent.spaceBetween),
+      new Row(
+          children: row1, mainAxisAlignment: MainAxisAlignment.spaceBetween),
+      new Row(
+          children: row2, mainAxisAlignment: MainAxisAlignment.spaceBetween),
+      new Row(
+          children: row3, mainAxisAlignment: MainAxisAlignment.spaceBetween),
       _makeDebugButtons()
     ]);
   }
@@ -222,6 +226,6 @@
           new Text('Player ${game.playerNumber}'),
           _makeButton('Deal', game.dealCardsUI),
           _makeDebugButtons()
-        ], justifyContent: FlexJustifyContent.spaceBetween));
+        ], mainAxisAlignment: MainAxisAlignment.spaceBetween));
   }
 }
diff --git a/lib/logic/card.dart b/lib/logic/card.dart
index 1303daf..3924b7f 100644
--- a/lib/logic/card.dart
+++ b/lib/logic/card.dart
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-import 'dart:collection';
-
 class Card {
   final String deck;
   final String identifier;
@@ -13,15 +11,19 @@
       : deck = cardData.split(" ")[0],
         identifier = cardData.split(" ")[1];
 
+  @override
   bool operator ==(Object other) {
     if (other is! Card) return false;
     Card o = other as Card;
     return deck == o.deck && identifier == o.identifier;
   }
 
+  @override
   int get hashCode => 37 * (deck.hashCode + 41 * identifier.hashCode);
 
-  static final List<Card> All = new UnmodifiableListView<Card>([
+  // TODO(alexfandrianto): https://github.com/dart-lang/sdk/issues/26184
+  // Put the UnmodifiableListView<Card> back when dartanalyzer no longer warns.
+  static final List<Card> all = <Card>[
     new Card("classic", "c1"),
     new Card("classic", "c2"),
     new Card("classic", "c3"),
@@ -74,7 +76,7 @@
     new Card("classic", "sj"),
     new Card("classic", "sq"),
     new Card("classic", "sk"),
-  ]);
+  ];
 
   toString() => "${deck} ${identifier}";
 
diff --git a/lib/logic/croupier.dart b/lib/logic/croupier.dart
index 2d51962..77661a5 100644
--- a/lib/logic/croupier.dart
+++ b/lib/logic/croupier.dart
@@ -26,12 +26,12 @@
 class Croupier {
   AppSettings appSettings;
   CroupierState state;
-  SettingsManager settings_manager;
+  SettingsManager settingsManager;
   CroupierSettings settings; // null, but loaded asynchronously.
-  Map<int,
-      CroupierSettings> settings_everyone; // empty, but loaded asynchronously
-  Map<String, GameStartData> games_found; // empty, but loads asynchronously
-  Map<int, int> players_found; // empty, but loads asynchronously
+  Map<int, CroupierSettings>
+      settingsEveryone; // empty, but loaded asynchronously
+  Map<String, GameStartData> gamesFound; // empty, but loads asynchronously
+  Map<int, int> playersFound; // empty, but loads asynchronously
   Game game; // null until chosen
   VoidCallback informUICb;
 
@@ -43,10 +43,10 @@
 
   Croupier(this.appSettings) {
     state = CroupierState.welcome;
-    settings_everyone = new Map<int, CroupierSettings>();
-    games_found = new Map<String, GameStartData>();
-    players_found = new Map<int, int>();
-    settings_manager = new SettingsManager(
+    settingsEveryone = new Map<int, CroupierSettings>();
+    gamesFound = new Map<String, GameStartData>();
+    playersFound = new Map<int, int>();
+    settingsManager = new SettingsManager(
         appSettings,
         _updateSettingsEveryoneCb,
         _updateGamesFoundCb,
@@ -54,19 +54,19 @@
         _updateGameStatusCb,
         _gameLogUpdateCb);
 
-    settings_manager.load().then((String csString) {
+    settingsManager.load().then((String csString) {
       settings = new CroupierSettings.fromJSONString(csString);
       if (this.informUICb != null) {
         this.informUICb();
       }
-      settings_manager.createSettingsSyncgroup(); // don't wait for this future.
+      settingsManager.createSettingsSyncgroup(); // don't wait for this future.
     });
   }
 
   // Updates the settings_everyone map as people join the main Croupier syncgroup
   // and change their settings.
   void _updateSettingsEveryoneCb(String key, String json) {
-    settings_everyone[int.parse(key)] =
+    settingsEveryone[int.parse(key)] =
         new CroupierSettings.fromJSONString(json);
     if (this.informUICb != null) {
       this.informUICb();
@@ -75,10 +75,10 @@
 
   void _updateGamesFoundCb(String gameAddr, String jsonData) {
     if (jsonData == null) {
-      games_found.remove(gameAddr);
+      gamesFound.remove(gameAddr);
     } else {
       GameStartData gsd = new GameStartData.fromJSONString(jsonData);
-      games_found[gameAddr] = gsd;
+      gamesFound[gameAddr] = gsd;
     }
     if (this.informUICb != null) {
       this.informUICb();
@@ -92,15 +92,15 @@
   }
 
   int userIDFromPlayerNumber(int playerNumber) {
-    return players_found.keys.firstWhere(
-        (int user) => players_found[user] == playerNumber,
+    return playersFound.keys.firstWhere(
+        (int user) => playersFound[user] == playerNumber,
         orElse: () => null);
   }
 
   void _setCurrentGame(Game g) {
     game = g;
     settings.lastGameID = g.gameID;
-    settings_manager.save(settings.userID, settings.toJSONString()); // async
+    settingsManager.save(settings.userID, settings.toJSONString()); // async
   }
 
   Game _createNewGame(GameType gt) {
@@ -114,7 +114,7 @@
 
   void _quitGame() {
     if (game != null) {
-      settings_manager.quitGame();
+      settingsManager.quitGame();
       game = null;
     }
   }
@@ -122,7 +122,7 @@
   CroupierSettings settingsFromPlayerNumber(int playerNumber) {
     int userID = userIDFromPlayerNumber(playerNumber);
     if (userID != null) {
-      return settings_everyone[userID];
+      return settingsEveryone[userID];
     }
     return null;
   }
@@ -135,13 +135,13 @@
     String playerID = sync_util.playerIDFromPlayerKey(playerKey);
     int id = int.parse(playerID);
     if (playerNum == null) {
-      if (!players_found.containsKey(id)) {
+      if (!playersFound.containsKey(id)) {
         // The player exists but has not sat down yet.
-        players_found[id] = null;
+        playersFound[id] = null;
       }
     } else {
       int playerNumber = int.parse(playerNum);
-      players_found[id] = playerNumber;
+      playersFound[id] = playerNumber;
 
       // If the player number changed was ours, then set it on our game.
       if (id == settings.userID) {
@@ -168,7 +168,7 @@
         }
         break;
       default:
-        print("Ignoring new status: ${newStatus}");
+        print("Ignoring new status: $newStatus");
     }
     if (this.informUICb != null) {
       this.informUICb();
@@ -196,19 +196,19 @@
         GameType gt = data as GameType;
         _setCurrentGame(_createNewGame(gt));
 
-        _advertiseFuture = settings_manager
+        _advertiseFuture = settingsManager
             .createGameSyncgroup(gameTypeToString(gt), game.gameID)
             .then((GameStartData gsd) {
           // Only the game chooser should be advertising the game.
-          return settings_manager.advertiseSettings(gsd);
+          return settingsManager.advertiseSettings(gsd);
         }); // don't wait for this future.
 
         break;
       case CroupierState.joinGame:
         // Note that if we were in join game, we must have been scanning.
         _scanFuture.then((_) {
-          settings_manager.stopScanSettings();
-          games_found.clear();
+          settingsManager.stopScanSettings();
+          gamesFound.clear();
           _scanFuture = null;
         });
 
@@ -222,22 +222,22 @@
         gsd.playerNumber = null; // At first, there is no player number.
         _setCurrentGame(_createExistingGame(gsd));
         String sgName;
-        games_found.forEach((String name, GameStartData g) {
+        gamesFound.forEach((String name, GameStartData g) {
           if (g == gsd) {
             sgName = name;
           }
         });
         assert(sgName != null);
 
-        players_found[gsd.ownerID] = null;
-        settings_manager.joinGameSyncgroup(sgName, gsd.gameID);
+        playersFound[gsd.ownerID] = null;
+        settingsManager.joinGameSyncgroup(sgName, gsd.gameID);
 
         break;
       case CroupierState.arrangePlayers:
         // Note that if we were arranging players, we might have been advertising.
         if (_advertiseFuture != null) {
           _advertiseFuture.then((_) {
-            settings_manager.stopAdvertiseSettings();
+            settingsManager.stopAdvertiseSettings();
             _advertiseFuture = null;
           });
         }
@@ -252,7 +252,7 @@
         // Data might be GameStartData. If so, then we must advertise it.
         GameStartData gsd = data;
         if (gsd != null) {
-          _advertiseFuture = settings_manager.advertiseSettings(gsd);
+          _advertiseFuture = settingsManager.advertiseSettings(gsd);
         }
         break;
       default:
@@ -263,14 +263,14 @@
     // They will need to be re-discovered in the future.
     switch (nextState) {
       case CroupierState.welcome:
-        games_found.clear();
-        players_found.clear();
+        gamesFound.clear();
+        playersFound.clear();
         _quitGame();
         break;
       case CroupierState.joinGame:
         // Start scanning for games since that's what's next for you.
         _scanFuture =
-            settings_manager.scanSettings(); // don't wait for this future.
+            settingsManager.scanSettings(); // don't wait for this future.
         break;
       case CroupierState.resumeGame:
         // We need to create the game again.
@@ -286,21 +286,20 @@
 
   // Resumes the game from the given gameID.
   Future _resumeGameAsynchronously(int gameIDData) async {
-    GameStartData gsd = await settings_manager.getGameStartData(gameIDData);
+    GameStartData gsd = await settingsManager.getGameStartData(gameIDData);
     bool wasOwner = (gsd.ownerID == settings?.userID);
-    print(
-        "The game was ${gsd.toJSONString()}, and was I the owner? ${wasOwner}");
+    print("The game was ${gsd.toJSONString()}, and was I the owner? $wasOwner");
     _setCurrentGame(_createExistingGame(gsd));
 
-    String sgName = await settings_manager.getGameSyncgroup(gameIDData);
-    print("The sg name was ${sgName}");
-    await settings_manager.joinGameSyncgroup(sgName, gameIDData);
+    String sgName = await settingsManager.getGameSyncgroup(gameIDData);
+    print("The sg name was $sgName");
+    await settingsManager.joinGameSyncgroup(sgName, gameIDData);
 
     // Since initial scan processing is done, we can now set isCreator
     game.isCreator = wasOwner;
-    String gameStatus = await settings_manager.getGameStatus(gameIDData);
+    String gameStatus = await settingsManager.getGameStatus(gameIDData);
 
-    print("The game's status was ${gameStatus}");
+    print("The game's status was $gameStatus");
     // Depending on the game state, we should go to a different screen.
     switch (gameStatus) {
       case "RUNNING":
diff --git a/lib/logic/croupier_settings.dart b/lib/logic/croupier_settings.dart
index 8f18c3a..5a781dd 100644
--- a/lib/logic/croupier_settings.dart
+++ b/lib/logic/croupier_settings.dart
@@ -8,18 +8,6 @@
 /// CroupierSettings is a simple struct that contains player-specific settings.
 /// Players can modify a subset of their settings via the UI.
 class CroupierSettings {
-  // Values the user does not set.
-  int userID;
-  int lastGameID; // Note: Some versions of Croupier do not support lastGameID.
-  bool get hasLastGame => lastGameID != null;
-
-  // Values the user can customize
-  String avatar;
-  String name;
-  int color;
-
-  static String makeAvatarUrl(String key) => 'images/avatars/${key}';
-
   CroupierSettings.random() {
     _randomInitialization();
   }
@@ -39,6 +27,18 @@
     color = data["color"];
   }
 
+  // Values the user does not set.
+  int userID;
+  int lastGameID; // Note: Some versions of Croupier do not support lastGameID.
+  bool get hasLastGame => lastGameID != null;
+
+  // Values the user can customize
+  String avatar;
+  String name;
+  int color;
+
+  static String makeAvatarUrl(String key) => 'images/avatars/$key';
+
   String getStringValue(String key) {
     switch (key) {
       case "name":
@@ -46,7 +46,7 @@
       case "avatar":
         return avatar;
       case "color":
-        return "${color}";
+        return "$color";
       default:
         return null;
     }
@@ -55,11 +55,11 @@
   void setStringValue(String key, String data) {
     switch (key) {
       case "name":
-        print("Setting name to ${data}");
+        print("Setting name to $data");
         name = data;
         break;
       case "avatar":
-        print("Setting avatar to ${data}");
+        print("Setting avatar to $data");
         avatar = data;
         break;
       case "color":
@@ -73,7 +73,7 @@
         } catch (e) {
           print(e);
         }
-        print("Setting color to 0x${newColor}.");
+        print("Setting color to 0x$newColor.");
         color = newColor;
         break;
       default:
diff --git a/lib/logic/game/game_command.part.dart b/lib/logic/game/game_command.part.dart
index 99fb5f2..60a70f6 100644
--- a/lib/logic/game/game_command.part.dart
+++ b/lib/logic/game/game_command.part.dart
@@ -18,6 +18,7 @@
 
   String get command => toString();
 
+  @override
   bool operator ==(Object other) {
     if (other is GameCommand) {
       return this.command == other.command;
@@ -25,7 +26,12 @@
     return false;
   }
 
+  @override
+  int get hashCode =>
+      23 * phase.hashCode + 37 * data.hashCode + 41 * simultaneity.hashCode;
+
+  @override
   String toString() {
-    return "${phase}|${data}";
+    return "$phase|$data";
   }
 }
diff --git a/lib/logic/game/game_def.part.dart b/lib/logic/game/game_def.part.dart
index 5de8037..6222466 100644
--- a/lib/logic/game/game_def.part.dart
+++ b/lib/logic/game/game_def.part.dart
@@ -56,6 +56,7 @@
 
   GameType get gameType => stringToGameType(type);
 
+  @override
   bool operator ==(Object other) {
     if (other is! GameStartData) {
       return false;
@@ -66,6 +67,13 @@
         gsd.gameID == gameID &&
         gsd.ownerID == ownerID;
   }
+
+  @override
+  int get hashCode =>
+      23 * type.hashCode +
+      37 * playerNumber.hashCode +
+      41 * gameID.hashCode +
+      43 * ownerID.hashCode;
 }
 
 // GameArrangeData details what a game needs before beginning.
@@ -87,41 +95,44 @@
 /// A game consists of multiple decks and tracks a single deck of cards.
 /// It also handles events; when cards are dragged to and from decks.
 abstract class Game {
-  GameArrangeData get gameArrangeData;
-  final GameType gameType;
-  String get gameTypeName; // abstract
-  bool isCreator; // True if this user created the game. Behavior can vary based on this flag, so it can make sense to defer setting it.
-
-  final List<List<Card>> cardCollections = new List<List<Card>>();
-  final List<Card> deck = new List<Card>.from(Card.All);
-  final int gameID;
-
-  final GameLog gamelog;
-
-  int _playerNumber;
-  int get playerNumber => _playerNumber;
-  // Some subclasses may wish to override this setter to do extra work.
-  void set playerNumber(int other) {
-    _playerNumber = other;
-  }
-
-  bool debugMode = false;
-  String debugString;
-
-  VoidCallback updateCallback; // Used to inform components of when a change has occurred. This is especially important when something non-UI related changes what should be drawn.
-
   // A super constructor, don't call this unless you're a subclass.
   Game.create(this.gameType, this.gamelog, int numCollections,
       {int gameID, bool isCreator})
       : gameID = gameID ?? new math.Random().nextInt(0x00FFFFFF),
         isCreator = isCreator ?? false {
-    print("The gameID is ${gameID}");
+    print("The gameID is $gameID");
     gamelog.setGame(this);
     for (int i = 0; i < numCollections; i++) {
       cardCollections.add(new List<Card>());
     }
   }
 
+  GameArrangeData get gameArrangeData;
+  final GameType gameType;
+  String get gameTypeName; // abstract
+  bool
+      isCreator; // True if this user created the game. Behavior can vary based on this flag, so it can make sense to defer setting it.
+
+  final List<List<Card>> cardCollections = new List<List<Card>>();
+  final List<Card> deck = new List<Card>.from(Card.all);
+  final int gameID;
+
+  final GameLog gamelog;
+
+  /*int _playerNumber;
+  int get playerNumber => _playerNumber;
+  // Some subclasses may wish to override this setter to do extra work.
+  void set playerNumber(int other) {
+    _playerNumber = other;
+  }*/
+  int playerNumber;
+
+  bool debugMode = false;
+  String debugString;
+
+  VoidCallback
+      updateCallback; // Used to inform components of when a change has occurred. This is especially important when something non-UI related changes what should be drawn.
+
   List<Card> deckPeek(int numCards, [int start = 0]) {
     assert(deck.length >= numCards);
 
@@ -145,7 +156,7 @@
       cardCollections[i].clear();
     }
     deck.clear();
-    deck.addAll(Card.All);
+    deck.addAll(Card.all);
   }
 
   // UNIMPLEMENTED
diff --git a/lib/logic/game/game_log.part.dart b/lib/logic/game/game_log.part.dart
index f502184..bd9544a 100644
--- a/lib/logic/game/game_log.part.dart
+++ b/lib/logic/game/game_log.part.dart
@@ -10,7 +10,7 @@
   // This list is normally empty, but may grow if multiple commands arrive.
   List<GameCommand> pendingCommands = new List<GameCommand>();
   bool hasFired = false; // if true, halts processing of later pendingCommands}
-  asyncKeyValueCallback watchUpdateCb; // May be null.
+  AsyncKeyValueCallback watchUpdateCb; // May be null.
 
   void setGame(Game g) {
     this.game = g;
@@ -94,9 +94,8 @@
     _tryPendingCommand();
   }
 
-  String toString() {
-    return log.toString();
-  }
+  @override
+  String toString() => log.toString();
 
   // UNIMPLEMENTED: Let subclasses override this.
   void addToLogCb(List<GameCommand> log, GameCommand newCommand);
diff --git a/lib/logic/hearts/hearts_command.part.dart b/lib/logic/hearts/hearts_command.part.dart
index 2e5d63f..7791234 100644
--- a/lib/logic/hearts/hearts_command.part.dart
+++ b/lib/logic/hearts/hearts_command.part.dart
@@ -66,7 +66,7 @@
 
   static String computeDeal(int playerId, List<Card> cards) {
     StringBuffer buff = new StringBuffer();
-    buff.write("${playerId}:");
+    buff.write("$playerId:");
     cards.forEach((card) => buff.write("${card.toString()}:"));
     buff.write("END");
     return buff.toString();
@@ -74,18 +74,18 @@
 
   static String computePass(int senderId, List<Card> cards) {
     StringBuffer buff = new StringBuffer();
-    buff.write("${senderId}:");
+    buff.write("$senderId:");
     cards.forEach((card) => buff.write("${card.toString()}:"));
     buff.write("END");
     return buff.toString();
   }
 
   static String computeTake(int takerId) {
-    return "${takerId}:END";
+    return "$takerId:END";
   }
 
   static String computePlay(int playerId, Card c) {
-    return "${playerId}:${c.toString()}:END";
+    return "$playerId:${c.toString()}:END";
   }
 
   static String computeAsk() {
@@ -97,7 +97,7 @@
   }
 
   static String computeReady(int playerId) {
-    return "${playerId}:END";
+    return "$playerId:END";
   }
 
   @override
@@ -107,7 +107,7 @@
     // logic.
     HeartsGame game = g as HeartsGame;
 
-    print("HeartsCommand is checking: ${data}");
+    print("HeartsCommand is checking: $data");
     List<String> parts = data.split(":");
     switch (phase) {
       case "Deal":
@@ -218,7 +218,7 @@
   void execute(Game g) {
     HeartsGame game = g as HeartsGame;
 
-    print("HeartsCommand is executing: ${data}");
+    print("HeartsCommand is executing: $data");
     List<String> parts = data.split(":");
     switch (phase) {
       case "Deal":
@@ -252,7 +252,7 @@
 
         int numPassing = parts.length - 2; // not senderId and not end
         if (numPassing != 3) {
-          throw new StateError("Must pass 3 cards, attempted ${numPassing}");
+          throw new StateError("Must pass 3 cards, attempted $numPassing");
         }
 
         // The last part is 'END', but the rest are cards.
@@ -296,7 +296,7 @@
         String reason = game.canPlay(playerId, c);
         if (reason != null) {
           throw new StateError(
-              "Player ${playerId} cannot play ${c.toString()} because ${reason}");
+              "Player $playerId cannot play ${c.toString()} because $reason");
         }
         this.transfer(hand, discard, c);
         game.asking = false;
diff --git a/lib/logic/hearts/hearts_game.part.dart b/lib/logic/hearts/hearts_game.part.dart
index 65a6eb8..fc015f9 100644
--- a/lib/logic/hearts/hearts_game.part.dart
+++ b/lib/logic/hearts/hearts_game.part.dart
@@ -29,6 +29,13 @@
 
   static const maxScore = 100; // Play until someone gets to 100.
 
+  HeartsGame({int gameID, bool isCreator})
+      : super.create(GameType.hearts, new HeartsLog(), 16,
+            gameID: gameID, isCreator: isCreator) {
+    resetGame();
+    unsetReady();
+  }
+
   // Note: These cards are final because the "classic" deck has 52 cards.
   // It is up to the renderer to reskin those cards as needed.
   final Card twoOfClubs = new Card("classic", "c2");
@@ -42,7 +49,7 @@
   HeartsPhase _phase = HeartsPhase.deal;
   HeartsPhase get phase => _phase;
   void set phase(HeartsPhase other) {
-    print('setting phase from ${_phase} to ${other}');
+    print('setting phase from $_phase to $other');
     _phase = other;
   }
 
@@ -67,13 +74,6 @@
   List<int> deltaScores = [0, 0, 0, 0];
   List<bool> ready;
 
-  HeartsGame({int gameID, bool isCreator})
-      : super.create(GameType.hearts, new HeartsLog(), 16,
-            gameID: gameID, isCreator: isCreator) {
-    resetGame();
-    unsetReady();
-  }
-
   void resetGame() {
     this.resetCards();
     heartsBroken = false;
@@ -204,7 +204,8 @@
 
   bool get hasGameEnded => this.scores.reduce(math.max) >= HeartsGame.maxScore;
 
-  bool get allDealt => cardCollections[playerA].length == 13 &&
+  bool get allDealt =>
+      cardCollections[playerA].length == 13 &&
       cardCollections[playerB].length == 13 &&
       cardCollections[playerC].length == 13 &&
       cardCollections[playerD].length == 13;
@@ -224,7 +225,8 @@
   bool get allPassed => numPassed == 4;
   bool hasTaken(int player) =>
       cardCollections[getTakeTarget(player) + offsetPass].length == 0;
-  bool get allTaken => cardCollections[playerPassA].length == 0 &&
+  bool get allTaken =>
+      cardCollections[playerPassA].length == 0 &&
       cardCollections[playerPassB].length == 0 &&
       cardCollections[playerPassC].length == 0 &&
       cardCollections[playerPassD].length == 0;
@@ -298,6 +300,7 @@
 
   static final GameArrangeData _arrangeData =
       new GameArrangeData(true, new Set.from([0, 1, 2, 3]));
+  @override
   GameArrangeData get gameArrangeData => _arrangeData;
 
   @override
@@ -315,7 +318,8 @@
   }
 
   // Note that this will be called by the UI.
-  // TODO: Does this really need to be overridden? That seems like bad structure in GameComponent.
+  // TODO(alexfandrianto): Does this really need to be overridden?
+  // That seems like bad structure in GameComponent.
   // Overrides Game's move method with the "move" logic for Hearts. Used for drag-drop.
   // Note that this can only be called in the Play Phase of your turn.
   // The UI will handle the drag-drop of the Pass Phase with its own state.
@@ -337,12 +341,12 @@
     }
     if (destId != playerNumber + offsetPlay) {
       throw new StateError(
-          'player ${playerNumber} is not playing to the correct list: ${destId}');
+          'player $playerNumber is not playing to the correct list: $destId');
     }
 
     gamelog.add(new HeartsCommand.play(playerNumber, card));
 
-    debugString = 'Play ${i} ${card.toString()}';
+    debugString = 'Play $i ${card.toString()}';
     print(debugString);
   }
 
@@ -410,13 +414,13 @@
       return "It is not the Play phase of Hearts.";
     }
     if (!cardCollections[player].contains(c)) {
-      return "Player ${player} does not have the card (${c.toString()})";
+      return "Player $player does not have the card (${c.toString()})";
     }
     if (this.allPlayed) {
       return "Trick not taken yet.";
     }
     if (this.whoseTurn != player && !lenient) {
-      return "It is not Player ${player}'s turn.";
+      return "It is not Player $player's turn.";
     }
     if (trickNumber == 0 && this.numPlayed == 0 && c != twoOfClubs) {
       return "You must play the 2 of Clubs";
@@ -470,7 +474,7 @@
     deltaScores = [0, 0, 0, 0];
 
     // Count up points and check if someone shot the moon.
-    int shotMoon = null;
+    int shotMoon;
     for (int i = 0; i < 4; i++) {
       int delta = computeScore(i);
       this.deltaScores[i] = delta;
diff --git a/lib/logic/hearts/hearts_log.part.dart b/lib/logic/hearts/hearts_log.part.dart
index 22849c5..fbb12c2 100644
--- a/lib/logic/hearts/hearts_log.part.dart
+++ b/lib/logic/hearts/hearts_log.part.dart
@@ -31,7 +31,7 @@
       this.update(hc);
       seenKeys.add(key);
     } else {
-      print("The log is ignoring repeated key: ${key}");
+      print("The log is ignoring repeated key: $key");
     }
   }
 
diff --git a/lib/logic/proto/proto_command.part.dart b/lib/logic/proto/proto_command.part.dart
index 80cfeee..953658f 100644
--- a/lib/logic/proto/proto_command.part.dart
+++ b/lib/logic/proto/proto_command.part.dart
@@ -12,7 +12,7 @@
   ProtoCommand.deal(int playerId, List<Card> cards)
       : super("Deal", computeDeal(playerId, cards));
 
-  // TODO: receiverId is actually implied by the game round. So it may end up being removable.
+  // TODO(alexfandrianto): receiverId is actually implied by the game round. So it may end up being removable.
   ProtoCommand.pass(int senderId, int receiverId, List<Card> cards)
       : super("Pass", computePass(senderId, receiverId, cards));
 
@@ -21,7 +21,7 @@
 
   static String computeDeal(int playerId, List<Card> cards) {
     StringBuffer buff = new StringBuffer();
-    buff.write("${playerId}:");
+    buff.write("$playerId:");
     cards.forEach((card) => buff.write("${card.toString()}:"));
     buff.write("END");
     return buff.toString();
@@ -29,14 +29,14 @@
 
   static String computePass(int senderId, int receiverId, List<Card> cards) {
     StringBuffer buff = new StringBuffer();
-    buff.write("${senderId}:${receiverId}:");
+    buff.write("$senderId:$receiverId:");
     cards.forEach((card) => buff.write("${card.toString()}:"));
     buff.write("END");
     return buff.toString();
   }
 
   static String computePlay(int playerId, Card c) {
-    return "${playerId}:${c.toString()}:END";
+    return "$playerId:${c.toString()}:END";
   }
 
   @override
@@ -46,7 +46,7 @@
 
   @override
   void execute(Game game) {
-    print("ProtoCommand is executing: ${data}");
+    print("ProtoCommand is executing: $data");
     List<String> parts = data.split(":");
     switch (phase) {
       case "Deal":
diff --git a/lib/logic/proto/proto_game.part.dart b/lib/logic/proto/proto_game.part.dart
index 3c38aba..99c9519 100644
--- a/lib/logic/proto/proto_game.part.dart
+++ b/lib/logic/proto/proto_game.part.dart
@@ -5,13 +5,6 @@
 part of proto;
 
 class ProtoGame extends Game {
-  @override
-  String get gameTypeName => "Proto";
-
-  static final GameArrangeData _arrangeData =
-      new GameArrangeData(false, new Set());
-  GameArrangeData get gameArrangeData => _arrangeData;
-
   ProtoGame({int gameID, bool isCreator})
       : super.create(GameType.proto, new ProtoLog(), 6,
             gameID: gameID, isCreator: isCreator) {
@@ -27,6 +20,14 @@
     deal(3, 1);
   }
 
+  @override
+  String get gameTypeName => "Proto";
+
+  static final GameArrangeData _arrangeData =
+      new GameArrangeData(false, new Set());
+  @override
+  GameArrangeData get gameArrangeData => _arrangeData;
+
   void deal(int playerId, int numCards) {
     gamelog.add(new ProtoCommand.deal(playerId, this.deckPeek(numCards)));
   }
@@ -46,7 +47,7 @@
 
     gamelog.add(new ProtoCommand.pass(i, destId, <Card>[card]));
 
-    debugString = 'Move ${i} ${card.toString()}';
+    debugString = 'Move $i ${card.toString()}';
     print(debugString);
   }
 
diff --git a/lib/logic/solitaire/solitaire_command.part.dart b/lib/logic/solitaire/solitaire_command.part.dart
index 5435d2b..b9fce31 100644
--- a/lib/logic/solitaire/solitaire_command.part.dart
+++ b/lib/logic/solitaire/solitaire_command.part.dart
@@ -39,7 +39,7 @@
   // Note: Depending on the target's position w.r.t. the targetPile, this may
   // actually move a group of cards instead.
   static String computeMove(Card target, int targetPile) {
-    return "${target.toString()}:${targetPile}:END";
+    return "${target.toString()}:$targetPile:END";
   }
 
   // Note: If there are no cards to draw, this will reset the draw pile.
@@ -48,7 +48,7 @@
   }
 
   static String computeFlip(int targetPile) {
-    return "${targetPile}:END";
+    return "$targetPile:END";
   }
 
   @override
@@ -58,7 +58,7 @@
     // logic.
     SolitaireGame game = g as SolitaireGame;
 
-    print("SolitaireCommand is checking: ${command}");
+    print("SolitaireCommand is checking: $command");
     List<String> parts = data.split(":");
     switch (phase) {
       case "Deal":
@@ -125,7 +125,7 @@
   void execute(Game g) {
     SolitaireGame game = g as SolitaireGame;
 
-    print("SolitaireCommand is executing: ${command}");
+    print("SolitaireCommand is executing: $command");
     List<String> parts = data.split(":");
     switch (phase) {
       case "Deal":
@@ -177,7 +177,7 @@
           throw new StateError("Cannot move unknown card ${c.toString()}");
         }
         if (targetId < 0 || targetId >= game.cardCollections.length) {
-          throw new StateError("Cannot move to unknown pile ${targetId}");
+          throw new StateError("Cannot move to unknown pile $targetId");
         }
         List<Card> source = game.cardCollections[sourceId];
         List<Card> dest = game.cardCollections[targetId];
@@ -186,7 +186,7 @@
         String reason = game.canPlay(c, dest);
         if (reason != null) {
           throw new StateError(
-              "Cannot move ${c.toString()} to Pile ${targetId} because ${reason}");
+              "Cannot move ${c.toString()} to Pile $targetId because $reason");
         }
         this.transferGroup(source, dest, c);
         return;
@@ -216,8 +216,7 @@
 
         int flipId = int.parse(parts[0]);
         if (flipId < 0 || flipId >= 7) {
-          throw new StateError(
-              "Cannot process flip command for index ${flipId}");
+          throw new StateError("Cannot process flip command for index $flipId");
         }
 
         List<Card> flipSource =
@@ -227,11 +226,11 @@
 
         if (flipDest.length != 0) {
           throw new StateError(
-              "Cannot flip ${flipId} because destination has cards");
+              "Cannot flip $flipId because destination has cards");
         }
         if (flipSource.length == 0) {
           throw new StateError(
-              "Cannot flip ${flipId} because source has no cards");
+              "Cannot flip $flipId because source has no cards");
         }
         this.transfer(flipSource, flipDest, flipSource[flipSource.length - 1]);
         return;
diff --git a/lib/logic/solitaire/solitaire_game.part.dart b/lib/logic/solitaire/solitaire_game.part.dart
index ebbe866..36195a8 100644
--- a/lib/logic/solitaire/solitaire_game.part.dart
+++ b/lib/logic/solitaire/solitaire_game.part.dart
@@ -7,9 +7,6 @@
 enum SolitairePileType { aces, discard, draw, down, up }
 
 class SolitaireGame extends Game {
-  @override
-  String get gameTypeName => "Solitaire";
-
   // Constants for the index-based offsets of the Solitaire Game's card collection.
   // There are 20 piles to track (4 aces, 1 discard, 1 draw, 7 down, 7 up).
   static const numPiles = 20;
@@ -19,23 +16,27 @@
   static const offsetDown = 6;
   static const offsetUp = 13;
 
+  SolitaireGame({int gameID, bool isCreator})
+      : super.create(GameType.solitaire, new SolitaireLog(), numPiles,
+            gameID: gameID, isCreator: isCreator) {
+    resetGame();
+  }
+
+  @override
+  String get gameTypeName => "Solitaire";
+
   static final GameArrangeData _arrangeData =
       new GameArrangeData(false, new Set());
+  @override
   GameArrangeData get gameArrangeData => _arrangeData;
 
   SolitairePhase _phase = SolitairePhase.deal;
   SolitairePhase get phase => _phase;
   void set phase(SolitairePhase other) {
-    print('setting phase from ${_phase} to ${other}');
+    print('setting phase from $_phase to $other');
     _phase = other;
   }
 
-  SolitaireGame({int gameID, bool isCreator})
-      : super.create(GameType.solitaire, new SolitaireLog(), numPiles,
-            gameID: gameID, isCreator: isCreator) {
-    resetGame();
-  }
-
   void resetGame() {
     this.resetCards();
   }
@@ -74,7 +75,8 @@
     return getCardSuit(c) == 's' || getCardSuit(c) == 'c';
   }
 
-  bool get canDrawCard => cardCollections[offsetDiscard].length +
+  bool get canDrawCard =>
+      cardCollections[offsetDiscard].length +
           cardCollections[offsetDraw].length >
       0;
 
@@ -123,7 +125,7 @@
     List<String> suits = new List<String>(4);
     Set<String> remainingSuits =
         new Set<String>.from(<String>['c', 'd', 'h', 's']);
-    int minLen = null;
+    int minLen;
     for (int i = 0; i < 4; i++) {
       int len = cardCollections[offsetAces + i].length;
 
@@ -155,25 +157,25 @@
         // Note: If we pull from up cards, the game may not be in a valid state,
         // but this is okay since we are cheating.
 
-        int index_offset;
+        int indexOffset;
         switch (suits[i]) {
           case 'c':
-            index_offset = 0;
+            indexOffset = 0;
             break;
           case 'd':
-            index_offset = 13;
+            indexOffset = 13;
             break;
           case 'h':
-            index_offset = 26;
+            indexOffset = 26;
             break;
           case 's':
-            index_offset = 39;
+            indexOffset = 39;
             break;
           default:
             print('the suit was ${suits[i]}');
             assert(false);
         }
-        Card c = Card.All[index_offset + minLen];
+        Card c = Card.all[indexOffset + minLen];
         int pileIndex = findCard(c);
 
         cardCollections[pileIndex].remove(c);
@@ -295,7 +297,7 @@
   String canPlay(Card c, List<Card> dest) {
     int destination = cardCollections.indexOf(dest);
     int source = findCard(c);
-    print("Can play? ${c}, ${source} ${destination}");
+    print("Can play? $c, $source $destination");
 
     if (phase != SolitairePhase.play) {
       return "It is not the Play phase of Solitaire.";
@@ -304,7 +306,7 @@
       return "Unknown card: (${c.toString()})";
     }
     if (dest == -1) {
-      return "Unknown destination: ${dest}";
+      return "Unknown destination: $dest";
     }
     if (source == destination) {
       return "Source Pile is same as Destination Pile";
diff --git a/lib/logic/solitaire/solitaire_log.part.dart b/lib/logic/solitaire/solitaire_log.part.dart
index 579c687..578721d 100644
--- a/lib/logic/solitaire/solitaire_log.part.dart
+++ b/lib/logic/solitaire/solitaire_log.part.dart
@@ -31,7 +31,7 @@
       this.update(sc);
       seenKeys.add(key);
     } else {
-      print("The log is ignoring repeated key: ${key}");
+      print("The log is ignoring repeated key: $key");
     }
   }
 
diff --git a/lib/main.dart b/lib/main.dart
index 3c091b6..38a5705 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -14,28 +14,31 @@
 import 'sound/sound_assets.dart';
 import 'styles/common.dart' as style;
 
-class CroupierApp extends StatefulComponent {
+class CroupierApp extends StatefulWidget {
   settings_client.AppSettings appSettings;
   SoundAssets sounds;
   CroupierApp(this.appSettings, this.sounds);
 
+  @override
   CroupierAppState createState() => new CroupierAppState();
 }
 
 class CroupierAppState extends State<CroupierApp> {
   Croupier croupier;
 
+  @override
   void initState() {
     super.initState();
     this.croupier = new Croupier(config.appSettings);
   }
 
+  @override
   Widget build(BuildContext context) {
     return new MaterialApp(
         title: 'Croupier',
-        routes: <String, RouteBuilder>{
-          "/": (RouteArguments args) => new MainRoute(croupier, config.sounds),
-          "/settings": (RouteArguments args) => new SettingsRoute(croupier)
+        routes: <String, WidgetBuilder>{
+          "/": (BuildContext _) => new MainRoute(croupier, config.sounds),
+          "/settings": (BuildContext _) => new SettingsRoute(croupier)
         },
         theme: style.theme);
   }
@@ -49,12 +52,13 @@
 }
 
 Future<SoundAssets> loadAudio() async {
+  // TODO(alexfandrianto): Sound is turned off since it's not convenient to get
+  // HEAD Mojo Shell built with the media service.
+
   final AssetBundle _bundle = _initBundle();
   SoundAssets _sounds = new SoundAssets(_bundle);
 
   // Load sounds in parallel.
-  // TODO(alexfandrianto): Sound is turned off since it's not convenient to get
-  // HEAD Mojo Shell built with the media service.
   //await Future.wait([_sounds.load("whooshIn"), _sounds.load("whooshOut")]);
   return _sounds;
 }
diff --git a/lib/sound/sound_assets.dart b/lib/sound/sound_assets.dart
index bf5f2d4..71228ba 100644
--- a/lib/sound/sound_assets.dart
+++ b/lib/sound/sound_assets.dart
@@ -10,7 +10,8 @@
 /// SoundAssets are used to play sounds in the game.
 class SoundAssets {
   SoundAssets(this._bundle) {
-    _soundEffectPlayer = new SoundEffectPlayer(20);
+    // TODO(alexfandrianto): We cannot play sounds without a media service.
+    //_soundEffectPlayer = new SoundEffectPlayer(20);
   }
 
   AssetBundle _bundle;
@@ -23,6 +24,7 @@
   }
 
   void play(String name) {
-    _soundEffectPlayer.play(_soundEffects[name]);
+    // TODO(alexfandrianto): We cannot play sounds without a media service.
+    //_soundEffectPlayer.play(_soundEffects[name]);
   }
 }
diff --git a/lib/src/mocks/log_writer.dart b/lib/src/mocks/log_writer.dart
index de01547..4291f11 100644
--- a/lib/src/mocks/log_writer.dart
+++ b/lib/src/mocks/log_writer.dart
@@ -7,10 +7,12 @@
 
 enum SimulLevel { turnBased, independent, dependent }
 
-typedef void keyValueCallback(String key, String value);
+typedef void KeyValueCallback(String key, String value);
 
 class LogWriter {
-  final keyValueCallback updateCallback;
+  LogWriter(this.updateCallback, this.users);
+
+  final KeyValueCallback updateCallback;
   final List<int> users;
   String logPrefix; // This can be completely ignored.
 
@@ -23,8 +25,6 @@
     return _fakeTime;
   }
 
-  LogWriter(this.updateCallback, this.users);
-
   Map<String, String> _data = new Map<String, String>();
 
   Future onChange(String rowKey, String value, bool duringScan) async {}
@@ -56,7 +56,7 @@
   // Helper that returns the log key using a mixture of timestamp + user.
   String _logKey(int user) {
     int ms = _getNextTime();
-    String key = "${ms}-${user}";
+    String key = "$ms-$user";
     return key;
   }
 
@@ -106,7 +106,7 @@
 
   // More helpers for proposals.
   String _proposalKey(int user) {
-    return "proposal${user}";
+    return "proposal$user";
   }
 
   bool _checkIsProposalDone() {
diff --git a/lib/src/mocks/settings_manager.dart b/lib/src/mocks/settings_manager.dart
index 3b1525f..047a9e9 100644
--- a/lib/src/mocks/settings_manager.dart
+++ b/lib/src/mocks/settings_manager.dart
@@ -10,11 +10,11 @@
 import 'util.dart' as util;
 
 class SettingsManager {
-  final util.keyValueCallback updateCallback;
-  final util.keyValueCallback updateGamesCallback;
-  final util.keyValueCallback updatePlayerFoundCallback;
-  final util.keyValueCallback updateGameStatusCallback;
-  final util.asyncKeyValueCallback updateGameLogCallback;
+  final util.KeyValueCallback updateCallback;
+  final util.KeyValueCallback updateGamesCallback;
+  final util.KeyValueCallback updatePlayerFoundCallback;
+  final util.KeyValueCallback updateGameStatusCallback;
+  final util.AsyncKeyValueCallback updateGameLogCallback;
 
   SettingsManager(
       settings_client.AppSettings _,
@@ -35,12 +35,12 @@
       }
       return new Future<String>(() => _data["settings"]);
     }
-    return new Future<String>(() => _data["${userID}"]);
+    return new Future<String>(() => _data["$userID"]);
   }
 
   Future save(int userID, String data) {
     _data["settings"] = data;
-    _data["${userID}"] = data;
+    _data["$userID"] = data;
     return new Future(() => null);
   }
 
diff --git a/lib/src/mocks/util.dart b/lib/src/mocks/util.dart
index 0096922..4f01cf5 100644
--- a/lib/src/mocks/util.dart
+++ b/lib/src/mocks/util.dart
@@ -4,8 +4,8 @@
 
 import 'dart:async';
 
-typedef void keyValueCallback(String key, String value);
-typedef Future asyncKeyValueCallback(String key, String value, bool duringScan);
+typedef void KeyValueCallback(String key, String value);
+typedef Future AsyncKeyValueCallback(String key, String value, bool duringScan);
 
 String gameIDFromGameKey(String gameKey) {
   return null;
diff --git a/lib/src/syncbase/croupier_client.dart b/lib/src/syncbase/croupier_client.dart
index 7f4558f..e923d41 100644
--- a/lib/src/syncbase/croupier_client.dart
+++ b/lib/src/syncbase/croupier_client.dart
@@ -6,7 +6,7 @@
 import 'dart:convert' show UTF8;
 import 'dart:io' show Platform;
 
-import 'package:flutter/services.dart' show shell;
+import 'package:flutter/shell.dart' show shell;
 import 'package:syncbase/src/naming/util.dart' as naming;
 import 'package:syncbase/syncbase_client.dart' as sc;
 
@@ -18,9 +18,9 @@
   final sc.SyncbaseClient _syncbaseClient;
   final DiscoveryClient _discoveryClient;
   final settings_client.AppSettings appSettings;
-  static final String syncbaseServerUrl = Platform.environment[
-          'SYNCBASE_SERVER_URL'] ??
-      'https://mojo.v.io/syncbase_server.mojo';
+  static final String syncbaseServerUrl =
+      Platform.environment['SYNCBASE_SERVER_URL'] ??
+          'https://mojo.v.io/syncbase_server.mojo';
 
   static final String discoveryTestKey = "TEST";
 
@@ -78,7 +78,7 @@
     if (!(await table.exists())) {
       await table.create(util.openPerms);
     }
-    util.log('CroupierClient: ${tableName} is ready');
+    util.log('CroupierClient: $tableName is ready');
     _tableLock[tableName].complete(table);
     return table;
   }
@@ -86,18 +86,18 @@
   // Creates (or joins) a syncgroup with the associated parameters.
   Future<sc.SyncbaseSyncgroup> createSyncgroup(String sgName, String tableName,
       {String prefix, String description, sc.Perms permissions}) async {
-    util.log("CroupierClient: Creating syncgroup ${sgName}");
+    util.log("CroupierClient: Creating syncgroup $sgName");
 
     // TODO(alexfandrianto): destroy is still unimplemented. Thus, we must do a
     // join or create for this syncgroup.
     try {
-      util.log("CroupierClient: But first attempting to join ${sgName}");
+      util.log("CroupierClient: But first attempting to join $sgName");
       sc.SyncbaseSyncgroup sg = await joinSyncgroup(sgName);
-      util.log("CroupierClient: Successfully joined ${sgName}");
+      util.log("CroupierClient: Successfully joined $sgName");
       return sg;
     } catch (e) {
       util.log(
-          "CroupierClient: ${sgName} doesn't exist, so actually creating it.");
+          "CroupierClient: $sgName doesn't exist, so actually creating it.");
     }
 
     var myInfo = sc.SyncbaseClient.syncgroupMemberInfo(syncPriority: 3);
@@ -120,7 +120,7 @@
 
   // Joins a syncgroup with the given name.
   Future<sc.SyncbaseSyncgroup> joinSyncgroup(String sgName) async {
-    util.log("CroupierClient: Joining syncgroup ${sgName}");
+    util.log("CroupierClient: Joining syncgroup $sgName");
     var myInfo = sc.SyncbaseClient.syncgroupMemberInfo(syncPriority: 3);
 
     sc.SyncbaseSyncgroup sg = await _getSyncgroup(sgName);
@@ -151,9 +151,9 @@
       sc.SyncbaseDatabase db,
       String tbName,
       String prefix,
-      util.asyncKeyValueCallback onChange,
+      util.AsyncKeyValueCallback onChange,
       {Comparator<sc.WatchChange> sorter}) async {
-    util.log('Watching for changes on ${tbName}:${prefix}...');
+    util.log('Watching for changes on $tbName:$prefix...');
 
     // For safety, be certain that the syncbase table at tbName exists.
     await createTable(db, tbName);
@@ -176,7 +176,7 @@
       await for (sc.KeyValue kv in scanStream) {
         String key = kv.key;
         String value = UTF8.decode(kv.value);
-        print("Scan found ${key}, ${value}");
+        print("Scan found $key, $value");
         await onChange(key, value, true);
       }
     } finally {
@@ -191,7 +191,7 @@
 
     // Define a change handler that will be applied whenever a WatchChange
     // arrives on the watchStream.
-    _handleChange(sc.WatchChange wc) async {
+    Future _handleChange(sc.WatchChange wc) async {
       // Accumulate the WatchChange's in watchSequence.
       watchSequence.add(wc);
       if (wc.continued) {
@@ -218,7 +218,7 @@
               assert(false);
           }
 
-          print("Watch found ${key}, ${value}");
+          print("Watch found $key, $value");
           await onChange(key, value, false);
         });
 
diff --git a/lib/src/syncbase/discovery_client.dart b/lib/src/syncbase/discovery_client.dart
index 672d413..5613a5f 100644
--- a/lib/src/syncbase/discovery_client.dart
+++ b/lib/src/syncbase/discovery_client.dart
@@ -5,7 +5,7 @@
 import 'dart:async';
 
 import 'package:v23discovery/discovery.dart' as discovery;
-import 'package:flutter/services.dart' show shell;
+import 'package:flutter/shell.dart' show shell;
 
 /// Make this into the Dart Discovery client
 /// https://github.com/vanadium/issues/issues/835
@@ -17,21 +17,17 @@
   final discovery.Client _discoveryClient =
       new discovery.Client(shell.connectToService, _discoveryUrl);
 
-  static discovery.Service serviceMaker(
-      {String instanceId,
-      String instanceName,
+  static discovery.Advertisement advertisementMaker(
+      {List<int> id,
       String interfaceName,
       Map<String, String> attrs,
       List<String> addrs}) {
     // Discovery requires that some of these values must be set.
     assert(interfaceName != null && interfaceName != '');
     assert(addrs != null && addrs.length > 0);
-    return new discovery.Service()
-      ..instanceId = instanceId
-      ..instanceName = instanceName
-      ..interfaceName = interfaceName
-      ..attrs = attrs
-      ..addrs = addrs;
+    return new discovery.Advertisement(interfaceName, addrs)
+      ..id = id
+      ..attributes = attrs;
   }
 
   // Scans for this query and handles found/lost objects with the handler.
@@ -46,11 +42,11 @@
     discovery.Scanner scanner = await _discoveryClient.scan(query);
     _scanners[key] = scanner;
 
-    scanner.onUpdate.listen((discovery.ScanUpdate update) {
-      if (update.updateType == discovery.UpdateType.found) {
-        onFound(update.service);
+    scanner.onUpdate.listen((discovery.Update update) {
+      if (update.updateType == discovery.UpdateTypes.found) {
+        onFound(update);
       } else {
-        onLost(update.service.instanceId);
+        onLost(update.id);
       }
     });
 
@@ -69,14 +65,14 @@
 
   // Advertises the given service information. Keeps track of the advertiser
   // handle via the key.
-  Future advertise(String key, discovery.Service service,
+  Future advertise(String key, discovery.Advertisement ad,
       {List<String> visibility}) async {
     // Return the existing advertisement if one is already going.
     if (_advertisers.containsKey(key)) {
       return _advertisers[key];
     }
     _advertisers[key] =
-        await _discoveryClient.advertise(service, visibility: visibility);
+        await _discoveryClient.advertise(ad, visibility: visibility);
 
     return _advertisers[key];
   }
diff --git a/lib/src/syncbase/log_writer.dart b/lib/src/syncbase/log_writer.dart
index ea7d5ef..862bce6 100644
--- a/lib/src/syncbase/log_writer.dart
+++ b/lib/src/syncbase/log_writer.dart
@@ -31,8 +31,16 @@
 enum SimulLevel { turnBased, independent, dependent }
 
 class LogWriter {
+  // The LogWriter takes a callback for watch updates, the list of users, and
+  // the logPrefix to write at on table.
+  LogWriter(this.updateCallback, this.users)
+      : _cc = new CroupierClient.singleton() {
+    _prepareLog();
+    _diffFileLog("=========Starting Log Writer=========");
+  }
+
   // This callback is called on each watch update, passing the key and value.
-  final util.keyValueCallback updateCallback;
+  final util.KeyValueCallback updateCallback;
 
   // The users that we should look for when coming to a proposal consensus.
   final List<int> users;
@@ -73,14 +81,6 @@
   final File _diffFile =
       new File('/data/data/org.chromium.mojo.shell/diffFile.txt');
 
-  // The LogWriter takes a callback for watch updates, the list of users, and
-  // the logPrefix to write at on table.
-  LogWriter(this.updateCallback, this.users)
-      : _cc = new CroupierClient.singleton() {
-    _prepareLog();
-    _diffFileLog("=========Starting Log Writer=========");
-  }
-
   DateTime _getLatestTime() {
     DateTime now = new DateTime.now();
     if (latestTime.isAfter(now)) {
@@ -103,7 +103,7 @@
   Future _diffFileLog(String s, [DateTime other]) async {
     DateTime now = _getLatestTime();
     int diff = other != null ? now.difference(other).inMilliseconds : null;
-    String logStr = "${now.millisecondsSinceEpoch}\t${diff}\t${s}\n";
+    String logStr = "${now.millisecondsSinceEpoch}\t$diff\t$s\n";
     print(logStr);
     await _diffFile.writeAsString(logStr, mode: FileMode.APPEND, flush: true);
   }
@@ -121,7 +121,7 @@
     String key = rowKey.replaceFirst("${this.logPrefix}/", "");
     String timeStr = key.split("-")[0];
     DateTime keyTime = _parseTime(timeStr);
-    await _diffFileLog("Key: ${key} Value: ${value}", keyTime);
+    await _diffFileLog("Key: $key Value: $value", keyTime);
 
     if (keyTime.isAfter(latestTime)) {
       latestTime = keyTime;
@@ -132,7 +132,7 @@
         await _receiveProposal(key, value);
       }
     } else {
-      print("Update callback: ${key}, ${value}");
+      print("Update callback: $key, $value");
       this.updateCallback(key, value);
     }
   }
@@ -164,7 +164,7 @@
   }
 
   String _rowKey(String key) {
-    return "${this.logPrefix}/${key}";
+    return "${this.logPrefix}/$key";
   }
 
   Future _deleteData(String key) async {
@@ -179,7 +179,7 @@
       latestTime = time;
     }
     int ms = time.millisecondsSinceEpoch;
-    String key = "${ms}-${user}";
+    String key = "$ms-$user";
     return key;
   }
 
@@ -240,7 +240,7 @@
       String value = pp["value"];
 
       _acceptedProposals.add(key);
-      print("All proposals accepted. Proceeding with ${key} ${value}");
+      print("All proposals accepted. Proceeding with $key $value");
       // WOULD DO A BATCH!
       for (int i = 0; i < users.length; i++) {
         await _deleteData(_proposalKey(users[i]));
@@ -262,7 +262,7 @@
   }
 
   String _proposalKey(int user) {
-    return "proposal/${user}";
+    return "proposal/$user";
   }
 
   Future<bool> _checkIsProposalDone() async {
diff --git a/lib/src/syncbase/settings_manager.dart b/lib/src/syncbase/settings_manager.dart
index 60b17b0..46b1f95 100644
--- a/lib/src/syncbase/settings_manager.dart
+++ b/lib/src/syncbase/settings_manager.dart
@@ -28,11 +28,11 @@
 import 'package:syncbase/syncbase_client.dart' as sc;
 
 class SettingsManager {
-  final util.keyValueCallback updateSettingsCallback;
-  final util.keyValueCallback updateGamesCallback;
-  final util.keyValueCallback updatePlayerFoundCallback;
-  final util.keyValueCallback updateGameStatusCallback;
-  final util.asyncKeyValueCallback updateGameLogCallback;
+  final util.KeyValueCallback updateSettingsCallback;
+  final util.KeyValueCallback updateGamesCallback;
+  final util.KeyValueCallback updatePlayerFoundCallback;
+  final util.KeyValueCallback updateGameStatusCallback;
+  final util.AsyncKeyValueCallback updateGameLogCallback;
   final CroupierClient _cc;
   sc.SyncbaseTable tb;
 
@@ -89,7 +89,7 @@
   Future<String> _tryReadData(sc.SyncbaseTable st, String rowkey) async {
     var row = st.row(rowkey);
     if (!(await row.exists())) {
-      print("${rowkey} did not exist");
+      print("$rowkey did not exist");
       return null;
     }
     return UTF8.decode(await row.get());
@@ -102,7 +102,7 @@
     util.log('SettingsManager.save');
     await _prepareSettingsTable();
 
-    await tb.row(util.settingsPersonalKey).put(UTF8.encode("${userID}"));
+    await tb.row(util.settingsPersonalKey).put(UTF8.encode("$userID"));
     await tb
         .row(util.settingsDataKeyFromUserID(userID))
         .put(UTF8.encode(jsonString));
@@ -139,7 +139,7 @@
             this.updatePlayerFoundCallback(key, null);
             break;
           default:
-            print("Unexpected key: ${key} with value ${value}");
+            print("Unexpected key: $key with value $value");
             assert(false);
         }
       }
@@ -156,7 +156,7 @@
 
   Future<logic_game.GameStartData> createGameSyncgroup(
       String type, int gameID) async {
-    print("Creating game syncgroup for ${type} and ${gameID}");
+    print("Creating game syncgroup for $type and $gameID");
     sc.SyncbaseDatabase db = await _cc.createDatabase();
     sc.SyncbaseTable gameTable = await _cc.createTable(db, util.tableNameGames);
 
@@ -168,12 +168,12 @@
       return a.rowKey.compareTo(b.rowKey);
     });
 
-    print("Now writing to some rows of ${gameID}");
+    print("Now writing to some rows of $gameID");
     // Start up the table and write yourself as player 0.
-    await gameTable.row(util.gameTypeKey(gameID)).put(UTF8.encode("${type}"));
+    await gameTable.row(util.gameTypeKey(gameID)).put(UTF8.encode("$type"));
 
     int id = await _getUserID();
-    await gameTable.row(util.gameOwnerKey(gameID)).put(UTF8.encode("${id}"));
+    await gameTable.row(util.gameOwnerKey(gameID)).put(UTF8.encode("$id"));
     await gameTable
         .row(util.playerSettingsKeyFromData(gameID, id))
         .put(UTF8.encode(await _mySettingsSyncgroupName()));
@@ -199,7 +199,7 @@
   }
 
   Future joinGameSyncgroup(String sgName, int gameID) async {
-    print("Now joining game syncgroup at ${sgName} and ${gameID}");
+    print("Now joining game syncgroup at $sgName and $gameID");
 
     sc.SyncbaseDatabase db = await _cc.createDatabase();
     sc.SyncbaseTable gameTable = await _cc.createTable(db, util.tableNameGames);
@@ -225,7 +225,7 @@
 
     await gameTable
         .row(util.playerNumberKeyFromData(gameID, userID))
-        .put(UTF8.encode("${playerNumber}"));
+        .put(UTF8.encode("$playerNumber"));
   }
 
   Future setGameStatus(int gameID, String status) async {
@@ -288,13 +288,15 @@
     String gameSuffix = util.syncgameSuffix("${gsd.gameID}");
     return _cc.discoveryClient.advertise(
         _discoveryGameAdKey,
-        DiscoveryClient.serviceMaker(
+        DiscoveryClient.advertisementMaker(
             interfaceName: util.discoveryInterfaceName,
             attrs: <String, String>{
               util.syncgameSettingsAttr: _cc.makeSyncgroupName(settingsSuffix),
               util.syncgameGameStartDataAttr: gsd.toJSONString()
             },
-            addrs: <String>[_cc.makeSyncgroupName(gameSuffix)]));
+            addrs: <String>[
+              _cc.makeSyncgroupName(gameSuffix)
+            ]));
   }
 
   Future stopAdvertiseSettings() {
@@ -315,7 +317,7 @@
       id = await _getUserID();
     }
 
-    return "${util.sgSuffix}-${id}";
+    return "${util.sgSuffix}-$id";
   }
 }
 
@@ -326,26 +328,27 @@
   CroupierClient _cc;
   Map<String, String> settingsAddrs;
   Map<String, String> gameAddrs;
-  util.keyValueCallback updateGamesCallback;
+  util.KeyValueCallback updateGamesCallback;
 
   SettingsScanHandler(this._cc, this.updateGamesCallback) {
     settingsAddrs = new Map<String, String>();
     gameAddrs = new Map<String, String>();
   }
 
-  void found(discovery.Service s) {
+  void found(discovery.Update s) {
     util.log(
-        "SettingsScanHandler Found ${s.instanceId} ${s.instanceName} ${s.addrs}");
+        "SettingsScanHandler Found ${s.id} ${s.interfaceName} ${s.addresses}");
 
-    if (s.addrs.length == 1 && s.attrs != null) {
+    if (s.addresses.length == 1 && s.attributes != null) {
       // Note: Assumes 1 address and attributes for the game.
-      settingsAddrs[s.instanceId] = s.attrs[util.syncgameSettingsAttr];
-      gameAddrs[s.instanceId] = s.addrs[0];
+      String id = s.id.toString();
+      settingsAddrs[id] = s.attributes[util.syncgameSettingsAttr];
+      gameAddrs[id] = s.addresses[0];
 
-      String gameSettingsJSON = s.attrs[util.syncgameGameStartDataAttr];
-      updateGamesCallback(gameAddrs[s.instanceId], gameSettingsJSON);
+      String gameSettingsJSON = s.attributes[util.syncgameGameStartDataAttr];
+      updateGamesCallback(gameAddrs[id], gameSettingsJSON);
 
-      _cc.joinSyncgroup(settingsAddrs[s.instanceId]);
+      _cc.joinSyncgroup(settingsAddrs[id]);
     } else {
       // An unexpected service was found. Who is advertising it?
       // https://github.com/vanadium/issues/issues/846
@@ -353,16 +356,17 @@
     }
   }
 
-  void lost(String instanceId) {
-    util.log("SettingsScanHandler Lost ${instanceId}");
+  void lost(List<int> idList) {
+    util.log("SettingsScanHandler Lost $idList");
 
     // TODO(alexfandrianto): Leave the syncgroup?
     // Looks like leave isn't actually implemented, so we can't do this.
-    String addr = gameAddrs[instanceId];
+    String id = idList.toString();
+    String addr = gameAddrs[id];
     if (addr != null) {
       updateGamesCallback(addr, null);
     }
-    settingsAddrs.remove(instanceId);
-    gameAddrs.remove(instanceId);
+    settingsAddrs.remove(id);
+    gameAddrs.remove(id);
   }
 }
diff --git a/lib/src/syncbase/util.dart b/lib/src/syncbase/util.dart
index 2715e30..e2ec4cc 100644
--- a/lib/src/syncbase/util.dart
+++ b/lib/src/syncbase/util.dart
@@ -11,7 +11,7 @@
 const String tableNameSettings = 'table_settings';
 
 String makeSgPrefix(String mounttable, String deviceID) {
-  return "${mounttable}/croupier-${deviceID}/%%sync";
+  return "$mounttable/croupier-$deviceID/%%sync";
 }
 
 const String sgSuffix = 'discovery';
@@ -22,8 +22,8 @@
 const String settingsPersonalKey = "personal";
 const String settingsWatchSyncPrefix = "users";
 
-typedef void keyValueCallback(String key, String value);
-typedef Future asyncKeyValueCallback(String key, String value, bool duringScan);
+typedef void KeyValueCallback(String key, String value);
+typedef Future AsyncKeyValueCallback(String key, String value, bool duringScan);
 
 const String openPermsJson =
     '{"Admin":{"In":["..."]},"Write":{"In":["..."]},"Read":{"In":["..."]},"Resolve":{"In":["..."]},"Debug":{"In":["..."]}}';
@@ -36,11 +36,11 @@
 
 // data should contain a JSON-encoded logic_game.GameStartData
 String syncgameSuffix(String data) {
-  return "${sgSuffixGame}-${data}";
+  return "$sgSuffixGame-$data";
 }
 
 String syncgamePrefix(int gameID) {
-  return "${gameID}";
+  return "$gameID";
 }
 
 const String syncgameSettingsAttr = "settings_sgname";
@@ -62,27 +62,27 @@
 }
 
 String gameOwnerKey(int gameID) {
-  return "${gameID}/owner";
+  return "$gameID/owner";
 }
 
 String gameTypeKey(int gameID) {
-  return "${gameID}/type";
+  return "$gameID/type";
 }
 
 String gameStatusKey(int gameID) {
-  return "${gameID}/status";
+  return "$gameID/status";
 }
 
 String gameSyncgroupKey(int gameID) {
-  return "${gameID}/game_sg";
+  return "$gameID/game_sg";
 }
 
 String playerSettingsKeyFromData(int gameID, int userID) {
-  return "${gameID}/players/${userID}/settings_sg";
+  return "$gameID/players/$userID/settings_sg";
 }
 
 String playerNumberKeyFromData(int gameID, int userID) {
-  return "${gameID}/players/${userID}/player_number";
+  return "$gameID/players/$userID/player_number";
 }
 
 bool isSettingsKey(String key) {
@@ -90,7 +90,7 @@
 }
 
 String settingsDataKeyFromUserID(int userID) {
-  return "${settingsWatchSyncPrefix}/${userID}/settings";
+  return "$settingsWatchSyncPrefix/$userID/settings";
 }
 
 String userIDFromSettingsDataKey(String dataKey) {
diff --git a/lib/styles/common.dart b/lib/styles/common.dart
index f3ba93e..e664511 100644
--- a/lib/styles/common.dart
+++ b/lib/styles/common.dart
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+import 'dart:ui' show Color;
 import 'package:flutter/material.dart';
 
 class Text {
@@ -28,10 +29,10 @@
 }
 
 class Spacing {
-  static final EdgeDims smallPaddingSide =
-      new EdgeDims.symmetric(horizontal: 5.0);
-  static final EdgeDims smallPadding = new EdgeDims.all(5.0);
-  static final EdgeDims normalPadding = new EdgeDims.all(10.0);
+  static final EdgeInsets smallPaddingSide =
+      new EdgeInsets.symmetric(horizontal: 5.0);
+  static final EdgeInsets smallPadding = new EdgeInsets.all(5.0);
+  static final EdgeInsets normalPadding = new EdgeInsets.all(10.0);
 }
 
 class Box {
@@ -42,7 +43,7 @@
   static final BoxDecoration background =
       new BoxDecoration(backgroundColor: theme.primaryColor);
   static final BoxDecoration brightBackground =
-      new BoxDecoration(backgroundColor: theme.primarySwatch[100]);
+      new BoxDecoration(backgroundColor: Colors.blueGrey[100]);
   static final BoxDecoration errorBackground =
       new BoxDecoration(backgroundColor: errorColor);
   static final BoxDecoration border = new BoxDecoration(
diff --git a/manifest.yaml b/manifest.yaml
index 5918e6b..ea18dcf 100644
--- a/manifest.yaml
+++ b/manifest.yaml
@@ -1,26 +1,5 @@
 name: croupier
-material-design-icons:
-  - name: action/build
-  - name: action/help
-  - name: action/settings
-  - name: av/play_arrow
-  - name: action/swap_vert
-  - name: hardware/tablet
-  - name: image/filter_none
-  - name: image/filter_1
-  - name: image/filter_2
-  - name: image/filter_3
-  - name: image/filter_4
-  - name: image/filter_5
-  - name: image/filter_6
-  - name: image/filter_7
-  - name: image/filter_8
-  - name: image/filter_9
-  - name: image/filter_9_plus
-  - name: navigation/arrow_back
-  - name: navigation/arrow_forward
-  - name: navigation/menu
-  - name: social/person_outline
+uses-material-design: true
 assets:
   - images/default/classic/down/c10.png
   - images/default/classic/down/c1.png
diff --git a/pubspec.lock b/pubspec.lock
index 0a088b1..7745ff3 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -2,35 +2,57 @@
 # See http://pub.dartlang.org/doc/glossary.html#lockfile
 packages:
   analyzer:
-    description: analyzer
+    description:
+      name: analyzer
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "0.27.2"
   archive:
-    description: archive
+    description:
+      name: archive
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "1.0.20"
   args:
-    description: args
+    description:
+      name: args
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.13.3+3"
+    version: "0.13.4"
   asn1lib:
-    description: asn1lib
+    description:
+      name: asn1lib
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "0.4.1"
   async:
-    description: async
+    description:
+      name: async
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.8.0"
+    version: "1.9.0"
   barback:
-    description: barback
+    description:
+      name: barback
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "0.15.2+7"
   bignum:
-    description: bignum
+    description:
+      name: bignum
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.0.7"
+    version: "0.1.0"
+  boolean_selector:
+    description:
+      name: boolean_selector
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.1"
   box2d:
-    description: box2d
+    description:
+      name: box2d
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "0.2.0"
   cassowary:
@@ -40,41 +62,77 @@
     source: path
     version: "0.1.7"
   charcode:
-    description: charcode
+    description:
+      name: charcode
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "1.1.0"
-  cipher:
-    description: cipher
+  cli_util:
+    description:
+      name: cli_util
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.7.1"
+    version: "0.0.1+2"
+  code_transformers:
+    description:
+      name: code_transformers
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.4.2+1"
   collection:
-    description: collection
+    description:
+      name: collection
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.4.0"
+    version: "1.5.1"
   contrast:
-    description: contrast
+    description:
+      name: contrast
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "0.1.1"
   convert:
-    description: convert
+    description:
+      name: convert
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "1.0.1"
   crypto:
-    description: crypto
+    description:
+      name: crypto
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.9.1"
+    version: "0.9.2"
   csslib:
-    description: csslib
+    description:
+      name: csslib
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "0.12.2"
+  dart_style:
+    description:
+      name: dart_style
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.2.4"
   den_api:
-    description: den_api
+    description:
+      name: den_api
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.1.0"
+  file:
+    description:
+      name: file
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "0.1.0"
   fixnum:
-    description: fixnum
+    description:
+      name: fixnum
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.9.1+2"
+    version: "0.10.2"
   flutter:
     description:
       path: "../../../flutter/packages/flutter"
@@ -100,71 +158,93 @@
     source: path
     version: "0.0.10"
   github:
-    description: github
+    description:
+      name: github
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "2.3.1+1"
   glob:
-    description: glob
+    description:
+      name: glob
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.1.0"
+    version: "1.1.2"
   html:
-    description: html
+    description:
+      name: html
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "0.12.2+1"
   http:
-    description: http
+    description:
+      name: http
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "0.11.3+3"
   http_multi_server:
-    description: http_multi_server
+    description:
+      name: http_multi_server
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.3.2"
+    version: "2.0.1"
   http_parser:
-    description: http_parser
+    description:
+      name: http_parser
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.1.0"
+    version: "2.2.1"
   intl:
-    description: intl
+    description:
+      name: intl
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "0.12.7"
+  json_schema:
+    description:
+      name: json_schema
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.3"
   logging:
-    description: logging
+    description:
+      name: logging
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "0.11.2"
   matcher:
-    description: matcher
+    description:
+      name: matcher
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.12.0+1"
-  material_design_icons:
-    description: material_design_icons
-    source: hosted
-    version: "0.0.3"
+    version: "0.12.0+2"
   mime:
-    description: mime
+    description:
+      name: mime
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "0.9.3"
   mojo:
-    description: mojo
+    description:
+      name: mojo
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.4.12"
-  mojo_apptest:
-    description: mojo_apptest
-    source: hosted
-    version: "0.2.16"
+    version: "0.4.18"
   mojo_sdk:
-    description: mojo_sdk
+    description:
+      name: mojo_sdk
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.2.11"
+    version: "0.2.22"
   mojo_services:
-    description: mojo_services
+    description:
+      name: mojo_services
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.4.14"
-  mojom:
-    description: mojom
-    source: hosted
-    version: "0.2.16"
+    version: "0.4.25"
   mustache4dart:
-    description: mustache4dart
+    description:
+      name: mustache4dart
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "1.0.10"
   newton:
@@ -174,122 +254,219 @@
     source: path
     version: "0.1.5"
   package_config:
-    description: package_config
+    description:
+      name: package_config
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "0.1.3"
   path:
-    description: path
+    description:
+      name: path
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "1.3.9"
   petitparser:
-    description: petitparser
+    description:
+      name: petitparser
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.5.1"
+    version: "1.5.3"
   plugin:
-    description: plugin
+    description:
+      name: plugin
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "0.1.0"
-  pool:
-    description: pool
+  pointycastle:
+    description:
+      name: pointycastle
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.2.1"
+    version: "0.10.0"
+  pool:
+    description:
+      name: pool
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.2.3"
   pub_package_data:
-    description: pub_package_data
+    description:
+      name: pub_package_data
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "0.0.1"
   pub_semver:
-    description: pub_semver
+    description:
+      name: pub_semver
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.2.3"
+    version: "1.2.4"
   quiver:
-    description: quiver
+    description:
+      name: quiver
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "0.21.4"
+  quiver_collection:
+    description:
+      name: quiver_collection
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.0"
+  quiver_iterables:
+    description:
+      name: quiver_iterables
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.0"
+  quiver_pattern:
+    description:
+      name: quiver_pattern
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.0"
+  reflectable:
+    description:
+      name: reflectable
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.5.4"
   shelf:
-    description: shelf
+    description:
+      name: shelf
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "0.6.5"
   shelf_static:
-    description: shelf_static
+    description:
+      name: shelf_static
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "0.2.3+3"
   shelf_web_socket:
-    description: shelf_web_socket
+    description:
+      name: shelf_web_socket
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.0.1+5"
+    version: "0.2.0"
   sky_engine:
-    description: sky_engine
-    source: hosted
-    version: "0.0.89"
+    description:
+      path: "../../../flutter/bin/cache/pkg/sky_engine"
+      relative: true
+    source: path
+    version: "0.0.99"
   sky_services:
-    description: sky_services
-    source: hosted
-    version: "0.0.89"
+    description:
+      path: "../../../flutter/bin/cache/pkg/sky_services"
+      relative: true
+    source: path
+    version: "0.0.99"
   source_map_stack_trace:
-    description: source_map_stack_trace
+    description:
+      name: source_map_stack_trace
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "1.0.4"
   source_maps:
-    description: source_maps
+    description:
+      name: source_maps
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.10.1"
+    version: "0.10.1+1"
   source_span:
-    description: source_span
+    description:
+      name: source_span
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.2.1"
+    version: "1.2.2"
   stack_trace:
-    description: stack_trace
+    description:
+      name: stack_trace
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.6.0"
+    version: "1.6.4"
   stream_channel:
-    description: stream_channel
+    description:
+      name: stream_channel
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "1.3.1"
   string_scanner:
-    description: string_scanner
+    description:
+      name: string_scanner
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "0.1.4+1"
   syncbase:
-    description: syncbase
+    description:
+      name: syncbase
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.0.29"
+    version: "0.0.36"
   test:
-    description: test
+    description:
+      name: test
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.12.6+1"
+    version: "0.12.13"
   typed_data:
-    description: typed_data
+    description:
+      name: typed_data
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.1.1"
+    version: "1.1.2"
   utf:
-    description: utf
+    description:
+      name: utf
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.9.0+2"
+    version: "0.9.0+3"
   v23discovery:
-    description: v23discovery
+    description:
+      name: v23discovery
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.0.13"
+    version: "0.0.17"
   vector_math:
-    description: vector_math
+    description:
+      name: vector_math
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "1.4.7"
   watcher:
-    description: watcher
+    description:
+      name: watcher
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "0.9.7"
+  web_socket_channel:
+    description:
+      name: web_socket_channel
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.2"
   when:
-    description: when
+    description:
+      name: when
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "0.2.0"
   which:
-    description: which
+    description:
+      name: which
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "0.1.3"
   xml:
-    description: xml
+    description:
+      name: xml
+      url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.4.1"
+    version: "2.4.3"
   yaml:
-    description: yaml
+    description:
+      name: yaml
+      url: "https://pub.dartlang.org"
     source: hosted
     version: "2.1.8"
+sdk: ">=1.14.0 <1.17.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index 4c1ab74..582ef88 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,7 +1,7 @@
 name: croupier
 dependencies:
-  v23discovery: ">=0.0.11 <0.1.0"
-  syncbase: ">=0.0.0 <0.1.0"
+  v23discovery: ">=0.0.17 <0.1.0"
+  syncbase: ">=0.0.34 <0.1.0"
   flutter:
     path: ../../../flutter/packages/flutter
   flutter_sprites:
diff --git a/shortcut_template b/shortcut_template
index 17ab9a8..febd364 100644
--- a/shortcut_template
+++ b/shortcut_template
@@ -5,7 +5,7 @@
 --verbose
 --args-for=mojo:flutter --enable-checked-mode
 --enable-multiprocess
---map-origin=https://mojo2.v.io=https://storage.googleapis.com/mojo_services/v23discovery/mojo_services/android/
+--map-origin=https://mojo2.v.io=https://storage.googleapis.com/mojo_services/v23discovery/mojo_services/arm_android/
 --args-for=https://mojo2.v.io/discovery.mojo host%DEVICE_ID%
 --map-origin=https://mojo.v.io=https://storage.googleapis.com/mojo_services/syncbase/mojo_services/android/
 --args-for=https://mojo.v.io/syncbase_server.mojo --v=0 --logtostderr=true --root-dir=/data/data/org.chromium.mojo.shell/app_home/syncbase_data --v23.credentials=/sdcard/v23creds %SYNCBASE_NAME_FLAG% %PROXY_FLAG%
diff --git a/test/hearts_test.dart b/test/hearts_test.dart
index 1fc19aa..e902e09 100644
--- a/test/hearts_test.dart
+++ b/test/hearts_test.dart
@@ -155,7 +155,7 @@
       game.cardCollections[HeartsGame.playerTrickA] = <Card>[];
       game.cardCollections[HeartsGame.playerTrickB] = <Card>[];
       game.cardCollections[HeartsGame.playerTrickC] = <Card>[];
-      game.cardCollections[HeartsGame.playerTrickD] = Card.All;
+      game.cardCollections[HeartsGame.playerTrickD] = Card.all;
       game.updateScore();
       expect(game.scores, equals([34, 42, 54, 0]));
       expect(game.deltaScores, equals([26, 26, 26, 0]));
@@ -210,17 +210,17 @@
 
       // Confirm cards in hands.
       List<Card> expectedAHand =
-          new List<Card>.from(Card.All.getRange(26, 26 + 5))
-            ..addAll(Card.All.getRange(13 + 5, 26));
+          new List<Card>.from(Card.all.getRange(26, 26 + 5))
+            ..addAll(Card.all.getRange(13 + 5, 26));
       List<Card> expectedBHand =
-          new List<Card>.from(Card.All.getRange(13, 13 + 5))
-            ..addAll(Card.All.getRange(39 + 5, 52));
+          new List<Card>.from(Card.all.getRange(13, 13 + 5))
+            ..addAll(Card.all.getRange(39 + 5, 52));
       List<Card> expectedCHand =
-          new List<Card>.from(Card.All.getRange(39, 39 + 5))
-            ..addAll(Card.All.getRange(0 + 5, 13));
+          new List<Card>.from(Card.all.getRange(39, 39 + 5))
+            ..addAll(Card.all.getRange(0 + 5, 13));
       List<Card> expectedDHand =
-          new List<Card>.from(Card.All.getRange(0, 0 + 5))
-            ..addAll(Card.All.getRange(26 + 5, 39));
+          new List<Card>.from(Card.all.getRange(0, 0 + 5))
+            ..addAll(Card.all.getRange(26 + 5, 39));
       expect(game.cardCollections[HeartsGame.playerA], equals(expectedAHand));
       expect(game.cardCollections[HeartsGame.playerB], equals(expectedBHand));
       expect(game.cardCollections[HeartsGame.playerC], equals(expectedCHand));
@@ -237,25 +237,25 @@
 
       // Confirm cards in hands and passes.
       List<Card> expectedAHand =
-          new List<Card>.from(Card.All.getRange(26 + 3, 26 + 5))
-            ..addAll(Card.All.getRange(13 + 5, 26));
+          new List<Card>.from(Card.all.getRange(26 + 3, 26 + 5))
+            ..addAll(Card.all.getRange(13 + 5, 26));
       List<Card> expectedBHand =
-          new List<Card>.from(Card.All.getRange(13 + 3, 13 + 5))
-            ..addAll(Card.All.getRange(39 + 5, 52));
+          new List<Card>.from(Card.all.getRange(13 + 3, 13 + 5))
+            ..addAll(Card.all.getRange(39 + 5, 52));
       List<Card> expectedCHand =
-          new List<Card>.from(Card.All.getRange(39 + 3, 39 + 5))
-            ..addAll(Card.All.getRange(0 + 5, 13));
+          new List<Card>.from(Card.all.getRange(39 + 3, 39 + 5))
+            ..addAll(Card.all.getRange(0 + 5, 13));
       List<Card> expectedDHand =
-          new List<Card>.from(Card.All.getRange(0 + 3, 0 + 5))
-            ..addAll(Card.All.getRange(26 + 5, 39));
+          new List<Card>.from(Card.all.getRange(0 + 3, 0 + 5))
+            ..addAll(Card.all.getRange(26 + 5, 39));
       List<Card> expectedAPass =
-          new List<Card>.from(Card.All.getRange(26, 26 + 3));
+          new List<Card>.from(Card.all.getRange(26, 26 + 3));
       List<Card> expectedBPass =
-          new List<Card>.from(Card.All.getRange(13, 13 + 3));
+          new List<Card>.from(Card.all.getRange(13, 13 + 3));
       List<Card> expectedCPass =
-          new List<Card>.from(Card.All.getRange(39, 39 + 3));
+          new List<Card>.from(Card.all.getRange(39, 39 + 3));
       List<Card> expectedDPass =
-          new List<Card>.from(Card.All.getRange(0, 0 + 3));
+          new List<Card>.from(Card.all.getRange(0, 0 + 3));
       expect(game.cardCollections[HeartsGame.playerA], equals(expectedAHand));
       expect(game.cardCollections[HeartsGame.playerB], equals(expectedBHand));
       expect(game.cardCollections[HeartsGame.playerC], equals(expectedCHand));
@@ -281,21 +281,21 @@
       // Confirm cards in hands again.
       // Note: I will eventually want to do a sorted comparison or set comparison instead.
       List<Card> expectedAHand =
-          new List<Card>.from(Card.All.getRange(26 + 3, 26 + 5))
-            ..addAll(Card.All.getRange(13 + 5, 26))
-            ..addAll(Card.All.getRange(13, 13 + 3));
+          new List<Card>.from(Card.all.getRange(26 + 3, 26 + 5))
+            ..addAll(Card.all.getRange(13 + 5, 26))
+            ..addAll(Card.all.getRange(13, 13 + 3));
       List<Card> expectedBHand =
-          new List<Card>.from(Card.All.getRange(13 + 3, 13 + 5))
-            ..addAll(Card.All.getRange(39 + 5, 52))
-            ..addAll(Card.All.getRange(39, 39 + 3));
+          new List<Card>.from(Card.all.getRange(13 + 3, 13 + 5))
+            ..addAll(Card.all.getRange(39 + 5, 52))
+            ..addAll(Card.all.getRange(39, 39 + 3));
       List<Card> expectedCHand =
-          new List<Card>.from(Card.All.getRange(39 + 3, 39 + 5))
-            ..addAll(Card.All.getRange(0 + 5, 13))
-            ..addAll(Card.All.getRange(0, 0 + 3));
+          new List<Card>.from(Card.all.getRange(39 + 3, 39 + 5))
+            ..addAll(Card.all.getRange(0 + 5, 13))
+            ..addAll(Card.all.getRange(0, 0 + 3));
       List<Card> expectedDHand =
-          new List<Card>.from(Card.All.getRange(0 + 3, 0 + 5))
-            ..addAll(Card.All.getRange(26 + 5, 39))
-            ..addAll(Card.All.getRange(26, 26 + 3));
+          new List<Card>.from(Card.all.getRange(0 + 3, 0 + 5))
+            ..addAll(Card.all.getRange(26 + 5, 39))
+            ..addAll(Card.all.getRange(26, 26 + 3));
       expect(game.cardCollections[HeartsGame.playerA], equals(expectedAHand));
       expect(game.cardCollections[HeartsGame.playerB], equals(expectedBHand));
       expect(game.cardCollections[HeartsGame.playerC], equals(expectedCHand));
@@ -463,7 +463,7 @@
       game.phase = HeartsPhase.score;
       expect(() {
         game.gamelog.add(new HeartsCommand.deal(
-            0, new List<Card>.from(Card.All.getRange(0, 13))));
+            0, new List<Card>.from(Card.all.getRange(0, 13))));
       }, throwsA(new isInstanceOf<StateError>()));
     });
     test("Dealing - missing card", () {
@@ -479,58 +479,58 @@
       game.phase = HeartsPhase.deal;
       expect(() {
         game.gamelog.add(new HeartsCommand.deal(
-            0, new List<Card>.from(Card.All.getRange(0, 15))));
+            0, new List<Card>.from(Card.all.getRange(0, 15))));
       }, throwsA(new isInstanceOf<StateError>()));
 
       game = new HeartsGame()..playerNumber = 0;
       game.phase = HeartsPhase.deal;
       expect(() {
         game.gamelog.add(new HeartsCommand.deal(
-            0, new List<Card>.from(Card.All.getRange(0, 5))));
+            0, new List<Card>.from(Card.all.getRange(0, 5))));
         game.gamelog.add(new HeartsCommand.deal(
-            0, new List<Card>.from(Card.All.getRange(5, 15))));
+            0, new List<Card>.from(Card.all.getRange(5, 15))));
       }, throwsA(new isInstanceOf<StateError>()));
     });
     test("Passing - wrong phase", () {
       HeartsGame game = new HeartsGame()..playerNumber = 0;
       game.phase = HeartsPhase.deal;
       game.gamelog.add(new HeartsCommand.deal(
-          0, new List<Card>.from(Card.All.getRange(0, 13))));
+          0, new List<Card>.from(Card.all.getRange(0, 13))));
       expect(() {
         game.gamelog.add(new HeartsCommand.pass(
-            0, new List<Card>.from(Card.All.getRange(0, 4))));
+            0, new List<Card>.from(Card.all.getRange(0, 4))));
       }, throwsA(new isInstanceOf<StateError>()));
     });
     test("Passing - missing card", () {
       HeartsGame game = new HeartsGame()..playerNumber = 0;
       game.phase = HeartsPhase.deal;
       game.gamelog.add(new HeartsCommand.deal(
-          0, new List<Card>.from(Card.All.getRange(0, 13))));
+          0, new List<Card>.from(Card.all.getRange(0, 13))));
       game.phase = HeartsPhase.pass;
       expect(() {
         game.gamelog.add(new HeartsCommand.pass(
-            0, new List<Card>.from(Card.All.getRange(13, 16))));
+            0, new List<Card>.from(Card.all.getRange(13, 16))));
       }, throwsA(new isInstanceOf<StateError>()));
     });
     test("Passing - wrong number of cards", () {
       HeartsGame game = new HeartsGame()..playerNumber = 0;
       game.phase = HeartsPhase.deal;
       game.gamelog.add(new HeartsCommand.deal(
-          0, new List<Card>.from(Card.All.getRange(0, 13))));
+          0, new List<Card>.from(Card.all.getRange(0, 13))));
       game.phase = HeartsPhase.pass;
       expect(() {
         game.gamelog.add(new HeartsCommand.pass(
-            0, new List<Card>.from(Card.All.getRange(0, 2))));
+            0, new List<Card>.from(Card.all.getRange(0, 2))));
       }, throwsA(new isInstanceOf<StateError>()));
 
       game = new HeartsGame()..playerNumber = 0;
       game.phase = HeartsPhase.deal;
       game.gamelog.add(new HeartsCommand.deal(
-          0, new List<Card>.from(Card.All.getRange(0, 13))));
+          0, new List<Card>.from(Card.all.getRange(0, 13))));
       game.phase = HeartsPhase.pass;
       expect(() {
         game.gamelog.add(new HeartsCommand.pass(
-            0, new List<Card>.from(Card.All.getRange(0, 4))));
+            0, new List<Card>.from(Card.all.getRange(0, 4))));
       }, throwsA(new isInstanceOf<StateError>()));
     });
     test("Taking - wrong phase", () {
@@ -544,7 +544,7 @@
       HeartsGame game = new HeartsGame()..playerNumber = 0;
       game.phase = HeartsPhase.deal;
       game.gamelog.add(new HeartsCommand.deal(
-          0, new List<Card>.from(Card.All.getRange(0, 13))));
+          0, new List<Card>.from(Card.all.getRange(0, 13))));
       expect(() {
         game.gamelog.add(new HeartsCommand.ask());
       }, throwsA(new isInstanceOf<StateError>()));
@@ -553,7 +553,7 @@
       HeartsGame game = new HeartsGame()..playerNumber = 0;
       game.phase = HeartsPhase.deal;
       game.gamelog.add(new HeartsCommand.deal(
-          0, new List<Card>.from(Card.All.getRange(0, 13))));
+          0, new List<Card>.from(Card.all.getRange(0, 13))));
       game.phase = HeartsPhase.play;
       game.gamelog.add(new HeartsCommand.ask());
       expect(() {
@@ -564,42 +564,42 @@
       HeartsGame game = new HeartsGame()..playerNumber = 0;
       game.phase = HeartsPhase.deal;
       game.gamelog.add(new HeartsCommand.deal(
-          0, new List<Card>.from(Card.All.getRange(0, 13))));
+          0, new List<Card>.from(Card.all.getRange(0, 13))));
       expect(() {
-        game.gamelog.add(new HeartsCommand.play(0, Card.All[0]));
+        game.gamelog.add(new HeartsCommand.play(0, Card.all[0]));
       }, throwsA(new isInstanceOf<StateError>()));
     });
     test("Playing - not asking", () {
       HeartsGame game = new HeartsGame()..playerNumber = 0;
       game.phase = HeartsPhase.deal;
       game.gamelog.add(new HeartsCommand.deal(
-          0, new List<Card>.from(Card.All.getRange(0, 13))));
+          0, new List<Card>.from(Card.all.getRange(0, 13))));
       game.phase = HeartsPhase.play;
       expect(() {
-        game.gamelog.add(new HeartsCommand.play(0, Card.All[0]));
+        game.gamelog.add(new HeartsCommand.play(0, Card.all[0]));
       }, throwsA(new isInstanceOf<StateError>()));
     });
     test("Playing - missing card", () {
       HeartsGame game = new HeartsGame()..playerNumber = 0;
       game.phase = HeartsPhase.deal;
       game.gamelog.add(new HeartsCommand.deal(
-          0, new List<Card>.from(Card.All.getRange(0, 13))));
+          0, new List<Card>.from(Card.all.getRange(0, 13))));
       game.phase = HeartsPhase.play;
       game.gamelog.add(new HeartsCommand.ask());
       expect(() {
-        game.gamelog.add(new HeartsCommand.play(0, Card.All[13]));
+        game.gamelog.add(new HeartsCommand.play(0, Card.all[13]));
       }, throwsA(new isInstanceOf<StateError>()));
     });
     test("Playing - invalid card (not 2 of clubs as first card)", () {
       HeartsGame game = new HeartsGame()..playerNumber = 0;
       game.phase = HeartsPhase.deal;
       game.gamelog.add(new HeartsCommand.deal(
-          0, new List<Card>.from(Card.All.getRange(0, 13))));
+          0, new List<Card>.from(Card.all.getRange(0, 13))));
       game.phase = HeartsPhase.play;
       game.lastTrickTaker = 0;
       game.gamelog.add(new HeartsCommand.ask());
       expect(() {
-        game.gamelog.add(new HeartsCommand.play(0, Card.All[0]));
+        game.gamelog.add(new HeartsCommand.play(0, Card.all[0]));
       }, throwsA(new isInstanceOf<StateError>()));
     });
     test("Playing - invalid card (no penalty on first round)", () {
@@ -608,90 +608,90 @@
       HeartsGame game = new HeartsGame()..playerNumber = 0;
       game.phase = HeartsPhase.deal;
       game.gamelog.add(new HeartsCommand.deal(
-          0, new List<Card>.from(Card.All.getRange(0, 13))));
+          0, new List<Card>.from(Card.all.getRange(0, 13))));
       game.gamelog.add(new HeartsCommand.deal(
-          1, new List<Card>.from(Card.All.getRange(13, 26))));
+          1, new List<Card>.from(Card.all.getRange(13, 26))));
       game.gamelog.add(new HeartsCommand.deal(
-          2, new List<Card>.from(Card.All.getRange(26, 39))));
+          2, new List<Card>.from(Card.all.getRange(26, 39))));
       game.gamelog.add(new HeartsCommand.deal(
-          3, new List<Card>.from(Card.All.getRange(39, 52))));
+          3, new List<Card>.from(Card.all.getRange(39, 52))));
       game.phase = HeartsPhase.play;
       game.lastTrickTaker = 0;
       game.gamelog.add(new HeartsCommand.ask());
-      game.gamelog.add(new HeartsCommand.play(0, Card.All[1]));
+      game.gamelog.add(new HeartsCommand.play(0, Card.all[1]));
       game.gamelog.add(new HeartsCommand.ask());
-      game.gamelog.add(new HeartsCommand.play(1, Card.All[13]));
+      game.gamelog.add(new HeartsCommand.play(1, Card.all[13]));
       game.gamelog.add(new HeartsCommand.ask());
       expect(() {
-        game.gamelog.add(new HeartsCommand.play(2, Card.All[26]));
+        game.gamelog.add(new HeartsCommand.play(2, Card.all[26]));
       }, throwsA(new isInstanceOf<StateError>()));
     });
     test("Playing - wrong turn", () {
       HeartsGame game = new HeartsGame()..playerNumber = 0;
       game.phase = HeartsPhase.deal;
       game.gamelog.add(new HeartsCommand.deal(
-          0, new List<Card>.from(Card.All.getRange(0, 13))));
+          0, new List<Card>.from(Card.all.getRange(0, 13))));
       game.gamelog.add(new HeartsCommand.deal(
-          1, new List<Card>.from(Card.All.getRange(13, 26))));
+          1, new List<Card>.from(Card.all.getRange(13, 26))));
       game.gamelog.add(new HeartsCommand.deal(
-          2, new List<Card>.from(Card.All.getRange(26, 39))));
+          2, new List<Card>.from(Card.all.getRange(26, 39))));
       game.gamelog.add(new HeartsCommand.deal(
-          3, new List<Card>.from(Card.All.getRange(39, 52))));
+          3, new List<Card>.from(Card.all.getRange(39, 52))));
       game.phase = HeartsPhase.play;
       game.lastTrickTaker = 0;
       game.gamelog.add(new HeartsCommand.ask());
       expect(() {
         game.gamelog.add(new HeartsCommand.play(
-            1, Card.All[13])); // player 0's turn, not player 1's.
+            1, Card.all[13])); // player 0's turn, not player 1's.
       }, throwsA(new isInstanceOf<StateError>()));
     });
     test("Playing - invalid card (suit mismatch)", () {
       HeartsGame game = new HeartsGame()..playerNumber = 0;
       game.phase = HeartsPhase.deal;
       game.gamelog.add(new HeartsCommand.deal(
-          0, new List<Card>.from(Card.All.getRange(0, 12))..add(Card.All[25])));
+          0, new List<Card>.from(Card.all.getRange(0, 12))..add(Card.all[25])));
       game.gamelog.add(new HeartsCommand.deal(
-          1, new List<Card>.from(Card.All.getRange(12, 25))));
+          1, new List<Card>.from(Card.all.getRange(12, 25))));
       game.gamelog.add(new HeartsCommand.deal(
-          2, new List<Card>.from(Card.All.getRange(26, 39))));
+          2, new List<Card>.from(Card.all.getRange(26, 39))));
       game.gamelog.add(new HeartsCommand.deal(
-          3, new List<Card>.from(Card.All.getRange(39, 52))));
+          3, new List<Card>.from(Card.all.getRange(39, 52))));
       game.phase = HeartsPhase.play;
       game.lastTrickTaker = 0;
       game.gamelog.add(new HeartsCommand.ask());
-      game.gamelog.add(new HeartsCommand.play(0, Card.All[1]));
+      game.gamelog.add(new HeartsCommand.play(0, Card.all[1]));
       game.gamelog.add(new HeartsCommand.ask());
       expect(() {
         game.gamelog
-            .add(new HeartsCommand.play(0, Card.All[13])); // should play 12
+            .add(new HeartsCommand.play(0, Card.all[13])); // should play 12
       }, throwsA(new isInstanceOf<StateError>()));
     });
     test("Playing - invalid card (hearts not broken yet)", () {
       HeartsGame game = new HeartsGame()..playerNumber = 0;
       game.phase = HeartsPhase.deal;
       game.gamelog.add(new HeartsCommand.deal(
-          0, new List<Card>.from(Card.All.getRange(0, 12))..add(Card.All[38])));
+          0, new List<Card>.from(Card.all.getRange(0, 12))..add(Card.all[38])));
       game.gamelog.add(new HeartsCommand.deal(
-          1, new List<Card>.from(Card.All.getRange(13, 26))));
+          1, new List<Card>.from(Card.all.getRange(13, 26))));
       game.gamelog.add(new HeartsCommand.deal(2,
-          new List<Card>.from(Card.All.getRange(26, 38))..add(Card.All[12])));
+          new List<Card>.from(Card.all.getRange(26, 38))..add(Card.all[12])));
       game.gamelog.add(new HeartsCommand.deal(
-          3, new List<Card>.from(Card.All.getRange(39, 52))));
+          3, new List<Card>.from(Card.all.getRange(39, 52))));
       game.phase = HeartsPhase.play;
       game.lastTrickTaker = 0;
       game.gamelog.add(new HeartsCommand.ask());
-      game.gamelog.add(new HeartsCommand.play(0, Card.All[1]));
+      game.gamelog.add(new HeartsCommand.play(0, Card.all[1]));
       game.gamelog.add(new HeartsCommand.ask());
-      game.gamelog.add(new HeartsCommand.play(1, Card.All[13]));
+      game.gamelog.add(new HeartsCommand.play(1, Card.all[13]));
       game.gamelog.add(new HeartsCommand.ask());
-      game.gamelog.add(new HeartsCommand.play(2, Card.All[12])); // 2 won!
+      game.gamelog.add(new HeartsCommand.play(2, Card.all[12])); // 2 won!
       game.gamelog.add(new HeartsCommand.ask());
-      game.gamelog.add(new HeartsCommand.play(3, Card.All[39]));
+      game.gamelog.add(new HeartsCommand.play(3, Card.all[39]));
       game.gamelog.add(new HeartsCommand.takeTrick());
       game.gamelog.add(new HeartsCommand.ask());
       expect(() {
         game.gamelog.add(new HeartsCommand.play(
-            2, Card.All[26])); // But 2 can't lead with a hearts.
+            2, Card.all[26])); // But 2 can't lead with a hearts.
       }, throwsA(new isInstanceOf<StateError>()));
     });
     test("Asking - trick not taken yet", () {
@@ -699,28 +699,28 @@
       game.phase = HeartsPhase.deal;
       game.gamelog.add(new HeartsCommand.deal(
           0,
-          new List<Card>.from(Card.All.getRange(0, 11))
-            ..add(Card.All[38])
-            ..add(Card.All[37])));
+          new List<Card>.from(Card.all.getRange(0, 11))
+            ..add(Card.all[38])
+            ..add(Card.all[37])));
       game.gamelog.add(new HeartsCommand.deal(
-          1, new List<Card>.from(Card.All.getRange(13, 26))));
+          1, new List<Card>.from(Card.all.getRange(13, 26))));
       game.gamelog.add(new HeartsCommand.deal(
           2,
-          new List<Card>.from(Card.All.getRange(26, 37))
-            ..add(Card.All[12])
-            ..add(Card.All[11])));
+          new List<Card>.from(Card.all.getRange(26, 37))
+            ..add(Card.all[12])
+            ..add(Card.all[11])));
       game.gamelog.add(new HeartsCommand.deal(
-          3, new List<Card>.from(Card.All.getRange(39, 52))));
+          3, new List<Card>.from(Card.all.getRange(39, 52))));
       game.phase = HeartsPhase.play;
       game.lastTrickTaker = 0;
       game.gamelog.add(new HeartsCommand.ask());
-      game.gamelog.add(new HeartsCommand.play(0, Card.All[1]));
+      game.gamelog.add(new HeartsCommand.play(0, Card.all[1]));
       game.gamelog.add(new HeartsCommand.ask());
-      game.gamelog.add(new HeartsCommand.play(1, Card.All[13]));
+      game.gamelog.add(new HeartsCommand.play(1, Card.all[13]));
       game.gamelog.add(new HeartsCommand.ask());
-      game.gamelog.add(new HeartsCommand.play(2, Card.All[12])); // 2 won!
+      game.gamelog.add(new HeartsCommand.play(2, Card.all[12])); // 2 won!
       game.gamelog.add(new HeartsCommand.ask());
-      game.gamelog.add(new HeartsCommand.play(3, Card.All[39]));
+      game.gamelog.add(new HeartsCommand.play(3, Card.all[39]));
 
       // Do not take trick. Ask instead.
       expect(() {
@@ -738,21 +738,21 @@
       HeartsGame game = new HeartsGame()..playerNumber = 0;
       game.phase = HeartsPhase.deal;
       game.gamelog.add(new HeartsCommand.deal(
-          0, new List<Card>.from(Card.All.getRange(0, 12))..add(Card.All[38])));
+          0, new List<Card>.from(Card.all.getRange(0, 12))..add(Card.all[38])));
       game.gamelog.add(new HeartsCommand.deal(
-          1, new List<Card>.from(Card.All.getRange(13, 26))));
+          1, new List<Card>.from(Card.all.getRange(13, 26))));
       game.gamelog.add(new HeartsCommand.deal(2,
-          new List<Card>.from(Card.All.getRange(26, 38))..add(Card.All[12])));
+          new List<Card>.from(Card.all.getRange(26, 38))..add(Card.all[12])));
       game.gamelog.add(new HeartsCommand.deal(
-          3, new List<Card>.from(Card.All.getRange(39, 52))));
+          3, new List<Card>.from(Card.all.getRange(39, 52))));
       game.phase = HeartsPhase.play;
       game.lastTrickTaker = 0;
       game.gamelog.add(new HeartsCommand.ask());
-      game.gamelog.add(new HeartsCommand.play(0, Card.All[1]));
+      game.gamelog.add(new HeartsCommand.play(0, Card.all[1]));
       game.gamelog.add(new HeartsCommand.ask());
-      game.gamelog.add(new HeartsCommand.play(1, Card.All[13]));
+      game.gamelog.add(new HeartsCommand.play(1, Card.all[13]));
       game.gamelog.add(new HeartsCommand.ask());
-      game.gamelog.add(new HeartsCommand.play(2, Card.All[12]));
+      game.gamelog.add(new HeartsCommand.play(2, Card.all[12]));
       game.gamelog.add(new HeartsCommand.ask());
       expect(() {
         game.gamelog.add(new HeartsCommand.takeTrick()); // too soon
diff --git a/test/solitaire_test.dart b/test/solitaire_test.dart
index be18af3..3cae95f 100644
--- a/test/solitaire_test.dart
+++ b/test/solitaire_test.dart
@@ -60,7 +60,7 @@
       for (int i = 0; i < 7; i++) {
         expect(game.cardCollections[SolitaireGame.offsetDown + i].length,
             equals(i),
-            reason: "Down pile ${i} starts with ${i} cards");
+            reason: "Down pile $i starts with $i cards");
         expect(
             game.cardCollections[SolitaireGame.offsetUp + i].length, equals(1),
             reason: "Up piles start with 1 card");
@@ -201,7 +201,7 @@
         SolitaireGame game = new SolitaireGame();
         game.phase = SolitairePhase.score;
         game.gamelog
-            .add(new SolitaireCommand.deal(new List<Card>.from(Card.All)));
+            .add(new SolitaireCommand.deal(new List<Card>.from(Card.all)));
       }, throwsA(new isInstanceOf<StateError>()));
     });
     test("Dealing - fake cards", () {
@@ -216,13 +216,13 @@
       expect(() {
         SolitaireGame game = new SolitaireGame();
         game.gamelog.add(new SolitaireCommand.deal(
-            new List<Card>.from(Card.All)..addAll(Card.All)));
+            new List<Card>.from(Card.all)..addAll(Card.all)));
       }, throwsA(new isInstanceOf<StateError>()));
       // missing cards
       expect(() {
         SolitaireGame game = new SolitaireGame();
         game.gamelog.add(new SolitaireCommand.deal(
-            new List<Card>.from(Card.All.getRange(0, 40))));
+            new List<Card>.from(Card.all.getRange(0, 40))));
       }, throwsA(new isInstanceOf<StateError>()));
     });
 
@@ -233,16 +233,16 @@
       // s2 _ h2 _   d2 c11
       // _ c3 h3 d1 s13 d3 d12
 
-      Card c3 = Card.All[0 + 2];
-      Card c11 = Card.All[0 + 10];
-      Card d1 = Card.All[13 + 0];
-      Card d2 = Card.All[13 + 1];
-      Card d3 = Card.All[13 + 2];
-      Card h2 = Card.All[26 + 1];
-      Card h3 = Card.All[26 + 2];
-      Card d12 = Card.All[13 + 11];
-      Card s2 = Card.All[39 + 1];
-      Card s13 = Card.All[39 + 12];
+      Card c3 = Card.all[0 + 2];
+      Card c11 = Card.all[0 + 10];
+      Card d1 = Card.all[13 + 0];
+      Card d2 = Card.all[13 + 1];
+      Card d3 = Card.all[13 + 2];
+      Card h2 = Card.all[26 + 1];
+      Card h3 = Card.all[26 + 2];
+      Card d12 = Card.all[13 + 11];
+      Card s2 = Card.all[39 + 1];
+      Card s13 = Card.all[39 + 12];
 
       SolitaireGame _makeArbitrarySolitaireGame() {
         SolitaireGame g = new SolitaireGame();