首页 > 解决方案 > 如何将小部件删除安排为未来事件?

问题描述

我正在寻找一种在未来进行小部件删除的方法。

通过示例(和 MWE)来描述问题是最容易的。

向用户呈现几个AnimatedPositioned容器,代表一个纸牌游戏。

PositionedContainer部分意味着每张卡都可以用于 Gin Rummy、Bridge 或实际上任何抽象数字纸牌游戏。

一套五张卡片

当用户点击一张卡片时,卡片向上滑动(使用 的Animated部分AnimatedContainer

点击后一张卡片向上滑动

然后我们希望卡片从小部件堆栈中移除,即“消失”(而不仅仅是通过不透明度隐藏)

抽出的卡片现在消失了

import 'package:flutter/material.dart';
import 'dart:math';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Cards'),
        ),
        body: Center(
          child: Container(
            alignment: Alignment.center,
            child: CardGameWidget(),
            decoration: BoxDecoration(
              border: Border.all(
                color: Colors.blueAccent,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class CardGameWidget extends StatefulWidget {
  @override
  CardGameWidgetState createState() => CardGameWidgetState();
}

class CardGameWidgetState extends State<CardGameWidget> {
  List<Card> cards = [];

  CardGameWidgetState() {
    for (var i = 0; i < 5; ++i) {
      this.cards.add(Card(
          offset: Offset(i * 100.0, 200),
          number: Random().nextInt(1 << 16))
      );
    }
  }

  Function onTap(int index) => (newOffset) {
        setState(() {
            cards[index].offset += Offset(0,-100);
        });
      };

  @override
  Widget build(BuildContext context) {
    List<CardWidget> cardWidgets = [];
    for (int i = 0; i < this.cards.length; ++i) {
      cardWidgets.add(CardWidget(
        onTap: onTap(i),
        offset: this.cards[i].offset,
        number: this.cards[i].number,
      ));
    }
    return Stack(children: cardWidgets);
  }
}

class Card {
  Card({this.offset, this.number});

  Offset offset;
  int number;
}

class CardWidget extends StatelessWidget {
  CardWidget({
    Key key,
    this.onTap,
    this.offset,
    this.number,
  });

  final Function onTap;
  final Offset offset;
  final int number;

  _handleTap(details) {
    onTap(details.globalPosition);
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedPositioned(
      left: this.offset.dx,
      top: this.offset.dy,
      width: 100,
      height: 100,
      duration: Duration(seconds: 1),
      child: GestureDetector(
          onTapUp: _handleTap,
          child: Container(
              color: Colors.cyan,
              padding: EdgeInsets.all(10),
              margin: EdgeInsets.all(10),
              child: FittedBox(
                clipBehavior: Clip.antiAlias,
                alignment: Alignment.centerLeft,
                fit: BoxFit.contain,
                child: Text(this.number.toString()),
              ))),
    );
  }
}

在动画完成后,如何将小部件删除安排为将来的事件?

标签: flutterdartanimation

解决方案


您可以查看AnimatedList

import 'package:flutter/material.dart';


void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: Colors.blue, brightness: Brightness.dark),
      home: SimpleAnimatedList(),
    );
  }
}

class SimpleAnimatedList extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SliceAnimatedList(),
    );
  }
}

class SliceAnimatedList extends StatefulWidget {
  @override
  _SliceAnimatedListState createState() => _SliceAnimatedListState();
}

class _SliceAnimatedListState extends State<SliceAnimatedList> {
  final GlobalKey<AnimatedListState> listKey = GlobalKey<AnimatedListState>();
  List<int> _items = [];
  int counter = 0;

  Widget slideIt(BuildContext context, int index, animation) {
    int item = _items[index];
    TextStyle textStyle = Theme.of(context).textTheme.headline4;
    return SlideTransition(
      position: Tween<Offset>(
        begin: const Offset(-1, 0),
        end: Offset(0, 0),
      ).animate(animation),
      child: SizedBox(
        height: 128.0,
        child: Card(
          color: Colors.primaries[item % Colors.primaries.length],
          child: Center(
            child: Text('Item $item', style: textStyle),
          ),
        ),
      ),
    );
  }


  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.max,
      children: <Widget>[
        Expanded(
          child: Container(
            height: double.infinity,
            child: AnimatedList(
              key: listKey,
              initialItemCount: _items.length,
              itemBuilder: (context, index, animation) {
                return slideIt(context, index, animation);
              },
            ),
          ),
        ),
        Container(
          decoration: BoxDecoration(color: Colors.greenAccent),
          child: Row(
            mainAxisSize: MainAxisSize.max,
            crossAxisAlignment: CrossAxisAlignment.center,
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              FlatButton(
                onPressed: () {
                  setState(() {
                    listKey.currentState.insertItem(0,
                        duration: const Duration(milliseconds: 500));
                    _items = []
                      ..add(counter++)
                      ..addAll(_items);
                  });
                },
                child: Text(
                  "Add item to first",
                  style: TextStyle(color: Colors.black, fontSize: 20),
                ),
              ),
              FlatButton(
                onPressed: () {
                  if (_items.length <= 1) return;
                  listKey.currentState.removeItem(
                      0, (_, animation) => slideIt(context, 0, animation),
                      duration: const Duration(milliseconds: 500));
                  setState(() {
                    _items.removeAt(0);
                  });
                },
                child: Text(
                  "Remove first item",
                  style: TextStyle(color: Colors.black, fontSize: 20),
                ),
              )
            ],
          ),
        ),
      ],
    );
  }
}

推荐阅读