Noch etwas flatterhaft: Cross-Plattform-Entwicklung mit Flutter

Seite 2: Erstellen eines Projektskeletts

Inhaltsverzeichnis

In einem früheren Artikel erzeugte der Autor eine Flutter-Applikation unter Verwendung von Android Studio. Diese Vorgehensweise mag bequem sein, erzeugt aber ein Projektskelett, das nur mit Android und iOS kompatibel ist. Aktualisierungen des Plug-ins oder der IDE bringen hier übrigens keine Besserung.

Ursache dafür ist, dass sowohl unter Android als auch unter iOS ein kleiner nativer "Urlader" erforderlich ist, der die Flutter-Runtime konfiguriert und mit Eingabequellen sowie dem OpenGL-Outputfenster verbindet. Momentan sind weder Android Studio noch flutter create zum Erzeugen dieser Stubs befähigt.

Im Interesse der Bequemlichkeit sollten Entwickler die Path-Variable ihrer Workstation an dieser Stelle um das SDK-Verzeichnis ergänzen. Alternativ dazu können sie nach dem folgenden Schema eine Deklaration anlegen, die nur im gerade geöffneten Terminalfenster gilt:

export PATH=$PATH:/home/tamhan/Downloads/flutter/flutter/bin

Unter Linux oder macOS arbeitende Entwickler setzen hier auf die in ihrem jeweiligen Betriebssystem vorgesehene Syntax. Wichtig ist nur, dass man die Kommandozeilenapplikation aus jedem beliebigen Verzeichnis der Workstation aufrufen kann.

Als Nächstes benötigt man ein Flutter-Projekt, das die Shell für die verschiedenen Desktop-Betriebssysteme enthält. Auf GitHub steht ein Projektskelett bereit, das Windows, macOS und Linux auf einen Schlag bedient. Es lässt sich durch das folgende Kommando herunterladen:

git clone https://github.com/google/flutter-desktop-embedding.git

Nennenswert ist, dass der daraufhin erstellte Ordner kein ausführbares Flutter-Projekt enthält – es findet sich stattdessen im Unterordner examples. An der Stelle können Entwickler abermals das Programm ausführen, was zur Ausgabe des folgenden Ergebnisses führt:

flutter run
Downloading linux-x64 tools... 6.4s

Die Runtime lädt nun noch einige Kompilierwerkzeuge und andere Helferlein herunter, um den Beispielcode danach zu starten. Er präsentiert sich – unter Ubuntu 18.04 – wie in Abbildung 4:

Auch unter Linux lassen sich mit Flutter Klicks zählen (Abb. 4).

An der Stelle stellt sich die Frage, wie man den bisherigen Android-Code am Desktop zum Laufen bekommt. In der Theorie müsste es möglich sein, die notwendigen Verzeichnisse in das andere Projekt "zu portieren", um unterm Strich eine lauffähige Applikation zu erhalten. Die Berichte im Support-Forum weisen unisono darauf hin, dass diese an sich logisch erscheinende Vorgehensweise in der Praxis nicht weiterhilft. Sinnvoller ist es stattdessen, die Struktur eines Flutter-Projekts näher anzusehen. Abbildung 5 bietet einen Überblick, der die von Android Studio generierte und die aus GitHub heruntergeladene Projektstruktur zeigt:

Auf Kommandozeilenebene erfährt man mehr über die innere Struktur von Flutter-Projekten (Abb. 5).

Auffällig ist, dass beide Projektstrukturen einen Ordner namens lib enthalten. Dieses unter Linux normalerweise für Bibliotheken vorgesehene Verzeichnis enthält unter Flutter – im Allgemeinen – den in Dart gehaltenen Benutzercode. Zudem gibt es noch eine Datei namens pubspec.yaml, die für die "Steuerung" der Toolchain und der restlichen Teile des Projekts verantwortlich ist. Sie ist vor allem deshalb relevant, weil sie für die Einbindung von beispielsweise Ressourcen sorgt.

In der Theorie spricht nichts dagegen, hier einfach den Korpus des lib-Verzeichnisses an einen neuen Aufenthaltsort zu kopieren und danach abermals einen Kompilierprozess loszulassen. Der Lohn dafür ist ein leeres graues Fenster. Die Runtime startet, kann mit dem Code aber nichts anfangen. Im für die Ausführung verantwortlichen Terminalfenster findet sich die Antwort darauf, die sich nach dem folgenden Schema präsentiert:

