Skip to content

Commit

Permalink
PlatformViewLink, handling creation of the PlatformViewSurface and di…
Browse files Browse the repository at this point in the history
…spose PlatformViewController (#37703)

* link

* review fixes

* review fixes

* remove extra line
  • Loading branch information
Chris Yang committed Aug 15, 2019
1 parent 07197e9 commit 5acf63d
Show file tree
Hide file tree
Showing 4 changed files with 252 additions and 0 deletions.
5 changes: 5 additions & 0 deletions packages/flutter/lib/src/services/platform_views.dart
Expand Up @@ -729,4 +729,9 @@ abstract class PlatformViewController {

/// Dispatches the `event` to the platform view.
void dispatchPointerEvent(PointerEvent event);

/// Disposes the platform view.
///
/// The [PlatformViewController] is unusable after calling dispose.
void dispose();
}
130 changes: 130 additions & 0 deletions packages/flutter/lib/src/widgets/platform_view.dart
Expand Up @@ -581,6 +581,136 @@ class _UiKitPlatformView extends LeafRenderObjectWidget {
}
}

/// The parameters used to create a [PlatformViewController].
///
/// See also [CreatePlatformViewController] which uses this object to create a [PlatformViewController].
class PlatformViewCreationParams {

const PlatformViewCreationParams._({
@required this.id,
@required this.onPlatformViewCreated
}) : assert(id != null),
assert(onPlatformViewCreated != null);

/// The unique identifier for the new platform view.
///
/// [PlatformViewController.viewId] should match this id.
final int id;

/// Callback invoked after the platform view has been created.
final PlatformViewCreatedCallback onPlatformViewCreated;
}

/// A factory for a surface presenting a platform view as part of the widget hierarchy.
///
/// The returned widget should present the platform view associated with `controller`.
///
/// See also:
/// * [PlatformViewSurface], a common widget for presenting platform views.
typedef PlatformViewSurfaceFactory = Widget Function(BuildContext context, PlatformViewController controller);

/// Constructs a [PlatformViewController].
///
/// The [PlatformViewController.id] field of the created controller must match the value of the
/// params [PlatformViewCreationParams.id] field.
///
/// See also [PlatformViewLink.onCreate].
typedef CreatePlatformViewController = PlatformViewController Function(PlatformViewCreationParams params);

/// Links a platform view with the Flutter framework.
///
/// Provides common functionality for embedding a platform view (e.g an android.view.View on Android)
/// with the Flutter framework.
///
/// {@macro flutter.widgets.platformViews.lifetime}
///
/// To implement a new platform view widget, return this widget in the `build` method.
/// For example:
/// ```dart
/// class FooPlatformView extends StatelessWidget {
/// @override
/// Widget build(BuildContext context) {
/// return PlatformViewLink(
/// createCallback: createFooWebView,
/// surfaceFactory: (BuildContext context, PlatformViewController controller) {
/// return PlatformViewSurface(
/// gestureRecognizers: gestureRecognizers,
/// controller: controller,
/// hitTestBehavior: PlatformViewHitTestBehavior.opaque,
/// );
/// },
/// );
/// }
/// }
/// ```
///
/// The `surfaceFactory` and the `createPlatformViewController` only take affect when the state of this widget is initialized.
/// If the widget is rebuilt without losing its state, `surfaceFactory` and `createPlatformViewController` are ignored.
class PlatformViewLink extends StatefulWidget {

/// Construct a [PlatformViewLink] widget.
///
/// The `surfaceFactory` and the `createPlatformViewController` must not be null.
///
/// See also:
/// * [PlatformViewSurface] for details on the widget returned by `surfaceFactory`.
/// * [PlatformViewCreationParams] for how each parameter can be used when implementing `createPlatformView`.
const PlatformViewLink({
Key key,
@required PlatformViewSurfaceFactory surfaceFactory,
@required CreatePlatformViewController createPlatformViewController,
}) : assert(surfaceFactory != null),
assert(createPlatformViewController != null),
_surfaceFactory = surfaceFactory,
_createPlatformViewController = createPlatformViewController,
super(key: key);


final PlatformViewSurfaceFactory _surfaceFactory;
final CreatePlatformViewController _createPlatformViewController;

@override
State<StatefulWidget> createState() => _PlatformViewLinkState();
}

