Flutter und schimmernde Skelette

von Andreas Scheidmeir
Flutter Shimmer Ladeeffekt

Nutzern einer Applikation muss veranschaulicht werden, wenn ein Ladevorgang im Gange ist. Dafür gibt es diverse Animationen: Ladebalken und -Spinner sind eine valide Option, aber geht es auch spannender? Eine Idee ist, das Ergebnis bereits im Ladevorgang stilisiert anzudeuten. Wie man dieses sogenannte "Shimmer Loading" in Flutter umsetzen kann, möchte ich in diesem Blogpost beschreiben.

Einführung

Das "Shimmer Loading" will ich hier anhand einer einfachen Liste veranschaulichen, welche aus imaginären Produktnamen mit Bild in einer Card besteht. Links sehen wir die abstrakte Vorschau und rechts das Ergebnis des Ladevorgangs.

Für die Umsetzung benötigen wir zwei Teile: Als Erstes ein Skelett / die Abstraktion der zu ladenden Inhalte und als Zweites die Ladeanimation als Shimmer-Effekt (Ein „Highlight“ der über das Skelett wandert), um Nutzern anzuzeigen, dass die App am Arbeiten ist.

Teil 1: Das Skelett

Der erste Schritt ist die Erstellung einer Abstraktion der zu ladenden Inhalte. Dieses Skelett muss nicht perfekt sein, sollte aber den Aufbau des Ergebnisses widerspiegeln. In unserem Beispiel zeichnen wir für das Skelett nur den Rahmen der Card (warum das wichtig ist sehen wir später), das Bild als Fläche, den Text als abstrakte Linie und das Icon unverändert. Die Farbe all dieser Elemente ist auf Grau gesetzt. Sollten verschiedene Elemente auf dem Screen geladen werden, muss entsprechend für jedes ein eigenes Skelett erzeugt werden. In unserem vereinfachten Beispiel ist dies aber nicht der Fall.

Ein Trick, um die Abstraktion interessanter zu gestalten, ist, die Länge der Text-Balken zu variieren. Dazu randomisieren wir einfach deren Breite bei Erstellung zwischen 100px und 200px. Hier ein Vergleich zwischen statischen Textbreiten links und der dynamischem Variante rechts:

Dieses Vorgehen hat zudem den Vorteil, dass der Ladescreen beim erneuten Aufruf nie genau gleich aussieht.

Teil 2: Der Schimmer

Um den Ladeeffekt darzustellen, wollen wir einen linearen Farbverlauf über die Skelett-Liste animieren. Mit dem Flutter Widget ShaderMask kann der Effekt auf die Elemente der Liste beschränkt werden. Es ermöglicht einen Shader (in unserem Fall der Farbverlauf) mittels eines zu definierenden BlendModes auf seine Kindelemente anzuwenden. Wem die technischen Details egal sind und wer nur den Effekt erzielen will, braucht sich nicht sorgen, denn auch hierfür gibt es ein Plugin namens Shimmer und kann den nächsten Abschnitt einfach überspringen. Für alle anderen hier eine kurze Beschreibung wie dieser Effekt zu erzielen ist.

ShaderMask(
  blendMode: BlendMode.srcIn,
  shaderCallback: (rect) {
    return const LinearGradient(
      begin: Alignment.topLeft,
      end: Alignment.centerRight,
      colors: <Color>[
        Colors.black26,
        Colors.white,
        Colors.black26,
      ],
      stops: 0.0, 0.5, 1.0],
    ).createShader(Rect.fromLTWH(
        -rect.width + (2 * rect.width * (_controller.value)),
        0.0,
        rect.width,
        rect.height));
  },
  child: ListView.builder(
      … // child code   
    ),
)

Der erste Parameter für ShaderMask beschreibt den Blend-Modus, mit dem der Shader auf die Kindelemente angewendet wird. Wer mit Bildbearbeitungsprogrammen wie Photohsop vertraut ist, dem sagt dieser Begriff vielleicht schon etwas. Damit lassen sich diverse Effekte erzielen, in unserem Fall nutzen wir es aber als Maske. BlendMode.scrIn wendet das Quellbild (den Gradienten auf den wir gleich eingehen) dort an, wo Quelle und Kindelemente überlappen (und ersetzt deren Farbe). Damit sollte auch klar werden, warum wir bei der Erstellung der Skelett-Card nur den Rand gezeichnet und die Hintergrundfarbe auf Transparent gesetzt haben. Hätten wir dies nicht getan, würde der Effekt vollflächig auf der Card liegen und keine Details im Inneren sichtbar sein.

