Skip to content

Flex

Understand the Flex widget and how it provides flexible layouts.


What is it?

Flex is the base widget that powers both Row and Column. It provides the core functionality for laying out children along a specific axis (horizontal or vertical) with flexible sizing, alignment, and spacing. While you typically use Row and Column directly, understanding Flex gives you deeper insight into Flutter's layout system and enables custom flex layouts.


Why does it exist?

Flex exists to:

  • Provide the foundation for Row and Column
  • Enable custom axis layouts
  • Control flex behavior and sizing
  • Manage layout direction and alignment
  • Support both horizontal and vertical layouts
  • Enable responsive and adaptive designs
  • Provide low-level flex layout control

Flex Basics

Flex is the parent of Row and Column.

// Flex vs Row/Column
class FlexBasics extends StatelessWidget {
  const FlexBasics({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 1. Row uses Flex with horizontal direction
        Row(
          children: [
            Container(width: 50, height: 50, color: Colors.red),
            Container(width: 50, height: 50, color: Colors.green),
          ],
        ),

        const SizedBox(height: 20),

        // 2. Column uses Flex with vertical direction
        Column(
          children: [
            Container(width: 50, height: 50, color: Colors.blue),
            Container(width: 50, height: 50, color: Colors.yellow),
          ],
        ),

        const SizedBox(height: 20),

        // 3. Flex directly with direction
        Flex(
          direction: Axis.horizontal,
          children: [
            Container(width: 50, height: 50, color: Colors.purple),
            Container(width: 50, height: 50, color: Colors.orange),
          ],
        ),
      ],
    );
  }
}

// Flex properties:
// 1. direction - Axis.horizontal or Axis.vertical
// 2. children - List of widgets
// 3. mainAxisAlignment - Alignment along main axis
// 4. crossAxisAlignment - Alignment along cross axis
// 5. mainAxisSize - Min or max space
// 6. textDirection - LTR or RTL
// 7. verticalDirection - Up or down

What's happening here? - Row = Flex(direction: Axis.horizontal) - Column = Flex(direction: Axis.vertical) - Flex can be used directly for custom layouts - All Row/Column properties work with Flex


Custom Flex Layouts

Using Flex directly for custom layouts.

// Custom flex layouts
class CustomFlexLayout extends StatelessWidget {
  const CustomFlexLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 1. Dynamic direction based on condition
        _buildDynamicFlex(),

        const SizedBox(height: 20),

        // 2. Custom spacing
        Flex(
          direction: Axis.horizontal,
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            Container(width: 50, height: 50, color: Colors.red),
            Container(width: 50, height: 50, color: Colors.green),
            Container(width: 50, height: 50, color: Colors.blue),
          ],
        ),

        const SizedBox(height: 20),

        // 3. Flex with children containing Expanded
        Flex(
          direction: Axis.horizontal,
          children: [
            Expanded(
              flex: 2,
              child: Container(color: Colors.red, height: 50),
            ),
            Expanded(
              flex: 1,
              child: Container(color: Colors.green, height: 50),
            ),
          ],
        ),
      ],
    );
  }

  Widget _buildDynamicFlex() {
    // Flexible layout based on condition
    final bool isHorizontal = true;

    return Flex(
      direction: isHorizontal ? Axis.horizontal : Axis.vertical,
      children: [
        Container(width: 50, height: 50, color: Colors.blue),
        Container(width: 50, height: 50, color: Colors.yellow),
      ],
    );
  }
}

What's happening here? - Flex can change direction dynamically - Control axis based on conditions - Same properties as Row/Column - More control over layout behavior


FlexDirection

Direction determines how children are arranged.

