Overview

OverflowBox is a widget that imposes different constraints on its child than it gets from its parent, possibly allowing the child to overflow the parent.

This widget is useful when you want a child to have a different size than what the parent allows, either larger or smaller. For example, it can be used to display large icons or images inside small containers.

See: https://api.flutter.dev/flutter/widgets/OverflowBox-class.html

When to use it?

  • When a child needs to exceed the parent’s size
  • When creating UI elements like popups or tooltips that extend outside the parent area
  • When creating effects where size temporarily expands during animations
  • When you need a fixed size that ignores parent constraints
  • When creating elements like overlays or badges that cross parent boundaries

Basic Usage

// Allow child larger than parent
Container({
  width: 100,
  height: 100,
  color: 'blue',
  child: OverflowBox({
    maxWidth: 200,
    maxHeight: 200,
    child: Container({
      width: 150,
      height: 150,
      color: 'red'
    })
  })
})

// Force child smaller than parent
Container({
  width: 200,
  height: 200,
  color: 'green',
  child: OverflowBox({
    maxWidth: 100,
    maxHeight: 100,
    child: Container({
      color: 'yellow'
    })
  })
})

// Fix to specific size
OverflowBox({
  minWidth: 150,
  maxWidth: 150,
  minHeight: 150,
  maxHeight: 150,
  child: Container({
    color: 'purple'
  })
})

Props

minWidth

Value: number | undefined

The minimum width constraint to apply to the child.

When undefined, uses the parent’s minimum width constraint.

OverflowBox({
  minWidth: 100,  // Child has minimum width of 100
  child: child
})

maxWidth

Value: number | undefined

The maximum width constraint to apply to the child.

When undefined, uses the parent’s maximum width constraint.

OverflowBox({
  maxWidth: 300,  // Child has maximum width of 300
  child: child
})

minHeight

Value: number | undefined

The minimum height constraint to apply to the child.

When undefined, uses the parent’s minimum height constraint.

OverflowBox({
  minHeight: 50,  // Child has minimum height of 50
  child: child
})

maxHeight

Value: number | undefined

The maximum height constraint to apply to the child.

When undefined, uses the parent’s maximum height constraint.

OverflowBox({
  maxHeight: 200,  // Child has maximum height of 200
  child: child
})

alignment

Value: Alignment (default: Alignment.center)

How to align the child when it has a different size than the parent.

OverflowBox({
  maxWidth: 200,
  maxHeight: 200,
  alignment: Alignment.topRight,  // Align to top right
  child: child
})

child

Value: Widget | undefined

The child widget to which the new constraints will be applied.

Practical Examples

Example 1: Floating Action Button Badge

const FloatingActionButtonWithBadge = ({ count, onPressed }) => {
  return Container({
    width: 56,
    height: 56,
    child: Stack({
      children: [
        // FAB
        FloatingActionButton({
          onPressed,
          child: Icon(Icons.shopping_cart)
        }),
        // Badge (overflows parent area)
        Positioned({
          top: 0,
          right: 0,
          child: OverflowBox({
            maxWidth: 30,
            maxHeight: 30,
            child: Container({
              width: 24,
              height: 24,
              decoration: BoxDecoration({
                color: 'red',
                shape: BoxShape.circle,
                border: Border.all({ 
                  color: 'white', 
                  width: 2 
                })
              }),
              child: Center({
                child: Text(count.toString(), {
                  style: TextStyle({
                    color: 'white',
                    fontSize: 12,
                    fontWeight: 'bold'
                  })
                })
              })
            })
          })
        })
      ]
    })
  });
};

Example 2: Zoom Preview

const ZoomPreview = ({ image, zoomLevel = 2.0 }) => {
  const [isHovering, setIsHovering] = useState(false);
  const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
  
  return GestureDetector({
    onHover: (event) => {
      setIsHovering(true);
      setMousePosition({ x: event.localPosition.x, y: event.localPosition.y });
    },
    onHoverExit: () => setIsHovering(false),
    child: Container({
      width: 300,
      height: 300,
      child: Stack({
        children: [
          // Original image
          Image({
            src: image,
            objectFit: 'cover'
          }),
          // Zoomed area
          if (isHovering) Positioned({
            left: mousePosition.x - 50,
            top: mousePosition.y - 50,
            child: OverflowBox({
              maxWidth: 200,
              maxHeight: 200,
              child: Container({
                width: 100 * zoomLevel,
                height: 100 * zoomLevel,
                decoration: BoxDecoration({
                  border: Border.all({ 
                    color: 'white', 
                    width: 2 
                  }),
                  borderRadius: BorderRadius.circular(50),
                  boxShadow: [
                    BoxShadow({
                      color: 'rgba(0,0,0,0.3)',
                      blurRadius: 10,
                      spreadRadius: 2
                    })
                  ]
                }),
                child: ClipOval({
                  child: Transform.scale({
                    scale: zoomLevel,
                    child: Transform.translate({
                      offset: {
                        x: -mousePosition.x,
                        y: -mousePosition.y
                      },
                      child: Image({
                        src: image,
                        objectFit: 'cover'
                      })
                    })
                  })
                })
              })
            })
          })
        ]
      })
    })
  });
};

Example 3: Custom Tooltip

