Hero-Widget in Flutter

Hero-Widget in Flutter

Das Hero Widget in Flutter

Wenn wir eine App entwickeln, haben wir im Idealfall ein einheitliches App Design. Dazu zählt natürlich auch die Verwendung von einheitlichen Symbolen und Bildern. Will man jetzt eine besondere User Experience kreieren, oder die Aufmerksamkeit des Nutzers auf etwas Bestimmtes richten, kann sich der Einsatz von animierten Widgets beim Wechsel von Seiten lohnen. Und genau das macht das Hero Widget.

Das Hero Widget lässt sich super einfach verwenden. Man muss es nur um das zu animierende Objekt herum packen. Das heißt unser Objekt wird zum child des Heros. Das Objekt muss auf beiden Seiten in einem Hero eingepackt sein, außerdem ist es ganz wichtig auf beiden Seiten dem Hero den gleichen tag zu geben, damit die App weiß, welche zwei Objekte zusammen gehören. Woraus sich wiederum schließen lässt, dass wir beliebig viele Hero Widgets auf einer Seite haben können, die auf die gleiche oder unterschiedliche Weise animiert werden.

 
Hero(
  tag: "DemoTag",
  child: Icon(
    Icons.thumb_up,
    size: 200.0,
    color: Colors.blue,
  ),
),


Das erste Hero Widget

In diesem Artikel lasse ich einen Floating Action Button auf die nächste Seite fliegen. Eine Anleitung wie man einen Floating Action Button erstellen und gestalten kann, findest du hier. Besonders hierbei ist, dass der Floating Action Button selbst schon ein Hero Widget ist. Das heißt, er muss nicht in einem Hero eingepackt sein, sondern kann direkt den heroTag als Parameter nehmen.
Hier der passende Code zu dem Floating Action Button als Hero Widget. Die transitionDuration habe ich hochgesetzt, damit man die Animation besser sieht.

class firstPage extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      floatingActionButton: FloatingActionButton(
          heroTag: "DemoTag",
          tooltip: 'like',
          onPressed: () {
            Navigator.push(
              context,
              PageRouteBuilder(
                transitionDuration: Duration(seconds: 2),
                pageBuilder: (_, __, ___) => secondPage(),
              ),
            );
          },
          child: Icon(Icons.thumb_up),
        ),
      );
    }
  }

class secondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Hero Widget - second page'),
      ),
      body: Center(
        child: Hero(
          tag: "DemoTag",
          child: Icon(
            Icons.thumb_up,
            size: 200.0,
            color: Colors.blue,
          ),
        ),
      ),
    );
  }
}

Einen Placeholder hinzufügen

Jetzt da unser Widget von einem Ort zum anderen fliegt, haben wir erstmal eine Freifläche. Diese können wir mit einem placeholderBuilder als Parameter des Heros füllen. Der placeholderBuilder erwartet als Rückgabewert einen Container, den wir gestalten können, wie wir wollen. In unserem Beispiel geben wir ihm als child einen CircularProgressIndicator (hier wäre auch jedes andere Widget denkbar). Wichtig ist, dass der Container größer oder mindestens gleich groß wie das Hero Widget ist, sonst springt es unschön am Ende.

Hero(
  tag: "DemoTag",
  child: Icon(
    Icons.thumb_up,
    size: 200.0,
    color: Colors.blue,
  ),
  placeholderBuilder: (context, size, widget) {
    return Container(
      height: 200.0,
      width: 200.0,
      child: CircularProgressIndicator(),
    );
  },
), 

Das Hero Widget verändern

Wir haben die Möglichkeit das Widget, dass von der einen Seite auf die andere fliegt, mit einem anderen zu ersetzen. Also können wir statt des Daumens nach oben auch ein Herz fliegen lassen.
Dazu verwenden wir den flightShuttleBuilder Parameter. Auch dieser erwartet ein Widget. In unserem Fall ein Icon.

Hero(
  tag: "DemoTag",
  child: Icon(
    Icons.thumb_up,
    size: 200.0,
    color: Colors.blue,
  ),
  flightShuttleBuilder: (flightContext, animation, direction,
    fromContext, toContext) {
      return Icon(Icons.favorite, size: 150.0, color: Colors.blue);
    },
  placeholderBuilder: (context, size, widget) {
    return Container(
      height: 200.0,
      width: 200.0,
      child: CircularProgressIndicator(),
    );
  },
),

Aktuell hat unser Herz immer eine Größe von 150.0 in beide Richtungen. Da der flightShuttleBuilder unter anderem auch einen direction Parameter hat, können wir diesen nutzen, um genau das anzupassen.

flightShuttleBuilder: (flightContext, animation, direction,
  fromContext, toContext) {
    if(direction == HeroFlightDirection.push) {
    return Icon(
      Icons.favorite,
      size: 150.0,
      color: Colors.blue,
    );
  } else if (direction == HeroFlightDirection.pop){
    return Icon(
      Icons.favorite,
      size: 70.0,
      color: Colors.blue,
    );
  }
},

Durch diese if-Abfrage können wir, je nach Richtung, die Größe individuell einstellen. Wir könnten hier sogar das Widget selbst ändern für die jeweilige Richtung, denn das Icon Widgeht wird in jedem Fall immer neu erstellt.

Jetzt haben wir also die Möglichkeit Dinge von einer Seite auf die nächste fliegen zu lassen. Das Projekt dazu findest du hier auf Github.