// Flex direction examples
class FlexDirectionExample extends StatelessWidget {
  const FlexDirectionExample({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 1. Horizontal direction (Row)
        Container(
          color: Colors.grey[200],
          padding: const EdgeInsets.all(8),
          child: Flex(
            direction: Axis.horizontal,
            children: [
              Container(width: 40, height: 40, color: Colors.red),
              Container(width: 40, height: 40, color: Colors.green),
              Container(width: 40, height: 40, color: Colors.blue),
            ],
          ),
        ),

        const SizedBox(height: 10),

        // 2. Vertical direction (Column)
        Container(
          color: Colors.grey[200],
          padding: const EdgeInsets.all(8),
          child: Flex(
            direction: Axis.vertical,
            children: [
              Container(width: 40, height: 40, color: Colors.red),
              Container(width: 40, height: 40, color: Colors.green),
              Container(width: 40, height: 40, color: Colors.blue),
            ],
          ),
        ),

        const SizedBox(height: 10),

        // 3. Vertical direction with alignment
        Container(
          height: 150,
          color: Colors.grey[200],
          padding: const EdgeInsets.all(8),
          child: Flex(
            direction: Axis.vertical,
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Container(width: 40, height: 40, color: Colors.red),
              Container(width: 40, height: 40, color: Colors.green),
              Container(width: 40, height: 40, color: Colors.blue),
            ],
          ),
        ),
      ],
    );
  }
}

What's happening here? - Horizontal = Row behavior - Vertical = Column behavior - Direction determines main axis - Properties adjust accordingly


Flex with Expanded

Expanded in Flex controls space distribution.

// Flex with Expanded children
class FlexWithExpanded extends StatelessWidget {
  const FlexWithExpanded({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 1. Horizontal flex
        Container(
          height: 100,
          color: Colors.grey[200],
          child: Flex(
            direction: Axis.horizontal,
            children: [
              Expanded(
                flex: 1,
                child: Container(color: Colors.red),
              ),
              Expanded(
                flex: 2,
                child: Container(color: Colors.green),
              ),
              Expanded(
                flex: 1,
                child: Container(color: Colors.blue),
              ),
            ],
          ),
        ),

        const SizedBox(height: 10),

        // 2. Vertical flex
        Container(
          height: 150,
          color: Colors.grey[200],
          child: Flex(
            direction: Axis.vertical,
            children: [
              Expanded(
                flex: 1,
                child: Container(color: Colors.red),
              ),
              Expanded(
                flex: 2,
                child: Container(color: Colors.green),
              ),
              Expanded(
                flex: 1,
                child: Container(color: Colors.blue),
              ),
            ],
          ),
        ),

        const SizedBox(height: 10),

        // 3. Mixed fixed and flexible
        Container(
          height: 100,
          color: Colors.grey[200],
          child: Flex(
            direction: Axis.horizontal,
            children: [
              Container(
                width: 50,
                color: Colors.red,
              ),
              Expanded(
                child: Container(color: Colors.green),
              ),
              Container(
                width: 50,
                color: Colors.blue,
              ),
            ],
          ),
        ),
      ],
    );
  }
}

What's happening here? - Expanded works with Flex - Flex factors control proportion - Works in both directions - Mix fixed and flexible children


Flex with Flexible

Flexible provides more control than Expanded.

// Flex with Flexible children
class FlexWithFlexible extends StatelessWidget {
  const FlexWithFlexible({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 1. Flexible with tight fit
        Container(
          height: 100,
          color: Colors.grey[200],
          child: Flex(
            direction: Axis.horizontal,
            children: [
              Flexible(
                fit: FlexFit.tight,
                child: Container(color: Colors.red),
              ),
              Flexible(
                fit: FlexFit.tight,
                child: Container(color: Colors.green),
              ),
            ],
          ),
        ),

        const SizedBox(height: 10),

        // 2. Flexible with loose fit
        Container(
          height: 100,
          color: Colors.grey[200],
          child: Flex(
            direction: Axis.horizontal,
            children: [
              Flexible(
                fit: FlexFit.loose,
                child: Container(
                  color: Colors.red,
                  child: const Text('Loose'),
                ),
              ),
              Flexible(
                fit: FlexFit.loose,
                child: Container(
                  color: Colors.green,
                  child: const Text('Loose'),
                ),
              ),
            ],
          ),
        ),

        const SizedBox(height: 10),

        // 3. Flexible with different flex values
        Container(
          height: 100,
          color: Colors.grey[200],
          child: Flex(
            direction: Axis.horizontal,
            children: [
              Flexible(
                flex: 2,
                fit: FlexFit.tight,
                child: Container(color: Colors.red),
              ),
              Flexible(
                flex: 1,
                fit: FlexFit.tight,
                child: Container(color: Colors.blue),
              ),
            ],
          ),
        ),
      ],
    );
  }
}

