Unverified Commit 816ae4b1 authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Include platformViewId in semantics tree (#28953)

parent 126c58ef
...@@ -7,6 +7,7 @@ import 'dart:ui'; ...@@ -7,6 +7,7 @@ import 'dart:ui';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/semantics.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'box.dart'; import 'box.dart';
...@@ -87,6 +88,7 @@ class RenderAndroidView extends RenderBox { ...@@ -87,6 +88,7 @@ class RenderAndroidView extends RenderBox {
_viewController = viewController { _viewController = viewController {
_motionEventsDispatcher = _MotionEventsDispatcher(globalToLocal, viewController); _motionEventsDispatcher = _MotionEventsDispatcher(globalToLocal, viewController);
updateGestureRecognizers(gestureRecognizers); updateGestureRecognizers(gestureRecognizers);
_viewController.addOnPlatformViewCreatedListener(_onPlatformViewCreated);
} }
_PlatformViewState _state = _PlatformViewState.uninitialized; _PlatformViewState _state = _PlatformViewState.uninitialized;
...@@ -99,10 +101,20 @@ class RenderAndroidView extends RenderBox { ...@@ -99,10 +101,20 @@ class RenderAndroidView extends RenderBox {
/// `viewController` must not be null. /// `viewController` must not be null.
set viewController(AndroidViewController viewController) { set viewController(AndroidViewController viewController) {
assert(_viewController != null); assert(_viewController != null);
assert(viewController != null);
if (_viewController == viewController) if (_viewController == viewController)
return; return;
_viewController.removeOnPlatformViewCreatedListener(_onPlatformViewCreated);
_viewController = viewController; _viewController = viewController;
_sizePlatformView(); _sizePlatformView();
if (_viewController.isCreated) {
markNeedsSemanticsUpdate();
}
_viewController.addOnPlatformViewCreatedListener(_onPlatformViewCreated);
}
void _onPlatformViewCreated(int id) {
markNeedsSemanticsUpdate();
} }
/// How to behave during hit testing. /// How to behave during hit testing.
...@@ -235,6 +247,17 @@ class RenderAndroidView extends RenderBox { ...@@ -235,6 +247,17 @@ class RenderAndroidView extends RenderBox {
} }
} }
@override
void describeSemanticsConfiguration (SemanticsConfiguration config) {
super.describeSemanticsConfiguration(config);
config.isSemanticBoundary = true;
if (_viewController.isCreated) {
config.platformViewId = _viewController.id;
}
}
@override @override
void detach() { void detach() {
_gestureRecognizer.reset(); _gestureRecognizer.reset();
...@@ -286,9 +309,9 @@ class RenderUiKitView extends RenderBox { ...@@ -286,9 +309,9 @@ class RenderUiKitView extends RenderBox {
/// must have been created by calling [PlatformViewsService.initUiKitView]. /// must have been created by calling [PlatformViewsService.initUiKitView].
UiKitViewController get viewController => _viewController; UiKitViewController get viewController => _viewController;
UiKitViewController _viewController; UiKitViewController _viewController;
set viewController(UiKitViewController viewId) { set viewController(UiKitViewController viewController) {
assert(viewId != null); assert(viewController != null);
_viewController = viewId; _viewController = viewController;
markNeedsPaint(); markNeedsPaint();
} }
......
...@@ -196,6 +196,7 @@ class SemanticsData extends Diagnosticable { ...@@ -196,6 +196,7 @@ class SemanticsData extends Diagnosticable {
@required this.scrollPosition, @required this.scrollPosition,
@required this.scrollExtentMax, @required this.scrollExtentMax,
@required this.scrollExtentMin, @required this.scrollExtentMin,
@required this.platformViewId,
this.tags, this.tags,
this.transform, this.transform,
this.customSemanticsActionIds, this.customSemanticsActionIds,
...@@ -295,6 +296,19 @@ class SemanticsData extends Diagnosticable { ...@@ -295,6 +296,19 @@ class SemanticsData extends Diagnosticable {
/// * [ScrollPosition.minScrollExtent], from where this value is usually taken. /// * [ScrollPosition.minScrollExtent], from where this value is usually taken.
final double scrollExtentMin; final double scrollExtentMin;
/// The id of the platform view, whose semantics nodes will be added as
/// children to this node.
///
/// If this value is non-null, the SemanticsNode must not have any children
/// as those would be replaced by the semantics nodes of the referenced
/// platform view.
///
/// See also:
///
/// * [AndroidView], which is the platform view for Android.
/// * [UiKitView], which is the platform view for iOS.
final int platformViewId;
/// The bounding box for this node in its coordinate system. /// The bounding box for this node in its coordinate system.
final Rect rect; final Rect rect;
...@@ -374,6 +388,7 @@ class SemanticsData extends Diagnosticable { ...@@ -374,6 +388,7 @@ class SemanticsData extends Diagnosticable {
properties.add(EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null)); properties.add(EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
if (textSelection?.isValid == true) if (textSelection?.isValid == true)
properties.add(MessageProperty('textSelection', '[${textSelection.start}, ${textSelection.end}]')); properties.add(MessageProperty('textSelection', '[${textSelection.start}, ${textSelection.end}]'));
properties.add(IntProperty('platformViewId', platformViewId, defaultValue: null));
properties.add(IntProperty('scrollChildren', scrollChildCount, defaultValue: null)); properties.add(IntProperty('scrollChildren', scrollChildCount, defaultValue: null));
properties.add(IntProperty('scrollIndex', scrollIndex, defaultValue: null)); properties.add(IntProperty('scrollIndex', scrollIndex, defaultValue: null));
properties.add(DoubleProperty('scrollExtentMin', scrollExtentMin, defaultValue: null)); properties.add(DoubleProperty('scrollExtentMin', scrollExtentMin, defaultValue: null));
...@@ -402,6 +417,7 @@ class SemanticsData extends Diagnosticable { ...@@ -402,6 +417,7 @@ class SemanticsData extends Diagnosticable {
&& typedOther.scrollPosition == scrollPosition && typedOther.scrollPosition == scrollPosition
&& typedOther.scrollExtentMax == scrollExtentMax && typedOther.scrollExtentMax == scrollExtentMax
&& typedOther.scrollExtentMin == scrollExtentMin && typedOther.scrollExtentMin == scrollExtentMin
&& typedOther.platformViewId == platformViewId
&& typedOther.transform == transform && typedOther.transform == transform
&& typedOther.elevation == elevation && typedOther.elevation == elevation
&& typedOther.thickness == thickness && typedOther.thickness == thickness
...@@ -411,25 +427,28 @@ class SemanticsData extends Diagnosticable { ...@@ -411,25 +427,28 @@ class SemanticsData extends Diagnosticable {
@override @override
int get hashCode { int get hashCode {
return ui.hashValues( return ui.hashValues(
flags, ui.hashValues(
actions, flags,
label, actions,
value, label,
increasedValue, value,
decreasedValue, increasedValue,
hint, decreasedValue,
textDirection, hint,
rect, textDirection,
tags, rect,
textSelection, tags,
scrollChildCount, textSelection,
scrollIndex, scrollChildCount,
scrollPosition, scrollIndex,
scrollExtentMax, scrollPosition,
scrollExtentMin, scrollExtentMax,
transform, scrollExtentMin,
elevation, platformViewId,
thickness, transform,
elevation,
thickness,
),
ui.hashList(customSemanticsActionIds), ui.hashList(customSemanticsActionIds),
); );
} }
...@@ -1464,6 +1483,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1464,6 +1483,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
_scrollExtentMin != config._scrollExtentMin || _scrollExtentMin != config._scrollExtentMin ||
_actionsAsBits != config._actionsAsBits || _actionsAsBits != config._actionsAsBits ||
indexInParent != config.indexInParent || indexInParent != config.indexInParent ||
platformViewId != config.platformViewId ||
_mergeAllDescendantsIntoThisNode != config.isMergingSemanticsOfDescendants; _mergeAllDescendantsIntoThisNode != config.isMergingSemanticsOfDescendants;
} }
...@@ -1663,6 +1683,20 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1663,6 +1683,20 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
double get scrollExtentMin => _scrollExtentMin; double get scrollExtentMin => _scrollExtentMin;
double _scrollExtentMin; double _scrollExtentMin;
/// The id of the platform view, whose semantics nodes will be added as
/// children to this node.
///
/// If this value is non-null, the SemanticsNode must not have any children
/// as those would be replaced by the semantics nodes of the referenced
/// platform view.
///
/// See also:
///
/// * [AndroidView], which is the platform view for Android.
/// * [UiKitView], which is the platform view for iOS.
int get platformViewId => _platformViewId;
int _platformViewId;
bool _canPerformAction(SemanticsAction action) => _actions.containsKey(action); bool _canPerformAction(SemanticsAction action) => _actions.containsKey(action);
static final SemanticsConfiguration _kEmptyConfig = SemanticsConfiguration(); static final SemanticsConfiguration _kEmptyConfig = SemanticsConfiguration();
...@@ -1684,6 +1718,11 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1684,6 +1718,11 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
if (_isDifferentFromCurrentSemanticAnnotation(config)) if (_isDifferentFromCurrentSemanticAnnotation(config))
_markDirty(); _markDirty();
assert(
config.platformViewId == null || childrenInInversePaintOrder.isEmpty,
'SemanticsNodes with children must not specify a platformViewId.'
);
_label = config.label; _label = config.label;
_decreasedValue = config.decreasedValue; _decreasedValue = config.decreasedValue;
_value = config.value; _value = config.value;
...@@ -1706,6 +1745,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1706,6 +1745,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
_scrollChildCount = config.scrollChildCount; _scrollChildCount = config.scrollChildCount;
_scrollIndex = config.scrollIndex; _scrollIndex = config.scrollIndex;
indexInParent = config.indexInParent; indexInParent = config.indexInParent;
_platformViewId = config._platformViewId;
_replaceChildren(childrenInInversePaintOrder ?? const <SemanticsNode>[]); _replaceChildren(childrenInInversePaintOrder ?? const <SemanticsNode>[]);
assert( assert(
...@@ -1740,6 +1780,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1740,6 +1780,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
double scrollPosition = _scrollPosition; double scrollPosition = _scrollPosition;
double scrollExtentMax = _scrollExtentMax; double scrollExtentMax = _scrollExtentMax;
double scrollExtentMin = _scrollExtentMin; double scrollExtentMin = _scrollExtentMin;
int platformViewId = _platformViewId;
final double elevation = _elevation; final double elevation = _elevation;
double thickness = _thickness; double thickness = _thickness;
final Set<int> customSemanticsActionIds = <int>{}; final Set<int> customSemanticsActionIds = <int>{};
...@@ -1774,6 +1815,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1774,6 +1815,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
scrollPosition ??= node._scrollPosition; scrollPosition ??= node._scrollPosition;
scrollExtentMax ??= node._scrollExtentMax; scrollExtentMax ??= node._scrollExtentMax;
scrollExtentMin ??= node._scrollExtentMin; scrollExtentMin ??= node._scrollExtentMin;
platformViewId ??= node._platformViewId;
if (value == '' || value == null) if (value == '' || value == null)
value = node._value; value = node._value;
if (increasedValue == '' || increasedValue == null) if (increasedValue == '' || increasedValue == null)
...@@ -1843,6 +1885,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1843,6 +1885,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
scrollPosition: scrollPosition, scrollPosition: scrollPosition,
scrollExtentMax: scrollExtentMax, scrollExtentMax: scrollExtentMax,
scrollExtentMin: scrollExtentMin, scrollExtentMin: scrollExtentMin,
platformViewId: platformViewId,
customSemanticsActionIds: customSemanticsActionIds.toList()..sort(), customSemanticsActionIds: customSemanticsActionIds.toList()..sort(),
); );
} }
...@@ -1898,6 +1941,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1898,6 +1941,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
textDirection: data.textDirection, textDirection: data.textDirection,
textSelectionBase: data.textSelection != null ? data.textSelection.baseOffset : -1, textSelectionBase: data.textSelection != null ? data.textSelection.baseOffset : -1,
textSelectionExtent: data.textSelection != null ? data.textSelection.extentOffset : -1, textSelectionExtent: data.textSelection != null ? data.textSelection.extentOffset : -1,
platformViewId: data.platformViewId != null ? data.platformViewId : -1,
scrollChildren: data.scrollChildCount != null ? data.scrollChildCount : 0, scrollChildren: data.scrollChildCount != null ? data.scrollChildCount : 0,
scrollIndex: data.scrollIndex != null ? data.scrollIndex : 0 , scrollIndex: data.scrollIndex != null ? data.scrollIndex : 0 ,
scrollPosition: data.scrollPosition != null ? data.scrollPosition : double.nan, scrollPosition: data.scrollPosition != null ? data.scrollPosition : double.nan,
...@@ -2038,6 +2082,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -2038,6 +2082,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
properties.add(DiagnosticsProperty<SemanticsSortKey>('sortKey', sortKey, defaultValue: null)); properties.add(DiagnosticsProperty<SemanticsSortKey>('sortKey', sortKey, defaultValue: null));
if (_textSelection?.isValid == true) if (_textSelection?.isValid == true)
properties.add(MessageProperty('text selection', '[${_textSelection.start}, ${_textSelection.end}]')); properties.add(MessageProperty('text selection', '[${_textSelection.start}, ${_textSelection.end}]'));
properties.add(IntProperty('platformViewId', platformViewId, defaultValue: null));
properties.add(IntProperty('scrollChildren', scrollChildCount, defaultValue: null)); properties.add(IntProperty('scrollChildren', scrollChildCount, defaultValue: null));
properties.add(IntProperty('scrollIndex', scrollIndex, defaultValue: null)); properties.add(IntProperty('scrollIndex', scrollIndex, defaultValue: null));
properties.add(DoubleProperty('scrollExtentMin', scrollExtentMin, defaultValue: null)); properties.add(DoubleProperty('scrollExtentMin', scrollExtentMin, defaultValue: null));
...@@ -3100,6 +3145,16 @@ class SemanticsConfiguration { ...@@ -3100,6 +3145,16 @@ class SemanticsConfiguration {
_hasBeenAnnotated = true; _hasBeenAnnotated = true;
} }
/// The id of the platform view, whose semantics nodes will be added as
/// children to this node.
int get platformViewId => _platformViewId;
int _platformViewId;
set platformViewId(int value) {
if (value == platformViewId)
return;
_platformViewId = value;
_hasBeenAnnotated = true;
}
/// Whether the semantic information provided by the owning [RenderObject] and /// Whether the semantic information provided by the owning [RenderObject] and
/// all of its descendants should be treated as one logical entity. /// all of its descendants should be treated as one logical entity.
...@@ -3583,6 +3638,9 @@ class SemanticsConfiguration { ...@@ -3583,6 +3638,9 @@ class SemanticsConfiguration {
return false; return false;
if ((_flags & other._flags) != 0) if ((_flags & other._flags) != 0)
return false; return false;
if (_platformViewId != null && other._platformViewId != null) {
return false;
}
if (_value != null && _value.isNotEmpty && other._value != null && other._value.isNotEmpty) if (_value != null && _value.isNotEmpty && other._value != null && other._value.isNotEmpty)
return false; return false;
return true; return true;
...@@ -3617,6 +3675,7 @@ class SemanticsConfiguration { ...@@ -3617,6 +3675,7 @@ class SemanticsConfiguration {
_indexInParent ??= child.indexInParent; _indexInParent ??= child.indexInParent;
_scrollIndex ??= child._scrollIndex; _scrollIndex ??= child._scrollIndex;
_scrollChildCount ??= child._scrollChildCount; _scrollChildCount ??= child._scrollChildCount;
_platformViewId ??= child._platformViewId;
textDirection ??= child.textDirection; textDirection ??= child.textDirection;
_sortKey ??= child._sortKey; _sortKey ??= child._sortKey;
...@@ -3672,6 +3731,7 @@ class SemanticsConfiguration { ...@@ -3672,6 +3731,7 @@ class SemanticsConfiguration {
.._indexInParent = indexInParent .._indexInParent = indexInParent
.._scrollIndex = _scrollIndex .._scrollIndex = _scrollIndex
.._scrollChildCount = _scrollChildCount .._scrollChildCount = _scrollChildCount
.._platformViewId = _platformViewId
.._actions.addAll(_actions) .._actions.addAll(_actions)
.._customSemanticsActions.addAll(_customSemanticsActions); .._customSemanticsActions.addAll(_customSemanticsActions);
} }
......
...@@ -25,6 +25,8 @@ final PlatformViewsRegistry platformViewsRegistry = PlatformViewsRegistry._insta ...@@ -25,6 +25,8 @@ final PlatformViewsRegistry platformViewsRegistry = PlatformViewsRegistry._insta
class PlatformViewsRegistry { class PlatformViewsRegistry {
PlatformViewsRegistry._instance(); PlatformViewsRegistry._instance();
// Always non-negative. The id value -1 is used in the accessibility bridge
// to indicate the absence of a platform view.
int _nextPlatformViewId = 0; int _nextPlatformViewId = 0;
/// Allocates a unique identifier for a platform view. /// Allocates a unique identifier for a platform view.
...@@ -77,7 +79,6 @@ class PlatformViewsService { ...@@ -77,7 +79,6 @@ class PlatformViewsService {
@required TextDirection layoutDirection, @required TextDirection layoutDirection,
dynamic creationParams, dynamic creationParams,
MessageCodec<dynamic> creationParamsCodec, MessageCodec<dynamic> creationParamsCodec,
PlatformViewCreatedCallback onPlatformViewCreated,
}) { }) {
assert(id != null); assert(id != null);
assert(viewType != null); assert(viewType != null);
...@@ -89,7 +90,6 @@ class PlatformViewsService { ...@@ -89,7 +90,6 @@ class PlatformViewsService {
creationParams, creationParams,
creationParamsCodec, creationParamsCodec,
layoutDirection, layoutDirection,
onPlatformViewCreated,
); );
} }
...@@ -406,7 +406,6 @@ class AndroidViewController { ...@@ -406,7 +406,6 @@ class AndroidViewController {
dynamic creationParams, dynamic creationParams,
MessageCodec<dynamic> creationParamsCodec, MessageCodec<dynamic> creationParamsCodec,
TextDirection layoutDirection, TextDirection layoutDirection,
PlatformViewCreatedCallback onPlatformViewCreated,
) : assert(id != null), ) : assert(id != null),
assert(viewType != null), assert(viewType != null),
assert(layoutDirection != null), assert(layoutDirection != null),
...@@ -415,7 +414,6 @@ class AndroidViewController { ...@@ -415,7 +414,6 @@ class AndroidViewController {
_creationParams = creationParams, _creationParams = creationParams,
_creationParamsCodec = creationParamsCodec, _creationParamsCodec = creationParamsCodec,
_layoutDirection = layoutDirection, _layoutDirection = layoutDirection,
_onPlatformViewCreated = onPlatformViewCreated,
_state = _AndroidViewState.waitingForSize; _state = _AndroidViewState.waitingForSize;
/// Action code for when a primary pointer touched the screen. /// Action code for when a primary pointer touched the screen.
...@@ -459,8 +457,6 @@ class AndroidViewController { ...@@ -459,8 +457,6 @@ class AndroidViewController {
final String _viewType; final String _viewType;
final PlatformViewCreatedCallback _onPlatformViewCreated;
/// The texture entry id into which the Android view is rendered. /// The texture entry id into which the Android view is rendered.
int _textureId; int _textureId;
...@@ -478,6 +474,25 @@ class AndroidViewController { ...@@ -478,6 +474,25 @@ class AndroidViewController {
MessageCodec<dynamic> _creationParamsCodec; MessageCodec<dynamic> _creationParamsCodec;
final List<PlatformViewCreatedCallback> _platformViewCreatedCallbacks = <PlatformViewCreatedCallback>[];
/// Whether the platform view has already been created.
bool get isCreated => _state == _AndroidViewState.created;
/// Adds a callback that will get invoke after the platform view has been
/// created.
void addOnPlatformViewCreatedListener(PlatformViewCreatedCallback listener) {
assert(listener != null);
assert(_state != _AndroidViewState.disposed);
_platformViewCreatedCallbacks.add(listener);
}
/// Removes a callback added with [addOnPlatformViewCreatedListener].
void removeOnPlatformViewCreatedListener(PlatformViewCreatedCallback listener) {
assert(_state != _AndroidViewState.disposed);
_platformViewCreatedCallbacks.remove(listener);
}
/// Disposes the Android view. /// Disposes the Android view.
/// ///
/// The [AndroidViewController] object is unusable after calling this. /// The [AndroidViewController] object is unusable after calling this.
...@@ -486,6 +501,7 @@ class AndroidViewController { ...@@ -486,6 +501,7 @@ class AndroidViewController {
Future<void> dispose() async { Future<void> dispose() async {
if (_state == _AndroidViewState.creating || _state == _AndroidViewState.created) if (_state == _AndroidViewState.creating || _state == _AndroidViewState.created)
await SystemChannels.platform_views.invokeMethod<void>('dispose', id); await SystemChannels.platform_views.invokeMethod<void>('dispose', id);
_platformViewCreatedCallbacks.clear();
_state = _AndroidViewState.disposed; _state = _AndroidViewState.disposed;
} }
...@@ -578,9 +594,10 @@ class AndroidViewController { ...@@ -578,9 +594,10 @@ class AndroidViewController {
); );
} }
_textureId = await SystemChannels.platform_views.invokeMethod('create', args); _textureId = await SystemChannels.platform_views.invokeMethod('create', args);
if (_onPlatformViewCreated != null)
_onPlatformViewCreated(id);
_state = _AndroidViewState.created; _state = _AndroidViewState.created;
for (PlatformViewCreatedCallback callback in _platformViewCreatedCallbacks) {
callback(id);
}
} }
} }
......
...@@ -367,10 +367,12 @@ class _AndroidViewState extends State<AndroidView> { ...@@ -367,10 +367,12 @@ class _AndroidViewState extends State<AndroidView> {
id: _id, id: _id,
viewType: widget.viewType, viewType: widget.viewType,
layoutDirection: _layoutDirection, layoutDirection: _layoutDirection,
onPlatformViewCreated: widget.onPlatformViewCreated,
creationParams: widget.creationParams, creationParams: widget.creationParams,
creationParamsCodec: widget.creationParamsCodec, creationParamsCodec: widget.creationParamsCodec,
); );
if (widget.onPlatformViewCreated != null) {
_controller.addOnPlatformViewCreatedListener(widget.onPlatformViewCreated);
}
} }
} }
......
...@@ -346,6 +346,7 @@ void main() { ...@@ -346,6 +346,7 @@ void main() {
' hint: ""\n' ' hint: ""\n'
' textDirection: null\n' ' textDirection: null\n'
' sortKey: null\n' ' sortKey: null\n'
' platformViewId: null\n'
' scrollChildren: null\n' ' scrollChildren: null\n'
' scrollIndex: null\n' ' scrollIndex: null\n'
' scrollExtentMin: null\n' ' scrollExtentMin: null\n'
...@@ -440,6 +441,7 @@ void main() { ...@@ -440,6 +441,7 @@ void main() {
' hint: ""\n' ' hint: ""\n'
' textDirection: null\n' ' textDirection: null\n'
' sortKey: null\n' ' sortKey: null\n'
' platformViewId: null\n'
' scrollChildren: null\n' ' scrollChildren: null\n'
' scrollIndex: null\n' ' scrollIndex: null\n'
' scrollExtentMin: null\n' ' scrollExtentMin: null\n'
......
...@@ -26,6 +26,8 @@ class FakeAndroidPlatformViewsController { ...@@ -26,6 +26,8 @@ class FakeAndroidPlatformViewsController {
Completer<void> resizeCompleter; Completer<void> resizeCompleter;
Completer<void> createCompleter;
void registerViewType(String viewType) { void registerViewType(String viewType) {
_registeredViewTypes.add(viewType); _registeredViewTypes.add(viewType);
} }
...@@ -46,7 +48,7 @@ class FakeAndroidPlatformViewsController { ...@@ -46,7 +48,7 @@ class FakeAndroidPlatformViewsController {
return Future<dynamic>.sync(() => null); return Future<dynamic>.sync(() => null);
} }
Future<dynamic> _create(MethodCall call) { Future<dynamic> _create(MethodCall call) async {
final Map<dynamic, dynamic> args = call.arguments; final Map<dynamic, dynamic> args = call.arguments;
final int id = args['id']; final int id = args['id'];
final String viewType = args['viewType']; final String viewType = args['viewType'];
...@@ -67,6 +69,10 @@ class FakeAndroidPlatformViewsController { ...@@ -67,6 +69,10 @@ class FakeAndroidPlatformViewsController {
message: 'Trying to create a platform view of unregistered type: $viewType', message: 'Trying to create a platform view of unregistered type: $viewType',
); );
if (createCompleter != null) {
await createCompleter.future;
}
_views[id] = FakeAndroidPlatformView(id, viewType, Size(width, height), layoutDirection, creationParams); _views[id] = FakeAndroidPlatformView(id, viewType, Size(width, height), layoutDirection, creationParams);
final int textureId = _textureCounter++; final int textureId = _textureCounter++;
return Future<int>.sync(() => textureId); return Future<int>.sync(() => textureId);
......
...@@ -105,14 +105,20 @@ void main() { ...@@ -105,14 +105,20 @@ void main() {
final PlatformViewCreatedCallback callback = (int id) { createdViews.add(id); }; final PlatformViewCreatedCallback callback = (int id) { createdViews.add(id); };
final AndroidViewController controller1 = PlatformViewsService.initAndroidView( final AndroidViewController controller1 = PlatformViewsService.initAndroidView(
id: 0, viewType: 'webview', layoutDirection: TextDirection.ltr, onPlatformViewCreated: callback); id: 0,
viewType: 'webview',
layoutDirection: TextDirection.ltr,
)..addOnPlatformViewCreatedListener(callback);
expect(createdViews, isEmpty); expect(createdViews, isEmpty);
await controller1.setSize(const Size(100.0, 100.0)); await controller1.setSize(const Size(100.0, 100.0));
expect(createdViews, orderedEquals(<int>[0])); expect(createdViews, orderedEquals(<int>[0]));
final AndroidViewController controller2 = PlatformViewsService.initAndroidView( final AndroidViewController controller2 = PlatformViewsService.initAndroidView(
id: 5, viewType: 'webview', layoutDirection: TextDirection.ltr, onPlatformViewCreated: callback); id: 5,
viewType: 'webview',
layoutDirection: TextDirection.ltr,
)..addOnPlatformViewCreatedListener(callback);
expect(createdViews, orderedEquals(<int>[0])); expect(createdViews, orderedEquals(<int>[0]));
await controller2.setSize(const Size(100.0, 200.0)); await controller2.setSize(const Size(100.0, 200.0));
......
...@@ -806,6 +806,53 @@ void main() { ...@@ -806,6 +806,53 @@ void main() {
expect(factoryInvocationCount, 1); expect(factoryInvocationCount, 1);
}); });
testWidgets('AndroidView has correct semantics', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
expect(currentViewId, greaterThanOrEqualTo(0));
final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController();
viewsController.registerViewType('webview');
viewsController.createCompleter = Completer<void>();
await tester.pumpWidget(
Semantics(
container: true,
child: const Align(
alignment: Alignment.bottomRight,
child: SizedBox(
width: 200.0,
height: 100.0,
child: AndroidView(
viewType: 'webview',
layoutDirection: TextDirection.ltr,
),
),
),
),
);
final SemanticsNode semantics = tester.getSemantics(find.byType(AndroidView));
// Platform view has not been created yet, no platformViewId.
expect(semantics.platformViewId, null);
expect(semantics.rect, Rect.fromLTWH(0, 0, 200, 100));
// A 200x100 rect positioned at bottom right of a 800x600 box.
expect(semantics.transform, Matrix4.translationValues(600, 500, 0));
expect(semantics.childrenCount, 0);
viewsController.createCompleter.complete();
await tester.pumpAndSettle();
expect(semantics.platformViewId, currentViewId + 1);
expect(semantics.rect, Rect.fromLTWH(0, 0, 200, 100));
// A 200x100 rect positioned at bottom right of a 800x600 box.
expect(semantics.transform, Matrix4.translationValues(600, 500, 0));
expect(semantics.childrenCount, 0);
handle.dispose();
});
}); });
group('UiKitView', () { group('UiKitView', () {
......
...@@ -364,6 +364,7 @@ Matcher matchesSemantics({ ...@@ -364,6 +364,7 @@ Matcher matchesSemantics({
Size size, Size size,
double elevation, double elevation,
double thickness, double thickness,
int platformViewId,
// Flags // // Flags //
bool hasCheckedState = false, bool hasCheckedState = false,
bool isChecked = false, bool isChecked = false,
...@@ -514,6 +515,7 @@ Matcher matchesSemantics({ ...@@ -514,6 +515,7 @@ Matcher matchesSemantics({
size: size, size: size,
elevation: elevation, elevation: elevation,
thickness: thickness, thickness: thickness,
platformViewId: platformViewId,
customActions: customActions, customActions: customActions,
hintOverrides: hintOverrides, hintOverrides: hintOverrides,
children: children, children: children,
...@@ -1698,6 +1700,7 @@ class _MatchesSemanticsData extends Matcher { ...@@ -1698,6 +1700,7 @@ class _MatchesSemanticsData extends Matcher {
this.size, this.size,
this.elevation, this.elevation,
this.thickness, this.thickness,
this.platformViewId,
this.customActions, this.customActions,
this.hintOverrides, this.hintOverrides,
this.children, this.children,
...@@ -1717,6 +1720,7 @@ class _MatchesSemanticsData extends Matcher { ...@@ -1717,6 +1720,7 @@ class _MatchesSemanticsData extends Matcher {
final Size size; final Size size;
final double elevation; final double elevation;
final double thickness; final double thickness;
final int platformViewId;
final List<Matcher> children; final List<Matcher> children;
@override @override
...@@ -1746,6 +1750,8 @@ class _MatchesSemanticsData extends Matcher { ...@@ -1746,6 +1750,8 @@ class _MatchesSemanticsData extends Matcher {
description.add(' with elevation: $elevation'); description.add(' with elevation: $elevation');
if (thickness != null) if (thickness != null)
description.add(' with thickness: $thickness'); description.add(' with thickness: $thickness');
if (platformViewId != null)
description.add(' with platformViewId: $platformViewId');
if (customActions != null) if (customActions != null)
description.add(' with custom actions: $customActions'); description.add(' with custom actions: $customActions');
if (hintOverrides != null) if (hintOverrides != null)
...@@ -1786,6 +1792,8 @@ class _MatchesSemanticsData extends Matcher { ...@@ -1786,6 +1792,8 @@ class _MatchesSemanticsData extends Matcher {
return failWithDescription(matchState, 'elevation was: ${data.elevation}'); return failWithDescription(matchState, 'elevation was: ${data.elevation}');
if (thickness != null && thickness != data.thickness) if (thickness != null && thickness != data.thickness)
return failWithDescription(matchState, 'thickness was: ${data.thickness}'); return failWithDescription(matchState, 'thickness was: ${data.thickness}');
if (platformViewId != null && platformViewId != data.platformViewId)
return failWithDescription(matchState, 'platformViewId was: ${data.platformViewId}');
if (actions != null) { if (actions != null) {
int actionBits = 0; int actionBits = 0;
for (SemanticsAction action in actions) for (SemanticsAction action in actions)
......
...@@ -502,6 +502,7 @@ void main() { ...@@ -502,6 +502,7 @@ void main() {
scrollPosition: null, scrollPosition: null,
scrollExtentMax: null, scrollExtentMax: null,
scrollExtentMin: null, scrollExtentMin: null,
platformViewId: 105,
customSemanticsActionIds: <int>[CustomSemanticsAction.getIdentifier(action)], customSemanticsActionIds: <int>[CustomSemanticsAction.getIdentifier(action)],
); );
final _FakeSemanticsNode node = _FakeSemanticsNode(); final _FakeSemanticsNode node = _FakeSemanticsNode();
...@@ -512,6 +513,7 @@ void main() { ...@@ -512,6 +513,7 @@ void main() {
size: const Size(10.0, 10.0), size: const Size(10.0, 10.0),
elevation: 3.0, elevation: 3.0,
thickness: 4.0, thickness: 4.0,
platformViewId: 105,
/* Flags */ /* Flags */
hasCheckedState: true, hasCheckedState: true,
isChecked: true, isChecked: true,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment