Meine Teilnahme an der Flutter Clock Challenge 2019/2020

Worum geht es

Die Flutter Clock Challenge ist ein Wettbewerb, bei dem es darum geht, eine Uhr für eine Lenovo Smart Clock Gerät zu entwickeln. Wie der Name „Flutter Clock Challenge“ schon erahnen lässt, wird hier nicht mit irgendeinem Framework gearbeitet, sondern mit Flutter.

Flutter ist ein Open-Source-UI-Framework für die Programmiersprache Dart, das von Google erstellt wurde. Es wird zur Entwicklung von Anwendungen für Android, iOS, Windows, Mac, Linux und das Web verwendet.

Der Abgabe-Termin für die Uhr war der 20 Januar 2020, 11:59PM PST (GMT-8).

Die Kriterien für die Bewertet Bewertung der Teilnehmer:

  • Aussehen der Uhr
  • Neuheit der Idee
  • Code Qualität
  • Gesamteindruck

Bewertet werden die Uhren von einer Jury die Aus Google-Mitarbeitern und erfahrenen Programmierern und Designern aus der Flutter Open Source Community.

Zu gewinnen gibt es

Der erste Pries ist ein Apple iMac Pro im Wert von ca. $10,000 USD

Für die Plätze 2-5 gibt es je ein 10“ Lenovo Smart Display mit dem Google Assistenten im Wert von etwa $249.00 USD

Die weiteren 25 Platzierungen erhalten je eine Lenovo Smart Clock mit dem Google Assistenten im Wert von etwa $79.00 USD.

Alle Teilnehmer erhalten zusätzlich noch ein Zertifikat für die erfolgte Teilnahme an dem Wettbewerb.

Wie habe ich davon erfahren

