From edfa932760103c0d730bf8f3551fa339d0b520f4 Mon Sep 17 00:00:00 2001 From: mahaonan Date: Tue, 17 Oct 2023 16:09:39 +0800 Subject: [PATCH] =?UTF-8?q?platform=5Fview=20dart=20=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=AE=8C=E5=96=84=EF=BC=8C=E5=A2=9E=E5=8A=A0=20direction=20?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/src/rendering/platform_view.dart | 38 +++- .../lib/src/services/platform_views.dart | 208 +++++++++++++++--- .../lib/src/widgets/platform_view.dart | 60 +++-- 3 files changed, 254 insertions(+), 52 deletions(-) diff --git a/packages/flutter/lib/src/rendering/platform_view.dart b/packages/flutter/lib/src/rendering/platform_view.dart index f268e657ed..11289118aa 100644 --- a/packages/flutter/lib/src/rendering/platform_view.dart +++ b/packages/flutter/lib/src/rendering/platform_view.dart @@ -522,9 +522,11 @@ class RenderOhosView extends RenderBox { assert(gestureRecognizers != null), _viewController = viewController { updateGestureRecognizers(gestureRecognizers); + _viewController.addOnPlatformViewCreatedListener(_onPlatformViewCreated); _setOffset(); } + _PlatformViewState _state = _PlatformViewState.uninitialized; OhosViewController get viewController => _viewController; OhosViewController _viewController; set viewController(OhosViewController value) { @@ -532,17 +534,25 @@ class RenderOhosView extends RenderBox { if (_viewController == value) { return; } + _viewController.removeOnPlatformViewCreatedListener(_onPlatformViewCreated); final bool needsSemanticsUpdate = _viewController.id != value.id; _viewController = value; + _sizePlatformView(); markNeedsPaint(); if (needsSemanticsUpdate) { markNeedsSemanticsUpdate(); } - _sizePlatformView(); + _viewController.addOnPlatformViewCreatedListener(_onPlatformViewCreated); + } + + void _onPlatformViewCreated(int id) { + markNeedsSemanticsUpdate(); } PlatformViewHitTestBehavior hitTestBehavior; + Size? _currentTextureSize; + void updateGestureRecognizers(Set> gestureRecognizers) { assert(gestureRecognizers != null); assert( @@ -642,6 +652,12 @@ class RenderOhosView extends RenderBox { super.detach(); } + @override + void dispose() { + _viewController.removeOnPlatformViewCreatedListener(_onPlatformViewCreated); + super.dispose(); + } + // Sets the offset of the underlying platform view on the platform side. // // This allows the Android native view to draw the a11y highlights in the same @@ -666,16 +682,22 @@ class RenderOhosView extends RenderBox { } Future _sizePlatformView() async { - // Android virtual displays cannot have a zero size. - // Trying to size it to 0 crashes the app, which was happening when starting the app - // with a locked screen (see: https://github.com/flutter/flutter/issues/20456). - if (size.isEmpty) { + if (_state == _PlatformViewState.resizing || size.isEmpty) { return; } + + _state = _PlatformViewState.resizing; + + markNeedsPaint(); Size targetSize; - targetSize = size; - await _viewController.setSize(targetSize); + do { + targetSize = size; + _currentTextureSize = await _viewController.setSize(targetSize); + } while (size != targetSize); + + _state = _PlatformViewState.ready; + markNeedsPaint(); } } @@ -716,7 +738,7 @@ class _OhosViewGestureRecognizer extends OneSequenceGestureRecognizer { } @override - String get debugDescription => 'UIKit view'; + String get debugDescription => 'Ohos view'; @override void didStopTrackingLastPointer(int pointer) { } diff --git a/packages/flutter/lib/src/services/platform_views.dart b/packages/flutter/lib/src/services/platform_views.dart index 61290471fe..010eb9a938 100644 --- a/packages/flutter/lib/src/services/platform_views.dart +++ b/packages/flutter/lib/src/services/platform_views.dart @@ -247,34 +247,30 @@ class PlatformViewsService { return UiKitViewController._(id, layoutDirection); } - static Future initOhosView({ + static OhosViewController initOhosView({ required int id, required String viewType, + required TextDirection layoutDirection, dynamic creationParams, MessageCodec? creationParamsCodec, VoidCallback? onFocus, - }) async { + }) { assert(id != null); assert(viewType != null); + assert(layoutDirection != null); assert(creationParams == null || creationParamsCodec != null); - final Map args = { - 'id': id, - 'viewType': viewType, - }; - if (creationParams != null) { - final ByteData paramsByteData = creationParamsCodec!.encodeMessage(creationParams)!; - args['params'] = Uint8List.view( - paramsByteData.buffer, - 0, - paramsByteData.lengthInBytes, - ); - } - await SystemChannels.platform_views.invokeMethod('create', args); - if (onFocus != null) { - _instance._focusCallbacks[id] = onFocus; - } - return OhosViewController._(id); + final OhosViewController controller = + OhosViewController._( + viewId: id, + viewType: viewType, + layoutDirection: layoutDirection, + creationParams: creationParams, + creationParamsCodec: creationParamsCodec, + ); + + _instance._focusCallbacks[id] = onFocus ?? () {}; + return controller; } } @@ -1456,17 +1452,70 @@ class UiKitViewController { } class OhosViewController { - OhosViewController._( - this.id - ) : assert(id != null); + OhosViewController._({ + required int viewId, + required String viewType, + required TextDirection layoutDirection, + dynamic creationParams, + MessageCodec? creationParamsCodec, + }) : assert(viewId != null), + assert(viewType != null), + assert(layoutDirection != null), + assert(creationParams == null || creationParamsCodec != null), + id = viewId, + _viewType = viewType, + _layoutDirection = layoutDirection, + _creationParams = creationParams == null + ? null + : _CreationParams(creationParams, creationParamsCodec!); final int id; + final String _viewType; + + TextDirection _layoutDirection; + + final List _platformViewCreatedCallbacks = + []; + bool _debugDisposed = false; /// The current offset of the platform view. Offset _offset = Offset.zero; + final _CreationParams? _creationParams; + + _AndroidViewState _state = _AndroidViewState.waitingForSize; + + static const int kOhosLayoutDirectionLtr = 0; + + static const int kOhosLayoutDirectionRtl = 1; + + /// 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(listener != null); + assert(_state != _AndroidViewState.disposed); + _platformViewCreatedCallbacks.remove(listener); + } + + static int _getOhosDirection(TextDirection direction) { + assert(direction != null); + switch (direction) { + case TextDirection.ltr: + return kOhosLayoutDirectionLtr; + case TextDirection.rtl: + return kOhosLayoutDirectionRtl; + } + } + Future acceptGesture() { final Map args = { 'id': id, @@ -1483,7 +1532,15 @@ class OhosViewController { Future dispose() async { _debugDisposed = true; - await SystemChannels.platform_views.invokeMethod('dispose', id); + + if (_state == _AndroidViewState.creating || + _state == _AndroidViewState.created) { + await SystemChannels.platform_views.invokeMethod('dispose', id); + } + + _platformViewCreatedCallbacks.clear(); + _state = _AndroidViewState.disposed; + PlatformViewsService._instance._focusCallbacks.remove(id); } Future setOffset(Offset offset) async { @@ -1491,7 +1548,10 @@ class OhosViewController { return; } - _debugDisposed = true; + if (_state != _AndroidViewState.created) { + return; + } + _offset = offset; await SystemChannels.platform_views.invokeMethod( @@ -1504,9 +1564,13 @@ class OhosViewController { ); } - Future _sendResizeMessage(Size size) async { + Future _sendResizeMessage(Size size) async { + assert(_state != _AndroidViewState.waitingForSize, + 'Ohos view must have an initial size. View id: $id'); assert(!size.isEmpty); - await SystemChannels.platform_views.invokeMapMethod( + + final Map? meta = + await SystemChannels.platform_views.invokeMapMethod( 'resize', { 'id': id, @@ -1514,6 +1578,58 @@ class OhosViewController { 'height': size.height, }, ); + assert(meta != null); + assert(meta!.containsKey('width')); + assert(meta!.containsKey('height')); + return Size(meta!['width']! as double, meta['height']! as double); + } + + Future _sendCreateMessage( + {required Size size, Offset? position}) async { + assert(!size.isEmpty, + 'trying to create $OhosViewController without setting a valid size.'); + + final Map args = { + 'id': id, + 'viewType': _viewType, + 'direction': OhosViewController._getOhosDirection(_layoutDirection), + if (size != null) 'width': size.width, + if (size != null) 'height': size.height, + if (position != null) 'left': position.dx, + if (position != null) 'top': position.dy, + }; + + if (_creationParams != null) { + final ByteData paramsByteData = + _creationParams!.codec.encodeMessage(_creationParams!.data)!; + args['params'] = Uint8List.view( + paramsByteData.buffer, + 0, + paramsByteData.lengthInBytes, + ); + } + return SystemChannels.platform_views.invokeMethod('create', args); + } + + Future create({Size? size, Offset? position}) async { + assert(_state != _AndroidViewState.disposed, + 'trying to create a disposed Ohos view'); + assert(_state == _AndroidViewState.waitingForSize, + 'Ohos view is already sized. View id: $id'); + + if (size == null) { + // Wait for a setSize call. + return; + } + + _state = _AndroidViewState.creating; + await _sendCreateMessage(size: size, position: position); + _state = _AndroidViewState.created; + + for (final PlatformViewCreatedCallback callback + in _platformViewCreatedCallbacks) { + callback(id); + } } /// Sizes the Android View. @@ -1530,8 +1646,44 @@ class OhosViewController { /// /// As a result, consumers are expected to clip the texture using [size], while using /// the return value to size the texture. - Future setSize(Size size) async { - _sendResizeMessage(size); + Future setSize(Size size) async { + assert(_state != _AndroidViewState.disposed, + 'Ohos view is disposed. View id: $id'); + if (_state == _AndroidViewState.waitingForSize) { + // Either `create` hasn't been called, or it couldn't run due to missing + // size information, so create the view now. + await create(size: size); + return size; + } else { + return _sendResizeMessage(size); + } + } + + /// Sets the layout direction for the Android view. + Future setLayoutDirection(TextDirection layoutDirection) async { + assert( + _state != _AndroidViewState.disposed, + 'trying to set a layout direction for a disposed UIView. View id: $id', + ); + + if (layoutDirection == _layoutDirection) { + return; + } + + assert(layoutDirection != null); + _layoutDirection = layoutDirection; + + // If the view was not yet created we just update _layoutDirection and return, as the new + // direction will be used in _create. + if (_state == _AndroidViewState.waitingForSize) { + return; + } + + await SystemChannels.platform_views + .invokeMethod('setDirection', { + 'id': id, + 'direction': _getOhosDirection(layoutDirection), + }); } } diff --git a/packages/flutter/lib/src/widgets/platform_view.dart b/packages/flutter/lib/src/widgets/platform_view.dart index 17045cbc32..25f3655845 100644 --- a/packages/flutter/lib/src/widgets/platform_view.dart +++ b/packages/flutter/lib/src/widgets/platform_view.dart @@ -207,6 +207,7 @@ class OhosView extends StatefulWidget { required this.viewType, this.onPlatformViewCreated, this.hitTestBehavior = PlatformViewHitTestBehavior.opaque, + this.layoutDirection, this.creationParams, this.creationParamsCodec, this.gestureRecognizers, @@ -220,6 +221,8 @@ class OhosView extends StatefulWidget { final PlatformViewHitTestBehavior hitTestBehavior; + final TextDirection? layoutDirection; + final dynamic creationParams; final MessageCodec? creationParamsCodec; @@ -231,7 +234,9 @@ class OhosView extends StatefulWidget { } class _OhosViewState extends State { - OhosViewController? _controller; + int? _id; + late OhosViewController _controller; + TextDirection? _layoutDirection; bool _initialized = false; late FocusNode _focusNode; @@ -248,7 +253,7 @@ class _OhosViewState extends State { focusNode: _focusNode, onFocusChange: (bool isFocused) => _onFocusChange(isFocused, controller), child: _OhosPlatformView( - controller: _controller!, + controller: _controller, hitTestBehavior: widget.hitTestBehavior, gestureRecognizers: widget.gestureRecognizers ?? _emptyRecognizersSet, ), @@ -261,51 +266,74 @@ class _OhosViewState extends State { } _initialized = true; _createNewOhosView(); + _focusNode = FocusNode(debugLabel: 'OhosView(id: $_id)'); } @override void didChangeDependencies() { super.didChangeDependencies(); + final TextDirection newLayoutDirection = _findLayoutDirection(); + final bool didChangeLayoutDirection = + _layoutDirection != newLayoutDirection; + _layoutDirection = newLayoutDirection; + _initializeOnce(); + if (didChangeLayoutDirection) { + // The native view will update asynchronously, in the meantime we don't want + // to block the framework. (so this is intentionally not awaiting). + _controller.setLayoutDirection(_layoutDirection!); + } } @override void didUpdateWidget(OhosView oldWidget) { super.didUpdateWidget(oldWidget); + final TextDirection newLayoutDirection = _findLayoutDirection(); + final bool didChangeLayoutDirection = + _layoutDirection != newLayoutDirection; + _layoutDirection = newLayoutDirection; + if (widget.viewType != oldWidget.viewType) { - _controller?.dispose(); + _controller.dispose(); _createNewOhosView(); return; } + + if (didChangeLayoutDirection) { + _controller.setLayoutDirection(_layoutDirection!); + } + } + + TextDirection _findLayoutDirection() { + assert( + widget.layoutDirection != null || debugCheckHasDirectionality(context)); + return widget.layoutDirection ?? Directionality.of(context); } @override void dispose() { - _controller?.dispose(); + _controller.dispose(); super.dispose(); } - Future _createNewOhosView() async { - final int id = platformViewsRegistry.getNextPlatformViewId(); - final OhosViewController controller = await PlatformViewsService.initOhosView( - id: id, + void _createNewOhosView() { + _id = platformViewsRegistry.getNextPlatformViewId(); + _controller = PlatformViewsService.initOhosView( + id: _id!, viewType: widget.viewType, + layoutDirection: _layoutDirection!, creationParams: widget.creationParams, creationParamsCodec: widget.creationParamsCodec, onFocus: () { _focusNode.requestFocus(); } ); - if (!mounted) { - controller.dispose(); - return; + + if (widget.onPlatformViewCreated != null) { + _controller + .addOnPlatformViewCreatedListener(widget.onPlatformViewCreated!); } - widget.onPlatformViewCreated?.call(id); - setState(() { - _controller = controller; - _focusNode = FocusNode(debugLabel: 'OhosView(id: $id)'); - }); } void _onFocusChange(bool isFocused, OhosViewController controller) { -- Gitee