Playing gifs bug in flutter

33 views Asked by At

In my flutter app I have a screen to show exercises names with gifs, the list is too long, when I add the gifs to the list, the gifs are not playing as expected, the gifs are only 2 seconds, when the first gif plays the first second it stops and waits until all other gifs inside the list play their first second when all of the gifs played first second, then returns to the first gif and plays the second (second), and it replays the gifs in that way, how can I solve this problem?

I used cached_network_images and Image.memory and Image.file, all of them have the save problem

Listview code:

ListView.builder(
                              physics: const NeverScrollableScrollPhysics(),
                              shrinkWrap: true,
                              itemCount: exercises.length,
                              itemBuilder:
                                  (BuildContext context, int index) =>
                                      Padding(
                                padding: const EdgeInsets.only(bottom: 10),
                                child: Column(
                                  children: [
                                    Container(
                                      width: double.infinity,
                                      padding: const EdgeInsets.symmetric(
                                        horizontal: 20,
                                        vertical: 20,
                                      ),
                                      decoration: BoxDecoration(
                                        color: theme.colorScheme.background,
                                        border: Border.all(
                                          width: 3,
                                          color: theme.colorScheme
                                              .tertiaryContainer,
                                        ),
                                        borderRadius:
                                            BorderRadius.circular(35),
                                      ),
                                      child: ListTile(
                                        minVerticalPadding: 0,
                                        visualDensity: const VisualDensity(
                                          vertical: 4,
                                        ),
                                        title: Row(
                                          children: [
                                            SizedBox(
                                              width: width * .1,
                                              height: width * .1,


//......................................Display gifs here.................................


                                              child: ImageLoader(
                                                imageUrl: '$baseUrl${exercises[index]['gif']}'
                                              ),
                                            ),
                                            SizedBox(width: width * .015),
                                            Column(
                                              crossAxisAlignment:
                                                  CrossAxisAlignment.start,
                                              children: [
                                                SizedBox(
                                                  width: width * .5,
                                                  child: BigText(
                                                    text: Functions()
                                                        .getAvailableData(
                                                      lang: lang,
                                                      data:
                                                          exercises[index],
                                                      type: TranslationTypes
                                                          .name,
                                                    ),
                                                    color: theme.cardColor,
                                                    fontSize: width * .026,
                                                    maxLines: 2,
                                                    height: 1.75,
                                                    textOverflow:
                                                        TextOverflow
                                                            .visible,
                                                  ),
                                                ),
                                                SizedBox(
                                                  height: width * .015,
                                                ),
                                                exercises[index]
                                                            ['muscle'] ==
                                                        null
                                                    ? const SizedBox()
                                                    : Container(
                                                        height:
                                                            height * .03,
                                                        alignment: Alignment
                                                            .center,
                                                        padding:
                                                            const EdgeInsets
                                                                .symmetric(
                                                          horizontal: 20,
                                                          vertical: 5,
                                                        ),
                                                        decoration:
                                                            BoxDecoration(
                                                          borderRadius:
                                                              BorderRadius
                                                                  .circular(
                                                                      1000),
                                                          color: const Color(
                                                                  0xFF86EE75)
                                                              .withOpacity(
                                                                  .15),
                                                        ),
                                                        child: BigText(
                                                          text: Functions()
                                                              .getAvailableData(
                                                            lang: lang,
                                                            data: exercises[
                                                                    index]
                                                                ['muscle'],
                                                            type:
                                                                TranslationTypes
                                                                    .name,
                                                          ),
                                                          color: theme
                                                              .colorScheme
                                                              .outline,
                                                          fontSize:
                                                              width * .018,
                                                        ),
                                                      ),
                                              ],
                                            ),
                                          ],
                                        ),
                                        trailing: Material(
                                          borderRadius:
                                              BorderRadius.circular(10000),
                                          color:
                                              theme.colorScheme.background,
                                          child: InkWell(
                                            onTap: () {
                                              addAndRemoveFromExercises(
                                                  index);
                                            },
                                            borderRadius:
                                                BorderRadius.circular(1000),
                                            child: Container(
                                              width: width * .065,
                                              height: width * .065,
                                              padding: EdgeInsets.all(
                                                width * .020,
                                              ),
                                              decoration: BoxDecoration(
                                                borderRadius:
                                                    BorderRadius.circular(
                                                        10000),
                                                border: Border.all(
                                                  width: 3,
                                                  color: theme.colorScheme
                                                      .tertiaryContainer,
                                                ),
                                              ),
                                              child: selectedIds.contains(
                                                          exercises[index]
                                                              ['id']) ==
                                                      true
                                                  ? SvgPicture.asset(
                                                      'assets/icons/check.svg',
                                                      color:
                                                          theme.cardColor,
                                                      width: 25,
                                                      height: 25,
                                                    )
                                                  : const SizedBox(),
                                            ),
                                          ),
                                        ),
                                      ),
                                    ),
                                  ],
                                ),
                              ),
                            ),

