Unverified Commit 0dd0c2ed authored by hellohuanlin's avatar hellohuanlin Committed by GitHub

[platform_view]Send platform message when platform view is focused (#105050)

parent 94e31845
...@@ -3657,6 +3657,17 @@ targets: ...@@ -3657,6 +3657,17 @@ targets:
- bin/** - bin/**
- .ci.yaml - .ci.yaml
- name: Mac native_platform_view_ui_tests_ios
bringup: true
recipe: devicelab/devicelab_drone
presubmit: false
timeout: 60
properties:
tags: >
["devicelab", "hostonly"]
task_name: native_platform_view_ui_tests_ios
scheduler: luci
- name: Mac run_release_test_macos - name: Mac run_release_test_macos
recipe: devicelab/devicelab_drone recipe: devicelab/devicelab_drone
timeout: 60 timeout: 60
......
...@@ -195,7 +195,7 @@ ...@@ -195,7 +195,7 @@
/dev/devicelab/bin/tasks/module_custom_host_app_name_test.dart @zanderso @flutter/tool /dev/devicelab/bin/tasks/module_custom_host_app_name_test.dart @zanderso @flutter/tool
/dev/devicelab/bin/tasks/module_host_with_custom_build_test.dart @zanderso @flutter/tool /dev/devicelab/bin/tasks/module_host_with_custom_build_test.dart @zanderso @flutter/tool
/dev/devicelab/bin/tasks/module_test.dart @zanderso @flutter/tool /dev/devicelab/bin/tasks/module_test.dart @zanderso @flutter/tool
/dev/devicelab/bin/tasks/native_ui_tests_ios.dart @jmagman @flutter/engine /dev/devicelab/bin/tasks/native_platform_view_ui_tests_ios.dart @hellohuanlin @flutter/ios
/dev/devicelab/bin/tasks/native_ui_tests_macos.dart @cbracken @flutter/desktop /dev/devicelab/bin/tasks/native_ui_tests_macos.dart @cbracken @flutter/desktop
/dev/devicelab/bin/tasks/plugin_test.dart @stuartmorgan @flutter/plugin /dev/devicelab/bin/tasks/plugin_test.dart @stuartmorgan @flutter/plugin
/dev/devicelab/bin/tasks/plugin_test_ios.dart @jmagman @flutter/ios /dev/devicelab/bin/tasks/plugin_test_ios.dart @jmagman @flutter/ios
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter_devicelab/framework/devices.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/ios.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
Future<void> main() async {
await task(() async {
final String projectDirectory = '${flutterDirectory.path}/dev/integration_tests/ios_platform_view_tests';
await inDirectory(projectDirectory, () async {
section('Build clean');
await flutter('clean');
section('Build platform view app');
await flutter(
'build',
options: <String>[
'ios',
'-v',
'--release',
'--config-only',
],
);
});
section('Run platform view XCUITests');
final Device device = await devices.workingDevice;
if (!await runXcodeTests(
platformDirectory: path.join(projectDirectory, 'ios'),
destination: 'id=${device.deviceId}',
testName: 'native_platform_view_ui_tests_ios',
)) {
return TaskResult.failure('Platform view XCUITests failed');
}
return TaskResult.success(null);
});
}
...@@ -83,6 +83,9 @@ TaskFunction createIOSPlatformViewTests() { ...@@ -83,6 +83,9 @@ TaskFunction createIOSPlatformViewTests() {
return DriverTest( return DriverTest(
'${flutterDirectory.path}/dev/integration_tests/ios_platform_view_tests', '${flutterDirectory.path}/dev/integration_tests/ios_platform_view_tests',
'lib/main.dart', 'lib/main.dart',
extraOptions: <String>[
'--dart-define=ENABLE_DRIVER_EXTENSION=true',
],
); );
} }
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@import XCTest;
@interface XCUIElement(KeyboardFocus)
@property (nonatomic, readonly) BOOL flt_hasKeyboardFocus;
@end
@implementation XCUIElement(KeyboardFocus)
- (BOOL)flt_hasKeyboardFocus {
return [[self valueForKey:@"hasKeyboardFocus"] boolValue];
}
@end
@interface PlatformViewUITests : XCTestCase
@property (strong) XCUIApplication *app;
@end
@implementation PlatformViewUITests
- (void)setUp {
self.continueAfterFailure = NO;
self.app = [[XCUIApplication alloc] init];
[self.app launch];
}
- (void)testPlatformViewFocus {
XCUIElement *entranceButton = self.app.buttons[@"platform view focus test"];
XCTAssertTrue([entranceButton waitForExistenceWithTimeout:1]);
[entranceButton tap];
XCUIElement *platformView = self.app.textFields[@"platform_view[0]"];
XCTAssertTrue([platformView waitForExistenceWithTimeout:1]);
XCUIElement *flutterTextField = self.app.textFields[@"Flutter Text Field"];
XCTAssertTrue([flutterTextField waitForExistenceWithTimeout:1]);
[flutterTextField tap];
XCTAssertTrue([self.app.windows.element waitForExistenceWithTimeout:1]);
XCTAssertFalse(platformView.flt_hasKeyboardFocus);
XCTAssertTrue(flutterTextField.flt_hasKeyboardFocus);
// Tapping on platformView should unfocus the previously focused flutterTextField
[platformView tap];
XCTAssertTrue(platformView.flt_hasKeyboardFocus);
XCTAssertFalse(flutterTextField.flt_hasKeyboardFocus);
}
@end
...@@ -14,8 +14,21 @@ ...@@ -14,8 +14,21 @@
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
E09898DE2853DBE800064317 /* ViewFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = E09898DD2853DBE800064317 /* ViewFactory.m */; };
E09898E12853DC3C00064317 /* TextFieldFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = E09898E02853DC3C00064317 /* TextFieldFactory.m */; };
E09898E92853E9F000064317 /* PlatformViewUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = E09898E82853E9F000064317 /* PlatformViewUITests.m */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
E09898EC2853E9F000064317 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 97C146ED1CF9000F007C117D;
remoteInfo = Runner;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */ /* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = { 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase; isa = PBXCopyFilesBuildPhase;
...@@ -44,6 +57,12 @@ ...@@ -44,6 +57,12 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
E09898DC2853DBE800064317 /* ViewFactory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewFactory.h; sourceTree = "<group>"; };
E09898DD2853DBE800064317 /* ViewFactory.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewFactory.m; sourceTree = "<group>"; };
E09898DF2853DC3C00064317 /* TextFieldFactory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TextFieldFactory.h; sourceTree = "<group>"; };
E09898E02853DC3C00064317 /* TextFieldFactory.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TextFieldFactory.m; sourceTree = "<group>"; };
E09898E62853E9F000064317 /* PlatformViewUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PlatformViewUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
E09898E82853E9F000064317 /* PlatformViewUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlatformViewUITests.m; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
...@@ -54,6 +73,13 @@ ...@@ -54,6 +73,13 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
E09898E32853E9F000064317 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
...@@ -73,6 +99,7 @@ ...@@ -73,6 +99,7 @@
children = ( children = (
9740EEB11CF90186004384FC /* Flutter */, 9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */, 97C146F01CF9000F007C117D /* Runner */,
E09898E72853E9F000064317 /* PlatformViewUITests */,
97C146EF1CF9000F007C117D /* Products */, 97C146EF1CF9000F007C117D /* Products */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -81,6 +108,7 @@ ...@@ -81,6 +108,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
97C146EE1CF9000F007C117D /* Runner.app */, 97C146EE1CF9000F007C117D /* Runner.app */,
E09898E62853E9F000064317 /* PlatformViewUITests.xctest */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -90,6 +118,10 @@ ...@@ -90,6 +118,10 @@
children = ( children = (
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
E09898DF2853DC3C00064317 /* TextFieldFactory.h */,
E09898E02853DC3C00064317 /* TextFieldFactory.m */,
E09898DC2853DBE800064317 /* ViewFactory.h */,
E09898DD2853DBE800064317 /* ViewFactory.m */,
97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
...@@ -109,6 +141,14 @@ ...@@ -109,6 +141,14 @@
name = "Supporting Files"; name = "Supporting Files";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
E09898E72853E9F000064317 /* PlatformViewUITests */ = {
isa = PBXGroup;
children = (
E09898E82853E9F000064317 /* PlatformViewUITests.m */,
);
path = PlatformViewUITests;
sourceTree = "<group>";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
...@@ -132,6 +172,24 @@ ...@@ -132,6 +172,24 @@
productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
}; };
E09898E52853E9F000064317 /* PlatformViewUITests */ = {
isa = PBXNativeTarget;
buildConfigurationList = E09898EE2853E9F000064317 /* Build configuration list for PBXNativeTarget "PlatformViewUITests" */;
buildPhases = (
E09898E22853E9F000064317 /* Sources */,
E09898E32853E9F000064317 /* Frameworks */,
E09898E42853E9F000064317 /* Resources */,
);
buildRules = (
);
dependencies = (
E09898ED2853E9F000064317 /* PBXTargetDependency */,
);
name = PlatformViewUITests;
productName = PlatformViewUITests;
productReference = E09898E62853E9F000064317 /* PlatformViewUITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing";
};
/* End PBXNativeTarget section */ /* End PBXNativeTarget section */
/* Begin PBXProject section */ /* Begin PBXProject section */
...@@ -144,6 +202,10 @@ ...@@ -144,6 +202,10 @@
97C146ED1CF9000F007C117D = { 97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1; CreatedOnToolsVersion = 7.3.1;
}; };
E09898E52853E9F000064317 = {
CreatedOnToolsVersion = 13.3.1;
TestTargetID = 97C146ED1CF9000F007C117D;
};
}; };
}; };
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
...@@ -160,6 +222,7 @@ ...@@ -160,6 +222,7 @@
projectRoot = ""; projectRoot = "";
targets = ( targets = (
97C146ED1CF9000F007C117D /* Runner */, 97C146ED1CF9000F007C117D /* Runner */,
E09898E52853E9F000064317 /* PlatformViewUITests */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
...@@ -176,6 +239,13 @@ ...@@ -176,6 +239,13 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
E09898E42853E9F000064317 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */ /* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */
...@@ -216,12 +286,30 @@ ...@@ -216,12 +286,30 @@
files = ( files = (
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
97C146F31CF9000F007C117D /* main.m in Sources */, 97C146F31CF9000F007C117D /* main.m in Sources */,
E09898E12853DC3C00064317 /* TextFieldFactory.m in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
E09898DE2853DBE800064317 /* ViewFactory.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
E09898E22853E9F000064317 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E09898E92853E9F000064317 /* PlatformViewUITests.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXSourcesBuildPhase section */ /* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
E09898ED2853E9F000064317 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 97C146ED1CF9000F007C117D /* Runner */;
targetProxy = E09898EC2853E9F000064317 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */ /* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = { 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup; isa = PBXVariantGroup;
...@@ -454,6 +542,79 @@ ...@@ -454,6 +542,79 @@
}; };
name = Release; name = Release;
}; };
E09898EF2853E9F000064317 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = io.flutter.platformview.PlatformViewUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = Runner;
};
name = Debug;
};
E09898F02853E9F000064317 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = io.flutter.platformview.PlatformViewUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = Runner;
};
name = Release;
};
E09898F12853E9F000064317 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = io.flutter.platformview.PlatformViewUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = Runner;
};
name = Profile;
};
/* End XCBuildConfiguration section */ /* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */ /* Begin XCConfigurationList section */
...@@ -477,6 +638,16 @@ ...@@ -477,6 +638,16 @@
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
E09898EE2853E9F000064317 /* Build configuration list for PBXNativeTarget "PlatformViewUITests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
E09898EF2853E9F000064317 /* Debug */,
E09898F02853E9F000064317 /* Release */,
E09898F12853E9F000064317 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */ /* End XCConfigurationList section */
}; };
rootObject = 97C146E61CF9000F007C117D /* Project object */; rootObject = 97C146E61CF9000F007C117D /* Project object */;
......
...@@ -27,8 +27,6 @@ ...@@ -27,8 +27,6 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion> <MacroExpansion>
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
...@@ -38,8 +36,18 @@ ...@@ -38,8 +36,18 @@
ReferencedContainer = "container:Runner.xcodeproj"> ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
<AdditionalOptions> <Testables>
</AdditionalOptions> <TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E09898E52853E9F000064317"
BuildableName = "PlatformViewUITests.xctest"
BlueprintName = "PlatformViewUITests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug" buildConfiguration = "Debug"
...@@ -61,8 +69,6 @@ ...@@ -61,8 +69,6 @@
ReferencedContainer = "container:Runner.xcodeproj"> ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Profile" buildConfiguration = "Profile"
......
...@@ -4,43 +4,8 @@ ...@@ -4,43 +4,8 @@
#import "AppDelegate.h" #import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h" #import "GeneratedPluginRegistrant.h"
#import "ViewFactory.h"
@interface PlatformView: NSObject<FlutterPlatformView> #import "TextFieldFactory.h"
@property (strong, nonatomic) UIView *platformView;
@end
@implementation PlatformView
- (instancetype)init
{
self = [super init];
if (self) {
self.platformView = [[UIView alloc] init];
self.platformView.backgroundColor = [UIColor blueColor];
}
return self;
}
- (UIView *)view {
return self.platformView;
}
@end
@interface ViewFactory: NSObject<FlutterPlatformViewFactory>
@end
@implementation ViewFactory
- (NSObject<FlutterPlatformView> *)createWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id)args {
PlatformView *platformView = [[PlatformView alloc] init];
return platformView;
}
@end
@implementation AppDelegate @implementation AppDelegate
...@@ -48,7 +13,9 @@ ...@@ -48,7 +13,9 @@
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self]; [GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch. // Override point for customization after application launch.
[[self registrarForPlugin:@"flutter"] registerViewFactory:[ViewFactory new] withId:@"platform_view"]; id<FlutterPluginRegistrar> registrar = [self registrarForPlugin:@"flutter"];
[registrar registerViewFactory:[[ViewFactory alloc] init] withId:@"platform_view"];
[registrar registerViewFactory:[[TextFieldFactory alloc] init] withId:@"platform_text_field"];
return [super application:application didFinishLaunchingWithOptions:launchOptions]; return [super application:application didFinishLaunchingWithOptions:launchOptions];
} }
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface TextFieldFactory : NSObject<FlutterPlatformViewFactory>
@end
NS_ASSUME_NONNULL_END
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "TextFieldFactory.h"
@interface PlatformTextField: NSObject<FlutterPlatformView>
@property (strong, nonatomic) UITextField *textField;
@end
@implementation PlatformTextField
- (instancetype)init
{
self = [super init];
if (self) {
_textField = [[UITextField alloc] init];
_textField.text = @"Platform Text Field";
}
return self;
}
- (UIView *)view {
return self.textField;
}
@end
@implementation TextFieldFactory
- (NSObject<FlutterPlatformView> *)createWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id)args {
return [[PlatformTextField alloc] init];
}
@end
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface ViewFactory: NSObject<FlutterPlatformViewFactory>
@end
NS_ASSUME_NONNULL_END
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ViewFactory.h"
@interface PlatformView: NSObject<FlutterPlatformView>
@property (strong, nonatomic) UIView *platformView;
@end
@implementation PlatformView
- (instancetype)init
{
self = [super init];
if (self) {
_platformView = [[UIView alloc] init];
_platformView.backgroundColor = [UIColor blueColor];
}
return self;
}
- (UIView *)view {
return self.platformView;
}
@end
@implementation ViewFactory
- (NSObject<FlutterPlatformView> *)createWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id)args {
return [[PlatformView alloc] init];
}
@end
...@@ -6,7 +6,12 @@ import 'package:flutter/material.dart'; ...@@ -6,7 +6,12 @@ import 'package:flutter/material.dart';
import 'package:flutter_driver/driver_extension.dart'; import 'package:flutter_driver/driver_extension.dart';
void main() { void main() {
// enableFlutterDriverExtension() will disable keyboard,
// which is required for flutter_driver tests
// But breaks the XCUITests
if (const bool.fromEnvironment('ENABLE_DRIVER_EXTENSION')) {
enableFlutterDriverExtension(); enableFlutterDriverExtension();
}
runApp(const MyApp()); runApp(const MyApp());
} }
...@@ -26,7 +31,7 @@ class MyApp extends StatelessWidget { ...@@ -26,7 +31,7 @@ class MyApp extends StatelessWidget {
} }
} }
/// A page with a button in the center. /// A page with several buttons in the center.
/// ///
/// On press the button, a page with platform view should be pushed into the scene. /// On press the button, a page with platform view should be pushed into the scene.
class MyHomePage extends StatefulWidget { class MyHomePage extends StatefulWidget {
...@@ -51,8 +56,8 @@ class _MyHomePageState extends State<MyHomePage> { ...@@ -51,8 +56,8 @@ class _MyHomePageState extends State<MyHomePage> {
onPressed: () { onPressed: () {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute<PlatformViewPage>( MaterialPageRoute<MergeThreadTestPage>(
builder: (BuildContext context) => const PlatformViewPage()), builder: (BuildContext context) => const MergeThreadTestPage()),
); );
}, },
), ),
...@@ -62,14 +67,25 @@ class _MyHomePageState extends State<MyHomePage> { ...@@ -62,14 +67,25 @@ class _MyHomePageState extends State<MyHomePage> {
child: const Text('Tap to unmerge threads'), child: const Text('Tap to unmerge threads'),
onPressed: () {}, onPressed: () {},
), ),
TextButton(
key: const ValueKey<String>('platform_view_focus_test'),
child: const Text('platform view focus test'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<FocusTestPage>(
builder: (BuildContext context) => const FocusTestPage()),
);
},
),
]), ]),
); );
} }
} }
/// A page contains the platform view to be tested. /// A page to test thread merge for platform view.
class PlatformViewPage extends StatelessWidget { class MergeThreadTestPage extends StatelessWidget {
const PlatformViewPage({super.key}); const MergeThreadTestPage({super.key});
static Key button = const ValueKey<String>('plus_button'); static Key button = const ValueKey<String>('plus_button');
...@@ -77,7 +93,7 @@ class PlatformViewPage extends StatelessWidget { ...@@ -77,7 +93,7 @@ class PlatformViewPage extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Platform View'), title: const Text('Platform View Thread Merge Tests'),
), ),
body: Column( body: Column(
children: <Widget>[ children: <Widget>[
...@@ -97,3 +113,44 @@ class PlatformViewPage extends StatelessWidget { ...@@ -97,3 +113,44 @@ class PlatformViewPage extends StatelessWidget {
); );
} }
} }
/// A page to test platform view focus.
class FocusTestPage extends StatefulWidget {
const FocusTestPage({super.key});
@override
State<FocusTestPage> createState() => _FocusTestPageState();
}
class _FocusTestPageState extends State<FocusTestPage> {
late TextEditingController _controller;
@override
void initState() {
super.initState();
_controller = TextEditingController();
_controller.text = 'Flutter Text Field';
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Platform View Focus Tests'),
),
body: Column(
children: <Widget>[
const SizedBox(
width: 300,
height: 50,
child: UiKitView(viewType: 'platform_text_field'),
),
TextField(
controller: _controller,
),
],
),
);
}
}
...@@ -569,12 +569,13 @@ class _UiKitViewState extends State<UiKitView> { ...@@ -569,12 +569,13 @@ class _UiKitViewState extends State<UiKitView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (_controller == null) { final UiKitViewController? controller = _controller;
if (controller == null) {
return const SizedBox.expand(); return const SizedBox.expand();
} }
return Focus( return Focus(
focusNode: _focusNode, focusNode: _focusNode,
onFocusChange: _onFocusChange, onFocusChange: (bool isFocused) => _onFocusChange(isFocused, controller),
child: _UiKitPlatformView( child: _UiKitPlatformView(
controller: _controller!, controller: _controller!,
hitTestBehavior: widget.hitTestBehavior, hitTestBehavior: widget.hitTestBehavior,
...@@ -659,8 +660,17 @@ class _UiKitViewState extends State<UiKitView> { ...@@ -659,8 +660,17 @@ class _UiKitViewState extends State<UiKitView> {
}); });
} }
void _onFocusChange(bool isFocused) { void _onFocusChange(bool isFocused, UiKitViewController controller) {
// TODO(hellohuanlin): send 'TextInput.setPlatformViewClient' channel message to engine after the engine is updated to handle this message. if (!isFocused) {
// Unlike Android, we do not need to send "clearFocus" channel message
// to the engine, because focusing on another view will automatically
// cancel the focus on the previously focused platform view.
return;
}
SystemChannels.textInput.invokeMethod<void>(
'TextInput.setPlatformViewClient',
<String, dynamic>{'platformViewId': controller.id},
);
} }
} }
......
...@@ -2064,6 +2064,45 @@ void main() { ...@@ -2064,6 +2064,45 @@ void main() {
expect(uiKitViewFocusNode.hasFocus, isTrue); expect(uiKitViewFocusNode.hasFocus, isTrue);
}); });
testWidgets('UiKitView sends TextInput.setPlatformViewClient when focused', (WidgetTester tester) async {
final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
final FakeIosPlatformViewsController viewsController = FakeIosPlatformViewsController();
viewsController.registerViewType('webview');
await tester.pumpWidget(
const UiKitView(viewType: 'webview', layoutDirection: TextDirection.ltr)
);
// First frame is before the platform view was created so the render object
// is not yet in the tree.
await tester.pump();
final Focus uiKitViewFocusWidget = tester.widget(
find.descendant(
of: find.byType(UiKitView),
matching: find.byType(Focus),
),
);
final FocusNode uiKitViewFocusNode = uiKitViewFocusWidget.focusNode!;
late Map<String, dynamic> channelArguments;
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.textInput, (MethodCall call) {
if (call.method == 'TextInput.setPlatformViewClient') {
channelArguments = call.arguments as Map<String, dynamic>;
}
return null;
});
expect(uiKitViewFocusNode.hasFocus, false);
uiKitViewFocusNode.requestFocus();
await tester.pump();
expect(uiKitViewFocusNode.hasFocus, true);
expect(channelArguments['platformViewId'], currentViewId + 1);
});
testWidgets('UiKitView has correct semantics', (WidgetTester tester) async { testWidgets('UiKitView has correct semantics', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics(); final SemanticsHandle handle = tester.ensureSemantics();
final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment