Flutter, React Native und NativeScript: Kopf an Kopf im Cross-Plattform-Rennen

Bekannte Technik

NativeScript und React Native sind Javascript-Mobilframeworks der zweiten Generation. Das zeigt sich unter anderem daran, dass sie ihren Code nicht wie der Pionier Adobe PhoneGap in einer WebView ausführen. Stattdessen kommt eine JavaScript-Laufzeitumgebung zum Einsatz, die dank Anbindungen an die native API analog zu einer gewöhnlichen Java-VM arbeitet.

Frameworks der ersten Generation existieren weiterhin. Unternehmen mit vielen Webdesignern können mit PhoneGap oder dessen geistigen Nachfolger Ionic beeindruckende Ergebnisse erzielen. Das liegt daran, dass sie das gesamte Benutzerinterface mit gewöhnlichen Webtechniken realisieren und über passende GUI-Stacks eine fast native Optik erreichen können.

In der Anfangszeit der Entwicklung für mobile Endgeräte galt das Nachbilden von Steuerelementen als unschön, da sich die Widgets unter anderem nicht an die Einhandbedienung angleichen. Das ist jedoch pragmatischer betrachtet nicht mehr gültig. Die Feststellung des Flutter-Teams, dass native Steuerelemente heute keine besondere Rolle mehr spielen, ist – aus Sicht des Autors leider – Teil der Marktrealität im Smartphone-Umfeld.

Sowohl React Native als auch NativeScript lassen sich in JavaScript programmieren. Wer die Sprache aus der Webprogrammierung kennt, muss sich lediglich in die jeweilige API einarbeiten. NativeScript weist eine besondere Nähe zum JavaScript-Aufsatz TypeScript auf, der statische Typisierung und Objektorientierung mitbringt. Beide Anbieter versuchen zudem, das Entwickeln mit bekannten Webframeworks zu ermöglichen. Sowohl Angular als auch Vue.js finden sich auf der offiziellen Website von NativeScript. Für React Native existieren diverse mehr oder weniger inoffizielle Erweiterungen.

Wer mit Flutter einsteigt, startet nahezu immer bei null. Das Framework setzt ausschließlich auf Dart. Die wie Flutter von Google stammende Programmiersprache ist zwar ebenfalls statisch typisiert und objektorientiert, aber allen anfänglichen Ambitionen als JavaScript- und TypeScript-Herausforderer zum Trotz nach wie vor wenig verbreitet.

Größe der Community

Entwickler suchen bei Problemen gern direkt nach Lösungen. StackOverflow hat sich dadurch zu einem guten Indikator für die Verbreitung von Programmiersprachen und Frameworks entwickelt. Sucht man nach den verschiedenen Frameworks, so präsentiert sich das Ergebnis als Indikator für die Popularität, wie Abbildung 1 zeigt.

In der StackOverflow-Statistik liegt React Native knapp vor Flutter und beide deutlich über NativeScript (Abb. 1).

Ein weiterer Indikator findet sich in den Google-Trends, die die jeweiligen Suchanfragen repräsentieren. Die Verteilung in der Statistik (s. Abb. 2) zeigt eine ähnliche Tendenz wie die von StackOverflow. Beide Ansichten stammen aus dem Juli 2019.

Bei Google Trends liegen Flutter und React Native noch dichter beisammen (Abb. 2).

Einbinden externer Steuerelemente

Oft zeigt sich die wahre Stärke eines Frameworks erst jenseits der vom Hersteller implementierten APIs und Funktionen. Während das Aktivieren von Algorithmen in allen Systemen mehr oder weniger gleich einfach ist, ist das Einbinden eines hauseigenen Widgets meist eine Wissenschaft für sich.

In der Praxis kommt das Szenario durchaus häufig vor, beispielsweise beim Einbinden eines Steuerelements zum Anzeigen von Werbung, das keine Anpassung für das Framework mitbringt.

In Flutter erstellen Entwickler für den Anwendungsfall in Android Studio ein Projekt auf Basis der Vorlage "Flutter Plug-in". Danach müssen sie einige Klassen implementieren und die einzubindende Erweiterung im ersten Schritt bei der Laufzeitumgebung anmelden. Anschließend müssen sie eine Instanz des jeweiligen Widgets bereitstellen.

Dreh- und Angelpunkt der Implementierung ist die Factory-Klasse, die sich um das eigentliche Generieren der GUI-Instanzen kümmert:

public class TextViewFactory extends PlatformViewFactory {
private final BinaryMessenger messenger;

public TextViewFactory(BinaryMessenger messenger) {
super(StandardMessageCodec.INSTANCE);
this.messenger = messenger;
}

@Override
public PlatformView create(Context context, int id, Object o) {
return new FlutterTextView(context, messenger, id);
}
}

Das anzuzeigende Steuerelement ist in einen Wrapper verpackt, der unter anderem einen MethodCallHandler implementiert. Flutter handhabt die Kommunikation zwischen dem nativen und dem in Dart gehaltenen Teil durch das Übertragen von Strings. In der GUI-Klasse findet sich deshalb eine Methode namens onMethodCall, die die eingehenden Informationen durch einen klassischen String-Vergleich voneinander trennt:

@Override
public void onMethodCall(MethodCall methodCall,
MethodChannel.Result result)
{
switch (methodCall.method) {
case "doABCD":
...

Da NativeScript eine vergleichsweise umfangreiche Verbindung zwischen der Java-Laufzeitumgebung und der eigentlichen JavaScript-API aufbaut, lassen sich native Steuerelemente einfacher verwenden. Im ersten Schritt erhält die XML-Datei einen Platzhalter, der ein spezielles Widget repräsentiert, das anstelle eines mehr oder weniger beliebigen anderen Steuerelements erscheint:

<Page xmlns="http://schemas.nativescript.org/tns.xsd">
<StackLayout>
<Placeholder creatingView="creatingView"/>
</StackLayout>
</Page>

Im JavaScript- beziehungsweise TypeScript-Code dahinter erstellen Entwickler eine Instanz des Steuerelements, die sie dem view-Attribut zuweisen. Für Android könnte der Code folgendermaßen aussehen:

function creatingView(args) {
var nativeView = new android.widget.TextView(args.context);
nativeView.setSingleLine(true);
nativeView.setEllipsize(android.text.TextUtils.TruncateAt.END);
nativeView.setText("Native");
args.view = nativeView;
}
exports.creatingView = creatingView;

Bemerkenswert ist, dass das view-Attribut der Adapterklasse eine native View-Instanz aufnimmt. Das funktioniert, weil NativeScript zur Laufzeit die meisten Steuerelemente als native Widgets umsetzt. Weitere Informationen hierzu finden sich in der Dokumentation von NativeScript.

Wer ein Widget vollständig in die NativeScript-Welt überführen möchte, implementiert stattdessen ein Plug-in. Weitere Informationen zur dazu notwendigen Vorgehensweise finden sich ebenfalls in der NativeScript-Dokumentation.

React Native arbeitet ähnlich wie Flutter über eine eigene Klasse, die eine View-Instanz generiert und an den GUI-Stack liefert. Die Handhabung ist weitgehend dieselbe wie bei Googles Framework. Weitere Informationen finden sich in der Dokumentation bei Facebook.