class _PlatformViewLinkState extends State<PlatformViewLink> {

int _id;
PlatformViewController _controller;
bool _platformViewCreated = false;
PlatformViewSurface _surface;

@override
Widget build(BuildContext context) {
if (!_platformViewCreated) {
return const SizedBox.expand();
}
_surface ??= widget._surfaceFactory(context, _controller);
return _surface;
}

@override
void initState() {
_initialize();
super.initState();
}

void _initialize() {
_id = platformViewsRegistry.getNextPlatformViewId();
_controller = widget._createPlatformViewController(PlatformViewCreationParams._(id:_id, onPlatformViewCreated:_onPlatformViewCreated));
}

void _onPlatformViewCreated(int id) {
setState(() => _platformViewCreated = true);
}

@override
void dispose() {
_controller?.dispose();
super.dispose();
}
}

/// 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]
Expand Down
8 changes: 8 additions & 0 deletions packages/flutter/test/services/fake_platform_views.dart
Expand Up @@ -17,6 +17,8 @@ class FakePlatformViewController extends PlatformViewController {
_id = id;
}

bool disposed = false;

/// Events that are dispatched;
List<PointerEvent> dispatchedPointerEvents = <PointerEvent>[];

Expand All @@ -32,6 +34,12 @@ class FakePlatformViewController extends PlatformViewController {

void clearTestingVariables() {
dispatchedPointerEvents.clear();
disposed = false;
}

@override
void dispose() {
disposed = true;
}
}

Expand Down
109 changes: 109 additions & 0 deletions packages/flutter/test/widgets/platform_view_test.dart
Expand Up @@ -1932,5 +1932,114 @@ void main() {
);
expect(factoryInvocationCount, 1);
});

testWidgets('PlatformViewLink Widget init, should create a SizedBox widget before onPlatformViewCreated and a PlatformViewSurface after', (WidgetTester tester) async {
final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
int createdPlatformViewId;

PlatformViewCreatedCallback onPlatformViewCreatedCallBack;

final PlatformViewLink platformViewLink = PlatformViewLink(createPlatformViewController: (PlatformViewCreationParams params){
onPlatformViewCreatedCallBack = params.onPlatformViewCreated;
createdPlatformViewId = params.id;
return FakePlatformViewController(params.id);
}, surfaceFactory: (BuildContext context, PlatformViewController controller) {
return PlatformViewSurface(
gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},
controller: controller,
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
);
});

await tester.pumpWidget(platformViewLink);
final SizedBox sizedBox = tester.allWidgets.firstWhere((Widget widget) => widget is SizedBox);
expect(sizedBox, isNotNull);

onPlatformViewCreatedCallBack(createdPlatformViewId);

await tester.pump();

final PlatformViewSurface surface = tester.allWidgets.firstWhere((Widget widget) => widget is PlatformViewSurface);
expect(surface, isNotNull);

expect(createdPlatformViewId, currentViewId+1);
});

testWidgets('PlatformViewLink Widget dispose', (WidgetTester tester) async {
FakePlatformViewController disposedController;
final PlatformViewLink platformViewLink = PlatformViewLink(createPlatformViewController: (PlatformViewCreationParams params){
params.onPlatformViewCreated(params.id);
disposedController = FakePlatformViewController(params.id);
params.onPlatformViewCreated(params.id);
return disposedController;
}, surfaceFactory: (BuildContext context,PlatformViewController controller) {
return PlatformViewSurface(
gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},
controller: controller,
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
);
});

await tester.pumpWidget(platformViewLink);

await tester.pumpWidget(Container());

expect(disposedController.disposed, true);
});

testWidgets('PlatformViewLink widget survives widget tree change', (WidgetTester tester) async {
final GlobalKey key = GlobalKey();
final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
final List<int> ids = <int>[];

FakePlatformViewController controller;

PlatformViewLink createPlatformViewLink() {
return PlatformViewLink(
key: key,
createPlatformViewController: (PlatformViewCreationParams params){
ids.add(params.id);
controller = FakePlatformViewController(params.id);
params.onPlatformViewCreated(params.id);
return controller;
},
surfaceFactory: (BuildContext context, PlatformViewController controller) {
return PlatformViewSurface(
gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},
controller: controller,
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
);
},
);
}
await tester.pumpWidget(
Center(
child: SizedBox(
width: 200.0,
height: 100.0,
child: createPlatformViewLink(),
),
),
);

await tester.pumpWidget(
Center(
child: Container(
child: SizedBox(
width: 200.0,
height: 100.0,
child: createPlatformViewLink(),
),
),
),
);

expect(
ids,
unorderedEquals(<int>[
currentViewId+1,
]),
);
});
});
}

0 comments on commit 5acf63d

Please sign in to comment.