flutterfirebaseflutter-animation

The animation does not work correctly when there are few items in the list and scrolling is not allowed


In my flutter application I have a scrolling animation every time a list is updated via firebase. This animation works perfectly, but I noticed that when the elements in the list are so few that scrolling is not possible, the animation starts from above the container that should contain it. I provide the list structure code:

class _HomePageState extends State<HomePage> {
  final CollectionReference pensieri =
      FirebaseFirestore.instance.collection("Pensieri");

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        backgroundColor: const Color(0xFF800020),
        body: SafeArea(
          child: Column(
            children: <Widget>[
              const Padding(
                padding: EdgeInsets.only(top: 40, bottom: 15),
                child: Center(
                  child: Text(
                    "Cronologia",
                    style: TextStyle(
                      color: Color.fromRGBO(255, 240, 245, 0.75),
                      fontSize: 18,
                    ),
                  ),
                ),
              ),
              Expanded(
                child: Container(
                  height: 200,
                  margin: const EdgeInsets.symmetric(horizontal: 40),
                  decoration: BoxDecoration(
                    border: Border.all(
                      width: 10,
                      color: const Color(0xFF9B111E),
                    ),
                    borderRadius: BorderRadius.circular(50),
                  ),
                  child: ShaderMask(
                    shaderCallback: (Rect bounds) {
                      return const LinearGradient(
                        begin: Alignment.topCenter,
                        end: Alignment.bottomCenter,
                        colors: [
                          Colors.black,
                          Colors.transparent,
                          Colors.transparent,
                          Colors.black,
                        ],
                        stops: [0.0, 0.05, 0.95, 1.0],
                      ).createShader(bounds);
                    },
                    blendMode: BlendMode.dstOut,
                    child: StreamBuilder(
                        stream: pensieri
                            .orderBy('time', descending: true)
                            .snapshots(),
                        builder: (context,
                            AsyncSnapshot<QuerySnapshot> streamSnapshot) {
                          if (streamSnapshot.hasData) {
                            final int docCount =
                                streamSnapshot.data!.docs.length;
                            if (docCount == 0) {
                              return const Center(
                                child: Text(
                                  "Non c'รจ ancora nulla qui!\nChe aspetti?",
                                  textAlign: TextAlign.center,
                                  style: TextStyle(
                                    color: Color.fromRGBO(255, 240, 245, 0.75),
                                    fontSize: 18,
                                  ),
                                ),
                              );
                            }
                            return ListView.builder(
                              padding: const EdgeInsets.all(25),
                              itemCount: docCount,
                              itemBuilder: (context, index) {
                                final DocumentSnapshot documentSnapshot =
                                    streamSnapshot.data!.docs[index];
                                Timestamp timestamp = documentSnapshot['time'];
                                DateTime dateTime = timestamp.toDate();
                                String formattedDate =
                                    DateFormat('dd/MM/yyyy - HH:mm')
                                        .format(dateTime);
                                return SlideInAnimation(
                                  key: ValueKey(documentSnapshot.id),
                                  child: Container(
                                    decoration: BoxDecoration(
                                      color: Colors.transparent,
                                      borderRadius: BorderRadius.circular(50),
                                    ),
                                    child: ListTile(
                                      contentPadding: const EdgeInsets.all(2.5),
                                      title: Row(
                                        mainAxisAlignment:
                                            MainAxisAlignment.spaceEvenly,
                                        children: [
                                          Text(
                                            documentSnapshot['id'] == 0
                                                ? "๐Ÿ‘ฑ๐Ÿปโ€โ™€๏ธ"
                                                : "๐Ÿ‘จ๐Ÿป",
                                            style: const TextStyle(
                                                fontSize: 30,
                                                color: Color(0xFFFFF0F5)),
                                          ),
                                          Text(
                                            formattedDate,
                                            style: const TextStyle(
                                              color: Color(0xFFFFF0F5),
                                              fontSize: 18,
                                            ),
                                          ),
                                        ],
                                      ),
                                    ),
                                  ),
                                );
                              },
                            );
                          }
                          return const Center(
                            child: CircularProgressIndicator(
                              color: Color(0xFF9B111E),
                            ),
                          );
                        }),
                  ),
                ),
              ),
              const HeartButton(),
            ],
          ),
        ),
      ),
    );
  }
}

I also provide the code of the class containing my animation:

class SlideInAnimationState extends State<SlideInAnimation>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<Offset> _slideAnimation;

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

    _slideAnimation = Tween<Offset>(
      begin: const Offset(0, -1),
      end: Offset.zero,
    ).animate(CurvedAnimation(
      parent: _controller,
      curve: Curves.easeInCubic,
    ));

    _controller.forward();
  }

  @override
  void didUpdateWidget(covariant SlideInAnimation oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.key != widget.key) {
      _controller.forward(from: 0.0);
    }
  }

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

  @override
  Widget build(BuildContext context) {
    return SlideTransition(
      position: _slideAnimation,
      child: widget.child,
    );
  }
}

