November 27, 2022

Animate Elements In ListView On Initialization

Widgets like AnimatedOpacity and AnimatedPositioned can be used to achieve this. However, lifecycle of the children widgets in a ListView is a bit complex. They get destroyed and recreated according to the scroll position. If the child widget has an animation that starts on initialization, it will reanimate whenever the child gets visible to the UI.

Here is my hacky solution. I used a static boolean to indicate whether it’s the first time or recreation state and simply ignore the recration. You can copy and try it in 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),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Leave a Reply

Your email address will not be published. Required fields are marked *