Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 5a11866

Browse files
authored
[CP] Fix crash on choosing share in selection controls on ipad (#48427)
Fixes flutter/flutter#138550 Original PR: #48220
1 parent cf7a9d0 commit 5a11866

File tree

3 files changed

+71
-0
lines changed

3 files changed

+71
-0
lines changed

shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#import <UIKit/UIKit.h>
1111

1212
#include "flutter/fml/logging.h"
13+
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h"
14+
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h"
1315
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h"
1416
#import "flutter/shell/platform/darwin/ios/framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.h"
1517

@@ -154,10 +156,38 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
154156

155157
- (void)showShareViewController:(NSString*)content {
156158
UIViewController* engineViewController = [_engine.get() viewController];
159+
157160
NSArray* itemsToShare = @[ content ?: [NSNull null] ];
158161
UIActivityViewController* activityViewController =
159162
[[[UIActivityViewController alloc] initWithActivityItems:itemsToShare
160163
applicationActivities:nil] autorelease];
164+
165+
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
166+
// On iPad, the share screen is presented in a popover view, and requires a
167+
// sourceView and sourceRect
168+
FlutterTextInputPlugin* _textInputPlugin = [_engine.get() textInputPlugin];
169+
UITextRange* range = _textInputPlugin.textInputView.selectedTextRange;
170+
171+
// firstRectForRange cannot be used here as it's current implementation does
172+
// not always return the full rect of the range.
173+
CGRect firstRect = [(FlutterTextInputView*)_textInputPlugin.textInputView
174+
caretRectForPosition:(FlutterTextPosition*)range.start];
175+
CGRect transformedFirstRect = [(FlutterTextInputView*)_textInputPlugin.textInputView
176+
localRectFromFrameworkTransform:firstRect];
177+
CGRect lastRect = [(FlutterTextInputView*)_textInputPlugin.textInputView
178+
caretRectForPosition:(FlutterTextPosition*)range.end];
179+
CGRect transformedLastRect = [(FlutterTextInputView*)_textInputPlugin.textInputView
180+
localRectFromFrameworkTransform:lastRect];
181+
182+
activityViewController.popoverPresentationController.sourceView = engineViewController.view;
183+
// In case of RTL Language, get the minimum x coordinate
184+
activityViewController.popoverPresentationController.sourceRect =
185+
CGRectMake(fmin(transformedFirstRect.origin.x, transformedLastRect.origin.x),
186+
transformedFirstRect.origin.y,
187+
abs(transformedLastRect.origin.x - transformedFirstRect.origin.x),
188+
transformedFirstRect.size.height);
189+
}
190+
161191
[engineViewController presentViewController:activityViewController animated:YES completion:nil];
162192
}
163193

shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,43 @@ - (void)testShareScreenInvoked {
161161
[self waitForExpectationsWithTimeout:1 handler:nil];
162162
}
163163

164+
- (void)testShareScreenInvokedOnIPad {
165+
FlutterEngine* engine = [[[FlutterEngine alloc] initWithName:@"test" project:nil] autorelease];
166+
[engine runWithEntrypoint:nil];
167+
std::unique_ptr<fml::WeakPtrFactory<FlutterEngine>> _weakFactory =
168+
std::make_unique<fml::WeakPtrFactory<FlutterEngine>>(engine);
169+
170+
XCTestExpectation* presentExpectation =
171+
[self expectationWithDescription:@"Share view controller presented on iPad"];
172+
173+
FlutterViewController* engineViewController =
174+
[[[FlutterViewController alloc] initWithEngine:engine nibName:nil bundle:nil] autorelease];
175+
FlutterViewController* mockEngineViewController = OCMPartialMock(engineViewController);
176+
OCMStub([mockEngineViewController
177+
presentViewController:[OCMArg isKindOfClass:[UIActivityViewController class]]
178+
animated:YES
179+
completion:nil]);
180+
181+
id mockTraitCollection = OCMClassMock([UITraitCollection class]);
182+
OCMStub([mockTraitCollection userInterfaceIdiom]).andReturn(UIUserInterfaceIdiomPad);
183+
184+
FlutterPlatformPlugin* plugin =
185+
[[[FlutterPlatformPlugin alloc] initWithEngine:_weakFactory->GetWeakPtr()] autorelease];
186+
FlutterPlatformPlugin* mockPlugin = OCMPartialMock(plugin);
187+
188+
FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:@"Share.invoke"
189+
arguments:@"Test"];
190+
FlutterResult result = ^(id result) {
191+
OCMVerify([mockEngineViewController
192+
presentViewController:[OCMArg isKindOfClass:[UIActivityViewController class]]
193+
animated:YES
194+
completion:nil]);
195+
[presentExpectation fulfill];
196+
};
197+
[mockPlugin handleMethodCall:methodCall result:result];
198+
[self waitForExpectationsWithTimeout:1 handler:nil];
199+
}
200+
164201
- (void)testClipboardHasCorrectStrings {
165202
[UIPasteboard generalPasteboard].string = nil;
166203
FlutterEngine* engine = [[[FlutterEngine alloc] initWithName:@"test" project:nil] autorelease];

shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@ FLUTTER_DARWIN_EXPORT
164164
- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
165165
- (instancetype)initWithOwner:(FlutterTextInputPlugin*)textInputPlugin NS_DESIGNATED_INITIALIZER;
166166

167+
// TODO(louisehsu): These are being exposed to support Share in FlutterPlatformPlugin
168+
// Consider moving that feature into FlutterTextInputPlugin to avoid exposing extra methods
169+
- (CGRect)localRectFromFrameworkTransform:(CGRect)incomingRect;
170+
- (CGRect)caretRectForPosition:(UITextPosition*)position;
167171
@end
168172

169173
@interface UIView (FindFirstResponder)

0 commit comments

Comments
 (0)