5#import <OCMock/OCMock.h>
6#import <XCTest/XCTest.h>
36@interface FlutterKeyboardInsetManager (Test)
37- (void)setUpKeyboardAnimationVsyncClient:
41@property(nonatomic, weak) id<FlutterKeyboardInsetManagerDelegate>
delegate;
44- (void)handleKeyboardNotification:(NSNotification*)notification;
45- (
BOOL)isKeyboardNotificationForDifferentView:(NSNotification*)notification;
46- (CGFloat)calculateKeyboardInset:(CGRect)keyboardFrame keyboardMode:(
int)keyboardMode;
47- (
BOOL)shouldIgnoreKeyboardNotification:(NSNotification*)notification;
48- (FlutterKeyboardMode)calculateKeyboardAttachMode:(NSNotification*)notification;
49- (CGFloat)calculateMultitaskingAdjustment:(CGRect)screenRect keyboardFrame:(CGRect)keyboardFrame;
50- (void)startKeyBoardAnimation:(NSTimeInterval)duration;
54- (void)setUpKeyboardSpringAnimationIfNeeded:(CAAnimation*)keyboardAnimation;
60@property(nonatomic, assign)
BOOL didCallStartKeyboardAnimation;
64- (void)startKeyBoardAnimation:(NSTimeInterval)duration {
65 [
super startKeyBoardAnimation:duration];
66 self.didCallStartKeyboardAnimation = YES;
71@property(nonatomic, strong) UIScreen* mockScreen;
72@property(nonatomic, strong) UIView* mockView;
74@property(nonatomic, assign) CGFloat currentInset;
75@property(nonatomic, copy)
void (^updateViewportMetricsBlock)(CGFloat inset);
76@property(nonatomic, assign)
BOOL isViewLoaded;
77@property(nonatomic, assign)
BOOL mockIsPadInSlideOverOrStageManagerMode;
78@property(nonatomic, assign) CGRect mockConvertedViewRect;
82- (void)updateViewportMetricsWithInset:(CGFloat)inset {
84 if (
self.updateViewportMetricsBlock) {
85 self.updateViewportMetricsBlock(inset);
88- (CGFloat)physicalViewInsetBottom {
89 return self.currentInset;
95 return self.mockEngine;
97- (UIScreen*)flutterScreenIfViewLoaded {
98 return self.mockScreen;
100- (
BOOL)isPadInSlideOverOrStageManagerMode {
101 return self.mockIsPadInSlideOverOrStageManagerMode;
103- (CGRect)convertViewRectToScreen:(CGRect)rect {
104 return self.mockConvertedViewRect;
116@property(nonatomic, assign)
BOOL didCallNotifyLowMemory;
123 userData:(nullable
void*)userData;
126- (
BOOL)runWithEntrypoint:(nullable NSString*)entrypoint;
133@synthesize lifecycleChannel;
134@synthesize keyEventChannel;
138- (void)notifyLowMemory {
139 _didCallNotifyLowMemory = YES;
142- (instancetype)init {
143 if (
self = [super init]) {
146 initWithTaskRunner:fml::MessageLoop::GetCurrent().GetTaskRunner()];
152 return _uiTaskRunner;
155- (
BOOL)runWithEntrypoint:(nullable NSString*)entrypoint {
165 userData:(
void*)userData API_AVAILABLE(ios(9.0)) {
180- (
BOOL)createShell:(NSString*)entrypoint
181 libraryURI:(NSString*)libraryURI
182 initialRoute:(NSString*)initialRoute;
188@interface FlutterEngine (TestLowMemory)
189- (void)notifyLowMemory;
215@interface FlutterKeyboardManager (Tests)
216@property(nonatomic, retain, readonly)
217 NSMutableArray<id<FlutterKeyPrimaryResponder>>* primaryResponders;
220@interface FlutterEmbedderKeyResponder (Tests)
224@interface NSObject (Tests)
228@interface FlutterViewController (Tests) <FlutterKeyboardInsetManagerDelegate>
230@property(nonatomic, assign)
double targetViewInsetBottom;
231@property(nonatomic, assign)
BOOL keyboardAnimationIsShowing;
234@property(nonatomic, assign)
BOOL awokenFromNib;
236- (void)createTouchRateCorrectionVSyncClientIfNeeded;
237- (void)surfaceUpdated:(
BOOL)appeared;
238- (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences;
239- (void)handlePressEvent:(FlutterUIPressProxy*)press
240 nextAction:(
void (^)())next API_AVAILABLE(ios(13.4));
241- (void)discreteScrollEvent:(UIPanGestureRecognizer*)recognizer;
242- (void)updateViewportMetricsIfNeeded;
243- (void)updateAutoResizeConstraints;
244- (void)checkAndUpdateAutoResizeConstraints;
245- (void)onUserSettingsChanged:(NSNotification*)notification;
246- (void)applicationWillTerminate:(NSNotification*)notification;
247- (void)goToApplicationLifecycle:(nonnull NSString*)state;
249- (void)addInternalPlugins;
250- (
flutter::PointerData)generatePointerDataForFake;
252 initialRoute:(nullable NSString*)initialRoute;
253- (void)applicationBecameActive:(NSNotification*)notification;
254- (void)applicationWillResignActive:(NSNotification*)notification;
255- (void)applicationWillTerminate:(NSNotification*)notification;
256- (void)applicationDidEnterBackground:(NSNotification*)notification;
257- (void)applicationWillEnterForeground:(NSNotification*)notification;
258- (void)sceneBecameActive:(NSNotification*)notification API_AVAILABLE(ios(13.0));
259- (void)sceneWillResignActive:(NSNotification*)notification API_AVAILABLE(ios(13.0));
260- (void)sceneWillDisconnect:(NSNotification*)notification API_AVAILABLE(ios(13.0));
261- (void)sceneDidEnterBackground:(NSNotification*)notification API_AVAILABLE(ios(13.0));
262- (void)sceneWillEnterForeground:(NSNotification*)notification API_AVAILABLE(ios(13.0));
263- (void)triggerTouchRateCorrectionIfNeeded:(NSSet*)touches;
264- (void)onAccessibilityStatusChanged:(NSNotification*)notification;
268@property(nonatomic, strong)
id mockEngine;
269@property(nonatomic, strong)
id mockTextInputPlugin;
270@property(nonatomic, strong)
id messageSent;
271- (void)sendMessage:(
id _Nullable)message reply:(
FlutterReply _Nullable)callback;
276@property(nonatomic, readwrite) UITouchPhase phase;
286 self.messageSent = nil;
292 [
self.mockEngine stopMocking];
293 self.mockEngine = nil;
294 self.mockTextInputPlugin = nil;
295 self.messageSent = nil;
298- (
id)setUpMockScreen {
299 UIScreen* mockScreen = OCMClassMock([UIScreen
class]);
301 CGRect screenBounds = CGRectMake(0, 0, 1170, 2532);
302 OCMStub([mockScreen bounds]).andReturn(screenBounds);
303 CGFloat screenScale = 1;
304 OCMStub([mockScreen scale]).andReturn(screenScale);
310 screen:(UIScreen*)screen
311 viewFrame:(CGRect)viewFrame
312 convertedFrame:(CGRect)convertedFrame {
313 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
314 UIView*
view = [[UIView alloc] initWithFrame:viewFrame];
315 UIWindow*
window = [[UIWindow alloc] initWithFrame:viewFrame];
316 [window addSubview:view];
318 OCMStub([viewControllerMock viewIfLoaded]).andReturn(view);
319 OCMStub([viewControllerMock view]).andReturn(view);
324- (void)testViewDidLoadWillInvokeCreateTouchRateCorrectionVSyncClient {
331 [viewControllerMock loadView];
332 [viewControllerMock viewDidLoad];
333 OCMVerify([viewControllerMock createTouchRateCorrectionVSyncClientIfNeeded]);
336- (void)testStartKeyboardAnimationWillInvokeSetupKeyboardSpringAnimationIfNeeded {
343 OCMStub([viewControllerMock isViewLoaded]).andReturn(YES);
344 [viewControllerMock view];
346 (id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock;
350 id managerMock = OCMPartialMock(
viewController.keyboardInsetManager);
353 id viewClassMock = OCMClassMock([UIView
class]);
354 OCMStub([viewClassMock animateWithDuration:0.25 animations:[OCMArg any] completion:[OCMArg any]])
355 .andDo(^(NSInvocation* invocation) {
356 void (^animations)(void);
357 [invocation getArgument:&animations atIndex:3];
361 void (^completion)(
BOOL finished);
362 [invocation getArgument:&completion atIndex:4];
369 [managerMock startKeyBoardAnimation:0.25];
371 OCMVerify([managerMock setUpKeyboardSpringAnimationIfNeeded:[OCMArg any]]);
374- (void)testSetupKeyboardSpringAnimationIfNeeded {
379 (id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock;
380 UIScreen* screen = [
self setUpMockScreen];
381 CGRect viewFrame = screen.bounds;
382 [
self setUpMockView:viewControllerMock
385 convertedFrame:viewFrame];
388 [viewController.keyboardInsetManager setUpKeyboardSpringAnimationIfNeeded:nil];
390 [viewController.keyboardInsetManager keyboardSpringAnimation];
391 XCTAssertTrue(keyboardSpringAnimation == nil);
394 CABasicAnimation* nonSpringAnimation = [CABasicAnimation animation];
395 nonSpringAnimation.duration = 1.0;
396 nonSpringAnimation.fromValue = [NSNumber numberWithFloat:0.0];
397 nonSpringAnimation.toValue = [NSNumber numberWithFloat:1.0];
398 nonSpringAnimation.keyPath =
@"position";
399 [viewController.keyboardInsetManager setUpKeyboardSpringAnimationIfNeeded:nonSpringAnimation];
400 keyboardSpringAnimation = [viewController.keyboardInsetManager keyboardSpringAnimation];
402 XCTAssertTrue(keyboardSpringAnimation == nil);
405 CASpringAnimation* springAnimation = [CASpringAnimation animation];
406 springAnimation.mass = 1.0;
407 springAnimation.stiffness = 100.0;
408 springAnimation.damping = 10.0;
409 springAnimation.keyPath =
@"position";
410 springAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)];
411 springAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(100, 100)];
412 [viewController.keyboardInsetManager setUpKeyboardSpringAnimationIfNeeded:springAnimation];
413 keyboardSpringAnimation = [viewController.keyboardInsetManager keyboardSpringAnimation];
414 XCTAssertTrue(keyboardSpringAnimation != nil);
424- (void)testKeyboardAnimationIsShowingAndCompounding {
425 UIScreen* screen = [
self setUpMockScreen];
426 CGFloat screenWidth = screen.bounds.size.width;
427 CGFloat screenHeight = screen.bounds.size.height;
428 CGRect viewFrame = screen.bounds;
442 id managerMock = OCMPartialMock(manager);
443 OCMStub([managerMock shouldIgnoreKeyboardNotification:[OCMArg any]]).andReturn(NO);
447 CGRect initialShowKeyboardBeginFrame = CGRectMake(0, screenHeight, screenWidth, 250);
448 CGRect initialShowKeyboardEndFrame = CGRectMake(0, screenHeight - 250, screenWidth, 500);
449 NSNotification* fakeNotification = [NSNotification
450 notificationWithName:UIKeyboardWillChangeFrameNotification
453 @"UIKeyboardFrameBeginUserInfoKey" : @(initialShowKeyboardBeginFrame),
454 @"UIKeyboardFrameEndUserInfoKey" : @(initialShowKeyboardEndFrame),
455 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
456 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
458 manager.targetViewInsetBottom = 0;
459 [managerMock handleKeyboardNotification:fakeNotification];
460 BOOL isShowingAnimation1 =
manager.keyboardAnimationIsShowing;
461 XCTAssertTrue(isShowingAnimation1);
464 CGRect compoundingShowKeyboardBeginFrame = CGRectMake(0, screenHeight - 250, screenWidth, 250);
465 CGRect compoundingShowKeyboardEndFrame = CGRectMake(0, screenHeight - 500, screenWidth, 500);
466 fakeNotification = [NSNotification
467 notificationWithName:UIKeyboardWillChangeFrameNotification
470 @"UIKeyboardFrameBeginUserInfoKey" : @(compoundingShowKeyboardBeginFrame),
471 @"UIKeyboardFrameEndUserInfoKey" : @(compoundingShowKeyboardEndFrame),
472 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
473 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
476 [managerMock handleKeyboardNotification:fakeNotification];
477 BOOL isShowingAnimation2 =
manager.keyboardAnimationIsShowing;
478 XCTAssertTrue(isShowingAnimation2);
479 XCTAssertTrue(isShowingAnimation1 == isShowingAnimation2);
482 CGRect initialHideKeyboardBeginFrame = CGRectMake(0, screenHeight - 500, screenWidth, 250);
483 CGRect initialHideKeyboardEndFrame = CGRectMake(0, screenHeight - 250, screenWidth, 500);
484 fakeNotification = [NSNotification
485 notificationWithName:UIKeyboardWillChangeFrameNotification
488 @"UIKeyboardFrameBeginUserInfoKey" : @(initialHideKeyboardBeginFrame),
489 @"UIKeyboardFrameEndUserInfoKey" : @(initialHideKeyboardEndFrame),
490 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
491 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
494 [managerMock handleKeyboardNotification:fakeNotification];
495 BOOL isShowingAnimation3 =
manager.keyboardAnimationIsShowing;
496 XCTAssertFalse(isShowingAnimation3);
497 XCTAssertTrue(isShowingAnimation2 != isShowingAnimation3);
500 CGRect compoundingHideKeyboardBeginFrame = CGRectMake(0, screenHeight - 250, screenWidth, 250);
501 CGRect compoundingHideKeyboardEndFrame = CGRectMake(0, screenHeight, screenWidth, 500);
502 fakeNotification = [NSNotification
503 notificationWithName:UIKeyboardWillChangeFrameNotification
506 @"UIKeyboardFrameBeginUserInfoKey" : @(compoundingHideKeyboardBeginFrame),
507 @"UIKeyboardFrameEndUserInfoKey" : @(compoundingHideKeyboardEndFrame),
508 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
509 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
512 [managerMock handleKeyboardNotification:fakeNotification];
513 BOOL isShowingAnimation4 =
manager.keyboardAnimationIsShowing;
514 XCTAssertFalse(isShowingAnimation4);
515 XCTAssertTrue(isShowingAnimation3 == isShowingAnimation4);
518- (void)testShouldIgnoreKeyboardNotification {
528 initWithDelegate:(id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock];
531 UIScreen* screen = [
self setUpMockScreen];
532 CGRect viewFrame = screen.bounds;
533 [
self setUpMockView:viewControllerMock
536 convertedFrame:viewFrame];
538 CGFloat screenWidth = screen.bounds.size.width;
539 CGFloat screenHeight = screen.bounds.size.height;
540 CGRect emptyKeyboard = CGRectZero;
541 CGRect zeroHeightKeyboard = CGRectMake(0, 0, screenWidth, 0);
542 CGRect validKeyboardEndFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
546 NSNotification* notification =
547 [NSNotification notificationWithName:UIKeyboardWillHideNotification
550 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
551 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
552 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
555 BOOL shouldIgnore = [managerMock shouldIgnoreKeyboardNotification:notification];
556 XCTAssertTrue(shouldIgnore == NO);
560 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
563 @"UIKeyboardFrameEndUserInfoKey" : @(emptyKeyboard),
564 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
565 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
567 shouldIgnore = [managerMock shouldIgnoreKeyboardNotification:notification];
568 XCTAssertTrue(shouldIgnore == YES);
573 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
576 @"UIKeyboardFrameEndUserInfoKey" : @(zeroHeightKeyboard),
577 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
578 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
580 shouldIgnore = [managerMock shouldIgnoreKeyboardNotification:notification];
581 XCTAssertTrue(shouldIgnore == NO);
586 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
589 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
590 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
591 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
593 shouldIgnore = [managerMock shouldIgnoreKeyboardNotification:notification];
594 XCTAssertTrue(shouldIgnore == YES);
599 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
602 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
603 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
604 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
606 shouldIgnore = [managerMock shouldIgnoreKeyboardNotification:notification];
607 XCTAssertTrue(shouldIgnore == NO);
609 [(id)viewControllerMock stopMocking];
612- (void)testKeyboardAnimationWillNotCrashWhenEngineDestroyed {
618 [viewController.keyboardInsetManager
619 setUpKeyboardAnimationVsyncClient:^(NSTimeInterval targetTime){
624- (void)testKeyboardAnimationFirstVsyncCallbackCalculatesSafeInset {
631 OCMStub([viewControllerMock isViewLoaded]).andReturn(YES);
632 [viewControllerMock view];
634 (id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock;
638 XCTestExpectation* expectation = [
self expectationWithDescription:@"metrics updated"];
640 __block CGFloat capturedInset = -1.0;
641 __block
BOOL fulfilled = NO;
642 OCMStub([viewControllerMock updateViewportMetricsWithInset:0])
643 .ignoringNonObjectArgs()
644 .andDo(^(NSInvocation* invocation) {
645 [invocation getArgument:&capturedInset atIndex:2];
650 [viewController.keyboardInsetManager invalidateKeyboardAnimationVSyncClient];
651 [expectation fulfill];
656 CASpringAnimation* springAnimation = [CASpringAnimation animation];
657 springAnimation.mass = 1.0;
658 springAnimation.stiffness = 100.0;
659 springAnimation.damping = 10.0;
660 springAnimation.keyPath =
@"position";
662 viewController.keyboardInsetManager.targetViewInsetBottom = 300.0;
665 [viewController.keyboardInsetManager startKeyBoardAnimation:0.25];
666 [viewController.keyboardInsetManager setUpKeyboardSpringAnimationIfNeeded:springAnimation];
673 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
676 XCTAssertFalse(isnan(capturedInset));
677 XCTAssertFalse(isinf(capturedInset));
678 XCTAssertGreaterThanOrEqual(capturedInset, 0.0);
679 XCTAssertLessThan(capturedInset, 300.0);
682- (void)testKeyboardAnimationWillWaitUIThreadVsync {
695 const int delayTime = 1;
700 id mockCADisplayLink = OCMClassMock([CADisplayLink
class]);
702 ClassMethod([mockCADisplayLink displayLinkWithTarget:[OCMArg any]
703 selector:sel_registerName(
"onDisplayLink:")]));
705 XCTestExpectation* expectation = [
self expectationWithDescription:@"keyboard animation callback"];
706 __block CFTimeInterval fulfillTime = 0;
707 CFTimeInterval startTime = CACurrentMediaTime();
708 [viewController.keyboardInsetManager
709 setUpKeyboardAnimationVsyncClient:^(NSTimeInterval targetTime) {
710 fulfillTime = CACurrentMediaTime();
711 [expectation fulfill];
717 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
718 NSTimeInterval epsilon = 0.005;
719 XCTAssertGreaterThanOrEqual(fulfillTime - startTime, delayTime - epsilon);
721 [mockCADisplayLink stopMocking];
724- (void)testCalculateKeyboardAttachMode {
730 (id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock;
731 UIScreen* screen = [
self setUpMockScreen];
732 CGRect viewFrame = screen.bounds;
733 [
self setUpMockView:viewControllerMock
736 convertedFrame:viewFrame];
738 CGFloat screenWidth = screen.bounds.size.width;
739 CGFloat screenHeight = screen.bounds.size.height;
742 CGRect keyboardFrame = CGRectZero;
743 NSNotification* notification =
744 [NSNotification notificationWithName:UIKeyboardWillHideNotification
747 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
748 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
749 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
751 FlutterKeyboardMode keyboardMode =
752 [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
753 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
756 keyboardFrame = CGRectZero;
757 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
760 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
761 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
762 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
764 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
765 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
768 keyboardFrame = CGRectMake(0, 0, screenWidth, 0);
769 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
772 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
773 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
774 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
776 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
777 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
780 keyboardFrame = CGRectMake(0, 0, 320, 320);
781 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
784 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
785 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
786 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
788 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
789 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
792 keyboardFrame = CGRectMake(0, 0, screenWidth, 320);
793 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
796 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
797 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
798 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
800 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
801 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
804 keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
805 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
808 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
809 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
810 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
812 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
813 XCTAssertTrue(keyboardMode == FlutterKeyboardModeDocked);
816 CGFloat longDecimalHeight = 320.666666666666666;
817 keyboardFrame = CGRectMake(0, screenHeight - longDecimalHeight, screenWidth, longDecimalHeight);
818 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
821 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
822 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
823 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
825 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
826 XCTAssertTrue(keyboardMode == FlutterKeyboardModeDocked);
829 keyboardFrame = CGRectMake(0, screenHeight - .0000001, screenWidth, longDecimalHeight);
830 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
833 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
834 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
835 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
837 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
838 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
841 keyboardFrame = CGRectMake(0, screenHeight, screenWidth, 320);
842 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
845 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
846 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
847 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
849 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
850 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
853- (void)testCalculateMultitaskingAdjustment {
854 UIScreen* screen = [UIScreen mainScreen];
855 CGFloat screenWidth = screen.bounds.size.width;
856 CGFloat screenHeight = screen.bounds.size.height;
857 CGRect screenRect = screen.bounds;
858 CGRect convertedViewFrame = CGRectMake(0, 0, 320, screenHeight - 20);
859 CGRect keyboardFrame = CGRectMake(20, screenHeight - 320, screenWidth, 300);
871 CGFloat adjustment = [manager calculateMultitaskingAdjustment:screenRect
872 keyboardFrame:keyboardFrame];
873 XCTAssertTrue(adjustment == 20);
876- (void)testCalculateKeyboardInset {
877 UIScreen* screen = [UIScreen mainScreen];
878 CGFloat screenWidth = screen.bounds.size.width;
879 CGFloat screenHeight = screen.bounds.size.height;
880 CGRect convertedViewFrame = CGRectMake(0, 0, 320, screenHeight - 20);
881 CGRect keyboardFrame = CGRectMake(20, screenHeight - 320, screenWidth, 300);
892 CGFloat inset = [manager calculateKeyboardInset:keyboardFrame
893 keyboardMode:FlutterKeyboardModeDocked];
894 XCTAssertTrue(inset == 300 * screen.scale);
897- (void)testHandleKeyboardNotification {
898 UIScreen* screen = [
self setUpMockScreen];
899 CGFloat screenWidth = screen.bounds.size.width;
900 CGFloat screenHeight = screen.bounds.size.height;
901 CGRect keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
902 CGRect viewFrame = screen.bounds;
904 NSNotification* notification = [NSNotification
905 notificationWithName:UIKeyboardWillShowNotification
908 @"UIKeyboardFrameEndUserInfoKey" : [NSValue valueWithCGRect:keyboardFrame],
909 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
910 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
932- (void)testEnsureBottomInsetIsZeroWhenKeyboardDismissed {
941 (id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock;
945 CGRect keyboardFrame = CGRectZero;
947 NSNotification* fakeNotification =
948 [NSNotification notificationWithName:UIKeyboardWillHideNotification
951 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
952 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
953 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
957 [viewController.keyboardInsetManager handleKeyboardNotification:fakeNotification];
958 XCTAssertTrue(
viewController.keyboardInsetManager.targetViewInsetBottom == 0);
961- (void)testStopKeyBoardAnimationWhenReceivedWillHideNotificationAfterWillShowNotification {
971 (id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock;
974 OCMStub([viewControllerMock isViewLoaded]).andReturn(YES);
975 [viewControllerMock view];
977 UIScreen* screen = [
self setUpMockScreen];
978 OCMStub([screen scale]).andReturn(1.0);
979 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
980 CGRect viewFrame = screen.bounds;
981 [
self setUpMockView:viewControllerMock
984 convertedFrame:viewFrame];
987 CGFloat screenWidth = screen.bounds.size.width;
988 CGFloat screenHeight = screen.bounds.size.height;
989 CGRect keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
993 NSNotification* fakeShowNotification =
994 [NSNotification notificationWithName:UIKeyboardWillShowNotification
997 UIKeyboardFrameEndUserInfoKey : @(keyboardFrame),
998 UIKeyboardAnimationDurationUserInfoKey : @0.25,
999 UIKeyboardIsLocalUserInfoKey : @(isLocal)
1001 [viewController.keyboardInsetManager handleKeyboardNotification:fakeShowNotification];
1002 XCTAssertEqual(
viewController.keyboardInsetManager.targetViewInsetBottom, 320 * screen.scale);
1005 NSNotification* fakeHideNotification =
1006 [NSNotification notificationWithName:UIKeyboardWillHideNotification
1009 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
1010 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.0),
1011 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
1013 [viewController.keyboardInsetManager handleKeyboardNotification:fakeHideNotification];
1014 XCTAssertEqual(
viewController.keyboardInsetManager.targetViewInsetBottom, 0);
1017 XCTAssertNil([
viewController.keyboardInsetManager keyboardAnimationView]);
1018 XCTAssertNil([
viewController.keyboardInsetManager keyboardSpringAnimation]);
1021- (void)testEnsureViewportMetricsWillInvokeAndDisplayLinkWillInvalidateInViewDidDisappear {
1029 (id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock;
1031 id managerMock = OCMPartialMock(
viewController.keyboardInsetManager);
1034 [viewControllerMock viewDidDisappear:YES];
1036 OCMVerify([managerMock ensureViewportMetricsIsCorrect]);
1037 OCMVerify([managerMock invalidateKeyboardAnimationVSyncClient]);
1040- (void)testViewDidDisappearDoesntPauseEngineWhenNotTheViewController {
1048 id viewControllerMock = OCMPartialMock(viewControllerA);
1049 OCMStub([viewControllerMock surfaceUpdated:NO]);
1051 [viewControllerA viewDidDisappear:NO];
1052 OCMReject([lifecycleChannel sendMessage:
@"AppLifecycleState.paused"]);
1053 OCMReject([viewControllerMock surfaceUpdated:[OCMArg any]]);
1056- (void)testAppWillTerminateViewDidDestroyTheEngine {
1058 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1063 OCMStub([viewControllerMock goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
1064 OCMStub([mockEngine destroyContext]);
1065 [viewController applicationWillTerminate:nil];
1066 OCMVerify([viewControllerMock goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
1067 OCMVerify([mockEngine destroyContext]);
1070- (void)testViewDidDisappearDoesPauseEngineWhenIsTheViewController {
1081 OCMStub([viewControllerMock surfaceUpdated:NO]);
1082 [viewController viewDidDisappear:NO];
1083 OCMVerify([lifecycleChannel sendMessage:
@"AppLifecycleState.paused"]);
1084 OCMVerify([viewControllerMock surfaceUpdated:NO]);
1086 XCTAssertNil(weakViewController);
1090 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillAppear {
1092 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1096 [viewController viewWillAppear:YES];
1101 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillAppear {
1103 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1113 [viewControllerA viewWillAppear:YES];
1114 OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]);
1118 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewDidAppear {
1120 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1124 [viewController viewDidAppear:YES];
1129 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewDidAppear {
1131 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1141 [viewControllerA viewDidAppear:YES];
1142 OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]);
1146 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillDisappear {
1154 [viewController viewWillDisappear:NO];
1155 OCMVerify([lifecycleChannel sendMessage:
@"AppLifecycleState.inactive"]);
1159 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillDisappear {
1170 [viewControllerA viewDidDisappear:NO];
1171 OCMReject([lifecycleChannel sendMessage:
@"AppLifecycleState.inactive"]);
1174- (void)testUpdateViewportMetricsIfNeeded_DoesntInvokeEngineWhenNotTheViewController {
1176 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1185 [viewControllerA updateViewportMetricsIfNeeded];
1187 OCMVerify(never(), [mockEngine updateViewportMetrics:viewportMetrics]);
1190- (void)testUpdateViewportMetricsIfNeeded_DoesInvokeEngineWhenIsTheViewController {
1192 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1198 OCMExpect([mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
1199 [viewController updateViewportMetricsIfNeeded];
1200 OCMVerifyAll(mockEngine);
1203- (void)testUpdatedViewportMetricsDoesResizeFlutterViewWhenAutoResizable {
1205 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1210 id mockVC = OCMPartialMock(realVC);
1213 OCMExpect([mockVC updateAutoResizeConstraints]);
1215 [mockVC setAutoResizable:YES];
1217 [mockVC viewDidLayoutSubviews];
1219 OCMVerifyAll(mockVC);
1222- (void)testUpdatedViewportMetricsDoesNotResizeFlutterViewWhenNotAutoResizable {
1224 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1229 id mockVC = OCMPartialMock(realVC);
1232 OCMReject([mockVC updateAutoResizeConstraints]);
1234 [mockVC setAutoResizable:NO];
1236 [mockVC viewDidLayoutSubviews];
1238 OCMVerifyAll(mockVC);
1241- (void)testUpdateViewportMetricsIfNeeded_DoesNotInvokeEngineWhenShouldBeIgnoredDuringRotation {
1243 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1248 UIScreen* screen = [
self setUpMockScreen];
1249 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
1252 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
1253 OCMStub([mockCoordinator transitionDuration]).andReturn(0.5);
1256 [viewController viewWillTransitionToSize:CGSizeZero withTransitionCoordinator:mockCoordinator];
1258 [viewController updateViewportMetricsIfNeeded];
1263- (void)testViewWillTransitionToSize_DoesDelayEngineCallIfNonZeroDuration {
1265 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1270 UIScreen* screen = [
self setUpMockScreen];
1271 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
1275 NSTimeInterval transitionDuration = 0.5;
1276 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
1277 OCMStub([mockCoordinator transitionDuration]).andReturn(transitionDuration);
1280 OCMExpect([mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
1282 [viewController viewWillTransitionToSize:CGSizeZero withTransitionCoordinator:mockCoordinator];
1284 [viewController updateViewportMetricsIfNeeded];
1289 XCTWaiterResult result = [XCTWaiter
1290 waitForExpectations:@[ [
self expectationWithDescription:@"Waiting for rotation duration"] ]
1291 timeout:transitionDuration];
1292 XCTAssertEqual(result, XCTWaiterResultTimedOut);
1294 OCMVerifyAll(mockEngine);
1297- (void)testViewWillTransitionToSize_DoesNotDelayEngineCallIfZeroDuration {
1299 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1304 UIScreen* screen = [
self setUpMockScreen];
1305 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
1309 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
1310 OCMStub([mockCoordinator transitionDuration]).andReturn(0);
1313 OCMExpect([mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
1316 [viewController viewWillTransitionToSize:CGSizeZero withTransitionCoordinator:mockCoordinator];
1317 [viewController updateViewportMetricsIfNeeded];
1319 OCMVerifyAll(mockEngine);
1322- (void)testViewDidLoadDoesntInvokeEngineWhenNotTheViewController {
1324 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1333 UIView*
view = viewControllerA.view;
1334 XCTAssertNotNil(view);
1335 OCMVerify(never(), [mockEngine attachView]);
1338- (void)testViewDidLoadDoesInvokeEngineWhenIsTheViewController {
1340 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1347 XCTAssertNotNil(view);
1348 OCMVerify(times(1), [mockEngine attachView]);
1351- (void)testViewDidLoadDoesntInvokeEngineAttachViewWhenEngineNeedsLaunch {
1353 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1359 [viewController sharedSetupWithProject:nil initialRoute:nil];
1362 XCTAssertNotNil(view);
1363 OCMVerify(never(), [mockEngine attachView]);
1366- (void)testSplashScreenViewRemoveNotCrash {
1371 [flutterViewController setSplashScreenView:[[UIView alloc] init]];
1372 [flutterViewController setSplashScreenView:nil];
1375- (void)testInternalPluginsWeakPtrNotCrash {
1381 [vc addInternalPlugins];
1384 [(NSArray<id<FlutterKeyPrimaryResponder>>*)keyboardManager.primaryResponders firstObject];
1385 sendEvent = [keyPrimaryResponder sendEvent];
1389 sendEvent({}, nil, nil);
1394- (void)testInternalPluginsInvokeInViewDidLoad {
1396 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1404 XCTAssertNotNil(view);
1405 [viewController viewDidLoad];
1409- (void)testBinaryMessenger {
1413 XCTAssertNotNil(vc);
1414 id messenger = OCMProtocolMock(
@protocol(FlutterBinaryMessenger));
1415 OCMStub([
self.mockEngine binaryMessenger]).andReturn(messenger);
1417 OCMVerify([
self.mockEngine binaryMessenger]);
1420- (void)testViewControllerIsReleased {
1422 __weak UIView* weakView;
1431 [viewController loadView];
1432 [viewController viewDidLoad];
1436 XCTAssertNil(weakViewController);
1437 XCTAssertNil(weakView);
1440#pragma mark - Platform Brightness
1442- (void)testItReportsLightPlatformBrightnessByDefault {
1445 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1452 [vc traitCollectionDidChange:nil];
1455 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1456 return [message[@"platformBrightness"] isEqualToString:@"light"];
1460 [settingsChannel stopMocking];
1463- (void)testItReportsPlatformBrightnessWhenViewWillAppear {
1467 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1468 OCMStub([mockEngine settingsChannel]).andReturn(settingsChannel);
1474 [vc viewWillAppear:false];
1477 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1478 return [message[@"platformBrightness"] isEqualToString:@"light"];
1482 [settingsChannel stopMocking];
1485- (void)testItReportsDarkPlatformBrightnessWhenTraitCollectionRequestsIt {
1488 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1489 id mockTraitCollection =
1490 [
self fakeTraitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
1499 OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
1502 [partialMockVC traitCollectionDidChange:nil];
1505 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1506 return [message[@"platformBrightness"] isEqualToString:@"dark"];
1510 [partialMockVC stopMocking];
1511 [settingsChannel stopMocking];
1512 [mockTraitCollection stopMocking];
1517- (UITraitCollection*)fakeTraitCollectionWithUserInterfaceStyle:(UIUserInterfaceStyle)style {
1518 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
1519 OCMStub([mockTraitCollection userInterfaceStyle]).andReturn(style);
1520 return mockTraitCollection;
1523- (void)testTraitCollectionDidChangeCallsResetIntrinsicContentSizeWhenAutoResizable {
1525 id mockEngine = OCMPartialMock([[
FlutterEngine alloc] init]);
1526 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1531 id partialMockVC = OCMPartialMock(realVC);
1533 id mockFlutterView = OCMClassMock([
FlutterView class]);
1534 OCMStub([partialMockVC flutterView]).andReturn(mockFlutterView);
1537 OCMStub([partialMockVC isAutoResizable]).andReturn(YES);
1540 OCMExpect([mockFlutterView resetIntrinsicContentSize]);
1543 [partialMockVC traitCollectionDidChange:nil];
1546 OCMVerifyAll(mockFlutterView);
1549 [partialMockVC stopMocking];
1550 [mockFlutterView stopMocking];
1553#pragma mark - Platform Contrast
1555- (void)testItReportsNormalPlatformContrastByDefault {
1558 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1565 [vc traitCollectionDidChange:nil];
1568 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1569 return [message[@"platformContrast"] isEqualToString:@"normal"];
1573 [settingsChannel stopMocking];
1576- (void)testItReportsPlatformContrastWhenViewWillAppear {
1578 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1582 OCMStub([mockEngine settingsChannel]).andReturn(settingsChannel);
1588 [vc viewWillAppear:false];
1591 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1592 return [message[@"platformContrast"] isEqualToString:@"normal"];
1596 [settingsChannel stopMocking];
1599- (void)testItReportsHighContrastWhenTraitCollectionRequestsIt {
1602 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1604 id mockTraitCollection = [
self fakeTraitCollectionWithContrast:UIAccessibilityContrastHigh];
1613 OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
1616 [partialMockVC traitCollectionDidChange:mockTraitCollection];
1619 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1620 return [message[@"platformContrast"] isEqualToString:@"high"];
1624 [partialMockVC stopMocking];
1625 [settingsChannel stopMocking];
1626 [mockTraitCollection stopMocking];
1629- (void)testItReportsAlwaysUsed24HourFormat {
1632 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1638 OCMStub([mockHourFormat isAlwaysUse24HourFormat]).andReturn(YES);
1639 OCMExpect([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1640 return [message[@"alwaysUse24HourFormat"] isEqual:@(YES)];
1642 [vc onUserSettingsChanged:nil];
1643 [mockHourFormat stopMocking];
1647 OCMStub([mockHourFormat isAlwaysUse24HourFormat]).andReturn(NO);
1648 OCMExpect([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1649 return [message[@"alwaysUse24HourFormat"] isEqual:@(NO)];
1651 [vc onUserSettingsChanged:nil];
1652 [mockHourFormat stopMocking];
1655 [settingsChannel stopMocking];
1658- (void)testOnAccessibilityStatusChangedCallsEnableSemanticsWithFlags {
1661 id mockAccessibilityFeatures = OCMClassMock([FlutterAccessibilityFeatures
class]);
1662 OCMStub([mockAccessibilityFeatures flags]).andReturn(333);
1664 OCMStub([mockViewController accessibilityFeatures]).andReturn(mockAccessibilityFeatures);
1666 [mockViewController onAccessibilityStatusChanged:nil];
1667 OCMVerify([
self.mockEngine enableSemantics:[OCMArg any] withFlags:333]);
1670- (void)testHandleAccessibilityNotifications {
1674 __block NSUInteger callsCount = 0;
1675 OCMStub([mockViewController onAccessibilityStatusChanged:[OCMArg isNotNil]])
1676 .andDo(^(NSInvocation* invocation) {
1680 FlutterAccessibilityFeatures* accessibilityFeatures = [[FlutterAccessibilityFeatures alloc] init];
1681 NSArray<NSString*>* accessibilityNotification = [accessibilityFeatures observedNotificationNames];
1683 for (NSUInteger
i = 0;
i < [accessibilityNotification count];
i++) {
1684 NSString* notificationName = [accessibilityNotification objectAtIndex:i];
1685 [[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:nil];
1686 XCTAssertEqual(callsCount,
i + 1);
1690- (void)testAccessibilityPerformEscapePopsRoute {
1692 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1694 OCMStub([mockEngine navigationChannel]).andReturn(mockNavigationChannel);
1701 OCMVerify([mockNavigationChannel invokeMethod:
@"popRoute" arguments:nil]);
1703 [mockNavigationChannel stopMocking];
1706- (void)testPerformOrientationUpdateForcesOrientationChange {
1707 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1708 currentOrientation:UIInterfaceOrientationLandscapeLeft
1709 didChangeOrientation:YES
1710 resultingOrientation:UIInterfaceOrientationPortrait];
1712 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1713 currentOrientation:UIInterfaceOrientationLandscapeRight
1714 didChangeOrientation:YES
1715 resultingOrientation:UIInterfaceOrientationPortrait];
1717 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1718 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1719 didChangeOrientation:YES
1720 resultingOrientation:UIInterfaceOrientationPortrait];
1722 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1723 currentOrientation:UIInterfaceOrientationLandscapeLeft
1724 didChangeOrientation:YES
1725 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1727 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1728 currentOrientation:UIInterfaceOrientationLandscapeRight
1729 didChangeOrientation:YES
1730 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1732 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1733 currentOrientation:UIInterfaceOrientationPortrait
1734 didChangeOrientation:YES
1735 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1737 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1738 currentOrientation:UIInterfaceOrientationPortrait
1739 didChangeOrientation:YES
1740 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1742 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1743 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1744 didChangeOrientation:YES
1745 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1747 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1748 currentOrientation:UIInterfaceOrientationPortrait
1749 didChangeOrientation:YES
1750 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1752 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1753 currentOrientation:UIInterfaceOrientationLandscapeRight
1754 didChangeOrientation:YES
1755 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1757 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1758 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1759 didChangeOrientation:YES
1760 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1762 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1763 currentOrientation:UIInterfaceOrientationPortrait
1764 didChangeOrientation:YES
1765 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1767 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1768 currentOrientation:UIInterfaceOrientationLandscapeLeft
1769 didChangeOrientation:YES
1770 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1772 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1773 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1774 didChangeOrientation:YES
1775 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1777 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1778 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1779 didChangeOrientation:YES
1780 resultingOrientation:UIInterfaceOrientationPortrait];
1783- (void)testPerformOrientationUpdateDoesNotForceOrientationChange {
1784 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1785 currentOrientation:UIInterfaceOrientationPortrait
1786 didChangeOrientation:NO
1787 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1789 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1790 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1791 didChangeOrientation:NO
1792 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1794 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1795 currentOrientation:UIInterfaceOrientationLandscapeLeft
1796 didChangeOrientation:NO
1797 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1799 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1800 currentOrientation:UIInterfaceOrientationLandscapeRight
1801 didChangeOrientation:NO
1802 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1804 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1805 currentOrientation:UIInterfaceOrientationPortrait
1806 didChangeOrientation:NO
1807 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1809 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1810 currentOrientation:UIInterfaceOrientationLandscapeLeft
1811 didChangeOrientation:NO
1812 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1814 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1815 currentOrientation:UIInterfaceOrientationLandscapeRight
1816 didChangeOrientation:NO
1817 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1819 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1820 currentOrientation:UIInterfaceOrientationPortrait
1821 didChangeOrientation:NO
1822 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1824 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1825 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1826 didChangeOrientation:NO
1827 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1829 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1830 currentOrientation:UIInterfaceOrientationLandscapeLeft
1831 didChangeOrientation:NO
1832 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1834 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1835 currentOrientation:UIInterfaceOrientationLandscapeRight
1836 didChangeOrientation:NO
1837 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1839 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1840 currentOrientation:UIInterfaceOrientationLandscapeLeft
1841 didChangeOrientation:NO
1842 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1844 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1845 currentOrientation:UIInterfaceOrientationLandscapeRight
1846 didChangeOrientation:NO
1847 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1852- (void)orientationTestWithOrientationUpdate:(UIInterfaceOrientationMask)mask
1853 currentOrientation:(UIInterfaceOrientation)currentOrientation
1854 didChangeOrientation:(
BOOL)didChange
1855 resultingOrientation:(UIInterfaceOrientation)resultingOrientation {
1856 id mockApplication = OCMClassMock([UIApplication
class]);
1860 __block __weak
id weakPreferences;
1866 if (@available(iOS 16.0, *)) {
1867 mockWindowScene = OCMClassMock([UIWindowScene
class]);
1868 mockVC = OCMPartialMock(realVC);
1869 OCMStub([mockVC flutterWindowSceneIfViewLoaded]).andReturn(mockWindowScene);
1870 if (realVC.supportedInterfaceOrientations == mask) {
1871 OCMReject([mockWindowScene requestGeometryUpdateWithPreferences:[OCMArg any]
1872 errorHandler:[OCMArg any]]);
1876 OCMExpect([mockWindowScene
1877 requestGeometryUpdateWithPreferences:[OCMArg checkWithBlock:^
BOOL(
1878 UIWindowSceneGeometryPreferencesIOS*
1880 weakPreferences = preferences;
1881 return preferences.interfaceOrientations == mask;
1883 errorHandler:[OCMArg any]]);
1885 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
1886 OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockWindowScene]);
1888 deviceMock = OCMPartialMock([UIDevice currentDevice]);
1890 OCMReject([deviceMock setValue:[OCMArg any] forKey:
@"orientation"]);
1892 OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:
@"orientation"]);
1894 mockWindowScene = OCMClassMock([UIWindowScene
class]);
1895 mockVC = OCMPartialMock(realVC);
1896 OCMStub([mockVC flutterWindowSceneIfViewLoaded]).andReturn(mockWindowScene);
1897 OCMStub(((UIWindowScene*)mockWindowScene).interfaceOrientation).andReturn(currentOrientation);
1900 [realVC performOrientationUpdate:mask];
1901 if (@available(iOS 16.0, *)) {
1902 OCMVerifyAll(mockWindowScene);
1904 OCMVerifyAll(deviceMock);
1907 [mockWindowScene stopMocking];
1908 [deviceMock stopMocking];
1909 [mockApplication stopMocking];
1910 XCTAssertNil(weakPreferences);
1915- (UITraitCollection*)fakeTraitCollectionWithContrast:(UIAccessibilityContrast)contrast {
1916 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
1917 OCMStub([mockTraitCollection accessibilityContrast]).andReturn(contrast);
1918 return mockTraitCollection;
1921- (void)testWillDeallocNotification {
1922 XCTestExpectation* expectation =
1923 [[XCTestExpectation alloc] initWithDescription:@"notification called"];
1930 [NSNotificationCenter.defaultCenter addObserverForName:FlutterViewControllerWillDealloc
1932 queue:[NSOperationQueue mainQueue]
1933 usingBlock:^(NSNotification* _Nonnull note) {
1934 [expectation fulfill];
1936 XCTAssertNotNil(realVC);
1939 [
self waitForExpectations:@[ expectation ] timeout:1.0];
1942- (void)testReleasesKeyboardManagerOnDealloc {
1947 [viewController addInternalPlugins];
1949 XCTAssertNotNil(weakKeyboardManager);
1950 [viewController deregisterNotifications];
1954 XCTAssertNil(weakKeyboardManager);
1957- (void)testDoesntLoadViewInInit {
1960 [engine createShell:@"" libraryURI:@"" initialRoute:nil];
1964 XCTAssertFalse([realVC isViewLoaded],
@"shouldn't have loaded since it hasn't been shown");
1968- (void)testHideOverlay {
1971 [engine createShell:@"" libraryURI:@"" initialRoute:nil];
1975 XCTAssertFalse(realVC.prefersHomeIndicatorAutoHidden,
@"");
1976 [NSNotificationCenter.defaultCenter postNotificationName:FlutterViewControllerHideHomeIndicator
1978 XCTAssertTrue(realVC.prefersHomeIndicatorAutoHidden,
@"");
1982- (void)testNotifyLowMemory {
1988 OCMStub([viewControllerMock surfaceUpdated:NO]);
1989 [viewController beginAppearanceTransition:NO animated:NO];
1990 [viewController endAppearanceTransition];
1994- (void)sendMessage:(
id _Nullable)message reply:(
FlutterReply _Nullable)callback {
1995 NSMutableDictionary* replyMessage = [@{
2008 if (@available(iOS 13.4, *)) {
2015 OCMStub([mockEngine.
keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]])
2016 .andCall(
self, @selector(sendMessage:reply:));
2017 OCMStub([
self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES);
2026 [vc addInternalPlugins];
2028 [vc handlePressEvent:keyUpEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0)
2032 XCTAssert(
self.messageSent != nil);
2033 XCTAssert([
self.messageSent[
@"keymap"] isEqualToString:
@"ios"]);
2034 XCTAssert([
self.messageSent[
@"type"] isEqualToString:
@"keyup"]);
2035 XCTAssert([
self.messageSent[
@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]);
2036 XCTAssert([
self.messageSent[
@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]);
2037 XCTAssert([
self.messageSent[
@"characters"] isEqualToString:
@""]);
2038 XCTAssert([
self.messageSent[
@"charactersIgnoringModifiers"] isEqualToString:
@""]);
2039 [vc deregisterNotifications];
2043 if (@available(iOS 13.4, *)) {
2051 OCMStub([mockEngine.
keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]])
2052 .andCall(
self, @selector(sendMessage:reply:));
2053 OCMStub([
self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES);
2061 [vc addInternalPlugins];
2063 [vc handlePressEvent:keyDownEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0f, "A",
2068 XCTAssert(
self.messageSent != nil);
2069 XCTAssert([
self.messageSent[
@"keymap"] isEqualToString:
@"ios"]);
2070 XCTAssert([
self.messageSent[
@"type"] isEqualToString:
@"keydown"]);
2071 XCTAssert([
self.messageSent[
@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]);
2072 XCTAssert([
self.messageSent[
@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]);
2073 XCTAssert([
self.messageSent[
@"characters"] isEqualToString:
@"A"]);
2074 XCTAssert([
self.messageSent[
@"charactersIgnoringModifiers"] isEqualToString:
@"a"]);
2075 [vc deregisterNotifications];
2080 if (@available(iOS 13.4, *)) {
2086 OCMStub([keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]])
2087 .andCall(
self, @selector(sendMessage:reply:));
2088 OCMStub([
self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES);
2089 OCMStub([
self.mockEngine keyEventChannel]).andReturn(keyEventChannel);
2097 [vc addInternalPlugins];
2099 [vc handlePressEvent:keyEventWithPhase(UIPressPhaseStationary, UIKeyboardHIDUsageKeyboardA,
2100 UIKeyModifierShift, 123.0)
2103 [vc handlePressEvent:keyEventWithPhase(UIPressPhaseCancelled, UIKeyboardHIDUsageKeyboardA,
2104 UIKeyModifierShift, 123.0)
2107 [vc handlePressEvent:keyEventWithPhase(UIPressPhaseChanged, UIKeyboardHIDUsageKeyboardA,
2108 UIKeyModifierShift, 123.0)
2112 XCTAssert(
self.messageSent == nil);
2113 OCMVerify(never(), [keyEventChannel sendMessage:[OCMArg any]]);
2114 [vc deregisterNotifications];
2118 if (@available(iOS 13.4, *)) {
2127 XCTAssertNotNil(vc);
2128 UIView*
view = vc.view;
2129 XCTAssertNotNil(view);
2130 NSArray* gestureRecognizers =
view.gestureRecognizers;
2131 XCTAssertNotNil(gestureRecognizers);
2134 for (
id gesture in gestureRecognizers) {
2135 if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]) {
2140 XCTAssertTrue(found);
2144 if (@available(iOS 13.4, *)) {
2153 XCTAssertNotNil(vc);
2155 id mockPanGestureRecognizer = OCMClassMock([UIPanGestureRecognizer
class]);
2156 XCTAssertNotNil(mockPanGestureRecognizer);
2158 [vc discreteScrollEvent:mockPanGestureRecognizer];
2161 [[mockPanGestureRecognizer verify] locationInView:[OCMArg any]];
2162 [[[
self.mockEngine verify] ignoringNonObjectArgs]
2163 dispatchPointerDataPacket:std::make_unique<flutter::PointerDataPacket>(0)];
2166- (void)testFakeEventTimeStamp {
2170 XCTAssertNotNil(vc);
2173 int64_t current_micros = [[NSProcessInfo processInfo] systemUptime] * 1000 * 1000;
2174 int64_t interval_micros = current_micros - pointer_data.time_stamp;
2175 const int64_t tolerance_millis = 2;
2176 XCTAssertTrue(interval_micros / 1000 < tolerance_millis,
2177 @"PointerData.time_stamp should be equal to NSProcessInfo.systemUptime");
2180- (void)testSplashScreenViewCanSetNil {
2183 [flutterViewController setSplashScreenView:nil];
2186- (void)testLifeCycleNotificationApplicationBecameActive {
2191 UIWindow*
window = [[UIWindow alloc] init];
2192 [window addSubview:flutterViewController.view];
2193 flutterViewController.view.bounds = CGRectMake(0, 0, 100, 100);
2194 [flutterViewController viewDidLayoutSubviews];
2195 NSNotification* sceneNotification =
2196 [NSNotification notificationWithName:UISceneDidActivateNotification object:nil userInfo:nil];
2197 NSNotification* applicationNotification =
2198 [NSNotification notificationWithName:UIApplicationDidBecomeActiveNotification
2201 id mockVC = OCMPartialMock(flutterViewController);
2202 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2203 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2204 OCMReject([mockVC sceneBecameActive:[OCMArg any]]);
2205 OCMVerify([mockVC applicationBecameActive:[OCMArg any]]);
2207 flutterViewController.keyboardInsetManager.isKeyboardInOrTransitioningFromBackground);
2208 OCMVerify([mockVC surfaceUpdated:YES]);
2209 XCTestExpectation* timeoutApplicationLifeCycle =
2210 [
self expectationWithDescription:@"timeoutApplicationLifeCycle"];
2211 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 *
NSEC_PER_SEC)),
2212 dispatch_get_main_queue(), ^{
2213 [timeoutApplicationLifeCycle fulfill];
2214 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.resumed"]);
2215 [flutterViewController deregisterNotifications];
2217 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
2220- (void)testLifeCycleNotificationSceneBecameActive {
2221 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2222 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2223 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2226 [engine runWithEntrypoint:nil];
2229 UIWindow*
window = [[UIWindow alloc] init];
2230 [window addSubview:flutterViewController.view];
2231 flutterViewController.view.bounds = CGRectMake(0, 0, 100, 100);
2232 [flutterViewController viewDidLayoutSubviews];
2233 NSNotification* sceneNotification =
2234 [NSNotification notificationWithName:UISceneDidActivateNotification object:nil userInfo:nil];
2235 NSNotification* applicationNotification =
2236 [NSNotification notificationWithName:UIApplicationDidBecomeActiveNotification
2239 id mockVC = OCMPartialMock(flutterViewController);
2240 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2241 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2242 OCMVerify([mockVC sceneBecameActive:[OCMArg any]]);
2243 OCMReject([mockVC applicationBecameActive:[OCMArg any]]);
2245 flutterViewController.keyboardInsetManager.isKeyboardInOrTransitioningFromBackground);
2246 OCMVerify([mockVC surfaceUpdated:YES]);
2247 XCTestExpectation* timeoutApplicationLifeCycle =
2248 [
self expectationWithDescription:@"timeoutApplicationLifeCycle"];
2249 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 *
NSEC_PER_SEC)),
2250 dispatch_get_main_queue(), ^{
2251 [timeoutApplicationLifeCycle fulfill];
2252 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.resumed"]);
2253 [flutterViewController deregisterNotifications];
2255 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
2256 [mockBundle stopMocking];
2259- (void)testLifeCycleNotificationApplicationWillResignActive {
2264 NSNotification* sceneNotification =
2265 [NSNotification notificationWithName:UISceneWillDeactivateNotification
2268 NSNotification* applicationNotification =
2269 [NSNotification notificationWithName:UIApplicationWillResignActiveNotification
2272 id mockVC = OCMPartialMock(flutterViewController);
2273 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2274 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2275 OCMReject([mockVC sceneWillResignActive:[OCMArg any]]);
2276 OCMVerify([mockVC applicationWillResignActive:[OCMArg any]]);
2277 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2278 [flutterViewController deregisterNotifications];
2281- (void)testLifeCycleNotificationSceneWillResignActive {
2282 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2283 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2284 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2287 [engine runWithEntrypoint:nil];
2290 NSNotification* sceneNotification =
2291 [NSNotification notificationWithName:UISceneWillDeactivateNotification
2294 NSNotification* applicationNotification =
2295 [NSNotification notificationWithName:UIApplicationWillResignActiveNotification
2298 id mockVC = OCMPartialMock(flutterViewController);
2299 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2300 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2301 OCMVerify([mockVC sceneWillResignActive:[OCMArg any]]);
2302 OCMReject([mockVC applicationWillResignActive:[OCMArg any]]);
2303 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2304 [flutterViewController deregisterNotifications];
2305 [mockBundle stopMocking];
2308- (void)testLifeCycleNotificationApplicationWillTerminate {
2313 NSNotification* sceneNotification =
2314 [NSNotification notificationWithName:UISceneDidDisconnectNotification
2317 NSNotification* applicationNotification =
2318 [NSNotification notificationWithName:UIApplicationWillTerminateNotification
2321 id mockVC = OCMPartialMock(flutterViewController);
2322 id mockEngine = OCMPartialMock(
engine);
2323 OCMStub([mockVC
engine]).andReturn(mockEngine);
2324 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2325 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2326 OCMReject([mockVC sceneWillDisconnect:[OCMArg any]]);
2327 OCMVerify([mockVC applicationWillTerminate:[OCMArg any]]);
2328 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
2329 OCMVerify([mockEngine destroyContext]);
2330 [flutterViewController deregisterNotifications];
2333- (void)testLifeCycleNotificationSceneWillTerminate {
2334 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2335 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2336 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2339 [engine runWithEntrypoint:nil];
2342 NSNotification* sceneNotification =
2343 [NSNotification notificationWithName:UISceneDidDisconnectNotification
2346 NSNotification* applicationNotification =
2347 [NSNotification notificationWithName:UIApplicationWillTerminateNotification
2350 id mockVC = OCMPartialMock(flutterViewController);
2351 id mockEngine = OCMPartialMock(
engine);
2352 OCMStub([mockVC
engine]).andReturn(mockEngine);
2353 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2354 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2355 OCMVerify([mockVC sceneWillDisconnect:[OCMArg any]]);
2356 OCMReject([mockVC applicationWillTerminate:[OCMArg any]]);
2357 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
2358 OCMVerify([mockEngine destroyContext]);
2359 [flutterViewController deregisterNotifications];
2360 [mockBundle stopMocking];
2363- (void)testLifeCycleNotificationApplicationDidEnterBackground {
2368 NSNotification* sceneNotification =
2369 [NSNotification notificationWithName:UISceneDidEnterBackgroundNotification
2372 NSNotification* applicationNotification =
2373 [NSNotification notificationWithName:UIApplicationDidEnterBackgroundNotification
2376 id mockVC = OCMPartialMock(flutterViewController);
2377 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2378 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2379 OCMReject([mockVC sceneDidEnterBackground:[OCMArg any]]);
2380 OCMVerify([mockVC applicationDidEnterBackground:[OCMArg any]]);
2382 flutterViewController.keyboardInsetManager.isKeyboardInOrTransitioningFromBackground);
2383 OCMVerify([mockVC surfaceUpdated:NO]);
2384 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.paused"]);
2385 [flutterViewController deregisterNotifications];
2388- (void)testLifeCycleNotificationSceneDidEnterBackground {
2389 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2390 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2391 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2394 [engine runWithEntrypoint:nil];
2397 NSNotification* sceneNotification =
2398 [NSNotification notificationWithName:UISceneDidEnterBackgroundNotification
2401 NSNotification* applicationNotification =
2402 [NSNotification notificationWithName:UIApplicationDidEnterBackgroundNotification
2405 id mockVC = OCMPartialMock(flutterViewController);
2406 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2407 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2408 OCMVerify([mockVC sceneDidEnterBackground:[OCMArg any]]);
2409 OCMReject([mockVC applicationDidEnterBackground:[OCMArg any]]);
2411 flutterViewController.keyboardInsetManager.isKeyboardInOrTransitioningFromBackground);
2412 OCMVerify([mockVC surfaceUpdated:NO]);
2413 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.paused"]);
2414 [flutterViewController deregisterNotifications];
2415 [mockBundle stopMocking];
2418- (void)testLifeCycleNotificationApplicationWillEnterForeground {
2423 NSNotification* sceneNotification =
2424 [NSNotification notificationWithName:UISceneWillEnterForegroundNotification
2427 NSNotification* applicationNotification =
2428 [NSNotification notificationWithName:UIApplicationWillEnterForegroundNotification
2431 id mockVC = OCMPartialMock(flutterViewController);
2432 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2433 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2434 OCMReject([mockVC sceneWillEnterForeground:[OCMArg any]]);
2435 OCMVerify([mockVC applicationWillEnterForeground:[OCMArg any]]);
2436 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2437 [flutterViewController deregisterNotifications];
2440- (void)testLifeCycleNotificationSceneWillEnterForeground {
2441 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2442 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2443 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2446 [engine runWithEntrypoint:nil];
2449 NSNotification* sceneNotification =
2450 [NSNotification notificationWithName:UISceneWillEnterForegroundNotification
2453 NSNotification* applicationNotification =
2454 [NSNotification notificationWithName:UIApplicationWillEnterForegroundNotification
2457 id mockVC = OCMPartialMock(flutterViewController);
2458 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2459 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2460 OCMVerify([mockVC sceneWillEnterForeground:[OCMArg any]]);
2461 OCMReject([mockVC applicationWillEnterForeground:[OCMArg any]]);
2462 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2463 [flutterViewController deregisterNotifications];
2464 [mockBundle stopMocking];
2467- (void)testLifeCycleNotificationCancelledInvalidResumed {
2472 NSNotification* applicationDidBecomeActiveNotification =
2473 [NSNotification notificationWithName:UIApplicationDidBecomeActiveNotification
2476 NSNotification* applicationWillResignActiveNotification =
2477 [NSNotification notificationWithName:UIApplicationWillResignActiveNotification
2480 id mockVC = OCMPartialMock(flutterViewController);
2481 [NSNotificationCenter.defaultCenter postNotification:applicationDidBecomeActiveNotification];
2482 [NSNotificationCenter.defaultCenter postNotification:applicationWillResignActiveNotification];
2483 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2485 XCTestExpectation* timeoutApplicationLifeCycle =
2486 [
self expectationWithDescription:@"timeoutApplicationLifeCycle"];
2487 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 *
NSEC_PER_SEC)),
2488 dispatch_get_main_queue(), ^{
2489 OCMReject([mockVC goToApplicationLifecycle:
@"AppLifecycleState.resumed"]);
2490 [timeoutApplicationLifeCycle fulfill];
2491 [flutterViewController deregisterNotifications];
2493 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
2496- (void)testSetupKeyboardAnimationVsyncClientWillCreateNewVsyncClientForFlutterViewController {
2497 id bundleMock = OCMPartialMock([NSBundle mainBundle]);
2501 double maxFrameRate = 120;
2502 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2510 [viewController.keyboardInsetManager setUpKeyboardAnimationVsyncClient:callback];
2511 XCTAssertNotNil(
viewController.keyboardInsetManager.keyboardAnimationVSyncClient);
2512 CADisplayLink* link =
2513 viewController.keyboardInsetManager.keyboardAnimationVSyncClient.displayLink;
2514 XCTAssertNotNil(link);
2515 CADisplayLink* linkMock = OCMPartialMock(link);
2516 if (@available(iOS 15.0, *)) {
2517 CAFrameRateRange range = CAFrameRateRangeMake(maxFrameRate / 2, maxFrameRate, maxFrameRate);
2518 NSValue* rangeValue = [NSValue valueWithBytes:&range objCType:@encode(CAFrameRateRange)];
2519 [[[(id)linkMock stub] andReturnValue:rangeValue] preferredFrameRateRange];
2521 XCTAssertEqual(linkMock.preferredFrameRateRange.maximum, maxFrameRate);
2522 XCTAssertEqual(linkMock.preferredFrameRateRange.preferred, maxFrameRate);
2523 XCTAssertEqual(linkMock.preferredFrameRateRange.minimum, maxFrameRate / 2);
2525 [[[(id)linkMock stub] andReturnValue:@(maxFrameRate)] preferredFramesPerSecond];
2526 XCTAssertEqual(linkMock.preferredFramesPerSecond, maxFrameRate);
2531 testCreateTouchRateCorrectionVSyncClientWillCreateVsyncClientWhenRefreshRateIsLargerThan60HZ {
2533 double maxFrameRate = 120;
2534 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2540 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2544- (void)testCreateTouchRateCorrectionVSyncClientWillNotCreateNewVSyncClientWhenClientAlreadyExists {
2546 double maxFrameRate = 120;
2547 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2554 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2556 XCTAssertNotNil(clientBefore);
2558 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2560 XCTAssertNotNil(clientAfter);
2562 XCTAssertTrue(clientBefore == clientAfter);
2565- (void)testCreateTouchRateCorrectionVSyncClientWillNotCreateVsyncClientWhenRefreshRateIs60HZ {
2567 double maxFrameRate = 60;
2568 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2574 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2578- (void)testTriggerTouchRateCorrectionVSyncClientCorrectly {
2580 double maxFrameRate = 120;
2581 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2587 [viewController loadView];
2588 [viewController viewDidLoad];
2593 UITouch* fakeTouchBegan = [[UITouch alloc] init];
2594 fakeTouchBegan.phase = UITouchPhaseBegan;
2596 UITouch* fakeTouchMove = [[UITouch alloc] init];
2597 fakeTouchMove.phase = UITouchPhaseMoved;
2599 UITouch* fakeTouchEnd = [[UITouch alloc] init];
2600 fakeTouchEnd.phase = UITouchPhaseEnded;
2602 UITouch* fakeTouchCancelled = [[UITouch alloc] init];
2603 fakeTouchCancelled.phase = UITouchPhaseCancelled;
2606 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchBegan, nil]];
2607 XCTAssertFalse(link.isPaused);
2610 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchEnd, nil]];
2611 XCTAssertTrue(link.isPaused);
2614 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchMove, nil]];
2615 XCTAssertFalse(link.isPaused);
2618 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchCancelled, nil]];
2619 XCTAssertTrue(link.isPaused);
2622 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc]
2623 initWithObjects:fakeTouchBegan, fakeTouchEnd, nil]];
2624 XCTAssertFalse(link.isPaused);
2627 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchEnd,
2628 fakeTouchCancelled, nil]];
2629 XCTAssertTrue(link.isPaused);
2632 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc]
2633 initWithObjects:fakeTouchMove, fakeTouchEnd, nil]];
2634 XCTAssertFalse(link.isPaused);
2637- (void)testFlutterViewControllerStartKeyboardAnimationWillCreateVsyncClientCorrectly {
2638 id mockDisplayLink = OCMClassMock([CADisplayLink
class]);
2639 OCMStub(ClassMethod([mockDisplayLink displayLinkWithTarget:[OCMArg any]
2640 selector:sel_registerName(
"onDisplayLink:")]))
2641 .andReturn(mockDisplayLink);
2650 manager.targetViewInsetBottom = 100;
2651 [manager startKeyBoardAnimation:0.25];
2653 XCTAssertNotNil(
manager.keyboardAnimationVSyncClient);
2656 [mockDisplayLink stopMocking];
2660 testSetupKeyboardAnimationVsyncClientWillNotCreateNewVsyncClientWhenKeyboardAnimationCallbackIsNil {
2666 [viewController.keyboardInsetManager setUpKeyboardAnimationVsyncClient:nil];
2667 XCTAssertNil(
viewController.keyboardInsetManager.keyboardAnimationVSyncClient);
2670- (void)testSupportsShowingSystemContextMenuForIOS16AndAbove {
2676 BOOL supportsShowingSystemContextMenu = [viewController supportsShowingSystemContextMenu];
2677 if (@available(iOS 16.0, *)) {
2678 XCTAssertTrue(supportsShowingSystemContextMenu);
2680 XCTAssertFalse(supportsShowingSystemContextMenu);
2684- (void)testStateIsActiveAndBackgroundWhenApplicationStateIsActive {
2690 id mockApplication = OCMClassMock([UIApplication
class]);
2691 OCMStub([mockApplication applicationState]).andReturn(UIApplicationStateActive);
2692 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2697- (void)testStateIsActiveAndBackgroundWhenApplicationStateIsBackground {
2703 id mockApplication = OCMClassMock([UIApplication
class]);
2704 OCMStub([mockApplication applicationState]).andReturn(UIApplicationStateBackground);
2705 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2710- (void)testStateIsActiveAndBackgroundWhenApplicationStateIsInactive {
2716 id mockApplication = OCMClassMock([UIApplication
class]);
2717 OCMStub([mockApplication applicationState]).andReturn(UIApplicationStateInactive);
2718 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2723- (void)testStateIsActiveAndBackgroundWhenSceneStateIsActive {
2724 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2725 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2726 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2729 [engine runWithEntrypoint:nil];
2734 OCMStub([mockVC activationState]).andReturn(UISceneActivationStateForegroundActive);
2738 [mockBundle stopMocking];
2739 [mockVC stopMocking];
2742- (void)testStateIsActiveAndBackgroundWhenSceneStateIsBackground {
2743 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2744 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2745 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2748 [engine runWithEntrypoint:nil];
2753 OCMStub([mockVC activationState]).andReturn(UISceneActivationStateBackground);
2757 [mockBundle stopMocking];
2758 [mockVC stopMocking];
2761- (void)testStateIsActiveAndBackgroundWhenSceneStateIsInactive {
2762 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2763 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2764 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2767 [engine runWithEntrypoint:nil];
2772 OCMStub([mockVC activationState]).andReturn(UISceneActivationStateForegroundInactive);
2776 [mockBundle stopMocking];
2777 [mockVC stopMocking];
2780- (void)testPerformImplicitEngineCallbacks {
2781 id mockRegistrant = OCMProtocolMock(
@protocol(FlutterPluginRegistrant));
2782 id appDelegate = [[UIApplication sharedApplication] delegate];
2783 [appDelegate setMockLaunchEngine:self.mockEngine];
2784 UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"Flutter" bundle:nil];
2785 XCTAssertTrue([appDelegate respondsToSelector:@selector(setPluginRegistrant:)]);
2786 [appDelegate setPluginRegistrant:mockRegistrant];
2789 [appDelegate setPluginRegistrant:nil];
2791 OCMVerify([
self.mockEngine performImplicitEngineCallback]);
2792 [appDelegate setMockLaunchEngine:nil];
2795- (void)testPerformImplicitEngineCallbacksUsesAppLaunchEventFallbacks {
2801 OCMStub([mockEngine performImplicitEngineCallback]).andReturn(YES);
2802 OCMStub([viewControllerMock awokenFromNib]).andReturn(YES);
2804 id mockApplication = OCMClassMock([UIApplication
class]);
2805 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2807 OCMStub([mockApplication delegate]).andReturn(mockApplicationDelegate);
2808 OCMStub([mockApplicationDelegate takeLaunchEngine]).andReturn(mockEngine);
2810 id mockScene = OCMClassMock([UIScene
class]);
2811 id mockSceneDelegate = OCMProtocolMock(
@protocol(UISceneDelegate));
2812 OCMStub([mockScene delegate]).andReturn(mockSceneDelegate);
2813 OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockScene]);
2817 OCMStub([mockApplicationDelegate lifeCycleDelegate]).andReturn(mockLifecycleDelegate);
2819 [viewControllerMock sharedSetupWithProject:nil initialRoute:nil];
2820 OCMVerify([mockLifecycleDelegate sceneFallbackWillFinishLaunchingApplication:mockApplication]);
2821 OCMVerify([mockLifecycleDelegate sceneFallbackDidFinishLaunchingApplication:mockApplication]);
2824- (void)testPerformImplicitEngineCallbacksNoAppLaunchEventFallbacksWhenNoStoryboard {
2830 OCMStub([mockEngine performImplicitEngineCallback]).andReturn(YES);
2831 OCMStub([viewControllerMock awokenFromNib]).andReturn(NO);
2833 id mockApplication = OCMClassMock([UIApplication
class]);
2834 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2836 OCMStub([mockApplication delegate]).andReturn(mockApplicationDelegate);
2837 OCMStub([mockApplicationDelegate takeLaunchEngine]).andReturn(mockEngine);
2839 id mockScene = OCMClassMock([UIScene
class]);
2840 id mockSceneDelegate = OCMProtocolMock(
@protocol(UISceneDelegate));
2841 OCMStub([mockScene delegate]).andReturn(mockSceneDelegate);
2842 OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockScene]);
2846 OCMStub([mockApplicationDelegate lifeCycleDelegate]).andReturn(mockLifecycleDelegate);
2848 [viewControllerMock sharedSetupWithProject:nil initialRoute:nil];
2849 OCMReject([mockLifecycleDelegate sceneFallbackWillFinishLaunchingApplication:mockApplication]);
2850 OCMReject([mockLifecycleDelegate sceneFallbackDidFinishLaunchingApplication:mockApplication]);
2853- (void)testPerformImplicitEngineCallbacksNoAppLaunchEventFallbacksWhenNoScenes {
2859 OCMStub([mockEngine performImplicitEngineCallback]).andReturn(YES);
2860 OCMStub([viewControllerMock awokenFromNib]).andReturn(YES);
2862 id mockApplication = OCMClassMock([UIApplication
class]);
2863 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2865 OCMStub([mockApplication delegate]).andReturn(mockApplicationDelegate);
2866 OCMStub([mockApplicationDelegate takeLaunchEngine]).andReturn(mockEngine);
2870 OCMStub([mockApplicationDelegate lifeCycleDelegate]).andReturn(mockLifecycleDelegate);
2872 [viewControllerMock sharedSetupWithProject:nil initialRoute:nil];
2873 OCMReject([mockLifecycleDelegate sceneFallbackWillFinishLaunchingApplication:mockApplication]);
2874 OCMReject([mockLifecycleDelegate sceneFallbackDidFinishLaunchingApplication:mockApplication]);
2877- (void)testGrabLaunchEngine {
2878 id appDelegate = [[UIApplication sharedApplication] delegate];
2879 XCTAssertTrue([appDelegate respondsToSelector:@selector(setMockLaunchEngine:)]);
2880 [appDelegate setMockLaunchEngine:self.mockEngine];
2881 UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"Flutter" bundle:nil];
2882 XCTAssertTrue(storyboard);
2888 [appDelegate setMockLaunchEngine:nil];
2891- (void)testDoesntGrabLaunchEngine {
2892 id appDelegate = [[UIApplication sharedApplication] delegate];
2893 XCTAssertTrue([appDelegate respondsToSelector:@selector(setMockLaunchEngine:)]);
2894 [appDelegate setMockLaunchEngine:self.mockEngine];
2896 XCTAssertNotNil(flutterViewController.
engine);
2897 XCTAssertNotEqual(flutterViewController.
engine,
self.mockEngine);
2898 [appDelegate setMockLaunchEngine:nil];
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterReply)(id _Nullable reply)
NSString *const kCADisableMinimumFrameDurationOnPhoneKey
Info.plist key enabling the full range of ProMotion refresh rates for CADisplayLink callbacks and CAA...
void hideKeyboardImmediately()
BOOL keyboardAnimationIsShowing
BOOL isKeyboardInOrTransitioningFromBackground
void ensureViewportMetricsIsCorrect()
UIView * keyboardAnimationView()
SpringAnimation * keyboardSpringAnimation()
CGFloat targetViewInsetBottom
void invalidateKeyboardAnimationVSyncClient()
id< FlutterKeyboardInsetManagerDelegate > delegate
FlutterVSyncClient * keyboardAnimationVSyncClient
static CFStringRef kMessageLoopCFRunLoopMode
static void EnsureInitializedForCurrentThread()
static FML_EMBEDDER_ONLY MessageLoop & GetCurrent()
void RunExpiredTasksNow()
void(* FlutterKeyEventCallback)(bool, void *)
FlutterDesktopBinaryReply callback
A manager type that queries display characteristics, such as high refresh rate capabilities.
BOOL runWithEntrypoint:(nullable NSString *entrypoint)
nullable FlutterFMLTaskRunner * uiTaskRunner()
FlutterViewController * viewController
BOOL runWithEntrypoint:(nullable NSString *entrypoint)
FlutterTextInputPlugin * textInputPlugin
FlutterBasicMessageChannel * lifecycleChannel
BOOL didCallNotifyLowMemory
FlutterViewController * viewController
FlutterBasicMessageChannel * keyEventChannel
void postTask:(void(^ task)(void))
Coordinates the animation of the bottom viewport inset in response to system keyboard visibility chan...
void invalidate()
Terminates any active animations and releases internal resources.
void handleKeyboardNotification:(NSNotification *notification)
Processes a system keyboard notification to update the target inset and begin any necessary animation...
CGFloat targetViewInsetBottom
The physical pixel value of the bottom inset once the current animation reaches its final state.
BOOL didCallStartKeyboardAnimation
A client that wraps a CADisplayLink to deliver synchronized vsync signals.
void onDisplayLink:(CADisplayLink *link)
CADisplayLink * displayLink
NSObject< FlutterBinaryMessenger > * binaryMessenger
BOOL mockIsPadInSlideOverOrStageManagerMode
FlutterEngine * mockEngine
CGRect mockConvertedViewRect
void(^ FlutterSendKeyEvent)(const FlutterKeyEvent &, _Nullable FlutterKeyEventCallback, void *_Nullable)
UITextSmartQuotesType smartQuotesType API_AVAILABLE(ios(11.0))
FlutterViewController * viewController
FlutterTextInputPlugin * textInputPlugin
NSNotificationName const FlutterViewControllerWillDealloc
void(^ FlutterKeyboardAnimationCallback)(NSTimeInterval)
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font manager
Manages the calculations and animations for software keyboard insets.