const CustomTooltip = ({ text, child }) => {
  const [isVisible, setIsVisible] = useState(false);
  
  return MouseRegion({
    onEnter: () => setIsVisible(true),
    onExit: () => setIsVisible(false),
    child: Stack({
      clipBehavior: Clip.none,
      children: [
        child,
        if (isVisible) Positioned({
          top: -40,
          left: 0,
          right: 0,
          child: OverflowBox({
            maxWidth: 300,  // Allow tooltip wider than parent
            child: Container({
              padding: EdgeInsets.symmetric({ 
                horizontal: 12, 
                vertical: 8 
              }),
              decoration: BoxDecoration({
                color: 'rgba(0,0,0,0.8)',
                borderRadius: BorderRadius.circular(6),
                boxShadow: [
                  BoxShadow({
                    color: 'rgba(0,0,0,0.2)',
                    blurRadius: 4,
                    offset: { x: 0, y: 2 }
                  })
                ]
              }),
              child: Row({
                mainAxisSize: MainAxisSize.min,
                children: [
                  Icon({
                    icon: Icons.info,
                    size: 14,
                    color: 'white'
                  }),
                  SizedBox({ width: 6 }),
                  Text(text, {
                    style: TextStyle({
                      color: 'white',
                      fontSize: 12
                    })
                  })
                ]
              })
            })
          })
        })
      ]
    })
  });
};

Example 4: Pulse Animation Effect

const PulseAnimation = ({ child, color = 'blue' }) => {
  const [scale, setScale] = useState(1.0);
  const [opacity, setOpacity] = useState(0.5);
  
  useEffect(() => {
    const interval = setInterval(() => {
      setScale(s => s === 1.0 ? 1.5 : 1.0);
      setOpacity(o => o === 0.5 ? 0.0 : 0.5);
    }, 1000);
    
    return () => clearInterval(interval);
  }, []);
  
  return Container({
    width: 100,
    height: 100,
    child: Stack({
      children: [
        // Pulse effect (larger than parent)
        Center({
          child: OverflowBox({
            maxWidth: 150,
            maxHeight: 150,
            child: AnimatedContainer({
              duration: Duration.milliseconds(1000),
              width: 100 * scale,
              height: 100 * scale,
              decoration: BoxDecoration({
                color: color,
                shape: BoxShape.circle,
                opacity: opacity
              })
            })
          })
        }),
        // Actual content
        Center({ child })
      ]
    })
  });
};

Example 5: Dropdown Menu

const CustomDropdown = ({ options, value, onChange }) => {
  const [isOpen, setIsOpen] = useState(false);
  
  return Container({
    width: 200,
    height: 48,
    child: Stack({
      clipBehavior: Clip.none,
      children: [
        // Dropdown button
        GestureDetector({
          onTap: () => setIsOpen(!isOpen),
          child: Container({
            padding: EdgeInsets.symmetric({ horizontal: 16 }),
            decoration: BoxDecoration({
              color: 'white',
              border: Border.all({ color: '#E0E0E0' }),
              borderRadius: BorderRadius.circular(8)
            }),
            child: Row({
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Text(value || 'Select...'),
                Icon({
                  icon: isOpen ? Icons.arrow_drop_up : Icons.arrow_drop_down
                })
              ]
            })
          })
        }),
        // Dropdown menu (can exceed parent width)
        if (isOpen) Positioned({
          top: 52,
          left: 0,
          child: OverflowBox({
            maxWidth: 300,  // Allow longer options to display
            child: Container({
              constraints: BoxConstraints({
                minWidth: 200  // At least parent width
              }),
              decoration: BoxDecoration({
                color: 'white',
                borderRadius: BorderRadius.circular(8),
                boxShadow: [
                  BoxShadow({
                    color: 'rgba(0,0,0,0.15)',
                    blurRadius: 8,
                    offset: { x: 0, y: 4 }
                  })
                ]
              }),
              child: Column({
                crossAxisAlignment: CrossAxisAlignment.start,
                mainAxisSize: MainAxisSize.min,
                children: options.map((option, index) => 
                  GestureDetector({
                    onTap: () => {
                      onChange(option);
                      setIsOpen(false);
                    },
                    child: Container({
                      width: double.infinity,
                      padding: EdgeInsets.all(16),
                      decoration: BoxDecoration({
                        border: index > 0 
                          ? Border(top: BorderSide({ color: '#F0F0F0' }))
                          : null
                      }),
                      child: Text(option, {
                        style: TextStyle({
                          color: value === option ? '#1976D2' : '#333'
                        })
                      })
                    })
                  })
                )
              })
            })
          })
        })
      ]
    })
  });
};

Understanding Size Behavior

OverflowBox Size Determination

// OverflowBox itself follows parent constraints
Container({
  width: 100,
  height: 100,
  color: 'blue',
  child: OverflowBox({
    maxWidth: 200,
    maxHeight: 200,
    child: Container({
      width: 150,
      height: 150,
      color: 'red'  // Appears outside blue area
    })
  })
})

// OverflowBox size: 100x100 (parent constraints)
// Child size: 150x150 (OverflowBox constraints)

Constraint Inheritance

// Change only some constraints
OverflowBox({
  maxWidth: 300,  // Change width only
  // minWidth: inherited from parent
  // minHeight: inherited from parent
  // maxHeight: inherited from parent
  child: child
})

// Change all constraints
OverflowBox({
  minWidth: 100,
  maxWidth: 200,
  minHeight: 50,
  maxHeight: 150,
  child: child
})

Important Notes

  • OverflowBox allows children to overflow parent area, which can cause layout issues
  • Overflowed areas are not clipped and may overlap other widgets
  • When using with Stack, set clipBehavior: Clip.none
  • Touch events are only detected within the parent area
  • Avoid excessive overflow for performance reasons
  • UnconstrainedBox: Widget that removes all constraints
  • ConstrainedBox: Widget that applies additional constraints
  • SizedBox: Widget that specifies fixed size
  • LimitedBox: Widget that limits size only in infinite constraints
  • FittedBox: Widget that scales child to fit parent