croupier: Add Tap to Play (and Flex cleanup)

* Add Tap to Play
  Note: Due to a bug with Flutter, I had to modify the flutter package
  a little to verify that it was working.
* Fix Flex issue with Debug Buttons (that showed up on Score View)
* Convert Flex to Row and Column, if possible

Change-Id: Iada9461752516a8215f4137d92eaec7aee6424e1
diff --git a/lib/components/card.dart b/lib/components/card.dart
index 68f683a..628b40d 100644
--- a/lib/components/card.dart
+++ b/lib/components/card.dart
@@ -13,6 +13,8 @@
 
 enum CardUIType { CARD, ZCARD }
 
+typedef void TapCallback(logic_card.Card card);
+
 class GlobalCardKey extends widgets.GlobalKey {
   logic_card.Card card;
   CardUIType type;
@@ -66,6 +68,7 @@
   final double rotation;
   final bool useKey;
   final bool visible;
+  final TapCallback tapCallback;
   final CardAnimationType animationType;
   final double z;
 
@@ -76,6 +79,7 @@
       bool useKey: false,
       this.visible: true,
       CardAnimationType animationType,
+      this.tapCallback,
       this.z})
       : animationType = animationType ?? CardAnimationType.NONE,
         card = card,
@@ -123,14 +127,18 @@
   }
 
   widgets.Widget build(widgets.BuildContext context) {
-    widgets.Widget image = new widgets.Opacity(
-        opacity: config.visible ? 1.0 : 0.0,
-        child: new widgets.Transform(
-            child: _imageFromCard(
-                config.card, config.faceUp, config.width, config.height),
-            transform:
-                new vector_math.Matrix4.identity().rotateZ(config.rotation),
-            alignment: new FractionalOffset(0.5, 0.5)));
+    widgets.Widget image = new widgets.GestureDetector(
+        onTap: config.tapCallback != null
+            ? () => config.tapCallback(config.card)
+            : null,
+        child: new widgets.Opacity(
+            opacity: config.visible ? 1.0 : 0.0,
+            child: new widgets.Transform(
+                child: _imageFromCard(
+                    config.card, config.faceUp, config.width, config.height),
+                transform:
+                    new vector_math.Matrix4.identity().rotateZ(config.rotation),
+                alignment: new FractionalOffset(0.5, 0.5))));
 
     return image;
   }
