Wrap native ViewController/Fragment
This section provides a guide about how to create a bridge for a native ViewController
(on iOS) and Fragment
(on Android) for Flutter.
It uses AppointmentsFragment
/AppointmentListViewController
as an example, but you can adapt it to any ViewController
/Fragment
of the SDK.
Android
Create the Fragment wrapper
class NablaAppointmentsView(
private val context: Context,
id: Int,
creationParams: Map<String?, Any?>?,
) : PlatformView {
private val view: FragmentContainerView
init {
val fragment = AppointmentsFragment.newInstance()
val viewId = Random().nextInt()
val view = FragmentContainerView(context)
view.setId(viewId)
view.doOnAttach {
val activity = it.context.getFragmentActivityOrThrow()
activity.supportFragmentManager.findFragmentByTag("flutter_fragment")?.let { flutterFragment ->
flutterFragment.childFragmentManager.commit {
replace(it.id, fragment)
}
}
}
this.view = view
}
override fun getView(): View = view
override fun dispose() {}
private fun Context.getFragmentActivityOrThrow(): FragmentActivity {
if (this is FragmentActivity) {
return this
}
var currentContext = this
while (currentContext is ContextWrapper) {
if (currentContext is FragmentActivity) {
return currentContext
}
currentContext = currentContext.baseContext
}
throw IllegalStateException("Unable to find activity")
}
}
class NablaAppointmentsViewFactory : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
override fun create(context: Context?, viewId: Int, args: Any?): PlatformView {
val creationParams = args as Map<String?, Any?>?
return NablaAppointmentsView(context!!, viewId, creationParams)
}
}
Wire the wrapper
In the MainActivity
file, add these lines to wire your newly created native view:
class MainActivity: FlutterFragmentActivity() {
private val delegate = AppCompatDelegate.create(this, null)
init {
addOnContextAvailableListener {
delegate.installViewFactory()
}
}
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
// Your other wiring here...
flutterEngine.platformViewsController
.registry
.registerViewFactory("nablaAppointments", NablaAppointmentsViewFactory())
}
}
If you activity is of type
FlutterActivity
, change it toFlutterFragmentActivity
(io.flutter.embedding.android.FlutterFragmentActivity
)
iOS
Create the ViewController wrapper
class NablaAppointmentsViewFactory : NSObject, FlutterPlatformViewFactory {
private let messenger: FlutterBinaryMessenger
private let rootViewController: UIViewController
init(messenger: FlutterBinaryMessenger, rootViewController: UIViewController) {
self.messenger = messenger
self.rootViewController = rootViewController
super.init()
}
func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView {
return NablaAppointmentsNativeView(
frame: frame,
viewIdentifier: viewId,
arguments: args,
binaryMessenger: messenger,
parentController: rootViewController
)
}
}
class NablaAppointmentsNativeView : NSObject, FlutterPlatformView {
private var _viewController: UIViewController
init(
frame: CGRect,
viewIdentifier viewId: Int64,
arguments args: Any?,
binaryMessenger messenger: FlutterBinaryMessenger,
parentController: UIViewController
) {
_viewController = UIViewController(nibName: nil, bundle: nil)
super.init()
_viewController = NablaSchedulingClient.shared.views.createAppointmentListViewController()
parentController.addChild(_viewController)
_viewController.view.frame = frame
_viewController.didMove(toParent: parentController)
}
func view() -> UIView {
return _viewController.view
}
}
Wire the wrapper
In the AppDelegate
file, add these lines to wire your newly created view:
weak var registrar = self.registrar(forPlugin: "nabla")
let factory = NablaAppointmentsViewFactory(messenger: registrar!.messenger(), rootViewController: flutterViewController)
registrar!.register(
factory,
withId: "nablaAppointments"
)
Flutter
Create a file named nablaappointmentswidget.dart
:
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
const String viewType = 'nablaAppointments';
Widget _buildAndroid(BuildContext context, Map<String, dynamic> creationParams) {
return PlatformViewLink(
viewType: viewType,
surfaceFactory:
(context, controller) {
return AndroidViewSurface(
controller: controller as AndroidViewController,
gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
);
},
onCreatePlatformView: (params) {
return PlatformViewsService.initExpensiveAndroidView(
id: params.id,
viewType: viewType,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
onFocus: () {
params.onFocusChanged(true);
},
)
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
..create();
},
);
}
Widget _buildIoS(BuildContext context, Map<String, dynamic> creationParams) {
return UiKitView(
viewType: viewType,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
}
class NablaAppointmentsWidget extends StatelessWidget {
const NablaAppointmentsWidget({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
// Pass parameters to the platform side.
final Map<String, dynamic> params = <String, dynamic>{};
switch (defaultTargetPlatform) {
case TargetPlatform.android:
return _buildAndroid(context, params);
case TargetPlatform.iOS:
return _buildIoS(context, params);
default:
throw UnsupportedError('Unsupported platform view');
}
}
}
You can now use the NablaAppointmentsWidget
in your Flutter code.
Updated 25 days ago