Skip to content

Commit

Permalink
Add MacOS AppKitView class. (#132583)
Browse files Browse the repository at this point in the history
Add derived classes from the Darwin platform view base classes for
MacOS. Functionality is largely the same as the `UiKitView`, but the two
are decoupled and and can further diverge in the future as needed. Some
unit tests remain skipped for now as the gesture recognizers for MacOS
are not yet implemented.

flutter/flutter#128519

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [ ] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#overview
[Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene
[test-exempt]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes
[Discord]: https://github.com/flutter/flutter/wiki/Chat

---------

Co-authored-by: Chris Bracken <[email protected]>
  • Loading branch information
yaakovschectman and cbracken authored Aug 31, 2023
1 parent b4753c3 commit e6e44de
Show file tree
Hide file tree
Showing 5 changed files with 1,205 additions and 14 deletions.
39 changes: 30 additions & 9 deletions packages/flutter/lib/src/rendering/platform_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,10 @@ abstract class RenderDarwinPlatformView<T extends DarwinPlatformViewController>
RenderDarwinPlatformView({
required T viewController,
required this.hitTestBehavior,
}) : _viewController = viewController;
required Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers,
}) : _viewController = viewController {
updateGestureRecognizers(gestureRecognizers);
}


/// The unique identifier of the platform view controlled by this controller.
Expand Down Expand Up @@ -313,6 +316,8 @@ abstract class RenderDarwinPlatformView<T extends DarwinPlatformViewController>

PointerEvent? _lastPointerDownEvent;

_UiKitViewGestureRecognizer? _gestureRecognizer;

@override
Size computeDryLayout(BoxConstraints constraints) {
return constraints.biggest;
Expand Down Expand Up @@ -374,6 +379,9 @@ abstract class RenderDarwinPlatformView<T extends DarwinPlatformViewController>
GestureBinding.instance.pointerRouter.removeGlobalRoute(_handleGlobalPointerEvent);
super.detach();
}

/// {@macro flutter.rendering.PlatformViewRenderBox.updateGestureRecognizers}
void updateGestureRecognizers(Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers);
}

/// A render object for an iOS UIKit UIView.
Expand Down Expand Up @@ -401,14 +409,11 @@ class RenderUiKitView extends RenderDarwinPlatformView<UiKitViewController> {
RenderUiKitView({
required super.viewController,
required super.hitTestBehavior,
required Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers
}) {
updateGestureRecognizers(gestureRecognizers);
}
required super.gestureRecognizers,
});