ImageLoader Code:

import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'spinkit.dart';

class ImageLoader extends StatelessWidget {
  const ImageLoader({
    Key? key,
    required this.imageUrl,
    this.borderRadius,
    this.errorHeight,
    this.errorWidth,
    this.fit,
    this.color,
    this.id,
    this.width,
    this.height,
  }) : super(key: key);

  final String imageUrl;
  final BorderRadius? borderRadius;
  final double? errorHeight, errorWidth, width, height;
  final BoxFit? fit;
  final String? id;
  final Color? color;

  @override
  Widget build(BuildContext context) {
    return ClipRRect(
      borderRadius: borderRadius ?? BorderRadius.circular(10),
      child: CachedNetworkImage(
        fit: fit ?? BoxFit.fill,
        height: height,
        width: width,
        color: color,
        cacheKey: id,
        imageUrl: imageUrl,
        errorWidget: (context, url, exc) {
          return Center(
            child: Icon(
              Icons.error,
              color: Colors.red.withOpacity(0.7),
              size: 30,
            ),
          );
        },
        placeholder: (context, url) {
          return const Spinkit();
        },
      ),
    );
  }
}

When all the gifs are the same there is no problem, the problem occurs only when thee gifs are different

1

There are 1 answers

0
Arash Ghazi On

Here are several approaches to solve this problem:

Preloading Images: Instead of having the GIFs load only when they come into view, you might consider preloading them. This can be resource-intensive and not recommended for a long list with many GIFs, but if your list is manageable, this could ensure that all GIFs are fully loaded and ready to play. You can use the precacheImage function for this.

Using a Dedicated GIF Library: Instead of using the standard image display widgets, consider using a package dedicated to handling GIFs better, such as flutter_gifimage. These libraries often provide better control over GIF animation and might handle the rendering in a way that avoids your current issue.

Custom Scroll Physics: Implement custom scroll physics for your ListView.builder that reduces or eliminates the off-screen optimization for GIFs. This is a more advanced solution and could lead to performance issues, but it allows you to keep GIFs running even when they are not in the immediate viewport.

Optimization and Size Reduction: Ensure your GIFs are optimized and not overly large. The larger and more complex the GIF, the harder it is for Flutter to manage many of them in a list. Reducing the frame rate or dimensions might help.

Alternative Animation Formats: Consider whether the animation needs to be a GIF. In some cases, converting GIFs to a video format like MP4 and using a video player (like flutter_video_player) can be more efficient. This is because video formats are generally better optimized for playback and can be paused and resumed more effectively than GIFs.

Single GIF at a Time: Modify the design to show only one animated GIF at a time while others are either paused or replaced with static images until the user interacts with them. This drastically reduces the load on the rendering engine.

Custom GIF Player: Implement a custom GIF player that better handles the lifecycle and playback of GIFs in a list. This could involve more detailed control over when GIFs start and stop based on their visibility in the viewport.

State Management: Make sure the state management of your app does not lead to unnecessary rebuilds of your widgets, which could reset the GIF animations. Use tools like Provider or Bloc to efficiently manage the state without causing rebuilds that aren't needed.