Animation 颤振动画列表还是janky?

Animation 颤振动画列表还是janky?,animation,flutter,dart,Animation,Flutter,Dart,我目前在我的Flitter应用程序中使用了一个动画列表,并且对移除列表项的动画制作方式有问题动画本身按预期工作,但一旦删除的项目完成动画,它就会消失,导致其他小部件跳入其位置。我原以为其他物品会转移到移除物品的位置 我尝试用ScaleTransition包装我的列表项,但没有帮助-其他列表项在完成动画之前仍然不会对删除的项作出反应 这有点违背了动画列表的目的,对吧?还是我做错了什么?关于AnimatedList的“本周小部件”视频清楚地显示,列表项通过改变其位置对新插入的项作出反应 这是我的密码

我目前在我的Flitter应用程序中使用了一个动画列表,并且对移除列表项的动画制作方式有问题
动画本身按预期工作,但一旦删除的项目完成动画,它就会消失,导致其他小部件跳入其位置。我原以为其他物品会转移到移除物品的位置

我尝试用
ScaleTransition
包装我的列表项,但没有帮助-其他列表项在完成动画之前仍然不会对删除的项作出反应

这有点违背了动画列表的目的,对吧?还是我做错了什么?关于AnimatedList的“本周小部件”视频清楚地显示,列表项通过改变其位置对新插入的项作出反应

这是我的密码:

@override
Widget build(BuildContext context) {
  return AnimatedList(
    padding: EdgeInsets.only(top: REGULAR_DIM,
        bottom: REGULAR_DIM + kBottomNavigationBarHeight),
    initialItemCount: data.length,
    itemBuilder: (context, index, animation) {
      return MyCustomWidget(
          data: data[index],
          animation: animation,
          disabled: false
      );
    },
  );
}

class MyCustomWidget extends AnimatedWidget {
  final MyModel data;
  final bool disabled;

  MyCustomWidget({
    @required this.data,
    @required Animation<double> animation,
    this.disabled = false
  }) : super(listenable: animation);

  Animation<double> get animation => listenable;


  @override
  Widget build(BuildContext context) {
    final content = ... ;

    return ScaleTransition(
      scale: CurvedAnimation(
          parent: animation,
          curve: Interval(0, 0.25)
      ).drive(Tween(begin: 0, end: 1)),
      child: FadeTransition(
        opacity: animation,
        child: SlideTransition(
          position: animation.drive(
              Tween(begin: Offset(-1, 0), end: Offset(0, 0))
                  .chain(CurveTween(curve: Curves.easeOutCubic))),
          child: content,
        ),
      ),
    );
  }
}

关键是触发两个转换,一个是SlideTransition(),另一个是SizeTransition,以消除在删除项目时跳转

下面是一些示例代码

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(title: Text('Update AnimatedList data')),
        body: BodyWidget(),
      ),
    );
  }
}

class BodyWidget extends StatefulWidget {
  @override
  BodyWidgetState createState() {
    return new BodyWidgetState();
  }
}

class BodyWidgetState extends State<BodyWidget>
    with SingleTickerProviderStateMixin {
  // the GlobalKey is needed to animate the list
  final GlobalKey<AnimatedListState> _listKey = GlobalKey(); // backing data
  List<String> _data = ['Horse', 'Cow', 'Camel', 'Sheep', 'Goat'];

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        SizedBox(
          height: 400,
          child: AnimatedList(
            key: _listKey,
            initialItemCount: _data.length,
            itemBuilder: (context, index, animation) {
              return _buildItem(
                _data[index],
                animation,
              );
            },
          ),
        ),
        RaisedButton(
          child: Text(
            'Insert single item',
            style: TextStyle(fontSize: 20),
          ),
          onPressed: () {
            _onButtonPress();
          },
        ),
        RaisedButton(
          child: Text(
            'Remove single item',
            style: TextStyle(fontSize: 20),
          ),
          onPressed: () {
            _removeSingleItems();
          },
        ),
      ],
    );
  }

  Widget _buildItem(String item, Animation<double> animation, {direction: 0}) {
    return (direction == 0)
        ? SizeTransition(
            sizeFactor: animation,
            child: Card(
              color: Colors.amber,
              child: ListTile(
                title: Text(
                  item,
                  style: TextStyle(fontSize: 20),
                ),
              ),
            ),
          )
        : Stack(
            children: [
              SizeTransition(
                sizeFactor: animation,
                child: Card(
                  color: Colors.transparent,
                  child: ListTile(
                    title: Text(
                      item,
                      style: TextStyle(fontSize: 20),
                    ),
                  ),
                ),
              ),
              Align(
                alignment: Alignment.topCenter,
                heightFactor: 0,
                child: SlideTransition(
                  position: animation
                      .drive(Tween(begin: Offset(-1, 0), end: Offset(0, 0))),
                  child: Card(
                    color: Colors.red,
                    child: ListTile(
                      title: Text(
                        item,
                        style: TextStyle(fontSize: 20),
                      ),
                    ),
                  ),
                ),
              ),
            ],
          );
  }

  void _onButtonPress() {
    _insertSingleItem();
  }

  void _insertSingleItem() {
    String item = "Pig";
    int insertIndex = 2;
    _data.insert(insertIndex, item);
    _listKey.currentState.insertItem(insertIndex);
  }

  void _removeSingleItems() {
    int removeIndex = 2;
    String removedItem = _data.removeAt(removeIndex);
    // This builder is just so that the animation has something
    // to work with before it disappears from view since the
    // original has already been deleted.
    AnimatedListRemovedItemBuilder builder = (context, animation) {
      // A method to build the Card widget.

      return _buildItem(removedItem, animation, direction: 1);
    };
    _listKey.currentState.removeItem(removeIndex, builder);
  }

  void _updateSingleItem() {
    final newValue = 'I like sheep';
    final index = 3;
    setState(() {
      _data[index] = newValue;
    });
  }
}
enter code here
导入“包装:颤振/材料.省道”;
void main(){
runApp(MyApp());
}
类MyApp扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
返回材料PP(
debugShowCheckedModeBanner:false,
家:脚手架(
appBar:appBar(标题:文本(“更新动画列表数据”),
body:BodyWidget(),
),
);
}
}
类BodyWidget扩展了StatefulWidget{
@凌驾
BodyWidgetState createState(){
返回新的BodyWidgetState();
}
}
类BodyWidgetState扩展了状态
使用SingleTickerProviderStateMixin{
//需要GlobalKey来设置列表的动画
final GlobalKey _listKey=GlobalKey();//支持数据
列表数据=[“马”、“牛”、“骆驼”、“羊”、“山羊”];
@凌驾
小部件构建(构建上下文){
返回列(
儿童:[
大小盒子(
身高:400,
子:动画列表(
键:_listKey,
initialItemCount:_data.length,
itemBuilder:(上下文、索引、动画){
返回_buildItem(
_数据[索引],
动画
);
},
),
),
升起的按钮(
子:文本(
“插入单个项目”,
样式:TextStyle(字体大小:20),
),
已按下:(){
_onButtonPress();
},
),
升起的按钮(
子:文本(
“删除单个项目”,
样式:TextStyle(字体大小:20),
),
已按下:(){
_删除singleitems();
},
),
],
);
}
小部件_buildItem(字符串项,动画,{方向:0}){
返回(方向==0)
?尺寸转换(
sizeFactor:动画,
孩子:卡片(
颜色:颜色。琥珀色,
孩子:ListTile(
标题:正文(
项目,,
样式:TextStyle(字体大小:20),
),
),
),
)
:堆栈(
儿童:[
尺寸转换(
sizeFactor:动画,
孩子:卡片(
颜色:颜色。透明,
孩子:ListTile(
标题:正文(
项目,,
样式:TextStyle(字体大小:20),
),
),
),
),
对齐(
对齐:alignment.topCenter,
高度系数:0,
子:幻灯片转换(
位置:动画
.drive(二者之间(开始:偏移(-1,0),结束:偏移(0,0)),
孩子:卡片(
颜色:颜色,红色,
孩子:ListTile(
标题:正文(
项目,,
样式:TextStyle(字体大小:20),
),
),
),
),
),
],
);
}
void _onButtonPress(){
_insertSingleItem();
}
void _insertSingleItem(){
String item=“Pig”;
int insertIndex=2;
_数据。插入(插入索引,项目);
_listKey.currentState.insertItem(insertIndex);
}
void _removeSingleItems(){
int-removeIndex=2;
字符串removedItem=\u data.removeAt(removeIndex);
//此生成器只是为了使动画具有某些特性
//在自
//原件已被删除。
AnimatedListRemovedItemBuilder=(上下文,动画){
//构建卡片小部件的方法。
返回_buildItem(removedItem,动画,方向:1);
};
_listKey.currentState.removeItem(removeIndex,生成器);
}
void _updateSingleItem(){
最终newValue=‘我喜欢羊’;
最终指数=3;
设置状态(){
_数据[索引]=新值;
});
}
}
在这里输入代码

您需要使用应用程序的发布版本测试性能。

我将两个动画放在一个堆栈中,并包装了SlideTransition()在高度因子为零的align小部件中,它不会影响堆栈高度,并允许大小动画设置长方体高度的动画。您还可以将SizeTransition()包裹在不透明度()中,使其完全消失
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(title: Text('Update AnimatedList data')),
        body: BodyWidget(),
      ),
    );
  }
}

class BodyWidget extends StatefulWidget {
  @override
  BodyWidgetState createState() {
    return new BodyWidgetState();
  }
}

class BodyWidgetState extends State<BodyWidget>
    with SingleTickerProviderStateMixin {
  // the GlobalKey is needed to animate the list
  final GlobalKey<AnimatedListState> _listKey = GlobalKey(); // backing data
  List<String> _data = ['Horse', 'Cow', 'Camel', 'Sheep', 'Goat'];

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        SizedBox(
          height: 400,
          child: AnimatedList(
            key: _listKey,
            initialItemCount: _data.length,
            itemBuilder: (context, index, animation) {
              return _buildItem(
                _data[index],
                animation,
              );
            },
          ),
        ),
        RaisedButton(
          child: Text(
            'Insert single item',
            style: TextStyle(fontSize: 20),
          ),
          onPressed: () {
            _onButtonPress();
          },
        ),
        RaisedButton(
          child: Text(
            'Remove single item',
            style: TextStyle(fontSize: 20),
          ),
          onPressed: () {
            _removeSingleItems();
          },
        ),
      ],
    );
  }

  Widget _buildItem(String item, Animation<double> animation, {direction: 0}) {
    return (direction == 0)
        ? SizeTransition(
            sizeFactor: animation,
            child: Card(
              color: Colors.amber,
              child: ListTile(
                title: Text(
                  item,
                  style: TextStyle(fontSize: 20),
                ),
              ),
            ),
          )
        : Stack(
            children: [
              SizeTransition(
                sizeFactor: animation,
                child: Card(
                  color: Colors.transparent,
                  child: ListTile(
                    title: Text(
                      item,
                      style: TextStyle(fontSize: 20),
                    ),
                  ),
                ),
              ),
              Align(
                alignment: Alignment.topCenter,
                heightFactor: 0,
                child: SlideTransition(
                  position: animation
                      .drive(Tween(begin: Offset(-1, 0), end: Offset(0, 0))),
                  child: Card(
                    color: Colors.red,
                    child: ListTile(
                      title: Text(
                        item,
                        style: TextStyle(fontSize: 20),
                      ),
                    ),
                  ),
                ),
              ),
            ],
          );
  }

  void _onButtonPress() {
    _insertSingleItem();
  }

  void _insertSingleItem() {
    String item = "Pig";
    int insertIndex = 2;
    _data.insert(insertIndex, item);
    _listKey.currentState.insertItem(insertIndex);
  }

  void _removeSingleItems() {
    int removeIndex = 2;
    String removedItem = _data.removeAt(removeIndex);
    // This builder is just so that the animation has something
    // to work with before it disappears from view since the
    // original has already been deleted.
    AnimatedListRemovedItemBuilder builder = (context, animation) {
      // A method to build the Card widget.

      return _buildItem(removedItem, animation, direction: 1);
    };
    _listKey.currentState.removeItem(removeIndex, builder);
  }

  void _updateSingleItem() {
    final newValue = 'I like sheep';
    final index = 3;
    setState(() {
      _data[index] = newValue;
    });
  }
}
enter code here