skip to Main Content

I’m new to Flutter and currently stuck on an image loading problem. In my application I’m querying feed posts from a firebase dataserver, which contain a URL to an image.
When scrolling down new posts are getting loaded and currently the old ones seem to be getting destroyed and need to get downloaded again when scrolling up.
Is there a quick solution to cache the images to reduce bandwith traffic?

Thanks in advance!

This is my code:

class Feed extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: StreamBuilder<List<Post>>( 
        stream: FeedService().getPosts(), application layer
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return Center(child: CircularProgressIndicator());
          }
          if (snapshot.hasError) {
            return Center(child: Text('Error: ${snapshot.error}'));
          }
          if (!snapshot.hasData || snapshot.data!.isEmpty) {
            return Center(child: Text('No posts available'));
          }

          final posts = snapshot.data!;

          return ListView.builder(
            itemCount: posts.length,
            itemBuilder: (context, index) {
              return PostItem(post: posts[index]);
            },
          );
        },
      ),
    floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => CreatePostScreen()),
          );
        },
        child: Icon(Icons.add),
        heroTag: 'createPostButton'
      ),
    );
  }
}

class PostItem extends StatefulWidget {
  final Post post;

  PostItem({required this.post});

  @override
  State<PostItem> createState() => _PostItemState();
}

class _PostItemState extends State<PostItem> {
  bool _isBookmarked = false;
  bool _isLiked = false;

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.all(10),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          
          ListTile(
            leading: CircleAvatar(
              backgroundImage: NetworkImage(widget.post.userProfileImageUrl),
            ),
            title: Text(widget.post.username),
            subtitle: Text(widget.post.timestamp.toDate().toString()),
            trailing: Icon(Icons.more_vert),
          ),

        CachedNetworkImage(
          imageUrl: widget.post.imageUrl,
          fit: BoxFit.cover,
          width: double.infinity,
          cacheManager: CacheManager(
            Config(
              'customCacheKey',
              stalePeriod: Duration(days: 7),
              maxNrOfCacheObjects: 100,
            ),
          ),
          placeholder: (context, url) => Center(child: CircularProgressIndicator()), 
          errorWidget: (context, url, error) => Icon(Icons.error), 
        ),

          // Like and comment section
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 10),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Row(
                  children: [
                    IconButton(
                      onPressed: () {
                        setState(() {
                          _isLiked = !_isLiked;
                        });
                      },
                      icon: Icon(_isLiked ? Icons.thumb_up : Icons.thumb_up_outlined),
                    ),
                    const SizedBox(width: 10),
                    IconButton(
                      onPressed: () {},
                      icon: const Icon(Icons.comment_outlined),
                    ),
                  ],
                ),
                IconButton(
                  onPressed: () {
                    setState(() {
                      _isBookmarked = !_isBookmarked;
                    });
                  },
                  icon: Icon(_isBookmarked ? Icons.bookmark : Icons.bookmark_outline),
                )
              ],
            ),
          ),

          // Post caption or text (if needed)
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16.0),
            child: Text(
              widget.post.description,
              style: TextStyle(fontSize: 16),
            ),
          ),
          const SizedBox(height: 10),
        ],
      ),
    );
  }
}

2

Answers


  1. To cache images efficiently in a Flutter app and reduce bandwidth usage when loading images from a URL, you can use the CachedNetworkImage package. By doing so, images will be cached locally and won’t need to be re-downloaded as the user scrolls through the feed.

    Here’s how you can implement it:
    Set up a global cache manager: This ensures all images share the same cache settings and benefit from caching across the app.
    Use CachedNetworkImage with the global cache manager: This will automatically handle the image loading and caching.
    Step 1: Create a global cache manager
    You can create a separate class for managing the cache settings:

    class MyCacheManager {
      static final CacheManager instance = CacheManager(
        Config(
          'customCacheKey',
          stalePeriod: Duration(days: 7),  // Cache images for 7 days
          maxNrOfCacheObjects: 200,  // Max 200 images in cache
        ),
      );
    }
    

    Step 2: Use CachedNetworkImage with the global cache manager
    Update your widget to use the global cache manager in the CachedNetworkImage widget:

    class PostItem extends StatelessWidget {
      final Post post;
    
      PostItem({required this.post});
    
      @override
      Widget build(BuildContext context) {
        return Card(
          margin: const EdgeInsets.all(10),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              ListTile(
                leading: CircleAvatar(
                  backgroundImage: NetworkImage(post.userProfileImageUrl),
                ),
                title: Text(post.username),
                subtitle: Text(post.timestamp.toDate().toString()),
                trailing: Icon(Icons.more_vert),
              ),
              CachedNetworkImage(
                imageUrl: post.imageUrl,
                fit: BoxFit.cover,
                width: double.infinity,
                cacheManager: MyCacheManager.instance,  // Use global cache manager
                placeholder: (context, url) => Center(child: CircularProgressIndicator()),
                errorWidget: (context, url, error) => Icon(Icons.error),
              ),
            ],
          ),
        );
      }
    }
    

    The images will be cached locally using this setup, reducing the need to re-download them when scrolling. The global cache manager ensures that cache settings are consistent and managed centrally. Adjust the cache settings like stalePeriod and maxNrOfCacheObjects based on your app’s needs.

    Login or Signup to reply.
  2. For optimal image performance, you can use the following technique:

    1. Preload images using:
    precacheImage(CachedNetworkImageProvider(imageUrl), context); // Use this while fetch data from the API
    

    This is how I implemented image preloading in my code when fetching them from the API:

    void cacheImagesBeforeLoad(List<Category> categoryData) {
      for (var category in categoryData) {
         precacheImage(CachedNetworkImageProvider(category.imageUrl), state!.context);
      }
    }
    
    1. Display images with CachedNetworkImage:
    CachedNetworkImage(
      imageUrl: post.imageUrl,
      fit: BoxFit.cover,
      width: double.infinity,
      placeholder: (context, url) => Center(child: CircularProgressIndicator()),
      errorWidget: (context, url, error) => Icon(Icons.error),
    );
    

    This approach preloads images for smooth performance and uses CachedNetworkImage for efficient caching and error handling.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search