Unverified Commit 688d96b6 authored by amirh's avatar amirh Committed by GitHub

Add a creation parameters argument to AndroidView. (#21067)

This allows to pass construction parameters for the embedded Android
view from the Dart side.
parent 7f0f938d
...@@ -3,10 +3,12 @@ ...@@ -3,10 +3,12 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async'; import 'dart:async';
import 'dart:typed_data';
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'message_codec.dart';
import 'system_channels.dart'; import 'system_channels.dart';
/// The [PlatformViewsRegistry] responsible for generating unique identifiers for platform views. /// The [PlatformViewsRegistry] responsible for generating unique identifiers for platform views.
...@@ -57,24 +59,36 @@ class PlatformViewsService { ...@@ -57,24 +59,36 @@ class PlatformViewsService {
/// Plugins can register a platform view factory with /// Plugins can register a platform view factory with
/// [PlatformViewRegistry#registerViewFactory](/javadoc/io/flutter/plugin/platform/PlatformViewRegistry.html#registerViewFactory-java.lang.String-io.flutter.plugin.platform.PlatformViewFactory-). /// [PlatformViewRegistry#registerViewFactory](/javadoc/io/flutter/plugin/platform/PlatformViewRegistry.html#registerViewFactory-java.lang.String-io.flutter.plugin.platform.PlatformViewFactory-).
/// ///
/// `creationParams` will be passed as the args argument of [PlatformViewFactory#create](/javadoc/io/flutter/plugin/platform/PlatformViewFactory.html#create-android.content.Context-int-java.lang.Object-)
///
/// `creationParamsCodec` is the codec used to encode `creationParams` before sending it to the
/// platform side. It should match the codec passed to the constructor of [PlatformViewFactory](/javadoc/io/flutter/plugin/platform/PlatformViewFactory.html#PlatformViewFactory-io.flutter.plugin.common.MessageCodec-).
/// This is typically one of: [StandardMessageCodec], [JSONMessageCodec], [StringCodec], or [BinaryCodec].
///
/// The Android view will only be created after [AndroidViewController.setSize] is called for the /// The Android view will only be created after [AndroidViewController.setSize] is called for the
/// first time. /// first time.
/// ///
/// The `id, `viewType, and `layoutDirection` parameters must not be null. /// The `id, `viewType, and `layoutDirection` parameters must not be null.
/// If `creationParams` is non null then `cretaionParamsCodec` must not be null.
static AndroidViewController initAndroidView({ static AndroidViewController initAndroidView({
@required int id, @required int id,
@required String viewType, @required String viewType,
@required TextDirection layoutDirection, @required TextDirection layoutDirection,
dynamic creationParams,
MessageCodec<dynamic> creationParamsCodec,
PlatformViewCreatedCallback onPlatformViewCreated, PlatformViewCreatedCallback onPlatformViewCreated,
}) { }) {
assert(id != null); assert(id != null);
assert(viewType != null); assert(viewType != null);
assert(layoutDirection != null); assert(layoutDirection != null);
assert(creationParams == null || creationParamsCodec != null);
return new AndroidViewController._( return new AndroidViewController._(
id, id,
viewType, viewType,
layoutDirection, creationParams,
onPlatformViewCreated creationParamsCodec,
layoutDirection,
onPlatformViewCreated,
); );
} }
} }
...@@ -348,12 +362,17 @@ class AndroidViewController { ...@@ -348,12 +362,17 @@ class AndroidViewController {
AndroidViewController._( AndroidViewController._(
this.id, this.id,
String viewType, String viewType,
dynamic creationParams,
MessageCodec<dynamic> creationParamsCodec,
TextDirection layoutDirection, TextDirection layoutDirection,
PlatformViewCreatedCallback onPlatformViewCreated, PlatformViewCreatedCallback onPlatformViewCreated,
) : assert(id != null), ) : assert(id != null),
assert(viewType != null), assert(viewType != null),
assert(layoutDirection != null), assert(layoutDirection != null),
assert(creationParams == null || creationParamsCodec != null),
_viewType = viewType, _viewType = viewType,
_creationParams = creationParams,
_creationParamsCodec = creationParamsCodec,
_layoutDirection = layoutDirection, _layoutDirection = layoutDirection,
_onPlatformViewCreated = onPlatformViewCreated, _onPlatformViewCreated = onPlatformViewCreated,
_state = _AndroidViewState.waitingForSize; _state = _AndroidViewState.waitingForSize;
...@@ -414,6 +433,10 @@ class AndroidViewController { ...@@ -414,6 +433,10 @@ class AndroidViewController {
_AndroidViewState _state; _AndroidViewState _state;
dynamic _creationParams;
MessageCodec<dynamic> _creationParamsCodec;
/// Disposes the Android view. /// Disposes the Android view.
/// ///
/// The [AndroidViewController] object is unusable after calling this. /// The [AndroidViewController] object is unusable after calling this.
...@@ -500,13 +523,22 @@ class AndroidViewController { ...@@ -500,13 +523,22 @@ class AndroidViewController {
} }
Future<void> _create(Size size) async { Future<void> _create(Size size) async {
_textureId = await SystemChannels.platform_views.invokeMethod('create', <String, dynamic> { final Map<String, dynamic> args = <String, dynamic> {
'id': id, 'id': id,
'viewType': _viewType, 'viewType': _viewType,
'width': size.width, 'width': size.width,
'height': size.height, 'height': size.height,
'direction': _getAndroidDirection(_layoutDirection), 'direction': _getAndroidDirection(_layoutDirection),
}); };
if (_creationParams != null) {
final ByteData paramsByteData = _creationParamsCodec.encodeMessage(_creationParams);
args['params'] = Uint8List.view(
paramsByteData.buffer,
0,
paramsByteData.lengthInBytes,
);
}
_textureId = await SystemChannels.platform_views.invokeMethod('create', args);
if (_onPlatformViewCreated != null) if (_onPlatformViewCreated != null)
_onPlatformViewCreated(id); _onPlatformViewCreated(id);
_state = _AndroidViewState.created; _state = _AndroidViewState.created;
......
...@@ -42,14 +42,18 @@ class AndroidView extends StatefulWidget { ...@@ -42,14 +42,18 @@ class AndroidView extends StatefulWidget {
/// Creates a widget that embeds an Android view. /// Creates a widget that embeds an Android view.
/// ///
/// The `viewType` and `hitTestBehavior` parameters must not be null. /// The `viewType` and `hitTestBehavior` parameters must not be null.
const AndroidView({ /// If `creationParams` is not null then `creationParamsCodec` must not be null.
AndroidView({
Key key, Key key,
@required this.viewType, @required this.viewType,
this.onPlatformViewCreated, this.onPlatformViewCreated,
this.hitTestBehavior = PlatformViewHitTestBehavior.opaque, this.hitTestBehavior = PlatformViewHitTestBehavior.opaque,
this.layoutDirection, this.layoutDirection,
this.creationParams,
this.creationParamsCodec
}) : assert(viewType != null), }) : assert(viewType != null),
assert(hitTestBehavior != null), assert(hitTestBehavior != null),
assert(creationParams == null || creationParamsCodec != null),
super(key: key); super(key: key);
/// The unique identifier for Android view type to be embedded by this widget. /// The unique identifier for Android view type to be embedded by this widget.
...@@ -74,6 +78,19 @@ class AndroidView extends StatefulWidget { ...@@ -74,6 +78,19 @@ class AndroidView extends StatefulWidget {
/// If this is null, the ambient [Directionality] is used instead. /// If this is null, the ambient [Directionality] is used instead.
final TextDirection layoutDirection; final TextDirection layoutDirection;
/// Passed as the args argument of [PlatformViewFactory#create](/javadoc/io/flutter/plugin/platform/PlatformViewFactory.html#create-android.content.Context-int-java.lang.Object-)
///
/// This can be used by plugins to pass constructor parameters to the embedded Android view.
final dynamic creationParams;
/// The codec used to encode `creationParams` before sending it to the
/// platform side. It should match the codec passed to the constructor of [PlatformViewFactory](/javadoc/io/flutter/plugin/platform/PlatformViewFactory.html#PlatformViewFactory-io.flutter.plugin.common.MessageCodec-).
///
/// This is typically one of: [StandardMessageCodec], [JSONMessageCodec], [StringCodec], or [BinaryCodec].
///
/// This must not be null if [creationParams] is not null.
final MessageCodec<dynamic> creationParamsCodec;
@override @override
State createState() => new _AndroidViewState(); State createState() => new _AndroidViewState();
} }
...@@ -150,10 +167,12 @@ class _AndroidViewState extends State<AndroidView> { ...@@ -150,10 +167,12 @@ class _AndroidViewState extends State<AndroidView> {
void _createNewAndroidView() { void _createNewAndroidView() {
_id = platformViewsRegistry.getNextPlatformViewId(); _id = platformViewsRegistry.getNextPlatformViewId();
_controller = PlatformViewsService.initAndroidView( _controller = PlatformViewsService.initAndroidView(
id: _id, id: _id,
viewType: widget.viewType, viewType: widget.viewType,
layoutDirection: _layoutDirection, layoutDirection: _layoutDirection,
onPlatformViewCreated: widget.onPlatformViewCreated onPlatformViewCreated: widget.onPlatformViewCreated,
creationParams: widget.creationParams,
creationParamsCodec: widget.creationParamsCodec,
); );
} }
} }
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async'; import 'dart:async';
import 'dart:typed_data';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
...@@ -60,6 +61,7 @@ class FakePlatformViewsController { ...@@ -60,6 +61,7 @@ class FakePlatformViewsController {
final double width = args['width']; final double width = args['width'];
final double height = args['height']; final double height = args['height'];
final int layoutDirection = args['direction']; final int layoutDirection = args['direction'];
final Uint8List creationParams = args['params'];
if (_views.containsKey(id)) if (_views.containsKey(id))
throw new PlatformException( throw new PlatformException(
...@@ -73,7 +75,7 @@ class FakePlatformViewsController { ...@@ -73,7 +75,7 @@ class FakePlatformViewsController {
message: 'Trying to create a platform view of unregistered type: $viewType', message: 'Trying to create a platform view of unregistered type: $viewType',
); );
_views[id] = new FakePlatformView(id, viewType, new Size(width, height), layoutDirection); _views[id] = new FakePlatformView(id, viewType, new Size(width, height), layoutDirection, creationParams);
final int textureId = _textureCounter++; final int textureId = _textureCounter++;
return new Future<int>.sync(() => textureId); return new Future<int>.sync(() => textureId);
} }
...@@ -152,10 +154,11 @@ class FakePlatformViewsController { ...@@ -152,10 +154,11 @@ class FakePlatformViewsController {
class FakePlatformView { class FakePlatformView {
FakePlatformView(this.id, this.type, this.size, this.layoutDirection); FakePlatformView(this.id, this.type, this.size, this.layoutDirection, [this.creationParams]);
final int id; final int id;
final String type; final String type;
final Uint8List creationParams;
Size size; Size size;
int layoutDirection; int layoutDirection;
...@@ -166,6 +169,7 @@ class FakePlatformView { ...@@ -166,6 +169,7 @@ class FakePlatformView {
final FakePlatformView typedOther = other; final FakePlatformView typedOther = other;
return id == typedOther.id && return id == typedOther.id &&
type == typedOther.type && type == typedOther.type &&
creationParams == typedOther.creationParams &&
size == typedOther.size; size == typedOther.size;
} }
...@@ -174,7 +178,7 @@ class FakePlatformView { ...@@ -174,7 +178,7 @@ class FakePlatformView {
@override @override
String toString() { String toString() {
return 'FakePlatformView(id: $id, type: $type, size: $size, layoutDirection: $layoutDirection)'; return 'FakePlatformView(id: $id, type: $type, size: $size, layoutDirection: $layoutDirection, creationParams: $creationParams)';
} }
} }
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async'; import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
...@@ -20,7 +21,7 @@ void main() { ...@@ -20,7 +21,7 @@ void main() {
viewsController.registerViewType('webview'); viewsController.registerViewType('webview');
await tester.pumpWidget( await tester.pumpWidget(
const Center( Center(
child: SizedBox( child: SizedBox(
width: 200.0, width: 200.0,
height: 100.0, height: 100.0,
...@@ -37,12 +38,50 @@ void main() { ...@@ -37,12 +38,50 @@ void main() {
); );
}); });
testWidgets('Create Android view with params', (WidgetTester tester) async {
final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
final FakePlatformViewsController viewsController = new FakePlatformViewsController(TargetPlatform.android);
viewsController.registerViewType('webview');
await tester.pumpWidget(
Center(
child: SizedBox(
width: 200.0,
height: 100.0,
child: AndroidView(
viewType: 'webview',
layoutDirection: TextDirection.ltr,
creationParams: 'creation parameters',
creationParamsCodec: const StringCodec(),
),
),
),
);
final FakePlatformView fakeView = viewsController.views.first;
final Uint8List rawCreationParams = fakeView.creationParams;
final ByteData byteData = new ByteData.view(
rawCreationParams.buffer,
rawCreationParams.offsetInBytes,
rawCreationParams.lengthInBytes
);
final dynamic actualParams = const StringCodec().decodeMessage(byteData);
expect(actualParams, 'creation parameters');
expect(
viewsController.views,
unorderedEquals(<FakePlatformView>[
new FakePlatformView(currentViewId + 1, 'webview', const Size(200.0, 100.0), AndroidViewController.kAndroidLayoutDirectionLtr, fakeView.creationParams)
]),
);
});
testWidgets('Zero sized Android view is not created', (WidgetTester tester) async { testWidgets('Zero sized Android view is not created', (WidgetTester tester) async {
final FakePlatformViewsController viewsController = new FakePlatformViewsController(TargetPlatform.android); final FakePlatformViewsController viewsController = new FakePlatformViewsController(TargetPlatform.android);
viewsController.registerViewType('webview'); viewsController.registerViewType('webview');
await tester.pumpWidget( await tester.pumpWidget(
const Center( Center(
child: SizedBox( child: SizedBox(
width: 0.0, width: 0.0,
height: 0.0, height: 0.0,
...@@ -62,7 +101,7 @@ void main() { ...@@ -62,7 +101,7 @@ void main() {
final FakePlatformViewsController viewsController = new FakePlatformViewsController(TargetPlatform.android); final FakePlatformViewsController viewsController = new FakePlatformViewsController(TargetPlatform.android);
viewsController.registerViewType('webview'); viewsController.registerViewType('webview');
await tester.pumpWidget( await tester.pumpWidget(
const Center( Center(
child: SizedBox( child: SizedBox(
width: 200.0, width: 200.0,
height: 100.0, height: 100.0,
...@@ -74,7 +113,7 @@ void main() { ...@@ -74,7 +113,7 @@ void main() {
viewsController.resizeCompleter = new Completer<void>(); viewsController.resizeCompleter = new Completer<void>();
await tester.pumpWidget( await tester.pumpWidget(
const Center( Center(
child: SizedBox( child: SizedBox(
width: 100.0, width: 100.0,
height: 50.0, height: 50.0,
...@@ -111,7 +150,7 @@ void main() { ...@@ -111,7 +150,7 @@ void main() {
viewsController.registerViewType('webview'); viewsController.registerViewType('webview');
viewsController.registerViewType('maps'); viewsController.registerViewType('maps');
await tester.pumpWidget( await tester.pumpWidget(
const Center( Center(
child: SizedBox( child: SizedBox(
width: 200.0, width: 200.0,
height: 100.0, height: 100.0,
...@@ -121,7 +160,7 @@ void main() { ...@@ -121,7 +160,7 @@ void main() {
); );
await tester.pumpWidget( await tester.pumpWidget(
const Center( Center(
child: SizedBox( child: SizedBox(
width: 200.0, width: 200.0,
height: 100.0, height: 100.0,
...@@ -142,7 +181,7 @@ void main() { ...@@ -142,7 +181,7 @@ void main() {
final FakePlatformViewsController viewsController = new FakePlatformViewsController(TargetPlatform.android); final FakePlatformViewsController viewsController = new FakePlatformViewsController(TargetPlatform.android);
viewsController.registerViewType('webview'); viewsController.registerViewType('webview');
await tester.pumpWidget( await tester.pumpWidget(
const Center( Center(
child: SizedBox( child: SizedBox(
width: 200.0, width: 200.0,
height: 100.0, height: 100.0,
...@@ -206,7 +245,7 @@ void main() { ...@@ -206,7 +245,7 @@ void main() {
final FakePlatformViewsController viewsController = new FakePlatformViewsController(TargetPlatform.android); final FakePlatformViewsController viewsController = new FakePlatformViewsController(TargetPlatform.android);
viewsController.registerViewType('webview'); viewsController.registerViewType('webview');
await tester.pumpWidget( await tester.pumpWidget(
const Align( Align(
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
child: SizedBox( child: SizedBox(
width: 200.0, width: 200.0,
...@@ -243,7 +282,7 @@ void main() { ...@@ -243,7 +282,7 @@ void main() {
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
onPointerDown: (PointerDownEvent e) { numPointerDownsOnParent++; }, onPointerDown: (PointerDownEvent e) { numPointerDownsOnParent++; },
), ),
const Positioned( Positioned(
child: SizedBox( child: SizedBox(
width: 200.0, width: 200.0,
height: 100.0, height: 100.0,
...@@ -286,7 +325,7 @@ void main() { ...@@ -286,7 +325,7 @@ void main() {
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
onPointerDown: (PointerDownEvent e) { numPointerDownsOnParent++; }, onPointerDown: (PointerDownEvent e) { numPointerDownsOnParent++; },
), ),
const Positioned( Positioned(
child: SizedBox( child: SizedBox(
width: 200.0, width: 200.0,
height: 100.0, height: 100.0,
...@@ -331,7 +370,7 @@ void main() { ...@@ -331,7 +370,7 @@ void main() {
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
onPointerDown: (PointerDownEvent e) { numPointerDownsOnParent++; }, onPointerDown: (PointerDownEvent e) { numPointerDownsOnParent++; },
), ),
const Positioned( Positioned(
child: SizedBox( child: SizedBox(
width: 200.0, width: 200.0,
height: 100.0, height: 100.0,
...@@ -370,7 +409,7 @@ void main() { ...@@ -370,7 +409,7 @@ void main() {
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
child: new Container( child: new Container(
margin: const EdgeInsets.all(10.0), margin: const EdgeInsets.all(10.0),
child: const SizedBox( child: SizedBox(
width: 200.0, width: 200.0,
height: 100.0, height: 100.0,
child: AndroidView(viewType: 'webview', layoutDirection: TextDirection.ltr), child: AndroidView(viewType: 'webview', layoutDirection: TextDirection.ltr),
...@@ -396,7 +435,7 @@ void main() { ...@@ -396,7 +435,7 @@ void main() {
final FakePlatformViewsController viewsController = new FakePlatformViewsController(TargetPlatform.android); final FakePlatformViewsController viewsController = new FakePlatformViewsController(TargetPlatform.android);
viewsController.registerViewType('maps'); viewsController.registerViewType('maps');
await tester.pumpWidget( await tester.pumpWidget(
const Center( Center(
child: SizedBox( child: SizedBox(
width: 200.0, width: 200.0,
height: 100.0, height: 100.0,
...@@ -413,7 +452,7 @@ void main() { ...@@ -413,7 +452,7 @@ void main() {
); );
await tester.pumpWidget( await tester.pumpWidget(
const Center( Center(
child: SizedBox( child: SizedBox(
width: 200.0, width: 200.0,
height: 100.0, height: 100.0,
...@@ -435,7 +474,7 @@ void main() { ...@@ -435,7 +474,7 @@ void main() {
final FakePlatformViewsController viewsController = new FakePlatformViewsController(TargetPlatform.android); final FakePlatformViewsController viewsController = new FakePlatformViewsController(TargetPlatform.android);
viewsController.registerViewType('maps'); viewsController.registerViewType('maps');
await tester.pumpWidget( await tester.pumpWidget(
const Directionality( Directionality(
textDirection: TextDirection.rtl, textDirection: TextDirection.rtl,
child: Center( child: Center(
child: SizedBox( child: SizedBox(
...@@ -455,7 +494,7 @@ void main() { ...@@ -455,7 +494,7 @@ void main() {
); );
await tester.pumpWidget( await tester.pumpWidget(
const Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: Center( child: Center(
child: SizedBox( child: SizedBox(
......
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