// TODO(schectman): Add gesture functionality to macOS platform view when implemented.
// https://github.com/flutter/flutter/issues/128519
/// {@macro flutter.rendering.PlatformViewRenderBox.updateGestureRecognizers}
@override
void updateGestureRecognizers(Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers) {
assert(
_factoriesTypeSet(gestureRecognizers).length == gestureRecognizers.length,
Expand All @@ -431,15 +436,31 @@ class RenderUiKitView extends RenderDarwinPlatformView<UiKitViewController> {
_lastPointerDownEvent = event.original ?? event;
}

_UiKitViewGestureRecognizer? _gestureRecognizer;

@override
void detach() {
_gestureRecognizer!.reset();
super.detach();
}
}

/// A render object for a macOS platform view.
class RenderAppKitView extends RenderDarwinPlatformView<AppKitViewController> {
/// Creates a render object for a macOS AppKitView.
RenderAppKitView({
required super.viewController,
required super.hitTestBehavior,
required super.gestureRecognizers,
});

// TODO(schectman): Add gesture functionality to macOS platform view when implemented.
// https://github.com/flutter/flutter/issues/128519
// This method will need to behave the same as the same-named method for RenderUiKitView,
// but use a _AppKitViewGestureRecognizer or equivalent, whose constructor shall accept an
// AppKitViewController.
@override
void updateGestureRecognizers(Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers) {}
}

// This recognizer constructs gesture recognizers from a set of gesture recognizer factories
// it was give, adds all of them to a gesture arena team with the _UiKitViewGestureRecognizer
// as the team captain.
Expand Down
53 changes: 52 additions & 1 deletion packages/flutter/lib/src/services/platform_views.dart
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ class PlatformViewsService {
assert(creationParams == null || creationParamsCodec != null);

// TODO(amirh): pass layoutDirection once the system channel supports it.
// https://github.com/flutter/flutter/issues/133682
final Map<String, dynamic> args = <String, dynamic>{
'id': id,
'viewType': viewType,
Expand All @@ -238,6 +239,49 @@ class PlatformViewsService {
}
return UiKitViewController._(id, layoutDirection);
}

/// Factory method to create an `AppKitView`.
///
/// `id` is an unused unique identifier generated with [platformViewsRegistry].
///
/// `viewType` is the identifier of the iOS view type to be created, a
/// factory for this view type must have been registered on the platform side.
/// Platform view factories are typically registered by plugin code.
///
/// `onFocus` is a callback that will be invoked when the UIKit view asks to
/// get the input focus.
/// The `id, `viewType, and `layoutDirection` parameters must not be null.
/// If `creationParams` is non null then `creationParamsCodec` must not be null.
static Future<AppKitViewController> initAppKitView({
required int id,
required String viewType,
required TextDirection layoutDirection,
dynamic creationParams,
MessageCodec<dynamic>? creationParamsCodec,
VoidCallback? onFocus,
}) async {
assert(creationParams == null || creationParamsCodec != null);

// TODO(amirh): pass layoutDirection once the system channel supports it.
// https://github.com/flutter/flutter/issues/133682
final Map<String, dynamic> args = <String, dynamic>{
'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<void>('create', args);
if (onFocus != null) {
_instance._focusCallbacks[id] = onFocus;
}
return AppKitViewController._(id, layoutDirection);
}
}

/// Properties of an Android pointer.
Expand Down Expand Up @@ -1317,7 +1361,6 @@ abstract class DarwinPlatformViewController {
TextDirection layoutDirection,
) : _layoutDirection = layoutDirection;


/// The unique identifier of the iOS view controlled by this controller.
///
/// This identifier is typically generated by
Expand Down Expand Up @@ -1389,6 +1432,14 @@ class UiKitViewController extends DarwinPlatformViewController {
);
}

/// Controller for a macOS platform view.
class AppKitViewController extends DarwinPlatformViewController {
AppKitViewController._(
super.id,
super.layoutDirection,
);
}

/// An interface for controlling a single platform view.
///
/// Used by [PlatformViewSurface] to interface with the platform view it embeds.
Expand Down
75 changes: 71 additions & 4 deletions packages/flutter/lib/src/widgets/platform_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,40 @@ class UiKitView extends _DarwinView {
State<UiKitView> createState() => _UiKitViewState();
}

/// Widget that contains a macOS AppKit view.
///
/// Embedding macOS views is an expensive operation and should be avoided where
/// a Flutter equivalent is possible.
///
/// The platform view's lifetime is the same as the lifetime of the [State]
/// object for this widget. When the [State] is disposed the platform view (and
/// auxiliary resources) are lazily released (some resources are immediately
/// released and some by platform garbage collector). A stateful widget's state
/// is disposed when the widget is removed from the tree or when it is moved
/// within the tree. If the stateful widget has a key and it's only moved
/// relative to its siblings, or it has a [GlobalKey] and it's moved within the
/// tree, it will not be disposed.
///
/// Construction of AppKitViews is done asynchronously, before the underlying
/// NSView is ready this widget paints nothing while maintaining the same
/// layout constraints.
class AppKitView extends _DarwinView {
/// Creates a widget that embeds a macOS AppKit NSView.
const AppKitView({
super.key,
required super.viewType,
super.onPlatformViewCreated,
super.hitTestBehavior = PlatformViewHitTestBehavior.opaque,
super.layoutDirection,
super.creationParams,
super.creationParamsCodec,
super.gestureRecognizers,
});

@override
State<AppKitView> createState() => _AppKitViewState();
}

/// Callback signature for when the platform view's DOM element was created.
///
/// [element] is the DOM element that was created.
Expand Down Expand Up @@ -711,6 +745,31 @@ class _UiKitViewState extends _DarwinViewState<UiKitView, UiKitViewController, R
}
}

class _AppKitViewState extends _DarwinViewState<AppKitView, AppKitViewController, RenderAppKitView, _AppKitPlatformView> {
@override
Future<AppKitViewController> createNewViewController(int id) async {
return PlatformViewsService.initAppKitView(
id: id,
viewType: widget.viewType,
layoutDirection: _layoutDirection!,
creationParams: widget.creationParams,
creationParamsCodec: widget.creationParamsCodec,
onFocus: () {
focusNode?.requestFocus();
}
);
}

@override
_AppKitPlatformView childPlatformView() {
return _AppKitPlatformView(
controller: _controller!,
hitTestBehavior: widget.hitTestBehavior,
gestureRecognizers: widget.gestureRecognizers ?? _DarwinViewState._emptyRecognizersSet,
);
}
}

class _AndroidPlatformView extends LeafRenderObjectWidget {
const _AndroidPlatformView({
required this.controller,
Expand Down Expand Up @@ -758,7 +817,8 @@ abstract class _DarwinPlatformView<TController extends DarwinPlatformViewControl
void updateRenderObject(BuildContext context, TRender renderObject) {
renderObject
..viewController = controller
..hitTestBehavior = hitTestBehavior;
..hitTestBehavior = hitTestBehavior
..updateGestureRecognizers(gestureRecognizers);
}
}

Expand All @@ -773,11 +833,18 @@ class _UiKitPlatformView extends _DarwinPlatformView<UiKitViewController, Render
gestureRecognizers: gestureRecognizers,
);
}
}

class _AppKitPlatformView extends _DarwinPlatformView<AppKitViewController, RenderAppKitView> {
const _AppKitPlatformView({required super.controller, required super.hitTestBehavior, required super.gestureRecognizers});

@override
void updateRenderObject(BuildContext context, RenderUiKitView renderObject) {
super.updateRenderObject(context, renderObject);
renderObject.updateGestureRecognizers(gestureRecognizers);
RenderObject createRenderObject(BuildContext context) {
return RenderAppKitView(
viewController: controller,
hitTestBehavior: hitTestBehavior,
gestureRecognizers: gestureRecognizers,
);
}
}

Expand Down
Loading

0 comments on commit e6e44de

Please sign in to comment.