mirror of
https://github.com/flutter/flutter
synced 2024-10-02 14:34:22 +00:00
[platform_view]Send platform message when platform view is focused (#105050)
This commit is contained in:
parent
94e318456e
commit
0dd0c2edca
11
.ci.yaml
11
.ci.yaml
|
@ -3657,6 +3657,17 @@ targets:
|
|||
- bin/**
|
||||
- .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
|
||||
recipe: devicelab/devicelab_drone
|
||||
timeout: 60
|
||||
|
|
|
@ -195,7 +195,7 @@
|
|||
/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_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/plugin_test.dart @stuartmorgan @flutter/plugin
|
||||
/dev/devicelab/bin/tasks/plugin_test_ios.dart @jmagman @flutter/ios
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
// 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() {
|
|||
return DriverTest(
|
||||
'${flutterDirectory.path}/dev/integration_tests/ios_platform_view_tests',
|
||||
'lib/main.dart',
|
||||
extraOptions: <String>[
|
||||
'--dart-define=ENABLE_DRIVER_EXTENSION=true',
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
// 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 @@
|
|||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
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 */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
E09898EC2853E9F000064317 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 97C146ED1CF9000F007C117D;
|
||||
remoteInfo = Runner;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
|
@ -44,6 +57,12 @@
|
|||
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>"; };
|
||||
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 */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -54,6 +73,13 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
E09898E32853E9F000064317 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
|
@ -73,6 +99,7 @@
|
|||
children = (
|
||||
9740EEB11CF90186004384FC /* Flutter */,
|
||||
97C146F01CF9000F007C117D /* Runner */,
|
||||
E09898E72853E9F000064317 /* PlatformViewUITests */,
|
||||
97C146EF1CF9000F007C117D /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
|
@ -81,6 +108,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
97C146EE1CF9000F007C117D /* Runner.app */,
|
||||
E09898E62853E9F000064317 /* PlatformViewUITests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
|
@ -90,6 +118,10 @@
|
|||
children = (
|
||||
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
|
||||
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
|
||||
E09898DF2853DC3C00064317 /* TextFieldFactory.h */,
|
||||
E09898E02853DC3C00064317 /* TextFieldFactory.m */,
|
||||
E09898DC2853DBE800064317 /* ViewFactory.h */,
|
||||
E09898DD2853DBE800064317 /* ViewFactory.m */,
|
||||
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
||||
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
||||
|
@ -109,6 +141,14 @@
|
|||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E09898E72853E9F000064317 /* PlatformViewUITests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E09898E82853E9F000064317 /* PlatformViewUITests.m */,
|
||||
);
|
||||
path = PlatformViewUITests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
|
@ -132,6 +172,24 @@
|
|||
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
|
||||
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 */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
|
@ -144,6 +202,10 @@
|
|||
97C146ED1CF9000F007C117D = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
};
|
||||
E09898E52853E9F000064317 = {
|
||||
CreatedOnToolsVersion = 13.3.1;
|
||||
TestTargetID = 97C146ED1CF9000F007C117D;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
|
||||
|
@ -160,6 +222,7 @@
|
|||
projectRoot = "";
|
||||
targets = (
|
||||
97C146ED1CF9000F007C117D /* Runner */,
|
||||
E09898E52853E9F000064317 /* PlatformViewUITests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
@ -176,6 +239,13 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
E09898E42853E9F000064317 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
|
@ -216,12 +286,30 @@
|
|||
files = (
|
||||
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
|
||||
97C146F31CF9000F007C117D /* main.m in Sources */,
|
||||
E09898E12853DC3C00064317 /* TextFieldFactory.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;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
E09898ED2853E9F000064317 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 97C146ED1CF9000F007C117D /* Runner */;
|
||||
targetProxy = E09898EC2853E9F000064317 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
|
@ -454,6 +542,79 @@
|
|||
};
|
||||
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 */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
|
@ -477,6 +638,16 @@
|
|||
defaultConfigurationIsVisible = 0;
|
||||
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 */
|
||||
};
|
||||
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||
|
|
|
@ -27,8 +27,6 @@
|
|||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
|
@ -38,8 +36,18 @@
|
|||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "E09898E52853E9F000064317"
|
||||
BuildableName = "PlatformViewUITests.xctest"
|
||||
BlueprintName = "PlatformViewUITests"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
|
@ -61,8 +69,6 @@
|
|||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Profile"
|
||||
|
|
|
@ -4,43 +4,8 @@
|
|||
|
||||
#import "AppDelegate.h"
|
||||
#import "GeneratedPluginRegistrant.h"
|
||||
|
||||
@interface PlatformView: NSObject<FlutterPlatformView>
|
||||
|
||||
@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
|
||||
#import "ViewFactory.h"
|
||||
#import "TextFieldFactory.h"
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
|
@ -48,7 +13,9 @@
|
|||
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||
[GeneratedPluginRegistrant registerWithRegistry:self];
|
||||
// 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];
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
// 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
|
|
@ -0,0 +1,37 @@
|
|||
// 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
|
|
@ -0,0 +1,14 @@
|
|||
// 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
|
|
@ -0,0 +1,38 @@
|
|||
// 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';
|
|||
import 'package:flutter_driver/driver_extension.dart';
|
||||
|
||||
void main() {
|
||||
enableFlutterDriverExtension();
|
||||
// enableFlutterDriverExtension() will disable keyboard,
|
||||
// which is required for flutter_driver tests
|
||||
// But breaks the XCUITests
|
||||
if (const bool.fromEnvironment('ENABLE_DRIVER_EXTENSION')) {
|
||||
enableFlutterDriverExtension();
|
||||
}
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
class MyHomePage extends StatefulWidget {
|
||||
|
@ -51,8 +56,8 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute<PlatformViewPage>(
|
||||
builder: (BuildContext context) => const PlatformViewPage()),
|
||||
MaterialPageRoute<MergeThreadTestPage>(
|
||||
builder: (BuildContext context) => const MergeThreadTestPage()),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
@ -62,14 +67,25 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||
child: const Text('Tap to unmerge threads'),
|
||||
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.
|
||||
class PlatformViewPage extends StatelessWidget {
|
||||
const PlatformViewPage({super.key});
|
||||
/// A page to test thread merge for platform view.
|
||||
class MergeThreadTestPage extends StatelessWidget {
|
||||
const MergeThreadTestPage({super.key});
|
||||
|
||||
static Key button = const ValueKey<String>('plus_button');
|
||||
|
||||
|
@ -77,7 +93,7 @@ class PlatformViewPage extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Platform View'),
|
||||
title: const Text('Platform View Thread Merge Tests'),
|
||||
),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
|
@ -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> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_controller == null) {
|
||||
final UiKitViewController? controller = _controller;
|
||||
if (controller == null) {
|
||||
return const SizedBox.expand();
|
||||
}
|
||||
return Focus(
|
||||
focusNode: _focusNode,
|
||||
onFocusChange: _onFocusChange,
|
||||
onFocusChange: (bool isFocused) => _onFocusChange(isFocused, controller),
|
||||
child: _UiKitPlatformView(
|
||||
controller: _controller!,
|
||||
hitTestBehavior: widget.hitTestBehavior,
|
||||
|
@ -659,8 +660,17 @@ class _UiKitViewState extends State<UiKitView> {
|
|||
});
|
||||
}
|
||||
|
||||
void _onFocusChange(bool isFocused) {
|
||||
// TODO(hellohuanlin): send 'TextInput.setPlatformViewClient' channel message to engine after the engine is updated to handle this message.
|
||||
void _onFocusChange(bool isFocused, UiKitViewController controller) {
|
||||
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() {
|
|||
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 {
|
||||
final SemanticsHandle handle = tester.ensureSemantics();
|
||||
final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
|
||||
|
|
Loading…
Reference in a new issue