Skip to content

AspectRatio

Understand how to maintain a specific aspect ratio for widgets.


What is it?

AspectRatio is a widget that attempts to size its child to a specific aspect ratio. The aspect ratio is expressed as a ratio of width to height (e.g., 16/9 for widescreen, 1/1 for square). AspectRatio is useful for creating widgets that maintain their proportions regardless of the available space.


Why does it exist?

AspectRatio exists to:

  • Maintain consistent proportions
  • Create square widgets
  • Support video and image containers
  • Build responsive designs
  • Handle different screen sizes
  • Preserve aspect ratios in layouts
  • Create uniform grid items

Basic AspectRatio

AspectRatio maintains width-to-height proportions.

// Basic AspectRatio usage
class BasicAspectRatio extends StatelessWidget {
  const BasicAspectRatio({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 1. Square (1:1)
        Container(
          width: 200,
          color: Colors.grey[200],
          child: const AspectRatio(
            aspectRatio: 1.0,
            child: ColoredBox(
              color: Colors.blue,
              child: Center(
                child: Text('1:1 Square'),
              ),
            ),
          ),
        ),

        const SizedBox(height: 20),

        // 2. Widescreen (16:9)
        Container(
          width: 300,
          color: Colors.grey[200],
          child: const AspectRatio(
            aspectRatio: 16 / 9,
            child: ColoredBox(
              color: Colors.green,
              child: Center(
                child: Text('16:9 Widescreen'),
              ),
            ),
          ),
        ),

        const SizedBox(height: 20),

        // 3. Portrait (3:4)
        Container(
          height: 200,
          color: Colors.grey[200],
          child: const AspectRatio(
            aspectRatio: 3 / 4,
            child: ColoredBox(
              color: Colors.red,
              child: Center(
                child: Text('3:4 Portrait'),
              ),
            ),
          ),
        ),
      ],
    );
  }
}

// AspectRatio properties:
// 1. aspectRatio - Width/Height ratio
// 2. child - The widget to size

What's happening here? - aspectRatio controls proportions - 1.0 = square - 16/9 = widescreen - 3/4 = portrait - Child fills the space


AspectRatio Calculations

How AspectRatio determines size.

// AspectRatio calculations
class AspectRatioCalculations extends StatelessWidget {
  const AspectRatioCalculations({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 1. Width constrained (height calculated)
        Container(
          width: 200,
          color: Colors.grey[200],
          child: const AspectRatio(
            aspectRatio: 2.0,
            child: ColoredBox(
              color: Colors.blue,
              child: Center(
                child: Text('Width 200, Height 100'),
              ),
            ),
          ),
        ),

        const SizedBox(height: 20),

        // 2. Height constrained (width calculated)
        Container(
          height: 100,
          color: Colors.grey[200],
          child: const AspectRatio(
            aspectRatio: 2.0,
            child: ColoredBox(
              color: Colors.green,
              child: Center(
                child: Text('Height 100, Width 200'),
              ),
            ),
          ),
        ),

        const SizedBox(height: 20),

        // 3. Both constrained (fits within)
        Container(
          width: 300,
          height: 200,
          color: Colors.grey[200],
          child: const AspectRatio(
            aspectRatio: 1.0,
            child: ColoredBox(
              color: Colors.red,
              child: Center(
                child: Text('Fits within 300x200'),
              ),
            ),
          ),
        ),
      ],
    );
  }
}

// Calculation rules:
// 1. If width is constrained, height = width / aspectRatio
// 2. If height is constrained, width = height * aspectRatio
// 3. If both constrained, child is centered within available space
// 4. If neither constrained, uses parent constraints

What's happening here? - Width constrained → height calculated - Height constrained → width calculated - Both constrained → fits within - Respects parent constraints


AspectRatio with Images

AspectRatio is perfect for image containers.

// AspectRatio with images
class AspectRatioImages extends StatelessWidget {
  const AspectRatioImages({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 1. Square image
        const AspectRatio(
          aspectRatio: 1.0,
          child: ColoredBox(
            color: Colors.blue,
            child: Center(
              child: Icon(
                Icons.image,
                size: 50,
                color: Colors.white,
              ),
            ),
          ),
        ),

        const SizedBox(height: 20),

        // 2. 16:9 image container
        const AspectRatio(
          aspectRatio: 16 / 9,
          child: ColoredBox(
            color: Colors.green,
            child: Center(
              child: Text(
                '16:9 Image',
                style: TextStyle(color: Colors.white),
              ),
            ),
          ),
        ),

        const SizedBox(height: 20),

        // 3. 4:3 image container
        Container(
          width: 300,
          color: Colors.grey[200],
          child: const AspectRatio(
            aspectRatio: 4 / 3,
            child: ColoredBox(
              color: Colors.red,
              child: Center(
                child: Text(
                  '4:3 Image',
                  style: TextStyle(color: Colors.white),
                ),
              ),
            ),
          ),
        ),
      ],
    );
  }
}

What's happening here? - Maintains image proportions - Square, widescreen, standard - Responsive to container size - Perfect for image grids


AspectRatio in Grids

AspectRatio creates uniform grid items.

// AspectRatio in grids
class AspectRatioGrid extends StatelessWidget {
  const AspectRatioGrid({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 1. Grid with square items
        SizedBox(
          height: 200,
          child: GridView.count(
            crossAxisCount: 3,
            padding: const EdgeInsets.all(8),
            crossAxisSpacing: 8,
            mainAxisSpacing: 8,
            children: [
              for (int i = 0; i < 6; i++)
                const AspectRatio(
                  aspectRatio: 1.0,
                  child: ColoredBox(
                    color: Colors.blue,
                    child: Center(
                      child: Text(
                        'Square',
                        style: TextStyle(color: Colors.white),
                      ),
                    ),
                  ),
                ),
            ],
          ),
        ),

        const SizedBox(height: 20),

        // 2. Grid with rectangular items
        SizedBox(
          height: 150,
          child: GridView.count(
            crossAxisCount: 2,
            padding: const EdgeInsets.all(8),
            crossAxisSpacing: 8,
            mainAxisSpacing: 8,
            children: [
              for (int i = 0; i < 4; i++)
                const AspectRatio(
                  aspectRatio: 2 / 1,
                  child: ColoredBox(
                    color: Colors.green,
                    child: Center(
                      child: Text(
                        '2:1',
                        style: TextStyle(color: Colors.white),
                      ),
                    ),
                  ),
                ),
            ],
          ),
        ),
      ],
    );
  }
}

What's happening here? - Uniform grid items - Consistent proportions - Responsive grid layout - Clean and organized


Real-World Examples

Common patterns using AspectRatio.

// 1. Video player container
class VideoPlayerContainer extends StatelessWidget {
  const VideoPlayerContainer({super.key});

  @override
  Widget build(BuildContext context) {
    return AspectRatio(
      aspectRatio: 16 / 9,
      child: Container(
        color: Colors.black,
        child: const Center(
          child: Icon(
            Icons.play_circle_outline,
            size: 64,
            color: Colors.white,
          ),
        ),
      ),
    );
  }
}

// 2. Square profile picture
class SquareProfilePicture extends StatelessWidget {
  const SquareProfilePicture({super.key});

  @override
  Widget build(BuildContext context) {
    return AspectRatio(
      aspectRatio: 1.0,
      child: Container(
        decoration: BoxDecoration(
          shape: BoxShape.circle,
          color: Colors.blue,
          image: const DecorationImage(
            image: NetworkImage('https://example.com/avatar.jpg'),
            fit: BoxFit.cover,
          ),
        ),
      ),
    );
  }
}

