MEDIA GUIDES / Image Effects

Using Flutter to Resize Images

Images are an essential part of mobile app design. They enhance aesthetics, improve usability, and help communicate information visually. However, large or unoptimized images can significantly affect your app’s performance, leading to slower loading times and increased bandwidth. And in some cases, users may also need to resize images for specific purposes, for instance in image editors or social media apps that support image uploads.

This article explores different methods for resizing images in Flutter, from using built-in widgets to external libraries and cloud-based media management platforms like Cloudinary, to resize and optimize images dynamically for an improved user experience.

In this article:

Set up a Flutter App

This tutorial assumes you’ve already set up a Flutter application. If you haven’t, install Flutter by following the official installation guide for your operating system. Alternatively, you can bootstrap a new Flutter app quickly using Firebase Studio, which is what we’ll be using in this tutorial.

Ways to Resize Images in Flutter

Before diving into methods for resizing images in Flutter, let’s first understand what image resizing means because it’s sometimes confused with other image editing techniques like cropping.

Image resizing (also known as scaling) means changing the width and height of an image while keeping the entire image content intact. In other words, the image is either scaled up or scaled down to fit a desired size, but no parts of the image are removed. When resizing, you typically want to optimize performance by reducing image file size, preserve visual consistency across devices with different resolutions or fit images within a specific layout or screen size.

Image cropping on the other hand, involves removing unwanted parts of an image to focus on a specific area or subject. Instead of resizing the entire image, cropping changes which portion of the image is visible.

In Flutter, there are multiple approaches to resize images, each suited to different use cases. Below, we explore three most commonly used methods.

1. Using the Image Widget with BoxFit

This is perhaps the simplest way to resize images in Flutter. This method works by adjusting how an image fits within a given space or container without altering the original file.

In this demo, we’ll be using this image with an original dimension of 640×427. To add the image to your project, create an assets folder in the root directory and copy the demo image into the folder, then add the following snippet under the flutter: section in pubspec.yaml (assuming you saved the image as dog.jpg):

flutter:
  assets:
    - assets/dog.jpg

Open your main.dart file and paste the following code to reduce the size of the image to a dimension of 300×250:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Image Resizing in Flutter',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const SimpleResizeExample(),
      debugShowCheckedModeBanner: false,
    );
  }
}

class SimpleResizeExample extends StatelessWidget {
  const SimpleResizeExample({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Resizing Images with Image Widget')),
      body: Center(
        child: Image.asset(
          'dog.jpg', 
          width: 300, // Resize width
          height: 250, // Resize height
          fit: BoxFit.cover, // Maintains aspect ratio while covering the box
        ),
      ),
    );
  }
}

You can see the image looks smaller than the original as shown below:

If you’re looking for a quick and easy method to scale an image and maintain its aspect ratio, this method works pretty well. However, this method doesn’t reduce the original file size (the original image is loaded as-is), but only controls how the image is rendered in the UI.

2. Using Dart Image Library

The Dart Image Library package allows you to load, transform, and save images programmatically in a variety of image formats. It supports operations like resizing, cropping, and compression, making it a good choice for preprocessing images in Flutter applications.

To install the package, and path_provider (for filesystem access), add the following under the dependencies: section in pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  image: ^1.0.0
  path_provider: ^2.1.0

Then paste the following code in main.dart:

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:image/image.dart' as img;
import 'package:path_provider/path_provider.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Dart Image Resize Demo',
      home: ImageResizeScreen(),
      debugShowCheckedModeBanner: false,
    );
  }
}

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

  @override
  ImageResizeScreenState createState() => ImageResizeScreenState();
}

class ImageResizeScreenState extends State<ImageResizeScreen> {
  File? resizedFile;

  Future<void> resizeImage() async {
    // 1. Load image bytes from assets
    final byteData = await rootBundle.load('assets/dog.jpg');
    final bytes = byteData.buffer.asUint8List();

    // 2. Decode to Image object
    final originalImage = img.decodeImage(bytes);

    // 3. Resize
    final resized = img.copyResize(
      originalImage!,
      width: 200,
      height: 200,
    );

    // 4. Save the resized file in temp folder
    final tempDir = await getTemporaryDirectory();
    final filePath = '${tempDir.path}/resized_sample.jpg';
    final file = File(filePath)..writeAsBytesSync(img.encodeJpg(resized));

    setState(() {
      resizedFile = file;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Resizing Images Using Dart Image Library")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            resizedFile != null
                ? Image.file(resizedFile!, width: 200, height: 200)
                : const Text("Press button to resize image"),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: resizeImage,
              child: const Text("Resize Image"),
            ),
          ],
        ),
      ),
    );
  }
}

In the above code, the copyResize function returns a resized copy of the input image passed to it. According to the docs:

  • If width is set but not height, height will be calculated to maintain the aspect ratio of src.
  • If height is set but not width, width will be calculated to maintain the aspect ratio of src.
  • If both width and height are set:
    • If maintainAspect (a boolean value) is not set or is false, src will be stretched.
    • If maintainAspect is true, src will fill the new resolution without changing the aspect ratio, using backgroundColor as the padding color.

This method works for both static images (as in our example) and dynamic images uploaded by users to your client application.

Here’s the resized image from example above:

While the Dart Image Library is powerful and can meet most of your needs for image resizing, it has some limitations that might affect the performance of your application. Because it’s a pure Dart implementation, it lacks native GPU acceleration, and it decodes the full image into memory. This high RAM usage can lead to UI lag and poor performance, especially when handling large images.

Using Cloudinary and Flutter to Resize Images

So far, we have explored two different methods for resizing images in Flutter apps, with each having its own strengths and limitations. For large scale apps or scenarios where you need to resize images dynamically, a cloud-based media management and delivery platform like Cloudinary is a more efficient solution. Cloudinary automates tasks like uploading, storage, manipulation (including resizing), optimization, and delivery of media assets, using programmable APIs, SDKs and URL-based transformations.

To set up Cloudinary in your Flutter app, you need to sign up for a Cloudinary account and then retrieve your product environment credentials from your dashboard. Uploading assets to Cloudinary can be done in a variety of ways. In a client application, we can use the Upload API to upload media assets to your Cloudinary product environment.

Add the following dependencies to pubspec.yaml:

dependencies:
  image_picker: ^1.0.0
  http: ^1.5.0
  • image_picker: A Flutter plugin for iOS and Android for picking images from the image library, and taking new pictures with the camera.
  • http: A package for making HTTP requests.

A typical image resizing workflow with Cloudinary and Flutter looks like this:

  • Upload the image asset to your Cloudinary product environment and note its public_id.
  • Then apply transformations to the image using its public_id.

Let’s see how this works in code.

Paste the following code in main.dart (the image used in this example has an original dimension of 853×1280):

import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:http/http.dart' as http;

void main() {
  runApp(
    MaterialApp(
      home: CloudinaryUploadResizeDemo(),
      debugShowCheckedModeBanner: false,
    ),
  );
}

class CloudinaryUploadResizeDemo extends StatefulWidget {
  @override
  _CloudinaryUploadResizeDemoState createState() =>
      _CloudinaryUploadResizeDemoState();
}

class _CloudinaryUploadResizeDemoState
    extends State<CloudinaryUploadResizeDemo> {
  String? uploadedImageUrl;
  bool uploading = false;

  final cloudName = "your_cloud_name"; // Change to your Cloud name
  final uploadPreset = "your_preset"; // Change to your unsigned upload preset

  Future<void> uploadToCloudinary(File file) async {
    setState(() => uploading = true);

    try {
      print("Uploading to Cloudinary...");

      final url = Uri.parse(
        'https://api.cloudinary.com/v1_1/$cloudName/upload',
      );

      final request = http.MultipartRequest('POST', url)
        ..fields['upload_preset'] = uploadPreset
        ..files.add(await http.MultipartFile.fromPath('file', file.path));

      final response = await request.send();

      if (response.statusCode == 200) {
        final responseData = await response.stream.toBytes();
        final responseString = String.fromCharCodes(responseData);
        final json = jsonDecode(responseString);

        setState(() {
          uploadedImageUrl = json['secure_url'];
          uploading = false;
        });

        print("Uploaded URL: $uploadedImageUrl");
      } else {
        print("Upload failed. Status: ${response.statusCode}");
        setState(() => uploading = false);
      }
    } catch (e) {
      print("Upload error: $e");
      setState(() => uploading = false);
    }
  }

  Future<void> pickAndUpload() async {
    final picked = await ImagePicker().pickImage(source: ImageSource.gallery);
    if (picked != null) {
      await uploadToCloudinary(File(picked.path));
    }
  }

  @override
  Widget build(BuildContext context) {
    final resizedImageUrl = uploadedImageUrl != null
        ? uploadedImageUrl!.replaceFirst(
            "/upload/",
            "/upload/w_400,h_850,c_pad/",
          ) // Resize here
        : null;
   print("Resized URL: $resizedImageUrl");


    return Scaffold(
      appBar: AppBar(title: Text("Cloudinary Image Upload and Resizing")),
      body: Center(
        child: uploading
            ? CircularProgressIndicator()
            : uploadedImageUrl == null
            ? Text("No image uploaded yet")
            : Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text("Uploaded and Resized Image"),
                  SizedBox(height: 10),
                  Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 16.0),
                    child: Image.network(
                      uploadedImageUrl!,
                      fit: BoxFit.contain,
                    ),
                  ),
                ],
              ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: pickAndUpload,
        child: Icon(Icons.upload),
      ),
    );
  }
}

Above, the uploadToCloudinary function asynchronously uploads the specified file to Cloudinary using http’s POST request, updates the UI to show the uploading status, and stores the received secure image URL upon successful completion. In build(BuildContext context), we recalculate the resizedImageUrl by taking the original uploadedImageUrl and inserting a Cloudinary transformation string (/w_400,h_850,c_fill/) into the URL path, which resizes the image for display.

Here’s the resulting transformed URL:

https://res.cloudinary.com/cloudinarymich/image/upload/w_400,h_850,c_pad/v1762419401/f1gt0oalwg4ble8m67ml.jpg

As you can see, choosing Cloudinary for image resizing in Flutter offers several advantages. For instance, it eliminates the need for you to manage your own image storage infrastructure. You gain the ability to perform dynamic resizing on-the-fly based on user input, and easily upscale or downscale images as required. You can read more about resizing images in the Cloudinary docs.

Wrapping Up

Resizing images in Flutter can be approached in multiple ways, each with unique advantages. The Image widget with BoxFit is often sufficient for quick UI adjustments and a perfect fit for responsive layouts. For use cases requiring actual file size reduction, Dart Image Library gives you fine-grained control over dimensions, quality, and format. For scalable and complex applications, Cloudinary’s dynamic URL transformations offload the heavy lifting to the cloud, ensuring fast load times and adaptive delivery across devices.

Transform your videos instantly with Cloudinary’s powerful API tools. Sign up for a Cloudinary account today and see how easy video transformation can be.

Frequently Asked Questions

How do I maintain quality when resizing images?

To maintain visual quality when resizing images, try to maintain the original aspect ratio. Also, scaling up an image beyond its original dimensions will likely reduce the image quality and cause blurred or pixelated outputs.

Does resizing an image reduce its file size?

Resizing an image can reduce its file size if you’re actually changing the pixel dimensions or compressing it, but simply changing the display size in Flutter using the Image widget for example, does not reduce the original file size. In Cloudinary, you can resize an image and compress its size at the same time using chained transformations.

What is the best tool for resizing images in Flutter?

The best tool for resizing images depends on your specific use case. For dynamic resizing and complex projects, external services like Cloudinary will make a good choice. While for local processing or static resizing, the Image widget or Dart Image Library works well.

QUICK TIPS
Colby Fayock
Cloudinary Logo Colby Fayock

In my experience, here are tips that can help you better resize and manage images in Flutter:

  1. Decode only the needed resolution using instantiateImageCodec
    Instead of decoding full-resolution images into memory, use instantiateImageCodec from Flutter’s ui library with targetWidth and targetHeight to reduce memory usage and render only what’s necessary—especially useful when dealing with large assets on low-end devices.
  2. Resize before upload to reduce bandwidth costs
    Use the Dart Image Library or image_picker with maxWidth and maxHeight to resize images before uploading to servers or Cloudinary. This ensures users don’t waste bandwidth on unnecessary resolution, and uploads complete faster.
  3. Avoid layout jank by precalculating image dimensions
    Store and reuse original image aspect ratios to size containers dynamically before the image loads. This prevents layout jumps in scrollable views or lazy-loaded galleries.
  4. Batch-process multiple images off the UI thread
    When resizing multiple images (e.g., in bulk upload scenarios), process them using compute() to run resizing logic in a separate isolate, avoiding main thread UI stalls.
  5. Use Cloudinary’s dpr_auto for high-DPI screens
    When displaying Cloudinary images on Retina or high-DPI screens, append dpr_auto in the transformation URL. This automatically serves high-resolution images without bloating bandwidth for standard displays.
  6. Integrate EXIF orientation correction pre-resize
    Always normalize image orientation from EXIF metadata before resizing. The Dart Image Library does not automatically correct orientation, which can result in upside-down or rotated outputs unless explicitly handled.
  7. Implement responsive resizing via MediaQuery
    Dynamically calculate image dimensions using MediaQuery.of(context).size to serve the best size for each screen. This ensures your app adapts smoothly across device types and aspect ratios.
  8. Use lossless resizing formats (like WebP) for quality retention
    When working with formats like JPEG, consider converting and resizing in WebP for superior compression-to-quality ratio. Cloudinary supports this natively through f_auto in the transformation URL.
  9. Throttle image resize triggers in UI-intensive flows
    When resizing in response to user input (e.g., sliders or pinch gestures), debounce or throttle resize events to prevent excessive CPU usage and frame drops during gesture interaction.
  10. Leverage Cloudinary’s named transformations for reuse
    Instead of defining resizing logic inline for every asset, use Cloudinary’s named transformations for consistency and easy maintenance. You can version and update transformations globally without.
Last updated: Nov 12, 2025