@@ -165,7 +173,7 @@
     }
     _pointQueue.add(config.endingPosition);
     _performance = new ValuePerformance<Point>(
-        variable: new AnimatedValue<Point>(Point.origin, curve: Curves.ease),
+        variable: new AnimatedValue<Point>(Point.origin, curve: Curves.easeOut),
         duration: this.animationDuration);
     _performance.addStatusListener((PerformanceStatus status) {
       if (status == PerformanceStatus.completed) {
@@ -180,9 +188,9 @@
       case CardAnimationType.NONE:
         return const Duration(milliseconds: 0);
       case CardAnimationType.NORMAL:
-        return const Duration(milliseconds: 250);
+        return const Duration(milliseconds: 200);
       case CardAnimationType.LONG:
-        return const Duration(milliseconds: 1500);
+        return const Duration(milliseconds: 1000);
       default:
         print("Unexpected animation type: ${config.animationType}");
         assert(false);
diff --git a/lib/components/card_collection.dart b/lib/components/card_collection.dart
index 6ede30d..ba31fe3 100644
--- a/lib/components/card_collection.dart
+++ b/lib/components/card_collection.dart
@@ -34,6 +34,7 @@
   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;
@@ -52,6 +53,7 @@
 
   CardCollectionComponent(this.cards, this.faceUp, this.orientation,
       {this.dragChildren: false,
+      this.cardTapCallback: null,
       DropType acceptType,
       this.acceptCallback: null,
       this.comparator: null,
@@ -316,6 +318,7 @@
               !config.useKeys, // TODO(alexfandrianto): Is there a case where you want an invisible card and a key?
           useKey: config.useKeys,
           z: 0.0 + i,
+          tapCallback: config.cardTapCallback,
           animationType: config.animationType);
 
       cardComponents.add(c);
diff --git a/lib/components/hearts/hearts.part.dart b/lib/components/hearts/hearts.part.dart
index 174da16..e59b1b3 100644
--- a/lib/components/hearts/hearts.part.dart
+++ b/lib/components/hearts/hearts.part.dart
@@ -237,24 +237,20 @@
 
   Widget _makeDebugButtons() {
     if (config.game.debugMode == false) {
-      return new Flex([
+      return new Row([
         new Flexible(flex: 4, child: _makeButton('Quit', _quitGameCallback))
       ]);
     }
-    return new Container(
-        width: config.width,
-        child: new Flex([
-          new Flexible(
-              flex: 1, child: new Text('P${config.game.playerNumber}')),
-          new Flexible(
-              flex: 5,
-              child: _makeButton('Switch Player', _switchPlayersCallback)),
-          new Flexible(
-              flex: 5, child: _makeButton('Switch View', _switchViewCallback)),
-          new Flexible(
-              flex: 5, child: _makeButton('End Round', _endRoundDebugCallback)),
-          new Flexible(flex: 4, child: _makeButton('Quit', _quitGameCallback))
-        ]));
+    return new Row([
+      new Flexible(flex: 1, child: new Text('P${config.game.playerNumber}')),
+      new Flexible(
+          flex: 5, child: _makeButton('Switch Player', _switchPlayersCallback)),
+      new Flexible(
+          flex: 5, child: _makeButton('Switch View', _switchViewCallback)),
+      new Flexible(
+          flex: 5, child: _makeButton('End Round', _endRoundDebugCallback)),
+      new Flexible(flex: 4, child: _makeButton('Quit', _quitGameCallback))
+    ]);
   }
 
   @override
@@ -496,6 +492,8 @@
     CardCollectionComponent c = new CardCollectionComponent(
         cards, game.playerNumber == p, CardCollectionOrientation.suit,
         dragChildren: true, // Can drag, but may not have anywhere to drop
+        cardTapCallback: (logic_card.Card card) => (_makeGameMoveCallback(
+            card, game.cardCollections[p + HeartsGame.OFFSET_PLAY])),
         comparator: _compareCards,
         width: config.width,
         useKeys: true);
@@ -572,7 +570,7 @@
           child: new Row([w, _makeButton("Return to Lobby", _quitGameCallback)],
               justifyContent: FlexJustifyContent.spaceAround),
           flex: 1),
-      new Flexible(child: new Row([_makeDebugButtons()]), flex: 1)
+      new Flexible(child: _makeDebugButtons(), flex: 1)
     ]);
   }
 
@@ -581,13 +579,11 @@
 
     return new Container(
         decoration: new BoxDecoration(backgroundColor: Colors.pink[500]),
-        child: new Flex([
+        child: new Column([
           new Text('Player ${game.playerNumber}'),
           new Text('Waiting for Deal...'),
           _makeDebugButtons()
-        ],
-            direction: FlexDirection.vertical,
-            justifyContent: FlexJustifyContent.spaceBetween));
+        ], justifyContent: FlexJustifyContent.spaceBetween));
   }
 
   Widget _helpPassTake(
@@ -615,10 +611,19 @@
         decoration: new BoxDecoration(backgroundColor: bgColor),
         padding: new EdgeDims.all(10.0),
         width: config.width,
-        child: new Flex(topCardWidgets,
+        child: new Row(topCardWidgets,
             justifyContent: FlexJustifyContent.spaceBetween));
     Widget combinedTopArea = new BlockBody([statusBar, topArea]);
 
+    List<logic_card.Card> emptyC;
+    if (c1.length == 0) {
+      emptyC = c1;
+    } else if (c2.length == 0) {
+      emptyC = c2;
+    } else {
+      emptyC = c3; // even if c3 is already filled, it will be replaced.
+    }
+
     Widget handArea = new CardCollectionComponent(
         hand, true, CardCollectionOrientation.suit,
         dragChildren: draggable,
@@ -626,6 +631,8 @@
         width: config.width,
         acceptCallback: cb,
         acceptType: cb != null ? DropType.card : null,
+        cardTapCallback:
+            cb != null ? (logic_card.Card c) => cb(c, emptyC) : null,
         backgroundColor: Colors.grey[500],
         altColor: Colors.grey[700],
         useKeys: true);
@@ -637,11 +644,17 @@
   }
 
   Widget _topCardWidget(List<logic_card.Card> cards, AcceptCb cb) {
+    HeartsGame game = config.game as HeartsGame;
+    List<logic_card.Card> passCards =
+        game.cardCollections[game.playerNumber + HeartsGame.OFFSET_PASS];
+
     Widget ccc = new CardCollectionComponent(
         cards, true, CardCollectionOrientation.show1,
         dragChildren: cb != null,
         acceptCallback: cb,
         acceptType: cb != null ? DropType.card : null,
+        cardTapCallback:
+            cb != null ? (logic_card.Card c) => cb(c, passCards) : null,
         backgroundColor: Colors.white,
         altColor: Colors.grey[200],
         useKeys: true);
diff --git a/lib/components/proto/proto.part.dart b/lib/components/proto/proto.part.dart
index 2e1bd88..94a84e5 100644
--- a/lib/components/proto/proto.part.dart
+++ b/lib/components/proto/proto.part.dart
@@ -44,7 +44,7 @@
 
     return new Container(
         decoration: new BoxDecoration(backgroundColor: Colors.pink[500]),
-        child: new Flex(cardCollections, direction: FlexDirection.vertical));
+        child: new Column(cardCollections));
   }
 
   void _makeGameMoveCallback(logic_card.Card card, List<logic_card.Card> dest) {
@@ -60,11 +60,11 @@
 
   Widget _makeDebugButtons() {
     if (config.game.debugMode == false) {
-      return new Flex([
+      return new Row([
         new Flexible(flex: 4, child: _makeButton('Quit', _quitGameCallback))
       ]);
     }
-    return new Flex([
+    return new Row([
       new Text('P${config.game.playerNumber}'),
       _makeButton('Switch View', _switchPlayersCallback),
       _makeButton('Quit', _quitGameCallback)
diff --git a/lib/components/solitaire/solitaire.part.dart b/lib/components/solitaire/solitaire.part.dart
index 28c071b..75e4d58 100644
--- a/lib/components/solitaire/solitaire.part.dart
+++ b/lib/components/solitaire/solitaire.part.dart
@@ -58,20 +58,17 @@
 
   Widget _makeDebugButtons() {
     if (config.game.debugMode == false) {
-      return new Flex([
+      return new Row([
         new Flexible(flex: 4, child: _makeButton('Quit', _quitGameCallback))
       ]);
     }
-    return new Container(
-        width: config.width,
-        child: new Flex([
-          new Flexible(
-              flex: 1, child: new Text('P${config.game.playerNumber}')),
-          new Flexible(flex: 5, child: _makeButton('Cheat', _cheatCallback)),
-          new Flexible(
-              flex: 5, child: _makeButton('End Round', _endRoundDebugCallback)),
-          new Flexible(flex: 4, child: _makeButton('Quit', _quitGameCallback))
-        ]));
+    return new Row([
+      new Flexible(flex: 1, child: new Text('P${config.game.playerNumber}')),
+      new Flexible(flex: 5, child: _makeButton('Cheat', _cheatCallback)),
+      new Flexible(
+          flex: 5, child: _makeButton('End Round', _endRoundDebugCallback)),
+      new Flexible(flex: 4, child: _makeButton('Quit', _quitGameCallback))
+    ]);
   }
 
   @override
@@ -206,11 +203,11 @@
 
     return new Container(
         decoration: new BoxDecoration(backgroundColor: Colors.pink[500]),
-        child: new Flex([
+        child: new Column([
           new Text('Player ${game.playerNumber}'),
           _makeButton("Return to Lobby", _quitGameCallback),
           _makeDebugButtons()
-        ], direction: FlexDirection.vertical));
+        ]));
   }
 
   Widget showDeal() {
@@ -218,12 +215,10 @@
 
     return new Container(
         decoration: new BoxDecoration(backgroundColor: Colors.pink[500]),
-        child: new Flex([
+        child: new Column([
           new Text('Player ${game.playerNumber}'),
           _makeButton('Deal', game.dealCardsUI),
           _makeDebugButtons()
-        ],
-            direction: FlexDirection.vertical,
-            justifyContent: FlexJustifyContent.spaceBetween));
+        ], justifyContent: FlexJustifyContent.spaceBetween));
   }
 }
diff --git a/pubspec.lock b/pubspec.lock
index 0d9d601..7491335 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -4,7 +4,7 @@
   analyzer:
     description: analyzer
     source: hosted
-    version: "0.27.0"
+    version: "0.27.1"
   archive:
     description: archive
     source: hosted
@@ -88,7 +88,7 @@
   html:
     description: html
     source: hosted
-    version: "0.12.2"
+    version: "0.12.2+1"
   http_multi_server:
     description: http_multi_server
     source: hosted
@@ -100,7 +100,7 @@
   intl:
     description: intl
     source: hosted
-    version: "0.12.4+3"
+    version: "0.12.5"
   logging:
     description: logging
     source: hosted