re-add semantic service events for tap and longPress (#17918)
This commit is contained in:
parent
7587f54b20
commit
b6ceff552b
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter/semantics.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
@ -86,6 +88,7 @@ class Feedback {
|
|||||||
/// * [wrapForTap] to trigger platform-specific feedback before executing a
|
/// * [wrapForTap] to trigger platform-specific feedback before executing a
|
||||||
/// [GestureTapCallback].
|
/// [GestureTapCallback].
|
||||||
static Future<Null> forTap(BuildContext context) async {
|
static Future<Null> forTap(BuildContext context) async {
|
||||||
|
context.findRenderObject().sendSemanticsEvent(const TapSemanticEvent());
|
||||||
switch (_platform(context)) {
|
switch (_platform(context)) {
|
||||||
case TargetPlatform.android:
|
case TargetPlatform.android:
|
||||||
case TargetPlatform.fuchsia:
|
case TargetPlatform.fuchsia:
|
||||||
@ -124,6 +127,7 @@ class Feedback {
|
|||||||
/// * [wrapForLongPress] to trigger platform-specific feedback before
|
/// * [wrapForLongPress] to trigger platform-specific feedback before
|
||||||
/// executing a [GestureLongPressCallback].
|
/// executing a [GestureLongPressCallback].
|
||||||
static Future<Null> forLongPress(BuildContext context) {
|
static Future<Null> forLongPress(BuildContext context) {
|
||||||
|
context.findRenderObject().sendSemanticsEvent(const LongPressSemanticsEvent());
|
||||||
switch (_platform(context)) {
|
switch (_platform(context)) {
|
||||||
case TargetPlatform.android:
|
case TargetPlatform.android:
|
||||||
case TargetPlatform.fuchsia:
|
case TargetPlatform.fuchsia:
|
||||||
|
@ -291,6 +291,7 @@ abstract class RenderToggleable extends RenderConstrainedBox {
|
|||||||
onChanged(false);
|
onChanged(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
sendSemanticsEvent(const TapSemanticEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleTapUp(TapUpDetails details) {
|
void _handleTapUp(TapUpDetails details) {
|
||||||
|
@ -2186,6 +2186,25 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
|
|||||||
// Nothing to do by default.
|
// Nothing to do by default.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sends a [SemanticsEvent] associated with this render object's [SemanticsNode].
|
||||||
|
///
|
||||||
|
/// If this render object has no semantics information, the first parent
|
||||||
|
/// render object with a non-null semantic node is used.
|
||||||
|
///
|
||||||
|
/// If semantics are disabled, no events are dispatched.
|
||||||
|
///
|
||||||
|
/// See [SemanticsNode.sendEvent] for a full description of the behavior.
|
||||||
|
void sendSemanticsEvent(SemanticsEvent semanticsEvent) {
|
||||||
|
if (owner.semanticsOwner == null)
|
||||||
|
return;
|
||||||
|
if (_semantics != null) {
|
||||||
|
_semantics.sendEvent(semanticsEvent);
|
||||||
|
} else if (parent != null) {
|
||||||
|
final RenderObject renderParent = parent;
|
||||||
|
renderParent.sendSemanticsEvent(semanticsEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Use [_semanticsConfiguration] to access.
|
// Use [_semanticsConfiguration] to access.
|
||||||
SemanticsConfiguration _cachedSemanticsConfiguration;
|
SemanticsConfiguration _cachedSemanticsConfiguration;
|
||||||
|
|
||||||
|
@ -107,3 +107,29 @@ class TooltipSemanticsEvent extends SemanticsEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An event which triggers long press semantic feedback.
|
||||||
|
///
|
||||||
|
/// Currently only honored on Android. Triggers a long-press specific sound
|
||||||
|
/// when TalkBack is enabled.
|
||||||
|
class LongPressSemanticsEvent extends SemanticsEvent {
|
||||||
|
|
||||||
|
/// Constructs an event that triggers a long-press semantic feedback by the platform.
|
||||||
|
const LongPressSemanticsEvent() : super('longPress');
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> getDataMap() => const <String, dynamic>{};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An event which triggers tap semantic feedback.
|
||||||
|
///
|
||||||
|
/// Currently only honored on Android. Triggers a tap specific sound when
|
||||||
|
/// TalkBack is enabled.
|
||||||
|
class TapSemanticEvent extends SemanticsEvent {
|
||||||
|
|
||||||
|
/// Constructs an event that triggers a long-press semantic feedback by the platform.
|
||||||
|
const TapSemanticEvent() : super('tap');
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> getDataMap() => const <String, dynamic>{};
|
||||||
|
}
|
||||||
|
@ -34,7 +34,8 @@ class SemanticsService {
|
|||||||
|
|
||||||
/// Sends a semantic announcement of a tooltip.
|
/// Sends a semantic announcement of a tooltip.
|
||||||
///
|
///
|
||||||
/// This is only used by Android.
|
/// Currently only honored on Android. The contents of [message] will be
|
||||||
|
/// read by TalkBack.
|
||||||
static Future<Null> tooltip(String message) async {
|
static Future<Null> tooltip(String message) async {
|
||||||
final TooltipSemanticsEvent event = new TooltipSemanticsEvent(message);
|
final TooltipSemanticsEvent event = new TooltipSemanticsEvent(message);
|
||||||
await SystemChannels.accessibility.send(event.toMap());
|
await SystemChannels.accessibility.send(event.toMap());
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
@ -200,4 +201,44 @@ void main() {
|
|||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(checkBoxValue, null);
|
expect(checkBoxValue, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('has semantic events', (WidgetTester tester) async {
|
||||||
|
dynamic semanticEvent;
|
||||||
|
bool checkboxValue = false;
|
||||||
|
SystemChannels.accessibility.setMockMessageHandler((dynamic message) {
|
||||||
|
semanticEvent = message;
|
||||||
|
});
|
||||||
|
final SemanticsTester semanticsTester = new SemanticsTester(tester);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
new Material(
|
||||||
|
child: new StatefulBuilder(
|
||||||
|
builder: (BuildContext context, StateSetter setState) {
|
||||||
|
return new Checkbox(
|
||||||
|
value: checkboxValue,
|
||||||
|
onChanged: (bool value) {
|
||||||
|
setState(() {
|
||||||
|
checkboxValue = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.tap(find.byType(Checkbox));
|
||||||
|
final RenderObject object = tester.firstRenderObject(find.byType(Checkbox));
|
||||||
|
|
||||||
|
expect(checkboxValue, true);
|
||||||
|
expect(semanticEvent, <String, dynamic>{
|
||||||
|
'type': 'tap',
|
||||||
|
'nodeId': object.debugSemantics.id,
|
||||||
|
'data': <String, dynamic>{},
|
||||||
|
});
|
||||||
|
expect(object.debugSemantics.getSemanticsData().hasAction(SemanticsAction.tap), true);
|
||||||
|
|
||||||
|
SystemChannels.accessibility.setMockMessageHandler(null);
|
||||||
|
semanticsTester.dispose();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,13 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
import '../widgets/semantics_tester.dart';
|
||||||
import 'feedback_tester.dart';
|
import 'feedback_tester.dart';
|
||||||
|
|
||||||
void main () {
|
void main () {
|
||||||
@ -21,8 +25,23 @@ void main () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
group('Feedback on Android', () {
|
group('Feedback on Android', () {
|
||||||
|
List<Map<String, Object>> semanticEvents;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
semanticEvents = <Map<String, Object>>[];
|
||||||
|
SystemChannels.accessibility.setMockMessageHandler((dynamic message) {
|
||||||
|
final Map<dynamic, dynamic> typedMessage = message;
|
||||||
|
semanticEvents.add(typedMessage.cast<String, Object>());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() {
|
||||||
|
SystemChannels.accessibility.setMockMessageHandler(null);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('forTap', (WidgetTester tester) async {
|
testWidgets('forTap', (WidgetTester tester) async {
|
||||||
|
final SemanticsTester semanticsTester = new SemanticsTester(tester);
|
||||||
|
|
||||||
await tester.pumpWidget(new TestWidget(
|
await tester.pumpWidget(new TestWidget(
|
||||||
tapHandler: (BuildContext context) {
|
tapHandler: (BuildContext context) {
|
||||||
return () => Feedback.forTap(context);
|
return () => Feedback.forTap(context);
|
||||||
@ -31,14 +50,27 @@ void main () {
|
|||||||
await tester.pumpAndSettle(kWaitDuration);
|
await tester.pumpAndSettle(kWaitDuration);
|
||||||
expect(feedback.hapticCount, 0);
|
expect(feedback.hapticCount, 0);
|
||||||
expect(feedback.clickSoundCount, 0);
|
expect(feedback.clickSoundCount, 0);
|
||||||
|
expect(semanticEvents, isEmpty);
|
||||||
|
|
||||||
await tester.tap(find.text('X'));
|
await tester.tap(find.text('X'));
|
||||||
await tester.pumpAndSettle(kWaitDuration);
|
await tester.pumpAndSettle(kWaitDuration);
|
||||||
|
final RenderObject object = tester.firstRenderObject(find.byType(GestureDetector));
|
||||||
|
|
||||||
expect(feedback.hapticCount, 0);
|
expect(feedback.hapticCount, 0);
|
||||||
expect(feedback.clickSoundCount, 1);
|
expect(feedback.clickSoundCount, 1);
|
||||||
|
expect(semanticEvents.single, <String, dynamic>{
|
||||||
|
'type': 'tap',
|
||||||
|
'nodeId': object.debugSemantics.id,
|
||||||
|
'data': <String, dynamic>{},
|
||||||
|
});
|
||||||
|
expect(object.debugSemantics.getSemanticsData().hasAction(SemanticsAction.tap), true);
|
||||||
|
|
||||||
|
semanticsTester.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('forTap Wrapper', (WidgetTester tester) async {
|
testWidgets('forTap Wrapper', (WidgetTester tester) async {
|
||||||
|
final SemanticsTester semanticsTester = new SemanticsTester(tester);
|
||||||
|
|
||||||
int callbackCount = 0;
|
int callbackCount = 0;
|
||||||
final VoidCallback callback = () {
|
final VoidCallback callback = () {
|
||||||
callbackCount++;
|
callbackCount++;
|
||||||
@ -56,12 +88,24 @@ void main () {
|
|||||||
|
|
||||||
await tester.tap(find.text('X'));
|
await tester.tap(find.text('X'));
|
||||||
await tester.pumpAndSettle(kWaitDuration);
|
await tester.pumpAndSettle(kWaitDuration);
|
||||||
|
final RenderObject object = tester.firstRenderObject(find.byType(GestureDetector));
|
||||||
|
|
||||||
expect(feedback.hapticCount, 0);
|
expect(feedback.hapticCount, 0);
|
||||||
expect(feedback.clickSoundCount, 1);
|
expect(feedback.clickSoundCount, 1);
|
||||||
expect(callbackCount, 1);
|
expect(callbackCount, 1);
|
||||||
|
expect(semanticEvents.single, <String, dynamic>{
|
||||||
|
'type': 'tap',
|
||||||
|
'nodeId': object.debugSemantics.id,
|
||||||
|
'data': <String, dynamic>{},
|
||||||
|
});
|
||||||
|
expect(object.debugSemantics.getSemanticsData().hasAction(SemanticsAction.tap), true);
|
||||||
|
|
||||||
|
semanticsTester.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('forLongPress', (WidgetTester tester) async {
|
testWidgets('forLongPress', (WidgetTester tester) async {
|
||||||
|
final SemanticsTester semanticsTester = new SemanticsTester(tester);
|
||||||
|
|
||||||
await tester.pumpWidget(new TestWidget(
|
await tester.pumpWidget(new TestWidget(
|
||||||
longPressHandler: (BuildContext context) {
|
longPressHandler: (BuildContext context) {
|
||||||
return () => Feedback.forLongPress(context);
|
return () => Feedback.forLongPress(context);
|
||||||
@ -73,11 +117,22 @@ void main () {
|
|||||||
|
|
||||||
await tester.longPress(find.text('X'));
|
await tester.longPress(find.text('X'));
|
||||||
await tester.pumpAndSettle(kWaitDuration);
|
await tester.pumpAndSettle(kWaitDuration);
|
||||||
|
final RenderObject object = tester.firstRenderObject(find.byType(GestureDetector));
|
||||||
|
|
||||||
expect(feedback.hapticCount, 1);
|
expect(feedback.hapticCount, 1);
|
||||||
expect(feedback.clickSoundCount, 0);
|
expect(feedback.clickSoundCount, 0);
|
||||||
|
expect(semanticEvents.single, <String, dynamic>{
|
||||||
|
'type': 'longPress',
|
||||||
|
'nodeId': object.debugSemantics.id,
|
||||||
|
'data': <String, dynamic>{},
|
||||||
|
});
|
||||||
|
expect(object.debugSemantics.getSemanticsData().hasAction(SemanticsAction.longPress), true);
|
||||||
|
|
||||||
|
semanticsTester.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('forLongPress Wrapper', (WidgetTester tester) async {
|
testWidgets('forLongPress Wrapper', (WidgetTester tester) async {
|
||||||
|
final SemanticsTester semanticsTester = new SemanticsTester(tester);
|
||||||
int callbackCount = 0;
|
int callbackCount = 0;
|
||||||
final VoidCallback callback = () {
|
final VoidCallback callback = () {
|
||||||
callbackCount++;
|
callbackCount++;
|
||||||
@ -89,6 +144,8 @@ void main () {
|
|||||||
},
|
},
|
||||||
));
|
));
|
||||||
await tester.pumpAndSettle(kWaitDuration);
|
await tester.pumpAndSettle(kWaitDuration);
|
||||||
|
final RenderObject object = tester.firstRenderObject(find.byType(GestureDetector));
|
||||||
|
|
||||||
expect(feedback.hapticCount, 0);
|
expect(feedback.hapticCount, 0);
|
||||||
expect(feedback.clickSoundCount, 0);
|
expect(feedback.clickSoundCount, 0);
|
||||||
expect(callbackCount, 0);
|
expect(callbackCount, 0);
|
||||||
@ -98,6 +155,14 @@ void main () {
|
|||||||
expect(feedback.hapticCount, 1);
|
expect(feedback.hapticCount, 1);
|
||||||
expect(feedback.clickSoundCount, 0);
|
expect(feedback.clickSoundCount, 0);
|
||||||
expect(callbackCount, 1);
|
expect(callbackCount, 1);
|
||||||
|
expect(semanticEvents.single, <String, dynamic>{
|
||||||
|
'type': 'longPress',
|
||||||
|
'nodeId': object.debugSemantics.id,
|
||||||
|
'data': <String, dynamic>{},
|
||||||
|
});
|
||||||
|
expect(object.debugSemantics.getSemanticsData().hasAction(SemanticsAction.longPress), true);
|
||||||
|
|
||||||
|
semanticsTester.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
@ -182,4 +183,40 @@ void main() {
|
|||||||
|
|
||||||
semantics.dispose();
|
semantics.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('has semantic events', (WidgetTester tester) async {
|
||||||
|
final SemanticsTester semantics = new SemanticsTester(tester);
|
||||||
|
final Key key = new UniqueKey();
|
||||||
|
dynamic semanticEvent;
|
||||||
|
int radioValue = 2;
|
||||||
|
SystemChannels.accessibility.setMockMessageHandler((dynamic message) {
|
||||||
|
semanticEvent = message;
|
||||||
|
});
|
||||||
|
|
||||||
|
await tester.pumpWidget(new Material(
|
||||||
|
child: new Radio<int>(
|
||||||
|
key: key,
|
||||||
|
value: 1,
|
||||||
|
groupValue: radioValue,
|
||||||
|
onChanged: (int i) {
|
||||||
|
radioValue = i;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
await tester.tap(find.byKey(key));
|
||||||
|
final RenderObject object = tester.firstRenderObject(find.byKey(key));
|
||||||
|
|
||||||
|
expect(radioValue, 1);
|
||||||
|
expect(semanticEvent, <String, dynamic>{
|
||||||
|
'type': 'tap',
|
||||||
|
'nodeId': object.debugSemantics.id,
|
||||||
|
'data': <String, dynamic>{},
|
||||||
|
});
|
||||||
|
expect(object.debugSemantics.getSemanticsData().hasAction(SemanticsAction.tap), true);
|
||||||
|
|
||||||
|
semantics.dispose();
|
||||||
|
SystemChannels.accessibility.setMockMessageHandler(null);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,9 +4,11 @@
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
import '../rendering/mock_canvas.dart';
|
import '../rendering/mock_canvas.dart';
|
||||||
|
import '../widgets/semantics_tester.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('Switch can toggle on tap', (WidgetTester tester) async {
|
testWidgets('Switch can toggle on tap', (WidgetTester tester) async {
|
||||||
@ -228,4 +230,48 @@ void main() {
|
|||||||
expect(value, isTrue);
|
expect(value, isTrue);
|
||||||
expect(tester.hasRunningAnimations, false);
|
expect(tester.hasRunningAnimations, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('switch has semantic events', (WidgetTester tester) async {
|
||||||
|
dynamic semanticEvent;
|
||||||
|
bool value = false;
|
||||||
|
SystemChannels.accessibility.setMockMessageHandler((dynamic message) {
|
||||||
|
semanticEvent = message;
|
||||||
|
});
|
||||||
|
final SemanticsTester semanticsTester = new SemanticsTester(tester);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new StatefulBuilder(
|
||||||
|
builder: (BuildContext context, StateSetter setState) {
|
||||||
|
return new Material(
|
||||||
|
child: new Center(
|
||||||
|
child: new Switch(
|
||||||
|
value: value,
|
||||||
|
onChanged: (bool newValue) {
|
||||||
|
setState(() {
|
||||||
|
value = newValue;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.tap(find.byType(Switch));
|
||||||
|
final RenderObject object = tester.firstRenderObject(find.byType(Switch));
|
||||||
|
|
||||||
|
expect(value, true);
|
||||||
|
expect(semanticEvent, <String, dynamic>{
|
||||||
|
'type': 'tap',
|
||||||
|
'nodeId': object.debugSemantics.id,
|
||||||
|
'data': <String, dynamic>{},
|
||||||
|
});
|
||||||
|
expect(object.debugSemantics.getSemanticsData().hasAction(SemanticsAction.tap), true);
|
||||||
|
|
||||||
|
semanticsTester.dispose();
|
||||||
|
SystemChannels.accessibility.setMockMessageHandler(null);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import 'dart:ui';
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -676,4 +677,51 @@ void main() {
|
|||||||
semantics.dispose();
|
semantics.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('has semantic events', (WidgetTester tester) async {
|
||||||
|
final List<dynamic> semanticEvents = <dynamic>[];
|
||||||
|
SystemChannels.accessibility.setMockMessageHandler((dynamic message) {
|
||||||
|
semanticEvents.add(message);
|
||||||
|
});
|
||||||
|
final SemanticsTester semantics = new SemanticsTester(tester);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
new MaterialApp(
|
||||||
|
home: new Center(
|
||||||
|
child: new Tooltip(
|
||||||
|
message: 'Foo',
|
||||||
|
child: new Container(
|
||||||
|
width: 100.0,
|
||||||
|
height: 100.0,
|
||||||
|
color: Colors.green[500],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.longPress(find.byType(Tooltip));
|
||||||
|
final RenderObject object = tester.firstRenderObject(find.byType(Tooltip));
|
||||||
|
|
||||||
|
expect(semanticEvents, unorderedEquals(<dynamic>[
|
||||||
|
<String, dynamic>{
|
||||||
|
'type': 'longPress',
|
||||||
|
'nodeId': findDebugSemantics(object).id,
|
||||||
|
'data': <String, dynamic>{},
|
||||||
|
},
|
||||||
|
<String, dynamic>{
|
||||||
|
'type': 'tooltip',
|
||||||
|
'data': <String, dynamic>{
|
||||||
|
'message': 'Foo',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]));
|
||||||
|
semantics.dispose();
|
||||||
|
SystemChannels.accessibility.setMockMessageHandler(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
SemanticsNode findDebugSemantics(RenderObject object) {
|
||||||
|
if (object.debugSemantics != null)
|
||||||
|
return object.debugSemantics;
|
||||||
|
return findDebugSemantics(object.parent);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user