Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
0dd0c2ed
Unverified
Commit
0dd0c2ed
authored
Jun 23, 2022
by
hellohuanlin
Committed by
GitHub
Jun 23, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[platform_view]Send platform message when platform view is focused (#105050)
parent
94e31845
Changes
15
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
522 additions
and
57 deletions
+522
-57
.ci.yaml
.ci.yaml
+11
-0
TESTOWNERS
TESTOWNERS
+1
-1
native_platform_view_ui_tests_ios.dart
...evicelab/bin/tasks/native_platform_view_ui_tests_ios.dart
+47
-0
integration_tests.dart
dev/devicelab/lib/tasks/integration_tests.dart
+3
-0
PlatformViewUITests.m
..._view_tests/ios/PlatformViewUITests/PlatformViewUITests.m
+51
-0
project.pbxproj
..._platform_view_tests/ios/Runner.xcodeproj/project.pbxproj
+171
-0
Runner.xcscheme
...s/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+12
-6
AppDelegate.m
...on_tests/ios_platform_view_tests/ios/Runner/AppDelegate.m
+5
-38
TextFieldFactory.h
...sts/ios_platform_view_tests/ios/Runner/TextFieldFactory.h
+14
-0
TextFieldFactory.m
...sts/ios_platform_view_tests/ios/Runner/TextFieldFactory.m
+37
-0
ViewFactory.h
...on_tests/ios_platform_view_tests/ios/Runner/ViewFactory.h
+14
-0
ViewFactory.m
...on_tests/ios_platform_view_tests/ios/Runner/ViewFactory.m
+38
-0
main.dart
dev/integration_tests/ios_platform_view_tests/lib/main.dart
+65
-8
platform_view.dart
packages/flutter/lib/src/widgets/platform_view.dart
+14
-4
platform_view_test.dart
packages/flutter/test/widgets/platform_view_test.dart
+39
-0
No files found.
.ci.yaml
View file @
0dd0c2ed
...
...
@@ -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
...
...
TESTOWNERS
View file @
0dd0c2ed
...
...
@@ -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
...
...
dev/devicelab/bin/tasks/native_platform_view_ui_tests_ios.dart
0 → 100644
View file @
0dd0c2ed
// 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
);
});
}
dev/devicelab/lib/tasks/integration_tests.dart
View file @
0dd0c2ed
...
...
@@ -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'
,
],
);
}
...
...
dev/integration_tests/ios_platform_view_tests/ios/PlatformViewUITests/PlatformViewUITests.m
0 → 100644
View file @
0dd0c2ed
// 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
dev/integration_tests/ios_platform_view_tests/ios/Runner.xcodeproj/project.pbxproj
View file @
0dd0c2ed
This diff is collapsed.
Click to expand it.
dev/integration_tests/ios_platform_view_tests/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
View file @
0dd0c2ed
...
...
@@ -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"
...
...
dev/integration_tests/ios_platform_view_tests/ios/Runner/AppDelegate.m
View file @
0dd0c2ed
...
...
@@ -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
];
}
...
...
dev/integration_tests/ios_platform_view_tests/ios/Runner/TextFieldFactory.h
0 → 100644
View file @
0dd0c2ed
// 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
dev/integration_tests/ios_platform_view_tests/ios/Runner/TextFieldFactory.m
0 → 100644
View file @
0dd0c2ed
// 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
dev/integration_tests/ios_platform_view_tests/ios/Runner/ViewFactory.h
0 → 100644
View file @
0dd0c2ed
// 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
dev/integration_tests/ios_platform_view_tests/ios/Runner/ViewFactory.m
0 → 100644
View file @
0dd0c2ed
// 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
dev/integration_tests/ios_platform_view_tests/lib/main.dart
View file @
0dd0c2ed
...
...
@@ -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
<
PlatformView
Page
>(
builder:
(
BuildContext
context
)
=>
const
PlatformView
Page
()),
MaterialPageRoute
<
MergeThreadTest
Page
>(
builder:
(
BuildContext
context
)
=>
const
MergeThreadTest
Page
()),
);
},
),
...
...
@@ -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
PlatformView
Page
extends
StatelessWidget
{
const
PlatformView
Page
({
super
.
key
});
/// A page
to test thread merge for platform view
.
class
MergeThreadTest
Page
extends
StatelessWidget
{
const
MergeThreadTest
Page
({
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
,
),
],
),
);
}
}
packages/flutter/lib/src/widgets/platform_view.dart
View file @
0dd0c2ed
...
...
@@ -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
},
);
}
}
...
...
packages/flutter/test/widgets/platform_view_test.dart
View file @
0dd0c2ed
...
...
@@ -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
();
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment