FlutterDart

Flutter, Image - Button Widget

In diesem Beitrag möchte ich gerne erläutern wie man ein Image Button Widget für Flutter in Dart schreibt, das zwischen gedrückten - und normalen Zustand faded.


Ich bin der "digitalen Bilderrahmen - Blog - Post - Serie" natürlich etwas voraus und dabei parallel die App zu programmieren. Dabei habe ich mich gegen React Native und für Flutter entschieden. Flutter ist ein Software Development Kit für die Entwicklung von Apps für iOS, Android und dem Web, das auf der Programmiersprache Dart aufsetzt. Dabei ist es in Sachen Performance React Native überlegen. Dart möchte sich als eine Alternative zu JavaScript sehen und versucht dabei, nach Ansicht der Entwickler, einige grundsätzliche Probleme von JavaScript zu lösen. Es lässt sich nach JavaScript transpilen und läuft so im Browser. Zudem lässt sich Dart - Code auch mittels VM auf Android, iOS, Windows und Linux ausführen. Ein kompilieren in Ausführbare Binärdateien ist vorab auch möglich.


Fading Image Button

Das Widget soll sich verhalten wie ein Button und beim Drücken von einen Bild in ein anderes faden. Demnach braucht es einen Zustand, welche von der StatefulWidget Klasse geerbt werden kann.

./lib/fading_image_button.dart
import 'package:flutter/material.dart';

class FadingImageButton extends StatefulWidget {
  final Function onPressed;
  final Image image;
  final Image onPressedImage;
  final double width;
  final double height;
  final EdgeInsets padding;
  final Duration duration;

  FadingImageButton({
    Key key,
    @required this.onPressed,
    @required this.image,
    @required this.onPressedImage,
    this.width,
    this.height,
    this.padding,
    this.duration,
  }) : super(key: key);

  @override
  _FadingImageButtonState createState() => _FadingImageButtonState();
}

Bisher definiert die Klasse nur ein paar Eigenschaften und die createState() Methode. Der Hauptteil spielt sich auch in der _FadingImageButtonState Klasse ab.

Der "_" im Namen von Membern definiert diese als private und sind so vor dem Zugriff von außerhalb der Library geschützt.

Benannte Parameter

Parameter in geschweiften Klammern sind benannte Parameter.

void func( {@required int a, int b, String c} ) { /* ... */ }

Benötigte benannte Parameter erhalten den Zusatz @required. Hier wird a benötigt. Der Aufruf wäre wie folgt: func(a: 1, b: 2, c: "Drei");

Optionale Parameter

Optionale Parameter werden in eckigen Klammern angegeben.

void func(int a, [int b], [String c]) { /* ... */ }

Hier sind b und c optional.

Nicht übergebene benannte oder optionale Parameter sind null.


Button State

Im gedrückten Zustand soll Bild 1 zu Bild 2 faden. Für diesen Effekt legen wir beide Bilder übereinander und setzen im Normalzustand die Opazität von Bild 2 auf 0. Beim Drücken wird dann die Opazität auf 1 gesetzt. Das AnimatedOpacity Widget macht das Animieren nicht mehr zu unseren Problem.

./lib/fading_image_button.dart
class _FadingImageButtonState extends State<FadingImageButton> {
  double opacity;

  @override
  void initState() {
    super.initState();
    setState(() => opacity = .0);
  }

  void didChangeDependencies() {
    super.didChangeDependencies();

    precacheImage(widget.image.image, context);
    precacheImage(widget.onPressedImage.image, context);
  }
  
  /* ... */

Die Methode initState initialisiert opacity mit 0,0 und didChangeDependencies werden die beiden Bilder gecached damit es nicht zu einem etwaigen Nachladen kommt.

./lib/fading_image_button.dart
/* ... */

@override
  Widget build(BuildContext context) {
    double width = widget.width ?? 128;
    double height = widget.height ?? 128;
    Duration duration = widget.duration ?? Duration(milliseconds: 250);
    EdgeInsets padding = widget.padding ?? EdgeInsets.all(5);

    return GestureDetector(
      onTapDown: (details) => setState(() => opacity = 1.0),
      onTapCancel: () => setState(() => opacity = .0),
      onTapUp: (details) {
        Future.delayed(
          Duration(
            milliseconds: (duration.inMilliseconds / 2).round(),
          ),
          () {
            setState(() => opacity = .0);
            widget.onPressed();
          },
        );
      },
      child: Container(
        height: height,
        width: width,
        padding: padding,
        child: Stack(
          children: <Widget>[
            widget.image,
            AnimatedOpacity(
              duration: duration,
              opacity: opacity,
              child: widget.onPressedImage,
            ),
          ],
        ),
      ),
    );
  }
}

Die build Methode erstellt auf Flutters Anweisung hin unser Widget und ist die letzte, die wir benötigen.

Zunächst weisen wir allen optionalen Eigenschaften, die nicht übergeben wurden, einen Standardwert zu.

double width = widget.width ?? 128 Der ?? Operator überprüft die Variable auf null. Ist sie null dann wird der angegebene Wert und wenn nicht dann der Variablenwert zugewiesen.

Als Basis dient uns ein GestureDetector Widget in dem die Image Widgets in einem Stack Widget übereinander gelegt werden. Das AnimatedOpacity Widget handled die Animation der Opazität.

In den onTap... Eigenschaften des GestureDetector Widgets lassen wir den Zustand von opacity ändern. Die Methode setState triggered dabei das Neurendern des Widgets. Beim schlichten setzen auf z.B. opacity = 1 hat Flutter keine Ahnung das sich der Wert geändert hat.

Damit bei einem eher schnellen Drücken auch die Animation abgespielt werden kann wird die onPressed Funktion etwas zeitverzögert ausgeführt. Die Verzögerung beträgt die Hälfte von duration.

Es ist kein Zufall das in den letzten drei Absätzen das Wort "Widget" ziemlich oft vorkam. In Flutter ist alles ein Widget.


Das Widget nutzen

Widget build(BuildContext context) {
    return FadingImageButton(
        onPressed: () => print(pressed),
        image: Image.asset("assets/button.png"),
        onPressedImage: Image.asset("assets/button-pressed.png"),
    );
}
Flutter Library
fading_image_button | Flutter Package
fading_image_button - A Flutter image button widget, that fades between default and pressed state.
image
Kompletter Source
jibbex/fading_image_button
Animated Flutter image button widget. Contribute to jibbex/fading_image_button development by creating an account on GitHub.
image