// 3. Product card with image
class ProductCard extends StatelessWidget {
  const ProductCard({super.key});

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.all(8),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // Product image with 1:1 ratio
          const AspectRatio(
            aspectRatio: 1.0,
            child: ColoredBox(
              color: Colors.blue,
              child: Center(
                child: Icon(
                  Icons.inventory,
                  size: 64,
                  color: Colors.white,
                ),
              ),
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(12),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                const Text(
                  'Product Name',
                  style: TextStyle(
                    fontWeight: FontWeight.bold,
                    fontSize: 16,
                  ),
                ),
                const SizedBox(height: 4),
                Text(
                  '\$99.99',
                  style: TextStyle(
                    color: Colors.grey[600],
                    fontSize: 14,
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

// 4. Hero image with overlay
class HeroImage extends StatelessWidget {
  const HeroImage({super.key});

  @override
  Widget build(BuildContext context) {
    return AspectRatio(
      aspectRatio: 16 / 9,
      child: Stack(
        children: [
          // Background image
          Container(
            decoration: const BoxDecoration(
              image: DecorationImage(
                image: NetworkImage('https://example.com/hero.jpg'),
                fit: BoxFit.cover,
              ),
            ),
          ),
          // Gradient overlay
          Container(
            decoration: BoxDecoration(
              gradient: LinearGradient(
                begin: Alignment.topCenter,
                end: Alignment.bottomCenter,
                colors: [
                  Colors.transparent,
                  Colors.black.withOpacity(0.7),
                ],
              ),
            ),
          ),
          // Text overlay
          const Padding(
            padding: EdgeInsets.all(16),
            child: Align(
              alignment: Alignment.bottomLeft,
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text(
                    'Hero Title',
                    style: TextStyle(
                      color: Colors.white,
                      fontSize: 24,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  Text(
                    'Hero subtitle goes here',
                    style: TextStyle(
                      color: Colors.white70,
                      fontSize: 16,
                    ),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

What's happening here? - Video player container - Square profile picture - Product card with image - Hero image with overlay


Best Practices

Use for Consistent Proportions

// Good - Maintains aspect ratio
@override
Widget build(BuildContext context) {
  return AspectRatio(
    aspectRatio: 16 / 9,
    child: Image.network('https://example.com/image.jpg'),
  );
}

Use with Grids

// Good - Uniform grid items
@override
Widget build(BuildContext context) {
  return GridView.builder(
    gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
      crossAxisCount: 3,
      childAspectRatio: 1.0, // Square items
    ),
    itemBuilder: (context, index) {
      return AspectRatio(
        aspectRatio: 1.0,
        child: Container(color: Colors.blue),
      );
    },
  );
}

Use for Media Containers

// Good - Video container
@override
Widget build(BuildContext context) {
  return AspectRatio(
    aspectRatio: 16 / 9,
    child: Container(
      color: Colors.black,
      child: const VideoPlayer(),
    ),
  );
}

Common Mistakes

Very Small Aspect Ratio

Wrong:

// Aspect ratio too small
AspectRatio(
  aspectRatio: 0.1, // Very wide
  child: Container(color: Colors.blue),
)

Correct:

// Reasonable aspect ratio
AspectRatio(
  aspectRatio: 16 / 9,
  child: Container(color: Colors.blue),
)

Unbounded Constraints

Wrong:

// No width or height constraints
AspectRatio(
  aspectRatio: 1.0,
  child: Container(color: Colors.blue),
)

Correct:

// With constraints
Container(
  width: 200,
  child: AspectRatio(
    aspectRatio: 1.0,
    child: Container(color: Colors.blue),
  ),
)


Summary

AspectRatio maintains width-to-height proportions for widgets. Use it for images, videos, profile pictures, and grid items. AspectRatio ensures consistent proportions across different screen sizes and orientations.


Next Steps


Did You Know?

  • AspectRatio 1.0 creates a square
  • AspectRatio calculates missing dimension
  • AspectRatio respects parent constraints
  • AspectRatio is used in many media widgets
  • AspectRatio works with any child
  • AspectRatio helps maintain proportions
  • AspectRatio is responsive by default
  • AspectRatio can be used in GridView