Skip to content

Commit

Permalink
Add a layoutDirection parameter to AndroidView (flutter#20838)
Browse files Browse the repository at this point in the history
  • Loading branch information
amirh authored Aug 23, 2018
1 parent 5d1f874 commit 1fc7814
Show file tree
Hide file tree
Showing 5 changed files with 281 additions and 48 deletions.
48 changes: 48 additions & 0 deletions packages/flutter/lib/src/services/platform_views.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,21 @@ class PlatformViewsService {
///
/// The Android view will only be created after [AndroidViewController.setSize] is called for the
/// first time.
///
/// The `id, `viewType, and `layoutDirection` parameters must not be null.
static AndroidViewController initAndroidView({
@required int id,
@required String viewType,
@required TextDirection layoutDirection,
PlatformViewCreatedCallback onPlatformViewCreated,
}) {
assert(id != null);
assert(viewType != null);
assert(layoutDirection != null);
return new AndroidViewController._(
id,
viewType,
layoutDirection,
onPlatformViewCreated
);
}
Expand Down Expand Up @@ -343,10 +348,13 @@ class AndroidViewController {
AndroidViewController._(
this.id,
String viewType,
TextDirection layoutDirection,
PlatformViewCreatedCallback onPlatformViewCreated,
) : assert(id != null),
assert(viewType != null),
assert(layoutDirection != null),
_viewType = viewType,
_layoutDirection = layoutDirection,
_onPlatformViewCreated = onPlatformViewCreated,
_state = _AndroidViewState.waitingForSize;

Expand Down Expand Up @@ -380,6 +388,12 @@ class AndroidViewController {
/// Android's [MotionEvent.ACTION_POINTER_UP](https://developer.android.com/reference/android/view/MotionEvent#ACTION_POINTER_UP)
static const int kActionPointerUp = 6;

/// Android's [View.LAYOUT_DIRECTION_LTR](https://developer.android.com/reference/android/view/View.html#LAYOUT_DIRECTION_LTR) value.
static const int kAndroidLayoutDirectionLtr = 0;

/// Android's [View.LAYOUT_DIRECTION_RTL](https://developer.android.com/reference/android/view/View.html#LAYOUT_DIRECTION_RTL) value.
static const int kAndroidLayoutDirectionRtl = 1;

/// The unique identifier of the Android view controlled by this controller.
final int id;

Expand All @@ -396,6 +410,8 @@ class AndroidViewController {
/// disposed.
int get textureId => _textureId;

TextDirection _layoutDirection;

_AndroidViewState _state;

/// Disposes the Android view.
Expand Down Expand Up @@ -430,6 +446,37 @@ class AndroidViewController {
});
}

/// Sets the layout direction for the Android view.
Future<void> setLayoutDirection(TextDirection layoutDirection) async {
if (_state == _AndroidViewState.disposed)
throw new FlutterError('trying to set a layout direction for a disposed Android View. View id: $id');

if (layoutDirection == _layoutDirection)
return;

assert(layoutDirection != null);
_layoutDirection = layoutDirection;

// If the view was not yet created we just update _layoutDirection and return, as the new
// direction will be used in _create.
if (_state == _AndroidViewState.waitingForSize)
return;

await SystemChannels.platform_views.invokeMethod('setDirection', <String, dynamic> {
'id': id,
'direction': _getAndroidDirection(layoutDirection),
});
}

static int _getAndroidDirection(TextDirection direction) {
switch (direction) {
case TextDirection.ltr:
return kAndroidLayoutDirectionLtr;
case TextDirection.rtl:
return kAndroidLayoutDirectionRtl;
}
}

/// Sends an Android [MotionEvent](https://developer.android.com/reference/android/view/MotionEvent)
/// to the view.
///
Expand All @@ -454,6 +501,7 @@ class AndroidViewController {
'viewType': _viewType,
'width': size.width,
'height': size.height,
'direction': _getAndroidDirection(_layoutDirection),
});
if (_onPlatformViewCreated != null)
_onPlatformViewCreated(id);
Expand Down
58 changes: 51 additions & 7 deletions packages/flutter/lib/src/widgets/platform_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';

import 'basic.dart';
import 'debug.dart';
import 'framework.dart';

/// Embeds an Android view in the Widget hierarchy.
Expand Down Expand Up @@ -45,6 +47,7 @@ class AndroidView extends StatefulWidget {
@required this.viewType,
this.onPlatformViewCreated,
this.hitTestBehavior = PlatformViewHitTestBehavior.opaque,
this.layoutDirection,
}) : assert(viewType != null),
assert(hitTestBehavior != null),
super(key: key);
Expand All @@ -66,13 +69,20 @@ class AndroidView extends StatefulWidget {
/// This defaults to [PlatformViewHitTestBehavior.opaque].
final PlatformViewHitTestBehavior hitTestBehavior;

/// The text direction to use for the embedded view.
///
/// If this is null, the ambient [Directionality] is used instead.
final TextDirection layoutDirection;

@override
State createState() => new _AndroidViewState();
}

class _AndroidViewState extends State<AndroidView> {
int _id;
AndroidViewController _controller;
TextDirection _layoutDirection;
bool _initialized = false;

@override
Widget build(BuildContext context) {
Expand All @@ -82,19 +92,53 @@ class _AndroidViewState extends State<AndroidView> {
);
}

@override
void initState() {
super.initState();
void _initializeOnce() {
if (_initialized) {
return;
}
_initialized = true;
_layoutDirection = _findLayoutDirection();
_createNewAndroidView();
}

@override
void didChangeDependencies() {
super.didChangeDependencies();
_initializeOnce();

final TextDirection newLayoutDirection = _findLayoutDirection();
final bool didChangeLayoutDirection = _layoutDirection != newLayoutDirection;
_layoutDirection = newLayoutDirection;

if (didChangeLayoutDirection) {
// The native view will update asynchronously, in the meantime we don't want
// to block the framework. (so this is intentionally not awaiting).
_controller.setLayoutDirection(_layoutDirection);
}
}

@override
void didUpdateWidget(AndroidView oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.viewType == oldWidget.viewType)

final TextDirection newLayoutDirection = _findLayoutDirection();
final bool didChangeLayoutDirection = _layoutDirection != newLayoutDirection;
_layoutDirection = newLayoutDirection;

if (widget.viewType != oldWidget.viewType) {
_controller.dispose();
_createNewAndroidView();
return;
_controller.dispose();
_createNewAndroidView();
}

if (didChangeLayoutDirection) {
_controller.setLayoutDirection(_layoutDirection);
}
}

TextDirection _findLayoutDirection() {
assert(widget.layoutDirection != null || debugCheckHasDirectionality(context));
return widget.layoutDirection ?? Directionality.of(context);
}

@override
Expand All @@ -108,10 +152,10 @@ class _AndroidViewState extends State<AndroidView> {
_controller = PlatformViewsService.initAndroidView(
id: _id,
viewType: widget.viewType,
layoutDirection: _layoutDirection,
onPlatformViewCreated: widget.onPlatformViewCreated
);
}

}

class _AndroidPlatformView extends LeafRenderObjectWidget {
Expand Down
27 changes: 23 additions & 4 deletions packages/flutter/test/services/fake_platform_views.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class FakePlatformViewsController {
return _resize(call);
case 'touch':
return _touch(call);
case 'setDirection':
return _setDirection(call);
}
return new Future<Null>.sync(() => null);
}
Expand All @@ -57,6 +59,7 @@ class FakePlatformViewsController {
final String viewType = args['viewType'];
final double width = args['width'];
final double height = args['height'];
final int layoutDirection = args['direction'];

if (_views.containsKey(id))
throw new PlatformException(
Expand All @@ -70,7 +73,7 @@ class FakePlatformViewsController {
message: 'Trying to create a platform view of unregistered type: $viewType',
);

_views[id] = new FakePlatformView(id, viewType, new Size(width, height));
_views[id] = new FakePlatformView(id, viewType, new Size(width, height), layoutDirection);
final int textureId = _textureCounter++;
return new Future<int>.sync(() => textureId);
}
Expand Down Expand Up @@ -130,15 +133,31 @@ class FakePlatformViewsController {
return new Future<Null>.sync(() => null);
}

Future<dynamic> _setDirection(MethodCall call) async {
final Map<dynamic, dynamic> args = call.arguments;
final int id = args['id'];
final int layoutDirection = args['direction'];

if (!_views.containsKey(id))
throw new PlatformException(
code: 'error',
message: 'Trying to resize a platform view with unknown id: $id',
);

_views[id].layoutDirection = layoutDirection;

return new Future<Null>.sync(() => null);
}
}

class FakePlatformView {

FakePlatformView(this.id, this.type, this.size);
FakePlatformView(this.id, this.type, this.size, this.layoutDirection);

final int id;
final String type;
Size size;
int layoutDirection;

@override
bool operator ==(dynamic other) {
Expand All @@ -151,11 +170,11 @@ class FakePlatformView {
}

@override
int get hashCode => hashValues(id, type, size);
int get hashCode => hashValues(id, type, size, layoutDirection);

@override
String toString() {
return 'FakePlatformView(id: $id, type: $type, size: $size)';
return 'FakePlatformView(id: $id, type: $type, size: $size, layoutDirection: $layoutDirection)';
}
}

Expand Down
Loading

0 comments on commit 1fc7814

Please sign in to comment.