初始化时对 ListView 中的元素进行动画处理

Animate elements in ListView on initialization

提问人:Bartek Pacia 提问时间:12/28/2019 最后编辑:Albert221Bartek Pacia 更新时间:7/15/2023 访问量:13482

问:

我想实现如下目标(动画风格无关紧要,我正在寻找做到这一点的方法

example

但是,所有资源和问题都只解释了如何创建 项目添加或删除动画。

我当前的代码(我使用 BLoC 模式)

class _MembersPageState extends State<MembersPage> {
  @override
  Widget build(BuildContext context) {
    return BlocProvider<MembersPageBloc>(
      create: (context) =>
          MembersPageBloc(userRepository: UserRepository.instance)..add(MembersPageShowed()),
      child: BlocBuilder<MembersPageBloc, MembersPageState>(
        builder: (context, state) {
          if (state is MembersPageSuccess) {
            return ListView.builder(
              itemCount: state.users.length,
              itemBuilder: (context, index) {
                User user = state.users[index];

                return ListTile(
                  isThreeLine: true,
                  leading: Icon(Icons.person, size: 36),
                  title: Text(user.name),
                  subtitle: Text(user.username),
                  onTap: () => null,
                );
              },
            );
          } else
            return Text("I don't care");
        },
      ),
    );
  }
}

flutter-animation flutter-animatedlist

评论

0赞 giorgio79 5/13/2023
stackoverflow.com/questions/57100219/......

答:

7赞 easeccy 12/29/2019 #1

像 和 这样的小部件可用于实现这一点。但是,子小部件的生命周期有点复杂。它们会根据滚动位置被破坏和重新创建。如果子 widget 具有在初始化时启动的动画,则每当子 widget 对 UI 可见时,它就会重新激活。AnimatedOpacityAnimatedPositionedListView

这是我的黑客解决方案。我使用静态布尔值来指示它是“第一次”还是“娱乐”状态,而简单地忽略“娱乐”。您可以在 Dartpad 中复制并试用。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.brown,
        body: ListView(
          children: List.generate(
            25,
            (i) => AnimatedListItem(i, key: ValueKey<int>(i)),
          ),
        ),
      ),
    );
  }
}

class AnimatedListItem extends StatefulWidget {
  final int index;

  const AnimatedListItem(this.index, {Key? key}) : super(key: key);

  @override
  State<AnimatedListItem> createState() => _AnimatedListItemState();
}

class _AnimatedListItemState extends State<AnimatedListItem> {
  bool _animate = false;

  static bool _isStart = true;

  @override
  void initState() {
    super.initState();
    if (_isStart) {
      Future.delayed(Duration(milliseconds: widget.index * 100), () {
        setState(() {
          _animate = true;
          _isStart = false;
        });
      });
    } else {
      _animate = true;
    }
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedOpacity(
      duration: const Duration(milliseconds: 1000),
      opacity: _animate ? 1 : 0,
      curve: Curves.easeInOutQuart,
      child: AnimatedPadding(
        duration: const Duration(milliseconds: 1000),
        padding: _animate
            ? const EdgeInsets.all(4.0)
            : const EdgeInsets.only(top: 10),
        child: Container(
          constraints: const BoxConstraints.expand(height: 100),
          child: Card(
            child: Padding(
              padding: const EdgeInsets.all(8.0),
              child: Text(
                widget.index.toString(),
                style: const TextStyle(fontSize: 24),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

评论

0赞 Bartek Pacia 12/29/2019
是的,我知道这一点,但我不会添加或删除项目。我已经从后端收到了一些物品。我想做的就是为这些创建“布局”动画。
0赞 Bartek Pacia 12/31/2019
好吧,我不得不说,当有更多项目时,它会在滚动时产生严重的问题。我会继续接受这一点,但它效果不佳。
1赞 easeccy 1/15/2020
我将更新我的答案,使其仅在开始时才显示动画。您也可以自己添加该逻辑。很抱歉回复晚了。
1赞 Stepan 3/3/2021
如何使用这种方法从上面的 gif 中实现“从底部滑动”效果。谢谢。
0赞 giorgio79 5/13/2023
api.flutter.dev/flutter/widgets/AnimatedList-class.html 怎么样
0赞 Brave Dolphin 10/26/2021 #2

我在 dartpad 上创建了一个要点,展示了如何为ListView

关键点是为每个列表项创建一个,缓存它,并在动画完成时进行清理。AnimationController

0赞 maryam hashemi 7/15/2023 #3
import 'package:flutter/material.dart';

class DifferentPage extends StatefulWidget {
  const DifferentPage({super.key});

  @override
  State<DifferentPage> createState() => DifferentStatePage();
}

class DifferentStatePage extends State<DifferentPage> {
  final _item = [];
  final GlobalKey<AnimatedListState> _key = GlobalKey();

  void _addItem() {
    _item.insert(0, 'Item ${_item.length + 1}');
    _key.currentState!.insertItem( 0,
      duration: const Duration(seconds: 1),
    );
  }

 void _removeItem(int index) {
   _key.currentState!.removeItem( index, (_, animation) {
     return SizeTransition(
       sizeFactor: animation,
       child: const Card(
         margin: EdgeInsets.all(10),
         color: Colors.red,
         child: ListTile(
           title: Text(
             'Deleted',
             style: TextStyle(fontSize: 22),
           ),
         ),
       ),
     );
   },
   duration: const Duration(milliseconds: 300),
 );
   _item.removeAt(index);
 }

 @override
 Widget build(BuildContext context) {
   return Column(
     children: [
       const SizedBox( height: 10 ),
     IconButton(
       onPressed: _addItem,
       icon: const Icon(Icons.add_box_rounded),
     ),
     Expanded(
       child: AnimatedList(
         key: _key,
         initialItemCount: 0,
         padding: const EdgeInsets.all(10),
         itemBuilder: (context, index, animation) {
           return SizeTransition(
             key: UniqueKey(),
             sizeFactor: animation,
             child: Card(
               margin: const EdgeInsets.all(10),
               color: Colors.amberAccent,
               child: ListTile(
                 title: Text(
                   _item[index],
                   style: const TextStyle(fontSize: 22),
                 ),
                 trailing: IconButton(
                   icon: const Icon(Icons.delete),
                   onPressed: () {
                     _removeItem(index);
                   },
                 ),
               ),
             ),
           );
         },
       ),
     )
   ],
 );
 }}