Noch etwas flatterhaft: Cross-Plattform-Entwicklung mit Flutter | heise online

Noch etwas flatterhaft: Cross-Plattform-Entwicklung mit Flutter

Flutter war von Anfang an sowohl für Android als auch für iOS verfügbar. Mittlerweile gibt es Portierungen für eine Vielzahl anderer Systeme. Der Artikel betrachtet Flutter für Linux und Flutter für das Web.

vorlesen Druckansicht 20 Kommentare lesen
Noch etwas flatterhaft: Cross-Plattform-Entwicklung mit Flutter

(Bild: Shutterstock)

Lesezeit: 13 Min.
Von
  • Tam Hanna
Inhaltsverzeichnis

Entwickler können eine Flutter-Applikation am Desktop ausführen, indem sie sie in einen Emulator verpacken. Diese Vorgehensweise ist allerdings nicht besonders vernünftig, weshalb Google für den Desktop als Shell bezeichnete Programme vorsieht. Wichtig ist hierbei die Warnung auf der Webseite des Projekts, die darauf hinweist, dass alle Desktop-Shells derzeit noch Prototypen sind.

Aufgrund der großen Nähe von macOS und iOS folgt, dass die Shell für den Mac als "besonders stabil" gilt. Größere Breaking Changes dürften, so zumindest die Meinung der Entwickler, nicht mehr auftreten. Wer mit Flutter für Windows oder Linux arbeitet, muss hingegen mit unschöneren Ergebnissen rechnen. Die praktische Auswirkung dieser API-Brüche dürfte für die meisten Entwickler allerdings kaum spürbar sein, da sie nur selten (Stichwort Plug-in) direkt mit der Shell interagieren.

Android Studio meldet sich bei jedem Start, wenn das Flutter-Plug-in veraltet ist. Der in der IDE enthaltene Update-Mechanismus ist jedoch nicht in der Lage, die Flutter-Version auf den aktuellen Stand zu bringen. Die Lösung dafür findet sich im Download-Verzeichnis, wo Entwickler nach dem folgenden Schema ein Update beziehen können:

~/Downloads/flutter/flutter/bin$ ./flutter upgrade
Upgrading Flutter from /home/tamhan/Downloads/flutter/flutter...
From https://github.com/flutter/flutter

Aufgrund der Instabilität der Desktop- und auch der Webwerkzeuge verlangt Google, dass die Basisversion des Filter-Codes auf "Master" zu setzen ist. Es handelt sich dabei um die Spitze des Versionskontrollsystembaums, weshalb es bei der praktischen Nutzung zu diversen Problemen kommen kann. Die bei vielen Entwicklern beliebtere Baseline "Beta" ist nicht zulässig.

Von Hand heruntergeladene Installationen des SDKs sitzen normalerweise im Beta-Channel, weshalb im ersten Schritt folgende Kommandofolge erforderlich ist:

./flutter channel master
Switching to flutter channel 'master'...
...
./flutter upgrade

Am Ende jedes Durchlaufs sehen Entwickler, wie in Abbildung 1 gezeigt, die Ergebnisse der Ausführung des device-Kommandos. Es informiert auf der Kommandozeile darüber, welche Zielsysteme mit dem PC verbunden sind.

Die Workstation erkennt sich selbst nicht als Deployment-Ziel (Abb. 1).

Das Problem lässt sich umgehen, indem man das folgende Kommando eingibt, um die Konfiguration anzupassen:

./flutter config --enable-linux-desktop
Setting "enable-linux-desktop" value to "true".

Wer nicht unter Linux arbeitet, muss stattdessen ein anderes Plattformaktivierungskommando verwenden:

flutter config --enable-macos-desktop 
flutter config --enable-windows-desktop

Lohn der Mühen ist jedenfalls, dass flutter devices die Workstation als Zielgerät erkennt:

Auch Ubuntu 18.04 nimmt ab sofort Flutter-Code entgegen (Abb. 2).

In der Praxis ist an dieser Stelle noch ein weiterer Lauf von flutter doctor empfehlenswert, der mitunter Konfigurationsfehler findet (Abb. 3):

Clang ist für das Kompilieren von Desktop-Code erforderlich (Abb. 3).

Im gezeigten Szenario fehlt beispielsweise die Ausführungsumgebung, die man nach folgendem Schema nachinstalliert:

sudo apt-get install clang

Am Ende der Konfigurationsarbeiten sollte ein grüner Haken neben der Zielplattform erscheinen – eventuelle Beschwerden über Android-Lizenzen oder Visual Studio Code ("soll falsch konfiguriert sein") sind bei der Desktop-Entwicklung irrelevant.

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.

ASM.JS, Emscripten und Co. brachten viele spätestens ins Staunen, als die Emscripten-Runtime erstmals Qt-Beispielcode im Browser ausführen konnte. Google ist sich dieser Trends und Entwicklungen bewusst und versucht, Flutter auch in diesem Bereich unterzubringen. Dabei kommt die in Abbildung 8 gezeigte Struktur zum Einsatz:

