web-dev-qa-db-fra.com

Animer l'élément ListView en plein écran dans Flutter

J'aimerais que mes éléments de liste exécutent cette animation ( mp4 ) lorsque vous appuyez dessus. J'ai essayé d'utiliser AnimatedCrossFade, mais il faut que ses deux enfants soient au même niveau, par exemple. la vue de détail effectue un fondu enchaîné avec ListView et non pas l'élément sélectionné. En fait, il semble qu'une animation Hero soit la seule à pouvoir animer des widgets. 

J'ai du mal à utiliser Hero. Devrait-il envelopper l'élément de la liste? Est-il important que la sous-arborescence Widget soit considérablement différente dans la source/destination Hero? De plus, les animations Hero peuvent-elles être utilisées avec LocalHistoryRoutes ou des animations en décalage ?

Modifier

Cela ressemble maintenant à ce que je dois faire est d'utiliser une Overlay, le plus difficile étant que je dois ajouter l'élément sélectionné à la superposition au même emplacement à l'écran où il a été exploité, la partie animation étant alors facile. Peut-être utile ici est un motif cible/suiveur, par ex. CompositedTransformTarget

6
Jacob Phillips

Vous pouvez simplement utiliser le widget Hero pour créer ce type d'animation. Voici mon exemple:

 enter image description here

et le code source:

import 'package:flutter/material.Dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new FirstPage(title: 'Color Palette'),
    );
  }
}

class FirstPage extends StatefulWidget {
  FirstPage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _FirstPageState createState() => new _FirstPageState();
}

class _FirstPageState extends State<FirstPage> {
  final palette = [
    {'#E53935': 0xFFE53935},
    {'#D81B60': 0xFFD81B60},
    {'#8E24AA': 0xFF8E24AA},
    {'#5E35B1': 0xFF5E35B1},
    {'#3949AB': 0xFF3949AB},
    {'#1E88E5': 0xFF1E88E5},
    {'#039BE5': 0xFF039BE5},
    {'#00ACC1': 0xFF00ACC1},
    {'#00897B': 0xFF00897B},
    {'#43A047': 0xFF43A047},
    {'#7CB342': 0xFF7CB342},
    {'#C0CA33': 0xFFC0CA33},
  ];

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Container(
        child: new ListView.builder(
            itemCount: palette.length,
            itemBuilder: (context, index) => new Hero(
                  tag: palette[index].keys.first,
                  child: new GestureDetector(
                    onTap: () {
                      Navigator
                          .of(context)
                          .Push(new ColorPageRoute(palette[index]));
                    },
                    child: new Container(
                      height: 64.0,
                      width: double.infinity,
                      color: new Color(palette[index].values.first),
                      child: new Center(
                        child: new Hero(
                          tag: 'text-${palette[index].keys.first}',
                          child: new Text(
                            palette[index].keys.first,
                            style: Theme.of(context).textTheme.title.copyWith(
                                  color: Colors.white,
                                ),
                          ),
                        ),
                      ),
                    ),
                  ),
                )),
      ),
    );
  }
}

class SecondPage extends StatelessWidget {
  final Map<String, int> color;

  SecondPage({this.color});

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Color'),
      ),
      body: new Hero(
        tag: color.keys.first,
        child: new Container(
          color: new Color(color.values.first),
          child: new Center(
            child: new Hero(
              tag: 'text-${color.keys.first}',
              child: new Text(
                color.keys.first,
                style:
                    Theme.of(context).textTheme.title.copyWith(color: Colors.white),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class ColorPageRoute extends MaterialPageRoute {
  ColorPageRoute(Map<String, int> color)
      : super(
            builder: (context) => new SecondPage(
                  color: color,
                ));

  @override
  Widget buildTransitions(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation, Widget child) {
    return FadeTransition(opacity: animation, child: child);
  }
}
16
hunghd

Je tricherais et encapsulerais le tout dans un calque Stack: la couche inférieure correspondrait à une page avec l'AppBar et le calque supérieur serait transparent jusqu'à ce qu'il soit peint. 

appuyez sur, dupliquez ListTile sur la surface supérieure, puis une animation Hero remplira tout l'écran. Ce n'est pas très élégant, mais le cadre ne permet pas (encore) de couvrir facilement l'AppBar. Il peut donc s'avérer astucieux de disposer d'une toile prête à être peinte pour d'autres animations difficiles. 

0
Christian