Im Dezember habe ich mir den Livestream zur Flutter Interact 2019 (https://www.youtube.com/watch?v=9OeMqgrY5J8) angesehen. Hier wurde bekannt gegeben, dass so ein Wettbewerb stattfindet. Ich habe schon lange einen guten Grund gesucht, mal ein Flutter Projekt zu realisieren und da kam mir der Wettbewerb gerade sehr gelegen. Also habe ich das Repository https://github.com/flutter/flutter_clock geklont und mir eine gute Idee für eine Uhr überlegt.

Die Idee für die Uhr

Auch eine Idee war schnell gefunden. Die Grundidee kommt aus der Maker-Szene. Die Tetris-Clock (https://github.com/witnessmenow/WiFi-Tetris-Clock) wollte ich schon lange mal für Android umsetzen. Allerdings war mir zu dem Zeitpunkt noch nicht klar, wie viel Arbeit es sein würde, die Ziffern der Uhr in Tetris Blöcken zu verpacken und die Blöcke zu animieren.

Die Arbeit an der Uhr

Zwischen Weihnachten und Neujahr habe ich mir dann also die Zeit genommen, meine Idee umzusetzen. Basierend auf dem existierenden Repository ist es recht einfach eine Uhr zu programmieren, da quasi schon alles vorgegeben ist. Ein Timer, der die Uhrzeit aktualisiert, ein Model, das alle wesentlichen Informationen enthält (Wetter, Temperator, Location, 12/24 Std. Uhr, etc.). Also muss nur noch das Display in einem regelmäßigen Intervall aktualisiert werden. Auch das feste Seitenverhältnis von 5/3 ist schon im Framework vorgegeben und man kann sich sofort auf die eigentliche Realisierung stürzen. Ich habe mich für ein 50×30 Grid entschieden, in dem die Tetris-Blöcke dann dargestellt werden sollten. Eine flüssige Animation habe ich bewusst nicht gewählt, da es sich meiner Meinung nach mit dem Tetris-Style nicht verträgt.

Als erstes habe ich es mit einem Container versucht, in dem Ich Columns und Rows verwendet habe. Aber relativ schnell wurde mir klar, dass ein 50×30 Layout mit 1500 Widgets wesentlich mehr Ressourcen verbraucht, als ein CustomPainter. Also noch mal alles umgebaut. Die CustomPainter Lösung lauft wesentlich besser und so konnte ich mich mit der Logik der Uhr beschäftigen.

Nachdem der Widget Teil erledigt war habe ich mir als nächstes überlegt, wie ich dem Widget die Daten übergebe. Hier war ein 2-dimensionales Array naheliegend, dass ich vom Timer aktualisieren lassen konnte.

Nun stand ich vor dem Problem, wie das Array am besten gefüllt wird. Dazu habe ich erst mal die Ziffern als Schablone auf ein Blatt gemalt und haufenweise Tetris Steine aus Papier ausgeschnitten. Als dann alle Schablonen mit Tetris Steinen gefüllt waren habe ich für jede Ziffer die Position, die Rotation und die X/Y Koordinaten in eine Json Datei übertragen und diese Json Datei dann für das Füllen des Arrays benutzt.

Zusammen mit meiner Frau und meinem Sohn habe ich dann die Farben der Steine festgelegt. Soll später ja auch einigermaßen bunt aussehen. Besonders mein Sohn hat dafür scheinbar ein sehr gutes Auge.

So weit so gut. Die Uhrzeit wird schon mal angezeigt. Als nächstes geht es daran die Tetris-Blöcke auch zu bewegen. Schließlich fallen beim echten Tetris die Blöcke auch nach unten und erscheinen nicht einfach dort wo sie am Ende landen. Für die Animation der Tetris Blöcke überlege ich mir eine einfache Json Datenstruktur, die auch wieder aus Listen von Block-Typen, X/Y-Positionen und Rotation bestehen. Um die Json Daten zu füllen nehme ich wieder die ausgeschnittenen Blöcke und die Schablonen zur Hilfe. Da ich ja schon weiß, wie die endgültige Position der Blöcke sein wird, führe ich die Animation auf dem Papier einfach rückwärts durch. Alle Schritte übernehme ich dann in eine Excel Tabelle, die ich später mit einem kleinen Shell-Script in das Json-Format konvertiere. Die fertigen Json Daten teste ich dann einmal durch und korrigiere evtl. kleinere Fehler.

So weit so gut

Nachdem ich die Uhr nun eine Zeitlang beobachtet habe ist mir aufgefallen, dass die Animation der Ziffern immer dann anfängt, wenn die Minute wechselt. Das ist so weit OK, aber schöner wäre es doch, wenn die die Animation der Ziffern endet, wenn die Minute wechselt. Um diesen Effekt zu erreichen muss der Zeitpunkt für den Start der Animation für jede Ziffer einzeln berechnet werden, da jede Ziffer eine andere Animationsdauer hat. Nach ein wenig Refactoring und ein paar Methoden zur Berechnung der Animations-Startzeit lauft es nun wieder ein wenig besser.

Als nächstes muss ein Splash-Screen har. Das Laden der Json-Daten dauert ein paar Sekunden und das kann man ja schön mit einem entsprechenden Splash-Screen überrücken. Bei Start der App wird dann als erstes der Splash-Screen angezeigt und danach die Json Dateien für die Definition der Tetris Blöcke (tetris_blocks.json), die Animationen der Nummern (tetris_number0.json, …., tetris_number09.json) und die Animationsdaten der Wetteranimationen (sun.json, cloudy.json, thunderstorm.json, snowy.json, foggy.json, windy.json, rainy.json) geladen.

Bei den Wetteranimationen tue ich mich ein wenig schwer. Vor allem die Animationen für den Wind und den Nebel sind nicht intuitiv erkennen. Aber das ist der Preis, wenn man mit 50×30 „Pixeln“ auskommen muss. Alle weiteren Daten im Modell wie z.B. Temperatur und Ort lasse ich direkt von vorneherein weg. Man könnte die Daten zwar einfach noch als Text anzeigen, aber das würde das Bild stören und so wichtig sind die Infos auch nicht. Für die AM/PM Anzeige bei einer 12 Stunden Uhr finde ich auch keine zufriedenstellende Lösung und entscheide dann auch darauf zu verzichten.

Testen Testen Testen

Mittlerweile ist mein Urlaub zu Ende und ich lasse die Uhr im Büro auf meinem Schreibtisch mittlaufen. Über einen längeren Zeitraum fällt mir dann auf, dass der Timer nicht exakt alle 100 Millisekunden Läuft. Also muss ich den Timer noch ein wenig anpassen. Das ist aber kein Problem. Die Uhr aus dem originalen Beispiel hatte auch eine solche Korrektur, die ich aber direkt am Anfang wegoptimiert habe J.

Schalter im Sourcecode

Einige Entscheidungen habe ich lange vor mir hergeschoben, da ich mich einfach nicht festlegen konnte wie alles am besten zusammenpasst. Diese aufgeschobenen Entscheidungen habe ich im Sourcecode mit Schaltern versehen, so dass ich immer mal wieder unterschiedliche Möglichkeiten ausprobieren konnte. Darunter fallen z.B. die folgenden Schalter:

/*
 * Color of the colon between hour and minute
 */
const int dotColor = 9;

/*
 * Hide the trailing 0 if hour is less than 10
 */
const bool hideTrailingHourZero = true;

/*
 * Show white/black dot for am/pm
 */
const bool showAmPm = false;

/*
 * Show weather conditions on screen
 */
const bool showWeather = true;

/*

 * Tetris animation step duration for animation of blocks
 */
const int tetrisAnimationSpeed = 100;

Zu viel gewollt

Zum Schluss wollte ich noch ein kleines „Easter-Egg“ einbauen. Eine Animation, bei der ein Pacman die Uhrzeit auffrisst. Die sollte immer nur um Mitternacht laufen, aber da mein Urlaub zu Ende ist und meine Freizeit begrenzt ist entscheide ich auf halbem Weg alles wieder rückgängig zu machen und darauf zu verzichten.

Fertig

Am Montag den 20 Januar habe ich also die Uhr eingereicht. Im Teilnahmeformular musste noch ein 30 sec. Video hochgeladen werden, dass die Funktionen der Uhr kurz zeigt. Also mit einem Screen Recorder ein Video gemacht, mit iMovie die wichtigsten Stellen zusammengeschnitten und fertig.

Am 22.01.2020 habe ich dann eine E-Mail bekommen in der meine Teilnahme bestätigt wurde. Ich rechne mir ganz gute Chancen aus, unter die Top-10 zu kommen. Das Github Repository ist ca. 200-mal geklont worden also gehe ich davon aus, dass maximal 300 Personen sich beteiligt haben, von denen sicher bis zu 25% nicht fertig geworden sind. Leider muss ich dann bei Twitter aber etwas anderes lesen.

Tatsächlich gibt es mehr als 700 Teilnehmer. Wer hätte gedacht, dass es schon so viele Flutter-Entwickler gibt – schließlich gibt es das Flutter-Framewok erst seit Mai 2017. Naja, hat trotzdem Spaß gemacht und ich habe eine sehr schöne Uhr für meinen Schreibtisch J

Hier noch ein paar Beispiele von Flutter Clock Challenge Teilnehmern, die auch sehr gute Arbeit abgeliefert haben.

Konkurrenz

Viele sehr schöne Uhren kann man unter Youtube und bei Twitter sehen. Hier ein paar schöne Beispiele die ich gefunden habe.

Noch sehr viel mehr kann man unter https://twitter.com/search?q=%23FlutterClock&src=typed_query

Und in der folgenden Youtube Playlist finden.

PS: Falls noch Videos in der Playlist fehlen, schreibt mir einfach ein Mail, ich nehme sie dann auf.

Ein paar Meinungen zu der Flutter Clock Challenge kann man bei reddit unter https://www.reddit.com/r/FlutterDev/comments/dy5rxn/flutter_clock_challenge/ nachlesen. Wie erwartet sind die Diskussionen bei reddit ziemlich kritisch, aber bei mehr als 700 Teilnehmern kann man das sicher ein wenig verstehen 🙂

Der letzte Stand meiner Uhr kann unter https://github.com/msoftware/flutter_clock gefunden werden. Auch zukünftige Updates werde ich hier posten, denn auch wenn der Contest nun vorbei ist, heißt es nicht, dass dieses Projekt nun für mich zu Ende ist. Die Tetris-Uhr steht weiterhin auf meinem Schreibtisch und zeigt mir die Uhrzeit an. Der nächste Schritt wäre wohl, das Demo-Model gegen ein echtes Model auszutauschen, dass die wirklich vorherrschenden Wetterdaten anzeigen kann. Aber noch warte ich ein wenig. Evtl. wird nach dem Mobile World Congress am 25 Februar von Google ein Model bereitgestellt, dass diese Aufgabe übernimmt.

Meine Tetris Clock habe ich auch mal für das “web” kompiliert und unter https://flutter-clock.android-entwickler.com/ veröffentlicht. Aktuell ist der flutter_clock_helper allerdings noch auf dem Stand “Entwicklung” und zeigt keine echten Daten zu Wetter, Temperatur und Ort an. Ich habe den Veranstaltern des Wettbewerbs aber schon geschrieben und gefragt, ob sie ein flutter_clock_helper Version zur verfügung stellen können, der dies unterstützt.

TO BE CONTINUED ….