Der Webbrowser lässt sich ebenfalls zur Ausführung von Flutter einspannen (Abb. 8).

(Bild: https://flutter.dev/web)

Es ist nicht notwendig, dem Browser Plug-ins und andere "Nettigkeiten" einzuschreiben. Dass Google den Code (analog zum Play-Backend) an die Bedürfnisse des hauseigenen Chrome-Browsers anpasst, ist allerdings durchaus vorstellbar.

Mehr Infos

Neu in 1.9

Flutter 1.9 bringt Entwicklern webbasierter Applikationen Erleichterung: Das Flutter-Kommandozeilenwerkzeug bietet erstmals die Möglichkeit an, die Web-Persönlichkeit automatisch zu einem existierenden Projekt hinzuzufügen. Weitere Informationen hierzu finden sich hier.

Ein weiteres "Sonderkriterium" von Flutter for Web ist, dass es sich dabei zwar ebenfalls noch um eine Technical Preview handelt, in der es noch zu Breaking Changes kommen kann. Google treibt die Entwicklung des Projekts allerdings nicht im Haupt-Repository, sondern in einem separaten GitHub-Projekt voran. Hier findet sich eine Gruppe durchaus ausgereifter Beispielapplikationen. Flutter for Web lässt sich (zumindest in der Theorie) also schon verwenden, um für Endkunden vorgesehene Applikationen zu realisieren – in einem Test des Autors funktionierten die Beispiele auch unter Firefox.

Die Bereitstellung der Webentwicklungswerkzeuge erfolgt durch Eingabe des folgenden Kommandos:

flutter pub global activate webdev
...
Activated webdev 2.5.1.

Analog zur Arbeit am Desktop gilt auch hier, dass Entwickler das Beispielprojekt separat von GitHub beziehen müssen:

git clone https://github.com/flutter/flutter_web.git
Cloning into 'flutter_web'...
...
cd flutter_web
tamhan@TAMHAN18:~/flutterweb/flutter_web$

Im eigentlichen Projektskelett ist dann noch eine Sondereinladung erforderlich, die den fehlenden Framework-Code herunterlädt:

~/flutterweb/flutter_web/examples/hello_world$ flutter pub upgrade
! flutter_web 0.0.0 from path ../../packages/flutter_web
! flutter_web_ui 0.0.0 from path ../../packages/flutter_web_ui
Running "flutter pub upgrade" in hello_world... 17.8s

Interessant ist, dass Flutter for Web die eigenen Dateien nicht in lib, sondern in Web speichert. main.dart sieht in Flutter for Web folgendermaßen aus:

import 'package:flutter_web_ui/ui.dart' as ui;
import 'package:flutter_web.examples.hello_world/main.dart'
as app;

main() async {
await ui.webOnlyInitializePlatform();
app.main();
}

In der für die Arbeit im Web vorgesehenen Version von main.dart findet sich zudem eine Einbindung, die auf die Hauptdatei verweist. Sie lädt das normale App-Objekt, um es zu verpacken und danach auszuführen. Die Bereitstellung der Rendering-Fläche erfolgt dann in HTML:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<script defer src="main.dart.js" type="application/javascript"></script>
</head>
<body>
</body>
</html>

Für die Ausführung des generierten Codes bringt Flutter einen Webserver mit, der sich im Projektverzeichnis ausführen lässt. Im Erfolgsfall zeigt er eine URL an, aber er ist derzeit noch instabil und wirft oft diverse Fehlermeldungen:

~/flutterweb/flutter_web/examples/hello_world$ webdev serve
/home/tamhan/.pub-cache/bin/webdev: 7: /home/tamhan/.pub-cache/bin/webdev: dart: not found

Wer sich ernsthaft mit Flutter for Web auseinandersetzen möchte, sollte sich eine IDE einrichten. Anders als für Flutter am Desktop gibt es schon Unterstützung für Visual Studio Code und IntelliJ IDEA.

Die durchgeführten Versuche zeigen, dass es möglich ist, eine Flutter-Applikation am Desktop auszuführen. Die Frage ist allerdings, ob sich Entwickler diesen Aufwand wirklich antun sollen. Flutter soll – wie jedes Cross-Plattform-Werkzeug – dafür sorgen, dass ihr Code ohne jegliche oder mit minimalen Änderungen auf verschiedenen Zielsystemen funktioniert. Wer keinen akuten Bedarf an einer Ausführung des Codes am Desktop sieht, sollte stattdessen auf ein Android-Ziel mit einem großen Display setzen und gegebenenfalls die Usability-Richtlinien von Android "ignorieren". Die endgültige Portierung der Applikationslogik kann erfolgen, wenn API und Co. stabilisiert wurden.

Tam Hanna
befasst sich seit dem Jahr 2004 mit Handcomputern und Elektronik. Derzeit liegt sein Fokus auf interdisziplinären Anwendungen von Informationstechnologie.
(ane)