What's happening here? - Tight fit forces fill - Loose fit allows natural size - Flex factors work the same - More control than Expanded


Flex Alignment

Alignment in Flex controls child positioning.

// Flex alignment examples
class FlexAlignment extends StatelessWidget {
  const FlexAlignment({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 1. Main alignment (horizontal)
        Container(
          color: Colors.grey[200],
          child: Flex(
            direction: Axis.horizontal,
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              Container(width: 40, height: 40, color: Colors.red),
              Container(width: 40, height: 40, color: Colors.green),
              Container(width: 40, height: 40, color: Colors.blue),
            ],
          ),
        ),

        const SizedBox(height: 10),

        // 2. Cross alignment (vertical)
        Container(
          height: 100,
          color: Colors.grey[200],
          child: Flex(
            direction: Axis.horizontal,
            crossAxisAlignment: CrossAxisAlignment.end,
            children: [
              Container(width: 40, height: 30, color: Colors.red),
              Container(width: 40, height: 60, color: Colors.green),
              Container(width: 40, height: 45, color: Colors.blue),
            ],
          ),
        ),

        const SizedBox(height: 10),

        // 3. Both alignments
        Container(
          height: 150,
          color: Colors.grey[200],
          child: Flex(
            direction: Axis.vertical,
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.end,
            children: [
              Container(width: 30, height: 30, color: Colors.red),
              Container(width: 60, height: 30, color: Colors.green),
              Container(width: 90, height: 30, color: Colors.blue),
            ],
          ),
        ),
      ],
    );
  }
}

What's happening here? - mainAxisAlignment along main axis - crossAxisAlignment along cross axis - Works in both directions - Full control over positioning


Flex in Real-World

Practical use cases for Flex.

// 1. Responsive toolbar
class ResponsiveToolbar extends StatelessWidget {
  const ResponsiveToolbar({super.key});

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        // Switch between horizontal and vertical
        final isWide = constraints.maxWidth > 600;

        return Flex(
          direction: isWide ? Axis.horizontal : Axis.vertical,
          mainAxisAlignment: isWide 
              ? MainAxisAlignment.spaceBetween 
              : MainAxisAlignment.center,
          children: [
            const Text('Logo', style: TextStyle(fontSize: 24)),
            if (isWide) ...[
              TextButton(child: const Text('Home'), onPressed: () {}),
              TextButton(child: const Text('About'), onPressed: () {}),
              TextButton(child: const Text('Contact'), onPressed: () {}),
            ],
            IconButton(
              icon: const Icon(Icons.menu),
              onPressed: () {},
            ),
          ],
        );
      },
    );
  }
}

