Passing variables from Dart to Kotlin and from Kotlin to Dart
We all love Flutter and know how great it is, but sometimes we have requirements that need to be implemented natively, or we just didn’t find another way yet. If that’s the case, then let’s see how you can pass some variables from Dart to native Kotlin and the other way around.
This is a basic example to get things started and to prove that communication between Dart and Kotlin it’s actually pretty simple.
If you want to take a look at the code first and get your hands on it, here you can take a look at the code base and clone this to try it out.
What does this code do?
In the main file, we are only displaying a message with a certain view height.
In order to communicate from Dart to Kotlin, we will use a channel that will be set when initiating the widget. We are setting a callback for receiving calls on this channel. When data it’s received, the widget will modify its view height.
static const channel = MethodChannel('FromDartMessageChannel');
void _onListenChannel() {
channel.setMethodCallHandler((call) async {
if (call.method == 'FromDartViewHeight') {
try {
final nativeHeight = call.arguments as int;
setState(() {
viewHeight = nativeHeight.toDouble();
});
} catch (ex) {
setState(() {
viewHeight = 0;
});
}
}
});
}
When we are building the widget, will use TargetPlatform
to identify the device platform and use an android view in this case.
In creating the platform view it’s important to register its view type as this will be used in Kotlin to identify the communication channel.
onCreatePlatformView
— will send our parameters to Kotlin following to be modified from there and sent back to Dart for rendering.
const viewType = 'CustomMessageFactory';
final creationParams = <String, String>{
'sizedBoxHeightModifiedInNative': widget.viewHeight.toString(),
};
TargetPlatform.android:
platformWidget = PlatformViewLink(
viewType: viewType,
surfaceFactory: (context, controller) {
return AndroidViewSurface(
controller: controller as AndroidViewController,
gestureRecognizers: const <
Factory<OneSequenceGestureRecognizer>>{},
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
);
},
onCreatePlatformView: (params) {
return PlatformViewsService.initSurfaceAndroidView(
id: params.id,
viewType: viewType,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
onFocus: () {
params.onFocusChanged(true);
},
)
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
..create();
},
);
We need to modify our Kotlin code (in order to receive the params from Dart) and send them back to Dart with the modified values.
In the main activity, we are configuring the channel and the view factory. For this to work it’s important to have the same values registered in Dart and in Kotlin, in our case they are:
channel = 'FromDartMessageChannel'
viewFactory ='CustomMessageFactory'
private lateinit var channel: MethodChannel
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "FromDartMessageChannel")
flutterEngine.platformViewsController
.registry
.registerViewFactory(
"CustomMessageFactory",
CustomMessageFactory(activity, channel)
)
}
And the final step, it’s to modify the received values from Dart in our Kotlin code. In order to do this will create a new class CustomMessageFactory
where on initialization will modify the params and send them back to Dart to be rendered.
class CustomMessageFactory(
private val activity: Activity,
private val channel: MethodChannel,
) :
PlatformViewFactory(StandardMessageCodec.INSTANCE) {
override fun create(context: Context, viewId: Int, args: Any?): PlatformView {
val creationParams = args as Map<String, String>
return CustomMessage(activity, channel, context, viewId, creationParams)
}
}
class CustomMessage(
activity: Activity,
channel: MethodChannel,
context: Context,
id: Int,
creationParams: Map<String, String>,
) :
PlatformView {
private val view: View
override fun getView(): View {
return view
}
override fun dispose() {}
init {
val containerViewGroup = FrameLayout(context)
val receivedParamFromDart =
creationParams["sizedBoxHeightModifiedInNative"]?.toInt()?.plus(20)
Handler(Looper.getMainLooper()).postDelayed({
channel.invokeMethod("FromDartViewHeight", receivedParamFromDart)
}, 300)
Handler(Looper.getMainLooper()).postDelayed({
channel.invokeMethod("FromNativeString", "Hello from kotlin!")
}, 300)
view = containerViewGroup
}
}
Now feel free to modify this app and try it yourself. Passing data between Dart ↔ Kotlin, it’s really easy once you understand the communication between them.