Flutter und native Widgets
Für das Projekt WhatsNear, was ich bereits seit Jahren in Gedanken beziehe umzusetzen, benötigt einen eigenen Renderingprozess für das Umgebungsradar.
Mittlerweile bin ich von natives Java über Android Studio zu flutter gewechselt. Ich schaue mir flutter bereits drei Tage an und musste feststellen, dass flutter im Gegensatz zu React native hier extrem simple und hilfreich ist. Man braucht sich als Entwickler überhaupt nicht mehr um das einrichten der Umgebung kümmern und kann direkt nach Installation mit einem neuen Projekt anfangen, ohne dass man erst dutzende Konfigurationsfehler oder fehlende Depencies umständlich beseitigen muss. Hier ist flutter echt ein Traum!

Einbinden von nativen Komponenten
Nach etwas längerer Recherche, ohne jetzt ein extra plugin_widget zu entwickeln und einzubinden, ist es auch Möglich, Kotlin- bzw. Javabasierte Klassen direkt als flutter-Widget einzubinden. Anstelle ein Widget (wie zum Beispiel das Text-Widget) wie folgt einzubinden:
class Radar extends StatelessWidget {
Radar();
@override
Widget build(BuildContext context) {
return Text('Replace me with native Widget!')
}
}
Code-Sprache: Dart (dart)
Ist es dann möglich, das eigene Widget wie folgt einzubinden:
import 'radarnative.dart';
class Radar extends StatelessWidget {
Radar();
@override
Widget build(BuildContext context) {
return RadarNative()
}
}
Code-Sprache: Dart (dart)
Skeleton
Die Dateien, die wir anlegen, werden wie folgt angeordnet:
- lib/
- android/app/src/main/kotlin/<appID>/
Integration auf nativer Seite
Um das eigene native Widget zu integrieren, registriert man hierzu einfach in Kotlin bzw. Java das eigene Widget (wie zum Beispiel RadarFactory):
// @File android/app/src/main/kotlin/<appID>/MainActivity.kt
package <appID>;
class MainActivity : FlutterActivity() {
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
/* Register native Views */
GeneratedPluginRegistrant.registerWith(flutterEngine)
flutterEngine.platformViewsController.registry.registerViewFactory("de.whatsnear/Radar", RadarFactory(flutterEngine.dartExecutor.binaryMessenger))
// [...]
}
}
Code-Sprache: Kotlin (kotlin)
Erstellt dann die RadarFactory als Klasse:
// @File android/app/src/main/kotlin/<appID>/RadarFactory.kt
package <appID>;
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.StandardMessageCodec
import io.flutter.plugin.platform.PlatformView
import io.flutter.plugin.platform.PlatformViewFactory
import android.content.Context
class RadarFactory(private val messenger: BinaryMessenger) : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
override fun create(context: Context, id: Int, arguments: Any?): PlatformView {
return RadarView(context, id, arguments)
}
}
Code-Sprache: Kotlin (kotlin)
und implementiert die RadarView, die dann auf ein Canvas zeichnen kann:
// @File android/app/src/main/kotlin/<appID>/RadarView.kt
package <appID>;
import io.flutter.plugin.platform.PlatformView
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.PixelFormat
import android.graphics.PorterDuff
import android.view.SurfaceHolder
import android.view.SurfaceView
import android.view.View
import androidx.annotation.NonNull
import androidx.annotation.Nullable
internal class RadarView(context: Context, id: Int, arguments: Any?) : SurfaceView(context), PlatformView {
private var surface: SurfaceHolder
private var canvas: Canvas? = null
init {
surface = holder.apply {
addCallback(object : SurfaceHolder.Callback {
override fun surfaceCreated(holder: SurfaceHolder) {
Thread {
canvas = holder.lockCanvas()
canvas?.apply {
/* Clear Canvas */
drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
paint()
holder.unlockCanvasAndPost(this)
}
}.start()
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
}
})
}
/* Set Ordering */
setZOrderOnTop(true)
/* Set Transparent */
surface.setFormat(PixelFormat.TRANSLUCENT)
}
private fun paint() {
/* Hier wird nun gezeichnet... */
val paint = Paint()
canvas?.drawCircle(200f, 200f, 200f, paint)
}
@Nullable
override fun getView(): View = this
override fun dispose() {}
}
Code-Sprache: Kotlin (kotlin)
Integration auf flutter Seite
Damit das Widget durch RadarNative() in flutter aufgerufen werden kann, muss dies noch als Widget registriert werden:
// @File lib/radarnative.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class RadarNative extends StatefulWidget {
@override
RadarNativeState createState() => RadarNativeState();
}
class RadarNativeState extends State<RadarNative> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return AndroidView(
viewType: 'de.whatsnear/Radar',
layoutDirection: TextDirection.ltr,
creationParams: <String, dynamic>{},
creationParamsCodec: const StandardMessageCodec(),
);
}
}
Code-Sprache: Dart (dart)