I think the problem may be dictated by the fact that in reality the animation is always wrong, but the scroll effect hides the error; therefore, when the scroll is not performed, the error is visible.


Solution

  • It was enough to wrap the ShaderMask on the home page with a ClipRect. This solved my problem

    class _HomePageState extends State<HomePage> {
      final CollectionReference pensieri =
          FirebaseFirestore.instance.collection("Pensieri");
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            backgroundColor: const Color(0xFF800020),
            body: SafeArea(
              child: Column(
                children: <Widget>[
                  const Padding(
                    padding: EdgeInsets.only(top: 40, bottom: 15),
                    child: Center(
                      child: Text(
                        "Cronologia",
                        style: TextStyle(
                          color: Color.fromRGBO(255, 240, 245, 0.75),
                          fontSize: 18,
                        ),
                      ),
                    ),
                  ),
                  Expanded(
                    child: Container(
                      constraints: const BoxConstraints(maxWidth: 425),
                      height: 200,
                      margin: const EdgeInsets.symmetric(horizontal: 40),
                      decoration: BoxDecoration(
                        border: Border.all(
                          width: 10,
                          color: const Color(0xFF9B111E),
                        ),
                        borderRadius: BorderRadius.circular(50),
                      ),
                      child: ClipRect(
                        child: ShaderMask(
                          shaderCallback: (Rect bounds) {
                            return const LinearGradient(
                              begin: Alignment.topCenter,
                              end: Alignment.bottomCenter,
                              colors: [
                                Colors.black,
                                Colors.transparent,
                                Colors.transparent,
                                Colors.black,
                              ],
                              stops: [0.0, 0.05, 0.95, 1.0],
                            ).createShader(bounds);
                          },
                          blendMode: BlendMode.dstOut,
                          child: StreamBuilder(
                              stream: pensieri
                                  .orderBy('time', descending: true)
                                  .snapshots(),
                              builder: (context,
                                  AsyncSnapshot<QuerySnapshot> streamSnapshot) {
                                if (streamSnapshot.hasData) {
                                  final int docCount =
                                      streamSnapshot.data!.docs.length;
                                  if (docCount == 0) {
                                    return const Center(
                                      child: Text(
                                        "Non c'รจ ancora nulla qui!\nChe aspetti?",
                                        textAlign: TextAlign.center,
                                        style: TextStyle(
                                          color:
                                              Color.fromRGBO(255, 240, 245, 0.75),
                                          fontSize: 18,
                                        ),
                                      ),
                                    );
                                  }
                                  return ListView.builder(
                                    padding: const EdgeInsets.all(25),
                                    itemCount: docCount,
                                    itemBuilder: (context, index) {
                                      final DocumentSnapshot documentSnapshot =
                                          streamSnapshot.data!.docs[index];
                                      Timestamp timestamp =
                                          documentSnapshot['time'];
                                      DateTime dateTime = timestamp.toDate();
                                      String formattedDate =
                                          DateFormat('dd/MM/yyyy - HH:mm')
                                              .format(dateTime);
                                      return SlideInAnimation(
                                        key: ValueKey(documentSnapshot.id),
                                        child: Container(
                                          decoration: BoxDecoration(
                                            color: Colors.transparent,
                                            borderRadius: BorderRadius.circular(50),
                                          ),
                                          child: ListTile(
                                            contentPadding:
                                                const EdgeInsets.all(2.5),
                                            title: Row(
                                              mainAxisAlignment:
                                                  MainAxisAlignment.spaceEvenly,
                                              children: [
                                                Text(
                                                  documentSnapshot['id'] == 0
                                                      ? "๐Ÿ‘ฑ๐Ÿปโ€โ™€๏ธ"
                                                      : "๐Ÿ‘จ๐Ÿป",
                                                  style: const TextStyle(
                                                      fontSize: 30,
                                                      color: Color(0xFFFFF0F5)),
                                                ),
                                                Text(
                                                  formattedDate,
                                                  style: const TextStyle(
                                                    color: Color(0xFFFFF0F5),
                                                    fontSize: 18,
                                                  ),
                                                ),
                                              ],
                                            ),
                                          ),
                                        ),
                                      );
                                    },
                                  );
                                }
                                return const Center(
                                  child: CircularProgressIndicator(
                                    color: Color(0xFF9B111E),
                                  ),
                                );
                              }),
                        ),
                      ),
                    ),
                  ),
                  const HeartButton(),
                ],
              ),
            ),
          ),
        );
      }
    }