Make UserAccountsDrawerHeader accessible (#13851)

Fixes #13743 
Fixes #12379
Follow-up to #13745

Also adds an option to hide gestures introduced by `InkWell` and `InkResponse` from the semantics tree (see also `GestureDetector.excludeFromSemantics`).
diff --git a/examples/flutter_gallery/lib/demo/material/drawer_demo.dart b/examples/flutter_gallery/lib/demo/material/drawer_demo.dart
index 7eaa46c..8d96bd5 100644
--- a/examples/flutter_gallery/lib/demo/material/drawer_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/drawer_demo.dart
@@ -100,17 +100,33 @@
                   package: _kGalleryAssetsPackage,
                 ),
               ),
-              otherAccountsPictures: const <Widget>[
-                const CircleAvatar(
-                  backgroundImage: const AssetImage(
-                    _kAsset1,
-                    package: _kGalleryAssetsPackage,
+              otherAccountsPictures:  <Widget>[
+                new GestureDetector(
+                  onTap: () {
+                    _onOtherAccountsTap(context);
+                  },
+                  child: new Semantics(
+                    label: 'Switch to Account B',
+                    child: const CircleAvatar(
+                      backgroundImage: const AssetImage(
+                        _kAsset1,
+                        package: _kGalleryAssetsPackage,
+                      ),
+                    ),
                   ),
                 ),
-                const CircleAvatar(
-                  backgroundImage: const AssetImage(
-                    _kAsset2,
-                    package: _kGalleryAssetsPackage,
+                new GestureDetector(
+                  onTap: () {
+                    _onOtherAccountsTap(context);
+                  },
+                  child: new Semantics(
+                    label: 'Switch to Account C',
+                    child: const CircleAvatar(
+                      backgroundImage: const AssetImage(
+                        _kAsset2,
+                        package: _kGalleryAssetsPackage,
+                      ),
+                    ),
                   ),
                 ),
               ],
@@ -213,4 +229,21 @@
       ),
     );
   }
+
+  void _onOtherAccountsTap(BuildContext context) {
+    showDialog<Null>(
+      context: context,
+      child: new AlertDialog(
+        title: const Text('Account switching not implemented.'),
+        actions: <Widget>[
+          new FlatButton(
+            child: const Text('OK'),
+            onPressed: () {
+              Navigator.of(context).pop();
+            },
+          ),
+        ],
+      ),
+    );
+  }
 }
diff --git a/packages/flutter/lib/src/material/ink_well.dart b/packages/flutter/lib/src/material/ink_well.dart
index 4707082..1186870 100644
--- a/packages/flutter/lib/src/material/ink_well.dart
+++ b/packages/flutter/lib/src/material/ink_well.dart
@@ -96,6 +96,7 @@
     this.highlightColor,
     this.splashColor,
     this.enableFeedback: true,
+    this.excludeFromSemantics: false,
   }) : assert(enableFeedback != null), super(key: key);
 
   /// The widget below this widget in the tree.
@@ -194,6 +195,15 @@
   ///  * [Feedback] for providing platform-specific feedback to certain actions.
   final bool enableFeedback;
 
+  /// Whether to exclude the gestures introduced by this widget from the
+  /// semantics tree.
+  ///
+  /// For example, a long-press gesture for showing a tooltip is usually
+  /// excluded because the tooltip itself is included in the semantics
+  /// tree directly and so having a gesture to show it would result in
+  /// duplication of information.
+  final bool excludeFromSemantics;
+
   /// The rectangle to use for the highlight effect and for clipping
   /// the splash effects if [containedInkWell] is true.
   ///
@@ -379,7 +389,8 @@
       onDoubleTap: widget.onDoubleTap != null ? _handleDoubleTap : null,
       onLongPress: widget.onLongPress != null ? () => _handleLongPress(context) : null,
       behavior: HitTestBehavior.opaque,
-      child: widget.child
+      child: widget.child,
+      excludeFromSemantics: widget.excludeFromSemantics,
     );
   }
 
@@ -427,6 +438,7 @@
     Color splashColor,
     BorderRadius borderRadius,
     bool enableFeedback: true,
+    bool excludeFromSemantics: false,
   }) : super(
     key: key,
     child: child,
@@ -440,5 +452,6 @@
     splashColor: splashColor,
     borderRadius: borderRadius,
     enableFeedback: enableFeedback,
+    excludeFromSemantics: excludeFromSemantics,
   );
 }
diff --git a/packages/flutter/lib/src/material/user_accounts_drawer_header.dart b/packages/flutter/lib/src/material/user_accounts_drawer_header.dart
index d793a85..58bb168 100644
--- a/packages/flutter/lib/src/material/user_accounts_drawer_header.dart
+++ b/packages/flutter/lib/src/material/user_accounts_drawer_header.dart
@@ -10,6 +10,7 @@
 import 'drawer_header.dart';
 import 'icons.dart';
 import 'ink_well.dart';
+import 'material_localizations.dart';
 import 'theme.dart';
 
 class _AccountPictures extends StatelessWidget {
@@ -31,21 +32,27 @@
           end: 0.0,
           child: new Row(
             children: (otherAccountsPictures ?? <Widget>[]).take(3).map((Widget picture) {
-              return new Container(
-                margin: const EdgeInsetsDirectional.only(start: 16.0),
-                width: 40.0,
-                height: 40.0,
-                child: picture
+              return new Semantics(
+                explicitChildNodes: true,
+                child: new Container(
+                  margin: const EdgeInsetsDirectional.only(start: 16.0),
+                  width: 40.0,
+                  height: 40.0,
+                  child: picture
+                ),
               );
             }).toList(),
           ),
         ),
         new Positioned(
           top: 0.0,
-          child: new SizedBox(
-            width: 72.0,
-            height: 72.0,
-            child: currentAccountPicture
+          child: new Semantics(
+            explicitChildNodes: true,
+            child: new SizedBox(
+              width: 72.0,
+              height: 72.0,
+              child: currentAccountPicture
+            ),
           ),
         ),
       ],
@@ -67,66 +74,170 @@
   final VoidCallback onTap;
   final bool isOpen;
 
-  Widget addDropdownIcon(Widget line) {
-    final Widget icon = new Icon(
-      isOpen ? Icons.arrow_drop_up : Icons.arrow_drop_down,
-      color: Colors.white
-    );
-    return new Expanded(
-      child: new Row(
-        crossAxisAlignment: CrossAxisAlignment.center,
-        children: line == null ? <Widget>[icon] : <Widget>[
-          new Expanded(child: line),
-          icon,
-        ],
-      ),
-    );
-  }
-
   @override
   Widget build(BuildContext context) {
+    assert(debugCheckHasDirectionality(context));
+
     final ThemeData theme = Theme.of(context);
-    Widget accountNameLine = accountName == null ? null : new DefaultTextStyle(
-      style: theme.primaryTextTheme.body2,
-      overflow: TextOverflow.ellipsis,
-      child: accountName,
-    );
-    Widget accountEmailLine = accountEmail == null ? null : new DefaultTextStyle(
-      style: theme.primaryTextTheme.body1,
-      overflow: TextOverflow.ellipsis,
-      child: accountEmail,
-    );
-    if (onTap != null) {
-      if (accountEmailLine != null)
-        accountEmailLine = addDropdownIcon(accountEmailLine);
-      else
-        accountNameLine = addDropdownIcon(accountNameLine);
+    final List<Widget> children = <Widget>[];
+
+    if (accountName != null) {
+      final Widget accountNameLine = new LayoutId(
+        id: _AccountDetailsLayout.accountName,
+        child: new Padding(
+          padding: const EdgeInsets.symmetric(vertical: 2.0),
+          child: new DefaultTextStyle(
+            style: theme.primaryTextTheme.body2,
+            overflow: TextOverflow.ellipsis,
+            child: accountName,
+          ),
+        ),
+      );
+      children.add(accountNameLine);
     }
 
-    Widget accountDetails;
-    if (accountEmailLine != null || accountNameLine != null) {
-      accountDetails = new Padding(
-        padding: const EdgeInsets.symmetric(vertical: 8.0),
-        child: new Column(
-          crossAxisAlignment: CrossAxisAlignment.start,
-          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
-          children: (accountEmailLine != null && accountNameLine != null)
-            ? <Widget>[accountNameLine, accountEmailLine]
-            : <Widget>[accountNameLine ?? accountEmailLine]
+    if (accountEmail != null) {
+      final Widget accountEmailLine = new LayoutId(
+        id: _AccountDetailsLayout.accountEmail,
+        child: new Padding(
+          padding: const EdgeInsets.symmetric(vertical: 2.0),
+          child: new DefaultTextStyle(
+            style: theme.primaryTextTheme.body1,
+            overflow: TextOverflow.ellipsis,
+            child: accountEmail,
+          ),
         ),
       );
+      children.add(accountEmailLine);
+    }
+
+    if (onTap != null) {
+      final MaterialLocalizations localizations = MaterialLocalizations.of(context);
+      final Widget dropDownIcon = new LayoutId(
+        id: _AccountDetailsLayout.dropdownIcon,
+        child: new Semantics(
+          container: true,
+          button: true,
+          onTap: onTap,
+          child: new SizedBox(
+            height: _kAccountDetailsHeight,
+            width: _kAccountDetailsHeight,
+            child: new Center(
+              child: new Icon(
+                isOpen ? Icons.arrow_drop_up : Icons.arrow_drop_down,
+                color: Colors.white,
+                semanticLabel: isOpen
+                    ? localizations.hideAccountsLabel
+                    : localizations.showAccountsLabel,
+              ),
+            ),
+          ),
+        ),
+      );
+      children.add(dropDownIcon);
+    }
+
+    Widget accountDetails = new CustomMultiChildLayout(
+      delegate: new _AccountDetailsLayout(
+        textDirection: Directionality.of(context),
+      ),
+      children: children,
+    );
+
+    if (onTap != null) {
+      accountDetails = new InkWell(
+        onTap: onTap,
+        child: accountDetails,
+        excludeFromSemantics: true,
+      );
     }
 
-    if (onTap != null)
-      accountDetails = new InkWell(onTap: onTap, child: accountDetails);
-
     return new SizedBox(
-      height: 56.0,
+      height: _kAccountDetailsHeight,
       child: accountDetails,
     );
   }
 }
 
+const double _kAccountDetailsHeight = 56.0;
+
+class _AccountDetailsLayout extends MultiChildLayoutDelegate {
+
+  _AccountDetailsLayout({ @required this.textDirection });
+
+  static final String accountName = 'accountName';
+  static final String accountEmail = 'accountEmail';
+  static final String dropdownIcon = 'dropdownIcon';
+
+  final TextDirection textDirection;
+
+  @override
+  void performLayout(Size size) {
+    Size iconSize;
+    if (hasChild(dropdownIcon)) {
+      // place the dropdown icon in bottom right (LTR) or bottom left (RTL)
+      iconSize = layoutChild(dropdownIcon, new BoxConstraints.loose(size));
+      positionChild(dropdownIcon, _offsetForIcon(size, iconSize));
+    }
+
+    final String bottomLine = hasChild(accountEmail) ? accountEmail : (hasChild(accountName) ? accountName : null);
+
+    if (bottomLine != null) {
+      final Size constraintSize = iconSize == null ? size : size - new Offset(iconSize.width, 0.0);
+      iconSize ??= const Size(_kAccountDetailsHeight, _kAccountDetailsHeight);
+
+      // place bottom line center at same height as icon center
+      final Size bottomLineSize = layoutChild(bottomLine, new BoxConstraints.loose(constraintSize));
+      final Offset bottomLineOffset = _offsetForBottomLine(size, iconSize, bottomLineSize);
+      positionChild(bottomLine, bottomLineOffset);
+
+      // place account name above account email
+      if (bottomLine == accountEmail && hasChild(accountName)) {
+        final Size nameSize = layoutChild(accountName, new BoxConstraints.loose(constraintSize));
+        positionChild(accountName, _offsetForName(size, nameSize, bottomLineOffset));
+      }
+    }
+  }
+
+  @override
+  bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) => true;
+
+  Offset _offsetForIcon(Size size, Size iconSize) {
+    switch (textDirection) {
+      case TextDirection.ltr:
+        return new Offset(size.width - iconSize.width, size.height - iconSize.height);
+      case TextDirection.rtl:
+        return new Offset(0.0, size.height - iconSize.height);
+    }
+    assert(false, 'Unreachable');
+    return null;
+  }
+
+  Offset _offsetForBottomLine(Size size, Size iconSize, Size bottomLineSize) {
+    final double y = size.height - 0.5 * iconSize.height - 0.5 * bottomLineSize.height;
+    switch (textDirection) {
+      case TextDirection.ltr:
+        return new Offset(0.0, y);
+      case TextDirection.rtl:
+        return new Offset(size.width - bottomLineSize.width, y);
+    }
+    assert(false, 'Unreachable');
+    return null;
+  }
+
+  Offset _offsetForName(Size size, Size nameSize, Offset bottomLineOffset) {
+    final double y = bottomLineOffset.dy - nameSize.height;
+    switch (textDirection) {
+      case TextDirection.ltr:
+        return new Offset(0.0, y);
+      case TextDirection.rtl:
+        return new Offset(size.width - nameSize.width, y);
+    }
+    assert(false, 'Unreachable');
+    return null;
+  }
+}
+
 /// A material design [Drawer] header that identifies the app's user.
 ///
 /// Requires one of its ancestors to be a [Material] widget.
@@ -195,29 +306,37 @@
   @override
   Widget build(BuildContext context) {
     assert(debugCheckHasMaterial(context));
-    return new DrawerHeader(
-      decoration: widget.decoration ?? new BoxDecoration(
-        color: Theme.of(context).primaryColor,
-      ),
-      margin: widget.margin,
-      child: new SafeArea(
-        bottom: false,
-        child: new Column(
-          crossAxisAlignment: CrossAxisAlignment.stretch,
-          children: <Widget>[
-            new Expanded(
-              child: new _AccountPictures(
-                currentAccountPicture: widget.currentAccountPicture,
-                otherAccountsPictures: widget.otherAccountsPictures,
-              )
-            ),
-            new _AccountDetails(
-              accountName: widget.accountName,
-              accountEmail: widget.accountEmail,
-              isOpen: _isOpen,
-              onTap: widget.onDetailsPressed == null ? null : _handleDetailsPressed,
-            ),
-          ],
+    return new Semantics(
+      container: true,
+      label: MaterialLocalizations.of(context).signedInLabel,
+      child: new DrawerHeader(
+        decoration: widget.decoration ?? new BoxDecoration(
+          color: Theme.of(context).primaryColor,
+        ),
+        margin: widget.margin,
+        padding: const EdgeInsetsDirectional.only(top: 16.0, start: 16.0),
+        child: new SafeArea(
+          bottom: false,
+          child: new Column(
+            crossAxisAlignment: CrossAxisAlignment.stretch,
+            children: <Widget>[
+              new Expanded(
+                child: new Padding(
+                  padding: const EdgeInsetsDirectional.only(end: 16.0),
+                  child: new _AccountPictures(
+                    currentAccountPicture: widget.currentAccountPicture,
+                    otherAccountsPictures: widget.otherAccountsPictures,
+                  ),
+                )
+              ),
+              new _AccountDetails(
+                accountName: widget.accountName,
+                accountEmail: widget.accountEmail,
+                isOpen: _isOpen,
+                onTap: widget.onDetailsPressed == null ? null : _handleDetailsPressed,
+              ),
+            ],
+          ),
         ),
       ),
     );
diff --git a/packages/flutter/test/material/ink_well_test.dart b/packages/flutter/test/material/ink_well_test.dart
index f2d4c6a..5b0872b 100644
--- a/packages/flutter/test/material/ink_well_test.dart
+++ b/packages/flutter/test/material/ink_well_test.dart
@@ -7,6 +7,7 @@
 import 'package:flutter_test/flutter_test.dart';
 
 import '../rendering/mock_canvas.dart';
+import '../widgets/semantics_tester.dart';
 import 'feedback_tester.dart';
 
 void main() {
@@ -156,4 +157,33 @@
     await runTest(true);
     await runTest(false);
   });
+
+  testWidgets('excludeFromSemantics', (WidgetTester tester) async {
+    final SemanticsTester semantics = new SemanticsTester(tester);
+
+    await tester.pumpWidget(new Directionality(
+      textDirection: TextDirection.ltr,
+      child: new Material(
+        child: new InkWell(
+          onTap: () { },
+          child: const Text('Button'),
+        ),
+      ),
+    ));
+    expect(semantics, includesNodeWith(label: 'Button', actions: <SemanticsAction>[SemanticsAction.tap]));
+
+    await tester.pumpWidget(new Directionality(
+      textDirection: TextDirection.ltr,
+      child: new Material(
+        child: new InkWell(
+          onTap: () { },
+          child: const Text('Button'),
+          excludeFromSemantics: true,
+        ),
+      ),
+    ));
+    expect(semantics, isNot(includesNodeWith(label: 'Button', actions: <SemanticsAction>[SemanticsAction.tap])));
+
+    semantics.dispose();
+  });
 }
diff --git a/packages/flutter/test/material/user_accounts_drawer_header_test.dart b/packages/flutter/test/material/user_accounts_drawer_header_test.dart
index f18956e..c304c34 100644
--- a/packages/flutter/test/material/user_accounts_drawer_header_test.dart
+++ b/packages/flutter/test/material/user_accounts_drawer_header_test.dart
@@ -2,59 +2,74 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'dart:ui' show SemanticsFlags;
+
 import 'package:flutter/material.dart';
 import 'package:flutter/rendering.dart';
 import 'package:flutter_test/flutter_test.dart';
 
-void main() {
-  testWidgets('UserAccountsDrawerHeader test', (WidgetTester tester) async {
-    final Key avatarA = const Key('A');
-    final Key avatarC = const Key('C');
-    final Key avatarD = const Key('D');
+import '../widgets/semantics_tester.dart';
 
-    await tester.pumpWidget(
-      new MaterialApp(
-        home: new MediaQuery(
-          data: const MediaQueryData(
-            padding: const EdgeInsets.only(
-              left: 10.0,
-              top: 20.0,
-              right: 30.0,
-              bottom: 40.0,
-            ),
+const Key avatarA = const Key('A');
+const Key avatarC = const Key('C');
+const Key avatarD = const Key('D');
+
+Future<Null> pumpTestWidget(WidgetTester tester, {
+  bool withName: true,
+  bool withEmail: true,
+  bool withOnDetailsPressedHandler: true,
+}) async {
+  await tester.pumpWidget(
+    new MaterialApp(
+      home: new MediaQuery(
+        data: const MediaQueryData(
+          padding: const EdgeInsets.only(
+            left: 10.0,
+            top: 20.0,
+            right: 30.0,
+            bottom: 40.0,
           ),
-          child: new Material(
-            child: new Center(
-              child: new UserAccountsDrawerHeader(
-                currentAccountPicture: new CircleAvatar(
+        ),
+        child: new Material(
+          child: new Center(
+            child: new UserAccountsDrawerHeader(
+              onDetailsPressed: withOnDetailsPressedHandler ? () {} : null,
+              currentAccountPicture: const ExcludeSemantics(
+                child: const CircleAvatar(
                   key: avatarA,
                   child: const Text('A'),
                 ),
-                otherAccountsPictures: <Widget>[
-                  const CircleAvatar(
-                    child: const Text('B'),
-                  ),
-                  new CircleAvatar(
-                    key: avatarC,
-                    child: const Text('C'),
-                  ),
-                  new CircleAvatar(
-                    key: avatarD,
-                    child: const Text('D'),
-                  ),
-                  const CircleAvatar(
-                    child: const Text('E'),
-                  )
-                ],
-                accountName: const Text('name'),
-                accountEmail: const Text('email'),
               ),
+              otherAccountsPictures: <Widget>[
+                const CircleAvatar(
+                  child: const Text('B'),
+                ),
+                const CircleAvatar(
+                  key: avatarC,
+                  child: const Text('C'),
+                ),
+                const CircleAvatar(
+                  key: avatarD,
+                  child: const Text('D'),
+                ),
+                const CircleAvatar(
+                  child: const Text('E'),
+                )
+              ],
+              accountName: withName ? const Text('name') : null,
+              accountEmail: withEmail ? const Text('email') : null,
             ),
           ),
         ),
       ),
-    );
+    ),
+  );
+}
 
+void main() {
+  testWidgets('UserAccountsDrawerHeader test', (WidgetTester tester) async {
+    await pumpTestWidget(tester);
+    
     expect(find.text('A'), findsOneWidget);
     expect(find.text('B'), findsOneWidget);
     expect(find.text('C'), findsOneWidget);
@@ -91,7 +106,7 @@
   });
 
 
-  testWidgets('UserAccountsDrawerHeader null parameters', (WidgetTester tester) async {
+  testWidgets('UserAccountsDrawerHeader null parameters LTR', (WidgetTester tester) async {
     Widget buildFrame({
       Widget currentAccountPicture,
       List<Widget> otherAccountsPictures,
@@ -134,6 +149,10 @@
       tester.getCenter(find.text('accountName')).dy,
       tester.getCenter(find.byType(Icon)).dy
     );
+    expect(
+      tester.getCenter(find.text('accountName')).dx,
+      lessThan(tester.getCenter(find.byType(Icon)).dx)
+    );
 
     await tester.pumpWidget(buildFrame(
       accountEmail: const Text('accountEmail'),
@@ -143,6 +162,10 @@
       tester.getCenter(find.text('accountEmail')).dy,
       tester.getCenter(find.byType(Icon)).dy
     );
+    expect(
+      tester.getCenter(find.text('accountEmail')).dx,
+      lessThan(tester.getCenter(find.byType(Icon)).dx)
+    );
 
     await tester.pumpWidget(buildFrame(
       accountName: const Text('accountName'),
@@ -154,6 +177,10 @@
       tester.getCenter(find.byType(Icon)).dy
     );
     expect(
+      tester.getCenter(find.text('accountEmail')).dx,
+      lessThan(tester.getCenter(find.byType(Icon)).dx)
+    );
+    expect(
       tester.getBottomLeft(find.text('accountEmail')).dy,
       greaterThan(tester.getBottomLeft(find.text('accountName')).dy)
     );
@@ -186,4 +213,198 @@
       greaterThan(tester.getBottomLeft(find.byKey(avatarA)).dy)
     );
   });
+
+  testWidgets('UserAccountsDrawerHeader null parameters RTL', (WidgetTester tester) async {
+    Widget buildFrame({
+      Widget currentAccountPicture,
+      List<Widget> otherAccountsPictures,
+      Widget accountName,
+      Widget accountEmail,
+      VoidCallback onDetailsPressed,
+      EdgeInsets margin,
+    }) {
+      return new MaterialApp(
+        home: new Directionality(
+          textDirection: TextDirection.rtl,
+          child: new Material(
+            child: new Center(
+              child: new UserAccountsDrawerHeader(
+                currentAccountPicture: currentAccountPicture,
+                otherAccountsPictures: otherAccountsPictures,
+                accountName: accountName,
+                accountEmail: accountEmail,
+                onDetailsPressed: onDetailsPressed,
+                margin: margin,
+              ),
+            ),
+          ),
+        ),
+      );
+    }
+
+    await tester.pumpWidget(buildFrame());
+    final RenderBox box = tester.renderObject(find.byType(UserAccountsDrawerHeader));
+    expect(box.size.height, equals(160.0 + 1.0)); // height + bottom edge)
+    expect(find.byType(Icon), findsNothing);
+
+    await tester.pumpWidget(buildFrame(
+      onDetailsPressed: () { },
+    ));
+    expect(find.byType(Icon), findsOneWidget);
+
+    await tester.pumpWidget(buildFrame(
+      accountName: const Text('accountName'),
+      onDetailsPressed: () { },
+    ));
+    expect(
+      tester.getCenter(find.text('accountName')).dy,
+      tester.getCenter(find.byType(Icon)).dy
+    );
+    expect(
+      tester.getCenter(find.text('accountName')).dx,
+      greaterThan(tester.getCenter(find.byType(Icon)).dx)
+    );
+
+    await tester.pumpWidget(buildFrame(
+      accountEmail: const Text('accountEmail'),
+      onDetailsPressed: () { },
+    ));
+    expect(
+      tester.getCenter(find.text('accountEmail')).dy,
+      tester.getCenter(find.byType(Icon)).dy
+    );
+    expect(
+      tester.getCenter(find.text('accountEmail')).dx,
+      greaterThan(tester.getCenter(find.byType(Icon)).dx)
+    );
+
+    await tester.pumpWidget(buildFrame(
+      accountName: const Text('accountName'),
+      accountEmail: const Text('accountEmail'),
+      onDetailsPressed: () { },
+    ));
+    expect(
+      tester.getCenter(find.text('accountEmail')).dy,
+      tester.getCenter(find.byType(Icon)).dy
+    );
+    expect(
+      tester.getCenter(find.text('accountEmail')).dx,
+      greaterThan(tester.getCenter(find.byType(Icon)).dx)
+    );
+    expect(
+      tester.getBottomLeft(find.text('accountEmail')).dy,
+      greaterThan(tester.getBottomLeft(find.text('accountName')).dy)
+    );
+    expect(
+      tester.getBottomRight(find.text('accountEmail')).dx,
+      tester.getBottomRight(find.text('accountName')).dx
+    );
+
+    await tester.pumpWidget(buildFrame(
+      currentAccountPicture: const CircleAvatar(child: const Text('A')),
+    ));
+    expect(find.text('A'), findsOneWidget);
+
+    await tester.pumpWidget(buildFrame(
+      otherAccountsPictures: <Widget>[const CircleAvatar(child: const Text('A'))],
+    ));
+    expect(find.text('A'), findsOneWidget);
+
+    final Key avatarA = const Key('A');
+    await tester.pumpWidget(buildFrame(
+      currentAccountPicture: new CircleAvatar(key: avatarA, child: const Text('A')),
+      accountName: const Text('accountName'),
+    ));
+    expect(
+      tester.getBottomRight(find.byKey(avatarA)).dx,
+      tester.getBottomRight(find.text('accountName')).dx
+    );
+    expect(
+      tester.getBottomLeft(find.text('accountName')).dy,
+      greaterThan(tester.getBottomLeft(find.byKey(avatarA)).dy)
+    );
+  });
+
+  testWidgets('UserAccountsDrawerHeader provides semantics', (WidgetTester tester) async {
+    final SemanticsTester semantics = new SemanticsTester(tester);
+    await pumpTestWidget(tester);
+
+    expect(
+      semantics,
+      hasSemantics(
+        new TestSemantics(
+          children: <TestSemantics>[
+            new TestSemantics(
+              label: 'Signed in\nname\nemail',
+              textDirection: TextDirection.ltr,
+              children: <TestSemantics>[
+                new TestSemantics(
+                  label: r'B',
+                  textDirection: TextDirection.ltr,
+                ),
+                new TestSemantics(
+                  label: r'C',
+                  textDirection: TextDirection.ltr,
+                ),
+                new TestSemantics(
+                  label: r'D',
+                  textDirection: TextDirection.ltr,
+                ),
+                new TestSemantics(
+                  flags: <SemanticsFlags>[SemanticsFlags.isButton],
+                  actions: <SemanticsAction>[SemanticsAction.tap],
+                  label: r'Show accounts',
+                  textDirection: TextDirection.ltr,
+                ),
+              ],
+            ),
+          ],
+        ),
+        ignoreId: true, ignoreTransform: true, ignoreRect: true,
+      ),
+    );
+
+    semantics.dispose();
+  });
+
+  testWidgets('UserAccountsDrawerHeader provides semantics with missing properties', (WidgetTester tester) async {
+    final SemanticsTester semantics = new SemanticsTester(tester);
+    await pumpTestWidget(
+      tester,
+      withEmail: false,
+      withName: false,
+      withOnDetailsPressedHandler: false,
+    );
+
+    expect(
+      semantics,
+      hasSemantics(
+        new TestSemantics(
+          children: <TestSemantics>[
+            new TestSemantics(
+              label: 'Signed in',
+              textDirection: TextDirection.ltr,
+              children: <TestSemantics>[
+                new TestSemantics(
+                  label: r'B',
+                  textDirection: TextDirection.ltr,
+                ),
+                new TestSemantics(
+                  label: r'C',
+                  textDirection: TextDirection.ltr,
+                ),
+                new TestSemantics(
+                  label: r'D',
+                  textDirection: TextDirection.ltr,
+                ),
+              ],
+            ),
+          ],
+        ),
+        ignoreId: true, ignoreTransform: true, ignoreRect: true,
+      ),
+    );
+
+    semantics.dispose();
+  });
 }