flutter run
Launching lib/main.dart on Linux in debug mode...
Building Linux application...
flutter: ??? EXCEPTION CAUGHT BY WIDGETS LIBRARY ????????????????????????????????????????????????????????????
flutter: The following assertion was thrown building MyApp(dirty):
flutter: Unknown platform.
flutter: linux was not recognized as a target platform. Consider updating the list of TargetPlatforms to
flutter: include this platform.

Die Ursache für das Problem wird offenbar, wenn man die Datei main.dart öffnen – Flutter-Applikationen basieren auf Dart, wie in einem früheren Artikel zu lesen war. Im von Google bereitgestellten Beispiel, das sich übrigens auch in der GitHub-Oberfläche ansehen lässt, findet sich die folgende Passage:

import 'package:flutter/foundation.dart'
show debugDefaultTargetPlatformOverride;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
// See https://github.com/flutter/flutter/wiki/Desktop-shells#target-platform-override
debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;

runApp(new MyApp());
}

Das Beispiel für Android kam bisher ohne das Manipulieren der Zielplattformvariable aus, der relevante Teil von main.dart präsentiert sich folgendermaßen:

import 'package:flutter/material.dart';
import 'scene2.dart';

void main() => runApp(MyApp());

Wer die Deklaration ergänzt und danach noch die pubspec.yaml-Datei kopiert, sieht danach das in Abbildung 6 gezeigte Ergebnis.

Zaghafte erste Versuche: Der Code läuft, sieht aber unsauber aus (Abb. 6).

Das Platform-Attribut lässt sich übrigens auch zur dynamischen Anpassung des Programmverhaltens verwenden. Google zeigt in der Dokumentation die folgende Methode, die die Vorgehensweise anschaulich illustriert:

void _setTargetPlatformForDesktop() {
TargetPlatform targetPlatform;
if (Platform.isMacOS) {
targetPlatform = TargetPlatform.iOS;
} else if (Platform.isLinux || Platform.isWindows) {
targetPlatform = TargetPlatform.android;
}
if (targetPlatform != null) {
debugDefaultTargetPlatformOverride = targetPlatform;
}
}

Die in der Kommandozeile eingeblendeten Tastenkombinationen sind übrigens kein Scherz. Möchten Entwickler ihr Programm beenden, drücken sie auf Q. Reloads lassen sich durch R oder r ausführen, das Aufrufen der angezeigten URL öffnet einen – zugegebenermaßen etwas simplen – Debugger.

Doch damit zurück zum Screenshot: Ursache für das seltsame Rendering ist, dass die Flutter-Engine unter Linux beziehungsweise am Desktop derzeit noch nicht in der Lage ist, die zur Darstellung der Steuerelemente notwendigen Fonts zu finden. Das wird in pubspec.yaml durch den folgenden Codeblock behoben:

flutter:
uses-material-design: true

# See https://github.com/flutter/flutter/wiki/Desktop-shells#fonts
fonts:
- family: Roboto
fonts:
- asset: fonts/Roboto/Roboto-Thin.ttf
weight: 100
- asset: fonts/Roboto/Roboto-Light.ttf
weight: 300
- asset: fonts/Roboto/Roboto-Regular.ttf
weight: 400
- asset: fonts/Roboto/Roboto-Medium.ttf
weight: 500
- asset: fonts/Roboto/Roboto-Bold.ttf
weight: 700
- asset: fonts/Roboto/Roboto-Black.ttf
weight: 900

Wer ihn in die pubspec.yaml des Hauptprojekts überträgt, erntet – wie in Abbildung 7 – ein komplett funktionierendes Projekt. Der GUI-Stack von Flutter arbeitet am Desktop mittlerweile fast perfekt. Dass Programme mit nativen Funktionen (Stichwort Zwischenablage) Probleme bekommen können, ist keine allein auf Flutter replizierbare Eigenheit.

Das Beispiel läuft nun unter Linux (Abb. 7).

Entwickler müssen zudem beachten, dass Flutter am Desktop derzeit nur Debug-Builds kompilieren kann. Das Übergeben von Release-Flags ignoriert die CLI stillschweigend, um sie nach wie vor mit Debug-Versionen auszustatten.