Ich stand mit meinen fast 5 Jahre alten Mittelklasse Smartphone vor einen nicht mehr ganz zeitgemäßen Problem. Anders als heutzutage üblich hat die Kamera App keinen QR Scanner integriert.
Dieser Artikel wurde ursprünglich am 20.03.2022 erstellt und wurde auf einem aktuellen Stand gebracht.
Dabei wurden alle Abhängigkeiten aktualisiert, ein anderes Modul zur Kamerasteuerung und QR Code dekodieren gewählt, das wesentlich leichter zu implementieren ist und stabiler funktioniert und alle Klassen Konstruktoren wurden an Darts super Parameter Feature angepasst.
Man muss sich dafür eine extra App aus dem Play Store organisieren. Geld ausgeben wollte ich für sowas nicht und die kostenlosen erkaufen sich diesen Zustand mit ziemlich aufdringlicher Werbung. Also tat ich das was ich meist in solchen Fällen tue und habe mir eine Lösung dafür programmiert. Klein genug für ein Tutorial war die App auch noch, also habe ich mich entschlossen hier etwas darüber zu schreiben.
Funktionsumfang
Die App soll später QR Codes scannen und dekodieren. Worauf hin Sie in einem Bottom Sheet den dekodierten Text anzeigt und entweder nach dem Text auf DuckDuckGo sucht oder sollte es sich um eine URL handeln diese im Browser öffnet oder die entsprechende App.
Glücklicherweise gibt es Libraries zum Scannen/Dekodieren von QR Codes und auch welche zum öffnen von URLs. Flutter selbst bietet alle Komponenten, die für das User Interface benötigt werden. Wir müssen nur noch alles in einer "Komposition" zusammenführen.
Implementation
Um den Programmieraufwand in Grenzen zu halten werden wir auf einige Libraries setzen. Die Bibliothek flutter_qr_bar_scannermobile_scanner bietet eine plattformunabhängige native Implementation der Kamera und dekodiert auch gleich QR Codes. Zum Starten von Apps bzw. einer URL im Browser setzen wir url_launcher ein. Damit App Bar und Bottom Sheet in einer stylischen Milchglas-Optik glänzen, nutzen wir glass.
Komponenten
Um die Codebasis übersichtlich zuhalten, erstellen wir für App Bar, Bottom Sheet, und noch ein paar weitere Komponenten "Module", die in extra *.dart Dateien ausgelagert werden.
App Bar
Wir erstellen eine Klasse, die auf der AppBar Klasse des material Packages aufbaut. Die AppBar Klasse ist ein Child der StatefulWidget Klasse und implementiert das PreferredSizeWidget Interface. Dem Row Widget wird die Methode asGlass() angehangen um es mit dem Glasseffekt zu rendern. Die glass Library implementiert eine Extension der Widget Klasse und erweitert sie um die Methode asGlass(). Extensions sind ein eleganter weg existierende Bibliotheken um Funktionalität zu erweitern und wurden mit Dart 2.7 eingeführt.
Der About Dialog
Die showAbout() Funktion von dem onPress Events des Icon Buttons ist in der Datei components/about.dart definiert. Um es übersichtlich zu halten ist ein List<Widget> Objekt in der Datei components/license.dart deklariert und initialisiert. Es hält die Liste der Lizenz-Widgets. Von diesen Dialog sind auch alle weiteren Lizensen der genutzen Packages einsehbar. Das ist eine rechtliche Notwendigkeit.
Mit der PackageInfo Klasse des package_info_plus Packets holen wir uns die aktuelle Version der App und deren Namen, die dann an die showAboutDialog() Funktion als Argumente übergeben werden.
Bottom Sheet
Das Bottom Sheet wird mit der Funktion showQrBottomSheet aufgerufen. Auf ihn wird der dekodierte QR Code dargestellt und es gibt ein Button zum Schließen und einen für das Suchen bzw. das Öffnen des Browser.
In der _handlePressed() Methode wird geprüft ob die URL ausführbar ist. Wenn dem so ist, dann wird die entsprechende App gestartet und wenn nicht, dann wird mit dem standard Browser eine Suchanfrage an https://duckduckgo.com gesendet.
Die showQrBottomSheet() Funktion ist an sich ein Wrapper für die showModalBottomSheet() Funktion. Wir übergeben nur die wirklich notwendigen Argumente und können so denn ganzen Boilerplate Code in eine extra Source - Datei auslagern. Alternativ hätte man auch nur die Builder Funktion in eine extra Datei definieren können. So wird allerdings die main.dart noch etwas schlanker.
Das App Widget
In der Datei main.dart führen wir nun alle Komponenten in der _ScanPageState Klasse zusammen.
Falls _scanned = false ist wird nach dem Dekodieren die Funktion showQrBottomSheet() aufgerufen und weist _scanned = true zu. Die _closeSheet() Methode navigiert im Seitenverlauf zurück und weist _scanned = false zu. Sie wird showQrBottomSheet als Argument für das Pressed Event des Close Buttons übergeben.