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,6 +427,7 @@ class SemanticsData extends Diagnosticable { ...@@ -411,6 +427,7 @@ class SemanticsData extends Diagnosticable {
@override @override
int get hashCode { int get hashCode {
return ui.hashValues( return ui.hashValues(
ui.hashValues(
flags, flags,
actions, actions,
label, label,
...@@ -427,9 +444,11 @@ class SemanticsData extends Diagnosticable { ...@@ -427,9 +444,11 @@ class SemanticsData extends Diagnosticable {
scrollPosition, scrollPosition,
scrollExtentMax, scrollExtentMax,
scrollExtentMin, scrollExtentMin,
platformViewId,
transform, transform,
elevation, elevation,
thickness, 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