mirror of
https://github.com/flutter/flutter
synced 2024-10-13 19:52:53 +00:00
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`).
This commit is contained in:
parent
f4040455d1
commit
94f48c2cc6
|
@ -100,17 +100,33 @@ class _DrawerDemoState extends State<DrawerDemo> with TickerProviderStateMixin {
|
|||
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 @@ class _DrawerDemoState extends State<DrawerDemo> with TickerProviderStateMixin {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
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();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,6 +96,7 @@ class InkResponse extends StatefulWidget {
|
|||
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 @@ class InkResponse extends StatefulWidget {
|
|||
/// * [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 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
|
|||
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 @@ class InkWell extends InkResponse {
|
|||
Color splashColor,
|
||||
BorderRadius borderRadius,
|
||||
bool enableFeedback: true,
|
||||
bool excludeFromSemantics: false,
|
||||
}) : super(
|
||||
key: key,
|
||||
child: child,
|
||||
|
@ -440,5 +452,6 @@ class InkWell extends InkResponse {
|
|||
splashColor: splashColor,
|
||||
borderRadius: borderRadius,
|
||||
enableFeedback: enableFeedback,
|
||||
excludeFromSemantics: excludeFromSemantics,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import 'debug.dart';
|
|||
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 @@ class _AccountPictures extends StatelessWidget {
|
|||
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 @@ class _AccountDetails extends StatelessWidget {
|
|||
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 @@ class _UserAccountsDrawerHeaderState extends State<UserAccountsDrawerHeader> {
|
|||
@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,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:flutter/rendering.dart';
|
|||
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 @@ void main() {
|
|||
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();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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 @@ void main() {
|
|||
});
|
||||
|
||||
|
||||
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 @@ void main() {
|
|||
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 @@ void main() {
|
|||
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'),
|
||||
|
@ -153,6 +176,10 @@ void main() {
|
|||
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)
|
||||
);
|
||||
expect(
|
||||
tester.getBottomLeft(find.text('accountEmail')).dy,
|
||||
greaterThan(tester.getBottomLeft(find.text('accountName')).dy)
|
||||
|
@ -186,4 +213,198 @@ void main() {
|
|||
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();
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue