Extract common PlatformView functionality: Painting and Semantics (#36955)
* painting and semantics * more comments * fixing ci * review fixes * add assert for id * rename custom layer factory to layer builder * review updates * partial review fixes * some doc updates * more doc updates * only expose getter for id in PlatformViewController * doc updates/removing all the references * remove extra * more doc updates * some doc updates * more doc fixes * review fixes
This commit is contained in:
parent
99fe0d078b
commit
9553f8daa7
@ -725,3 +725,69 @@ class _MotionEventsDispatcher {
|
||||
bool isSinglePointerAction(PointerEvent event) =>
|
||||
!(event is PointerDownEvent) && !(event is PointerUpEvent);
|
||||
}
|
||||
|
||||
/// A render object for embedding a platform view.
|
||||
///
|
||||
/// [PlatformViewRenderBox] presents a platform view by adding a [PlatformViewLayer] layer, integrates it with the gesture arenas system
|
||||
/// and adds relevant semantic nodes to the semantics tree.
|
||||
class PlatformViewRenderBox extends RenderBox {
|
||||
|
||||
/// Creating a render object for a [PlatformViewSurface].
|
||||
///
|
||||
/// The `controller` parameter must not be null.
|
||||
PlatformViewRenderBox({
|
||||
@required PlatformViewController controller,
|
||||
|
||||
}) : assert(controller != null && controller.viewId != null && controller.viewId > -1),
|
||||
_controller = controller;
|
||||
|
||||
/// Sets the [controller] for this render object.
|
||||
///
|
||||
/// This value must not be null, and setting it to a new value will result in a repaint.
|
||||
set controller(PlatformViewController controller) {
|
||||
assert(controller != null);
|
||||
assert(controller.viewId != null && controller.viewId > -1);
|
||||
|
||||
if ( _controller == controller) {
|
||||
return;
|
||||
}
|
||||
final bool needsSemanticsUpdate = _controller.viewId != controller.viewId;
|
||||
_controller = controller;
|
||||
markNeedsPaint();
|
||||
if (needsSemanticsUpdate) {
|
||||
markNeedsSemanticsUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
PlatformViewController _controller;
|
||||
|
||||
@override
|
||||
bool get sizedByParent => true;
|
||||
|
||||
@override
|
||||
bool get alwaysNeedsCompositing => true;
|
||||
|
||||
@override
|
||||
bool get isRepaintBoundary => true;
|
||||
|
||||
@override
|
||||
void performResize() {
|
||||
size = constraints.biggest;
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
assert(_controller.viewId != null);
|
||||
context.addLayer(PlatformViewLayer(
|
||||
rect: offset & size,
|
||||
viewId: _controller.viewId));
|
||||
}
|
||||
|
||||
@override
|
||||
void describeSemanticsConfiguration (SemanticsConfiguration config) {
|
||||
super.describeSemanticsConfiguration(config);
|
||||
assert(_controller.viewId != null);
|
||||
config.isSemanticBoundary = true;
|
||||
config.platformViewId = _controller.viewId;
|
||||
}
|
||||
}
|
||||
|
@ -713,3 +713,16 @@ class UiKitViewController {
|
||||
await SystemChannels.platform_views.invokeMethod<void>('dispose', id);
|
||||
}
|
||||
}
|
||||
|
||||
/// An interface for a controlling a single platform view.
|
||||
///
|
||||
/// Used by [PlatformViewSurface] to interface with the platform view it embeds.
|
||||
abstract class PlatformViewController {
|
||||
|
||||
/// The viewId associated with this controller.
|
||||
///
|
||||
/// The viewId should always be unique and non-negative. And it must not be null.
|
||||
///
|
||||
/// See also [PlatformViewRegistry] which is a helper for managing platform view ids.
|
||||
int get viewId;
|
||||
}
|
||||
|
@ -580,3 +580,45 @@ class _UiKitPlatformView extends LeafRenderObjectWidget {
|
||||
renderObject.updateGestureRecognizers(gestureRecognizers);
|
||||
}
|
||||
}
|
||||
|
||||
/// Integrates a platform view with Flutter's compositor, touch, and semantics subsystems.
|
||||
///
|
||||
/// The compositor integration is done by adding a [PlatformViewLayer] to the layer tree. [PlatformViewLayer]
|
||||
/// isn't supported on all platforms (e.g on Android platform views are composited using a [TextureLayer]).
|
||||
/// Custom Flutter embedders can support [PlatformViewLayer]s by implementing a SystemCompositor.
|
||||
///
|
||||
/// The widget fills all available space, the parent of this object must provide bounded layout
|
||||
/// constraints.
|
||||
///
|
||||
/// If the associated platform view is not created the [PlatformViewSurface] does not paint any contents.
|
||||
///
|
||||
/// See also:
|
||||
/// * [AndroidView] which embeds an Android platform view in the widget hierarchy.
|
||||
/// * [UIKitView] which embeds an iOS platform view in the widget hierarchy.
|
||||
// TODO(amirh): Link to the embedder's system compositor documentation once available.
|
||||
class PlatformViewSurface extends LeafRenderObjectWidget {
|
||||
|
||||
/// Construct a `PlatformViewSurface`.
|
||||
///
|
||||
/// The [controller] must not be null.
|
||||
const PlatformViewSurface({
|
||||
@required this.controller,
|
||||
}) : assert(controller != null);
|
||||
|
||||
/// The controller for the platform view integrated by this [PlatformViewSurface].
|
||||
///
|
||||
/// [PlatformViewController] is used for dispatching touch events to the platform view.
|
||||
/// [PlatformViewController.viewId] identifies the platform view whose contents are painted by this widget.
|
||||
final PlatformViewController controller;
|
||||
|
||||
@override
|
||||
RenderObject createRenderObject(BuildContext context) {
|
||||
return PlatformViewRenderBox(controller: controller);
|
||||
}
|
||||
|
||||
@override
|
||||
void updateRenderObject(BuildContext context, PlatformViewRenderBox renderObject) {
|
||||
renderObject
|
||||
..controller = controller;
|
||||
}
|
||||
}
|
||||
|
60
packages/flutter/test/rendering/platform_view_test.dart
Normal file
60
packages/flutter/test/rendering/platform_view_test.dart
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright 2019 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/rendering.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '../services/fake_platform_views.dart';
|
||||
import 'rendering_tester.dart';
|
||||
|
||||
void main() {
|
||||
|
||||
group('PlatformViewRenderBox', () {
|
||||
FakePlatformViewController fakePlatformViewController;
|
||||
PlatformViewRenderBox platformViewRenderBox;
|
||||
setUp((){
|
||||
fakePlatformViewController = FakePlatformViewController(0);
|
||||
platformViewRenderBox = PlatformViewRenderBox(controller: fakePlatformViewController);
|
||||
});
|
||||
|
||||
test('layout should size to max constraint', () {
|
||||
layout(platformViewRenderBox);
|
||||
platformViewRenderBox.layout(const BoxConstraints(minWidth: 50, minHeight: 50, maxWidth: 100, maxHeight: 100));
|
||||
expect(platformViewRenderBox.size, const Size(100, 100));
|
||||
});
|
||||
|
||||
test('send semantics update if id is changed', (){
|
||||
final RenderObject tree = RenderConstrainedBox(
|
||||
additionalConstraints: const BoxConstraints.tightFor(height: 20.0, width: 20.0),
|
||||
child: platformViewRenderBox,
|
||||
);
|
||||
int semanticsUpdateCount = 0;
|
||||
final SemanticsHandle semanticsHandle = renderer.pipelineOwner.ensureSemantics(
|
||||
listener: () {
|
||||
++semanticsUpdateCount;
|
||||
}
|
||||
);
|
||||
layout(tree, phase: EnginePhase.flushSemantics);
|
||||
// Initial semantics update
|
||||
expect(semanticsUpdateCount, 1);
|
||||
|
||||
semanticsUpdateCount = 0;
|
||||
|
||||
// Request semantics update even though nothing changed.
|
||||
platformViewRenderBox.markNeedsSemanticsUpdate();
|
||||
pumpFrame(phase: EnginePhase.flushSemantics);
|
||||
expect(semanticsUpdateCount, 0);
|
||||
|
||||
semanticsUpdateCount = 0;
|
||||
|
||||
final FakePlatformViewController updatedFakePlatformViewController = FakePlatformViewController(10);
|
||||
platformViewRenderBox.controller = updatedFakePlatformViewController;
|
||||
pumpFrame(phase: EnginePhase.flushSemantics);
|
||||
// Update id should update the semantics.
|
||||
expect(semanticsUpdateCount, 1);
|
||||
|
||||
semanticsHandle.dispose();
|
||||
});
|
||||
});
|
||||
}
|
@ -9,6 +9,19 @@ import 'package:collection/collection.dart';
|
||||
import 'package:flutter/painting.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
/// Used in internal testing.
|
||||
class FakePlatformViewController extends PlatformViewController {
|
||||
|
||||
FakePlatformViewController(int id) {
|
||||
_id = id;
|
||||
}
|
||||
|
||||
int _id;
|
||||
|
||||
@override
|
||||
int get viewId => _id;
|
||||
}
|
||||
|
||||
class FakeAndroidPlatformViewsController {
|
||||
FakeAndroidPlatformViewsController() {
|
||||
SystemChannels.platform_views.setMockMethodCallHandler(_onMethodCall);
|
||||
|
@ -1667,4 +1667,21 @@ void main() {
|
||||
handle.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
group('Common PlatformView', () {
|
||||
FakePlatformViewController controller;
|
||||
|
||||
setUp((){
|
||||
controller = FakePlatformViewController(0);
|
||||
});
|
||||
|
||||
testWidgets('PlatformViewSurface should create platform view layer', (WidgetTester tester) async {
|
||||
final PlatformViewSurface surface = PlatformViewSurface(controller: controller);
|
||||
await tester.pumpWidget(surface);
|
||||
final PlatformViewLayer layer = tester.layers.firstWhere((Layer layer){
|
||||
return layer is PlatformViewLayer;
|
||||
});
|
||||
expect(layer, isNotNull);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user