Merge pull request #2354 from yjbanov/wait-for-pause-on-start

[driver] wait for the isolate to enter pause on start
diff --git a/doc/index.html b/doc/index.html
index 861218c..1db959f 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -51,7 +51,7 @@
           <header class="post-header">
           </header>
           <article class="post-content">
-            <p><a href="http://flutter.io">Flutter</a> is a new project to help developers
+            <p>Flutter is a new project to help developers
               build high-performance, high-fidelity,
               mobile apps for iOS and Android
               from a single codebase.
@@ -104,7 +104,7 @@
               </svg>
             </div>
             <ul class="contact-list">
-              <li>Flutter</li>
+              <li><a href="https://flutter.io">Flutter</a></li>
             </ul>
             <p class="terms">
               <a href="https://twitter.com/flutterio">@flutterio</a> &bull;
diff --git a/packages/flutter/lib/src/material/icon.dart b/packages/flutter/lib/src/material/icon.dart
index e3ed79d..95c2b0d 100644
--- a/packages/flutter/lib/src/material/icon.dart
+++ b/packages/flutter/lib/src/material/icon.dart
@@ -9,11 +9,24 @@
 import 'icon_theme.dart';
 import 'theme.dart';
 
+/// A material design icon.
+///
+/// Available icons are shown on this page:
+/// <https://design.google.com/icons/>
+///
+/// Icons are identified by their name (as given on that page), with
+/// spaces converted to underscores, from the [Icons] class. For
+/// example, the "alarm add" icon is [Icons.alarm_add].
+///
+/// To use this class, make sure you set `uses-material-design: true`
+/// in your project's `flutter.yaml` file. This ensures that the
+/// MaterialIcons font is included in your application. This font is
+/// used to display the icons.
 class Icon extends StatelessComponent {
   Icon({
     Key key,
-    this.size: 24.0,
     this.icon,
+    this.size: 24.0,
     this.color
   }) : super(key: key) {
     assert(size != null);
@@ -47,21 +60,24 @@
     if (icon == null)
       return new SizedBox(width: size, height: size);
 
+    final double iconOpacity = IconTheme.of(context)?.clampedOpacity ?? 1.0;
     Color iconColor = color ?? _getDefaultColor(context);
-    final int iconAlpha = (255.0 * (IconTheme.of(context)?.clampedOpacity ?? 1.0)).round();
-    if (iconAlpha != 255)
-        iconColor = color.withAlpha((iconAlpha * color.opacity).round());
+    if (iconOpacity != 1.0)
+      iconColor = iconColor.withAlpha((255.0 * iconColor.opacity * iconOpacity).round());
 
-    return new SizedBox(
-      width: size,
-      height: size,
-      child: new Center(
-        child: new Text(new String.fromCharCode(icon.codePoint),
-          style: new TextStyle(
-            inherit: false,
-            color: iconColor,
-            fontSize: size,
-            fontFamily: 'MaterialIcons'
+    return new ExcludeSemantics(
+      child: new SizedBox(
+        width: size,
+        height: size,
+        child: new Center(
+          child: new Text(
+            new String.fromCharCode(icon.codePoint),
+            style: new TextStyle(
+              inherit: false,
+              color: iconColor,
+              fontSize: size,
+              fontFamily: 'MaterialIcons'
+            )
           )
         )
       )
diff --git a/packages/flutter/lib/src/material/tabs.dart b/packages/flutter/lib/src/material/tabs.dart
index e560d10..794c266 100644
--- a/packages/flutter/lib/src/material/tabs.dart
+++ b/packages/flutter/lib/src/material/tabs.dart
@@ -704,7 +704,8 @@
   void _updateScrollBehavior() {
     scrollTo(scrollBehavior.updateExtents(
       containerExtent: config.scrollDirection == Axis.vertical ? _viewportSize.height : _viewportSize.width,
-      contentExtent: _tabWidths.reduce((double sum, double width) => sum + width)
+      contentExtent: _tabWidths.reduce((double sum, double width) => sum + width),
+      scrollOffset: scrollOffset
     ));
   }
 
@@ -780,7 +781,7 @@
     );
 
     if (config.isScrollable) {
-      child: new Viewport(
+      return new Viewport(
         scrollDirection: Axis.horizontal,
         paintOffset: scrollOffsetToPixelDelta(scrollOffset),
         onPaintOffsetUpdateNeeded: _handlePaintOffsetUpdateNeeded,
diff --git a/packages/flutter/lib/src/painting/box_painter.dart b/packages/flutter/lib/src/painting/box_painter.dart
index dce01de..e34b7a7 100644
--- a/packages/flutter/lib/src/painting/box_painter.dart
+++ b/packages/flutter/lib/src/painting/box_painter.dart
@@ -560,6 +560,12 @@
   Paint paint = new Paint()..isAntiAlias = false;
   if (colorFilter != null)
     paint.colorFilter = colorFilter;
+  if (sourceSize != destinationSize) {
+    // Use the "low" quality setting to scale the image, which corresponds to
+    // bilinear interpolation, rather than the default "none" which corresponds
+    // to nearest-neighbor.
+    paint.filterQuality = FilterQuality.low;
+  }
   double dx = (outputSize.width - destinationSize.width) * (alignX ?? 0.5);
   double dy = (outputSize.height - destinationSize.height) * (alignY ?? 0.5);
   Point destinationPosition = rect.topLeft + new Offset(dx, dy);
diff --git a/packages/flutter/test/widget/tabs_test.dart b/packages/flutter/test/widget/tabs_test.dart
index ed1d45d..a8ded32 100644
--- a/packages/flutter/test/widget/tabs_test.dart
+++ b/packages/flutter/test/widget/tabs_test.dart
@@ -7,12 +7,13 @@
 import 'package:flutter/widgets.dart';
 import 'package:test/test.dart';
 
-Widget buildFrame({ List<String> tabs, String value, bool isScrollable: false }) {
+Widget buildFrame({ List<String> tabs, String value, bool isScrollable: false, Key tabBarKey }) {
   return new Material(
     child: new TabBarSelection<String>(
       value: value,
       values: tabs,
       child: new TabBar<String>(
+        key: tabBarKey,
         labels: new Map<String, TabLabel>.fromIterable(tabs, value: (String tab) => new TabLabel(text: tab)),
         isScrollable: isScrollable
       )
@@ -98,4 +99,48 @@
       expect(selection.value, equals('A'));
     });
   });
+
+  test('Scrollable TabBar tap centers selected tab', () {
+    testWidgets((WidgetTester tester) {
+      List<String> tabs = <String>['AAAAAA', 'BBBBBB', 'CCCCCC', 'DDDDDD', 'EEEEEE', 'FFFFFF', 'GGGGGG', 'HHHHHH', 'IIIIII', 'JJJJJJ', 'KKKKKK', 'LLLLLL'];
+      Key tabBarKey = new Key('TabBar');
+      tester.pumpWidget(buildFrame(tabs: tabs, value: 'AAAAAA', isScrollable: true, tabBarKey: tabBarKey));
+      TabBarSelectionState<String> selection = TabBarSelection.of(tester.findText('AAAAAA'));
+      expect(selection, isNotNull);
+      expect(selection.value, equals('AAAAAA'));
+
+      expect(tester.getSize(tester.findElementByKey(tabBarKey)).width, equals(800.0));
+      // The center of the FFFFFF item is to the right of the TabBar's center
+      expect(tester.getCenter(tester.findText('FFFFFF')).x, greaterThan(401.0));
+
+      tester.tap(tester.findText('FFFFFF'));
+      tester.pump();
+      tester.pump(const Duration(seconds: 1)); // finish the scroll animation
+      expect(selection.value, equals('FFFFFF'));
+      // The center of the FFFFFF item is now at the TabBar's center
+      expect(tester.getCenter(tester.findText('FFFFFF')).x, closeTo(400.0, 1.0));
+    });
+  });
+
+
+  test('TabBar can be scrolled independent of the selection', () {
+    testWidgets((WidgetTester tester) {
+      List<String> tabs = <String>['AAAAAA', 'BBBBBB', 'CCCCCC', 'DDDDDD', 'EEEEEE', 'FFFFFF', 'GGGGGG', 'HHHHHH', 'IIIIII', 'JJJJJJ', 'KKKKKK', 'LLLLLL'];
+      Key tabBarKey = new Key('TabBar');
+      tester.pumpWidget(buildFrame(tabs: tabs, value: 'AAAAAA', isScrollable: true, tabBarKey: tabBarKey));
+      TabBarSelectionState<String> selection = TabBarSelection.of(tester.findText('AAAAAA'));
+      expect(selection, isNotNull);
+      expect(selection.value, equals('AAAAAA'));
+
+      // Fling-scroll the TabBar to the left
+      expect(tester.getCenter(tester.findText('HHHHHH')).x, lessThan(700.0));
+      tester.fling(tester.findElementByKey(tabBarKey), const Offset(-20.0, 0.0), 1000.0);
+      tester.pump();
+      tester.pump(const Duration(seconds: 1)); // finish the scroll animation
+      expect(tester.getCenter(tester.findText('HHHHHH')).x, lessThan(500.0));
+
+      // Scrolling the TabBar doesn't change the selection
+      expect(selection.value, equals('AAAAAA'));
+    });
+  });
 }