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!

https://hovida.de/wp-content/uploads/widget_hover.png

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:

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)

Kommentare (0)

Derzeit sind keine Kommentare vorhanden.

Kommentiere unsere Artikel

Damit du diesen Artikel kommentieren kannst, musst du registriert sein.