Als Zweites definieren wir im shaderCallback den linearen Gradienten, welcher von Grau zu Weiß und zurück zu Grau verläuft und durch die Parameter in begin und end leicht schräg gestellt wird. Mit der Funktion createShader können wir diesen Gradienten einfach in einen Shader umwandeln, mit dem das ShaderMask-Widget arbeiten kann.

Spannend wird es jetzt noch beim Rechteck, in dem das Ganze gezeichnet werden soll. Der shaderCallback übergibt mit dem Parameter rect die Bounding-Box der Kindelemente, auf Grundlage dessen wir arbeiten können. Ziel ist es, den Gradienten nicht einfach statisch zu zeichnen, sondern von links nach rechts über die Maske zu animieren. Dafür erzeugen wir mit Rect.formLTWH (fom left, top, width, height) ein neues Rechteck, welches die gleiche Höhe und Breite besitzt, aber durch den Offset links einmal über das gesamte Objekt geschoben wird. Gesteuert wird das Ganze von einem AnimationController, der zwischen 0.0 und 1.0 animiert und damit den prozentualen Fortschritt eines Animations-Durchgangs beschreibt. Beim Start ist der Gradient komplett links von der Maske, bei 0.5 genau über den Kindelementen und bei Abschluss ist das Rechteck komplett rechts aus dem Bild gelaufen. Dabei machen wir uns zunutze, dass links und rechts davon einfach weiter der Grauton des Gradienten gezeichnet wird.

Die Animation mag auf den ersten Blick zwar etwas verwirrend aussehen, aber das ist schon der ganze Zauber, um den Shimmer-Effekt in Flutter umzusetzen. Wie bereits beschrieben gibt es aber auch hierfür bereits ein Community Plugin, welches den Effekt kapselt und die notwendigen Konfigurationsmöglichkeiten offenlegt:

Shimmer.fromColors(
    baseColor: Colors.black26,
    highlightColor: Colors.white,
    enabled: _loading,
    direction: ShimmerDirection.ltr,
    child: ListView.builder(
      … // Child code   
    ),
  )

Die Parameter baseColor und highlightColor definieren die Farben des Shimmers, welche hier identisch zum Beispiel oben gesetzt sind. Mit dem Parameter enabled kann die Animation gestartet oder gestoppt werden und mittels der direction kann der Animationsverlauf gesteuert werden. Hierzu stehen von oben nach unten (ttb), links nach rechts (ltr) und jeweils in die entgegengesetzte Richtung zur Verfügung. Einzig der BlendMode kann nicht frei definiert werden, hier ist im Plugin BlendMode.scrIn fest gesetzt.

Fazit

Ich hoffe ich konnte in diesem Blogpost eine einfache und visuell ansprechende Alternative vorstellen, Ladeanimationen interessanter zu gestalten. Dank der mächtigen Animationsmöglichkeiten in Flutter ist solch ein Effekt leicht umzusetzen, und durch das Community-Plugin Shimmer schnell in das eigene Projekt integriert. Zudem ist dieser Effekt auf allen aktuell von Flutter unterstützten Plattformen lauffähig und man kann ihn auf beliebige Kindelemente anwenden. Viel Spaß beim Experimentieren.

Links

Zurück

© 2006-2024 exensio GmbH
Einstellungen gespeichert
Datenschutzeinstellungen

Wir nutzen Cookies auf unserer Website. Einige von ihnen sind essenziell, während andere uns helfen, diese Website und Ihre Erfahrung zu verbessern.

Sie können Ihre Einwilligung jederzeit ändern oder widerrufen, indem Sie auf den Link in der Datenschutzerklärung klicken.

Zu den gesetzlichen Rechenschaftspflichten gehört die Einwilligung (Opt-In) zu protokollieren und archivieren. Aus diesem Grund wird Ihre Opt-In Entscheidung in eine LOG-Datei geschrieben. In dieser Datei werden folgende Daten gespeichert:

 

  • IP-Adresse des Besuchers
  • Vom Besucher gewählte Datenschutzeinstellung (Privacy Level)
  • Datum und Zeit des Speicherns
  • Domain
You are using an outdated browser. The website may not be displayed correctly. Close