web-dev-qa-db-fra.com

Balayez l'élément de la liste pour plus d'options (Flutter)

Il y a quelques jours, j'ai décidé de choisir une interface utilisateur pour une application de Pinterest afin de pouvoir créer des applications avec Flutter, mais je suis coincé avec le curseur qui affiche les boutons "plus" et "supprimer" en cas de traînée horizontale. Image à droite .

Je n'ai pas assez de connaissances pour utiliser les gestes combinés avec des animations pour créer quelque chose comme cela en voltige. C’est la raison pour laquelle j’espère que l’un d’entre vous puisse donner l’exemple à tout le monde comme moi pour que nous comprenions comment implémenter quelque chose comme ceci dans ListView.builder.

enter image description here(Source)

Un exemple gif de l'application macOS mail:

enter image description here

25
Lukas Kirner

J'ai créé un paquet pour faire ce genre de mise en page: flutter_slidable (Merci Rémi Rousselet pour l'idée de base)

Avec ce package, il est plus facile de créer des actions contextuelles pour un élément de la liste. Par exemple, si vous souhaitez créer le type d'animation que vous avez décrit:

Drawer (iOS) animation

Vous allez utiliser ce code:

new Slidable(
  delegate: new SlidableDrawerDelegate(),
  actionExtentRatio: 0.25,
  child: new Container(
    color: Colors.white,
    child: new ListTile(
      leading: new CircleAvatar(
        backgroundColor: Colors.indigoAccent,
        child: new Text('$3'),
        foregroundColor: Colors.white,
      ),
      title: new Text('Tile n°$3'),
      subtitle: new Text('SlidableDrawerDelegate'),
    ),
  ),
  actions: <Widget>[
    new IconSlideAction(
      caption: 'Archive',
      color: Colors.blue,
      icon: Icons.archive,
      onTap: () => _showSnackBar('Archive'),
    ),
    new IconSlideAction(
      caption: 'Share',
      color: Colors.Indigo,
      icon: Icons.share,
      onTap: () => _showSnackBar('Share'),
    ),
  ],
  secondaryActions: <Widget>[
    new IconSlideAction(
      caption: 'More',
      color: Colors.black45,
      icon: Icons.more_horiz,
      onTap: () => _showSnackBar('More'),
    ),
    new IconSlideAction(
      caption: 'Delete',
      color: Colors.red,
      icon: Icons.delete,
      onTap: () => _showSnackBar('Delete'),
    ),
  ],
);
42
Romain Rastel

Il existe déjà un widget pour ce genre de geste. Cela s'appelle Dismissible.

Vous pouvez le trouver ici. https://docs.flutter.io/flutter/widgets/Dismissible-class.html

[~ # ~] éditer [~ # ~]

Si vous avez besoin de la même traduction, vous devrez probablement la mettre en œuvre vous-même. J'ai fait un exemple de base. Vous voudrez probablement modifier légèrement l’animation, mais cela fonctionne au moins.

enter image description here

class Test extends StatefulWidget {
  @override
  _TestState createState() => new _TestState();
}

class _TestState extends State<Test> {
  double rating = 3.5;

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new ListView(
        children: ListTile
            .divideTiles(
              context: context,
              tiles: new List.generate(42, (index) {
                return new SlideMenu(
                  child: new ListTile(
                    title: new Container(child: new Text("Drag me")),
                  ),
                  menuItems: <Widget>[
                    new Container(
                      child: new IconButton(
                        icon: new Icon(Icons.delete),
                      ),
                    ),
                    new Container(
                      child: new IconButton(
                        icon: new Icon(Icons.info),
                      ),
                    ),
                  ],
                );
              }),
            )
            .toList(),
      ),
    );
  }
}

class SlideMenu extends StatefulWidget {
  final Widget child;
  final List<Widget> menuItems;

  SlideMenu({this.child, this.menuItems});

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

class _SlideMenuState extends State<SlideMenu> with SingleTickerProviderStateMixin {
  AnimationController _controller;

  @override
  initState() {
    super.initState();
    _controller = new AnimationController(vsync: this, duration: const Duration(milliseconds: 200));
  }

  @override
  dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final animation = new Tween(
      begin: const Offset(0.0, 0.0),
      end: const Offset(-0.2, 0.0)
    ).animate(new CurveTween(curve: Curves.decelerate).animate(_controller));

    return new GestureDetector(
      onHorizontalDragUpdate: (data) {
        // we can access context.size here
        setState(() {
          _controller.value -= data.primaryDelta / context.size.width;
        });
      },
      onHorizontalDragEnd: (data) {
        if (data.primaryVelocity > 2500)
          _controller.animateTo(.0); //close menu on fast swipe in the right direction
        else if (_controller.value >= .5 || data.primaryVelocity < -2500) // fully open if dragged a lot to left or on fast swipe to left
          _controller.animateTo(1.0);
        else // close if none of above
          _controller.animateTo(.0);
      },
      child: new Stack(
        children: <Widget>[
          new SlideTransition(position: animation, child: widget.child),
          new Positioned.fill(
            child: new LayoutBuilder(
              builder: (context, constraint) {
                return new AnimatedBuilder(
                  animation: _controller,
                  builder: (context, child) {
                    return new Stack(
                      children: <Widget>[
                        new Positioned(
                          right: .0,
                          top: .0,
                          bottom: .0,
                          width: constraint.maxWidth * animation.value.dx * -1,
                          child: new Container(
                            color: Colors.black26,
                            child: new Row(
                              children: widget.menuItems.map((child) {
                                return new Expanded(
                                  child: child,
                                );
                              }).toList(),
                            ),
                          ),
                        ),
                      ],
                    );
                  },
                );
              },
            ),
          )
        ],
      ),
    );
  }
}

[~ # ~] éditer [~ # ~]

Le flutter ne permet plus le type Animation<FractionalOffset> dans la propriété SlideTransitionanimation. Selon ce message https://groups.google.com/forum/#!topic/flutter-dev/fmr-C9xK5t4 , il devrait être remplacé par AlignmentTween, mais cela ne le sera pas non plus. t fonctionne. Au lieu de cela, selon ce problème: https://github.com/flutter/flutter/issues/13812 le remplacer à la place par un Tween brut et créer directement Offset l'objet fonctionne à la place. Malheureusement, le code est beaucoup moins clair.

21
Rémi Rousselet