// 2. Card layout with flex
class CardWithFlex extends StatelessWidget {
  const CardWithFlex({super.key});

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.all(16),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Flex(
          direction: Axis.horizontal,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // Left: Icon
            Container(
              width: 60,
              height: 60,
              decoration: const BoxDecoration(
                color: Colors.blue,
                shape: BoxShape.circle,
              ),
              child: const Icon(Icons.person, color: Colors.white),
            ),
            const SizedBox(width: 16),

            // Right: Content
            Expanded(
              child: Flex(
                direction: Axis.vertical,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  const Text(
                    'John Doe',
                    style: TextStyle(
                      fontSize: 18,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(height: 4),
                  Text(
                    'Flutter Developer',
                    style: TextStyle(
                      color: Colors.grey[600],
                      fontSize: 14,
                    ),
                  ),
                  const SizedBox(height: 8),
                  Flex(
                    direction: Axis.horizontal,
                    children: [
                      Icon(Icons.star, color: Colors.yellow[700]),
                      Icon(Icons.star, color: Colors.yellow[700]),
                      Icon(Icons.star, color: Colors.yellow[700]),
                      Icon(Icons.star, color: Colors.yellow[700]),
                      Icon(Icons.star, color: Colors.yellow[700]),
                    ],
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

// 3. Dynamic flex grid
class DynamicFlexGrid extends StatelessWidget {
  const DynamicFlexGrid({super.key});

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        final crossAxisCount = (constraints.maxWidth / 120).floor().clamp(2, 4);

        return Flex(
          direction: Axis.vertical,
          children: List.generate(3, (rowIndex) {
            return Flex(
              direction: Axis.horizontal,
              children: List.generate(crossAxisCount, (colIndex) {
                return Expanded(
                  child: Container(
                    margin: const EdgeInsets.all(4),
                    height: 100,
                    color: Colors.blue[100 * ((rowIndex + colIndex) % 9 + 1)],
                    child: Center(
                      child: Text('${rowIndex * crossAxisCount + colIndex + 1}'),
                    ),
                  ),
                );
              }),
            );
          }),
        );
      },
    );
  }
}

What's happening here? - Responsive toolbar switching direction - Card with mixed flex directions - Dynamic grid with Flex - Real-world flex usage


Best Practices

Use Flex for Dynamic Direction

// Good - Dynamic direction
@override
Widget build(BuildContext context) {
  final isHorizontal = MediaQuery.of(context).size.width > 600;

  return Flex(
    direction: isHorizontal ? Axis.horizontal : Axis.vertical,
    children: [
      Container(color: Colors.red),
      Container(color: Colors.green),
    ],
  );
}

Keep Flex Trees Shallow

// Good - Shallow flex tree
@override
Widget build(BuildContext context) {
  return Flex(
    direction: Axis.horizontal,
    children: [
      Expanded(child: Container(color: Colors.red)),
      Expanded(child: Container(color: Colors.green)),
    ],
  );
}

// Bad - Deep flex tree
@override
Widget build(BuildContext context) {
  return Flex(
    direction: Axis.horizontal,
    children: [
      Flex(
        direction: Axis.vertical,
        children: [
          Flex(
            direction: Axis.horizontal,
            children: [
              // Very deep nesting
            ],
          ),
        ],
      ),
    ],
  );
}

Common Mistakes

Wrong Direction

Wrong:

// Using Flex horizontal for vertical layout
Flex(
  direction: Axis.horizontal,
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
    Container(height: 50, color: Colors.red),
    Container(height: 50, color: Colors.green),
  ],
)

Correct:

// Use appropriate direction
Flex(
  direction: Axis.vertical,
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
    Container(height: 50, color: Colors.red),
    Container(height: 50, color: Colors.green),
  ],
)

Overusing Flex

Wrong:

// Using Flex when Row/Column would be clearer
Flex(
  direction: Axis.horizontal,
  children: [
    Container(color: Colors.red),
  ],
)

Correct:

// Use Row/Column for readability
Row(
  children: [
    Container(color: Colors.red),
  ],
)


Summary

Flex is the foundation for Row and Column, providing flexible layout capabilities in any direction. Use Flex directly when you need dynamic direction or custom behavior. Understanding Flex helps build more flexible and responsive layouts.


Next Steps


Did You Know?

  • Row = Flex(direction: Axis.horizontal)
  • Column = Flex(direction: Axis.vertical)
  • Flex can change direction dynamically
  • All Row/Column properties work with Flex
  • Flex is lower-level than Row/Column
  • Flex supports custom flex layouts
  • Flex is used throughout Flutter
  • Understanding Flex helps debug layout issues