Wrap native views
This section provides a guide about how to create a bridge for a native View
for Flutter.
It uses the ConversationListView
as an example, but you can adapt it to any View
of the SDK.
See the Flutter Android view bridging doc and the Flutter iOS view bridging doc for more details.
Android
Create the view wrapper
class NablaAndroidConversationsView(
private val context: Context,
id: Int,
creationParams: Map<String?, Any?>?,
) : PlatformView {
private val view: ConversationListView
init {
val view = ConversationListView(context)
view.doOnAttach {
val activity = context.getComponentActivityOrThrow()
val viewModel: ConversationListViewModel by activity.viewModels {
ConversationListViewModelFactory(owner = activity)
}
view.bindViewModel(viewModel, onConversationClicked = { conversationId ->
activity.startActivity(ConversationActivity.newIntent(context, conversationId))
})
}
this.view = view
}
override fun getView(): View = view
override fun dispose() {}
private fun Context.getComponentActivityOrThrow(): ComponentActivity {
if (this is ComponentActivity) {
return this
}
var currentContext = this
while (currentContext is ContextWrapper) {
if (currentContext is ComponentActivity) {
return currentContext
}
currentContext = currentContext.baseContext
}
throw IllegalStateException("Unable to find activity")
}
}
class NablaAndroidConversationsViewFactory : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
override fun create(context: Context?, viewId: Int, args: Any?): PlatformView {
val creationParams = args as Map<String?, Any?>?
return NablaAndroidConversationsView(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("nablaConversations", NablaAndroidConversationsViewFactory())
}
}
If you activity is of type
FlutterActivity
, change it toFlutterFragmentActivity
(io.flutter.embedding.android.FlutterFragmentActivity
)
iOS
Create the view wrapper
class NablaConversationViewFactory : NSObject, FlutterPlatformViewFactory {
private let messenger: FlutterBinaryMessenger
init(messenger: FlutterBinaryMessenger) {
self.messenger = messenger
super.init()
}
func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView {
return NablaConversationNativeView(
frame: frame,
viewIdentifier: viewId,
arguments: args,
binaryMessenger: messenger
)
}
}
class NablaConversationNativeView : NSObject, FlutterPlatformView, ConversationListDelegate {
private var _view: UIView
private var nablaNavigationController: UINavigationController?
init(
frame: CGRect,
viewIdentifier viewId: Int64,
arguments args: Any?,
binaryMessenger messenger: FlutterBinaryMessenger
) {
_view = UIView()
super.init()
_view = NablaMessagingClient.shared.views.createConversationListView(delegate: self)
_view.frame = frame
}
func view() -> UIView {
return _view
}
func conversationList(didSelect conversation: Conversation) {
guard let controller : UIViewController = _view.window?.rootViewController else {
print("Unable to find parent UIViewController")
return
}
let conversationViewController = NablaClient.shared.messaging.views.createConversationViewController(conversationId)
// We wrap the ConversationViewController inside a UINavigationController
// so that the navigation bar is displayed in the conversation
nablaNavigationController = UINavigationController(
rootViewController: conversationViewController
)
conversationViewController.navigationItem.leftBarButtonItem = UIBarButtonItem(
image: UIImage(systemName: "xmark"),
style: .plain,
target: self,
action: #selector(self.dismissNavigationController))
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
nablaNavigationController?.navigationBar.standardAppearance = appearance
nablaNavigationController?.navigationBar.scrollEdgeAppearance = appearance
nablaNavigationController?.modalPresentationStyle = .fullScreen
controller.present(nablaNavigationController!, animated: true, completion: nil)
}
@objc private func dismissNavigationController() {
nablaNavigationController?.dismiss(animated: true)
nablaNavigationController = nil
}
}
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 = NablaConversationViewFactory(messenger: registrar!.messenger())
registrar!.register(
factory,
withId: "nablaConversations"
)
Flutter
Create a file named nablaconversationlistwidget.dart
:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/foundation.dart';
const String viewType = 'nablaConversations';
Widget _buildAndroid(BuildContext context, Map<String, dynamic> creationParams) {
return AndroidView(
viewType: viewType,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
}
Widget _buildIoS(BuildContext context, Map<String, dynamic> creationParams) {
return UiKitView(
viewType: viewType,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
}
class NablaConversationsWidget extends StatelessWidget {
const NablaConversationsWidget({
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 NablaConversationsWidget
in your Flutter code.
Updated 25 days ago