Accelerate your Flutter UI development, without compromising clarity.
exui
helps you build clean, maintainable UIs faster, with no wrappers and zero boilerplate.
exui
is a lightweight, zero-dependency extension library for Flutter that enhances your UI code with expressive, chainable utilities. It streamlines common layout and styling patterns using pure Dart and Flutter, no reliance on Material, Cupertino, or third-party frameworks.
Designed to work seamlessly in any project, exui
makes your widget code more concise, readable, and efficient, all while keeping full control over your widget tree.
- Extensions - for most used Flutter widgets. Same names, same behavior.
- Lightweight and efficient - wraps existing widgets without creating new classes.
- Actively maintained - Production-ready and continuously evolving.
- Zero dependencies - Pure Dart. No bloat. Add it to any project safely.
- Battle-tested - backed by hundreds of unit tests to ensure safety and reliability.
- Exceptional documentation - every extension is well documented with clear examples and fast navigation.
Each section below links to detailed documentation for a specific extension group. (Emojis only added to distinguish easily between extensions)
π padding
- Quickly Add Padding
π― center
- Center Widgets
expanded
- Fill Available Space
𧬠flex
- fast Flexibles
π align
- Position Widgets
π positioned
- Position Inside a Stack
π³ intrinsic
- Size Widgets
β margin
- Add Outer Spacing
gap
- Performant gaps
π§± row
/ column
- Rapid Layouts
π§ row*
/ column*
- Rapid Aligned Layouts
π§ stack
- Overlay Widgets
ποΈ visible
- Conditional Visibility
π«οΈ opacity
- Widget Transparency
π± safeArea
- SafeArea Padding
π gesture
- Detect Gestures
π¦Έ hero
- Shared Element Transitions
π¦ sizedBox
- Put in a SizedBox
π§ constrained
- Limit Widget Sizes
π₯ coloredBox
- Wrap in a Colored Box
π¨ decoratedBox
- Borders, Gradients & Effects
βοΈ clip
- Clip Widgets into Shapes
πͺ fittedBox
- Fit Widgets
π text
- String to Widget
ποΈ styled text
- Style Text
π£ icon
- Create and Style Icons
exui
is built on pure Flutter (flutter/widgets.dart
) and avoids bundling unnecessary Material or Cupertino designs by default. For convenience, optional libraries are provided for those who want seamless integration with Flutterβs built-in design systems.
Library | Description |
---|---|
exui/exui.dart |
Core extensions for pure Flutter. Lightweight and universal. |
exui/material.dart |
Adds extensions tailored to Material widgets and components. |
exui/cupertino.dart |
Adds extensions for Cupertino widgets and iOS-style components. |
- π²οΈ
button
- buttons - π¨
coloredBox
- background color
exui
is a practical toolkit for Flutter UI development β focused on saving time, reducing boilerplate, and writing layout code thatβs readable, consistent, and fun.
This isnβt about replacing widgets β itβs about using concise, chainable extensions to help you build better UIs faster. You stay in full control of your widget tree and design system.
Whether you're working on layout, spacing, visibility, or sizing, exui
gives you expressive helpers for the most common tasks β with zero dependencies and seamless integration into any codebase.
With exui
:
Text("Hello").paddingAll(16)
Without:
Padding(
padding: EdgeInsets.all(16),
child: Text("Hello"),
)
With additional extensions for quickly adding specific padding: paddingHorizontal
, paddingVertical
, paddingOnly
, paddingSymmetric
, paddingLeft
, paddingRight
, paddingTop
, paddingBottom
With exui
:
Text("Stretch me").expanded3
Without:
Expanded(
flex: 3,
child: Text("Stretch me"),
)
With additional extensions for quickly adding specific flex values: expanded2
, expanded3
, expanded4
, expanded5
, expanded6
, expanded7
, expanded8
or expandedFlex(int)
With exui
:
Icon(Icons.star).center()
Without:
Center(
child: Icon(Icons.star),
)
With additional extensions for quickly adding specific center factors: centerWidth(double)
, centerHeight(double)
or with parameters center({widthFactor, heightFactor})
With exui
:
Column(
children: [
Text("A"),
16.gapColumn,
Text("B"),
],
)
Without:
Column(
children: [
Text("A"),
SizedBox(height: 16),
Text("B"),
],
)
With additional extensions for quickly adding specific gap values: gapRow
, gapColumn
, gapVertical
, gapHorizontal
etc.
With exui
:
Text("Visible?").visibleIf(showText)
Without:
showText ? Text("Visible?") : const SizedBox.shrink()
With additional extensions for quickly adding specific visibility conditions: hide()
visibleIfNot(bool)
or visibleIfNull(bool)
and more.
With exui
:
Image.asset("logo.png").maxWidth(200)
Without:
ConstrainedBox(
constraints: BoxConstraints(maxWidth: 200),
child: Image.asset("logo.png"),
)
With additional extensions for quickly adding specific constraints: constrainedWidth
, constrainedHeight
, minWidth
, maxWidth
, minHeight
, maxHeight
or with parameters constrained({minWidth, maxWidth, minHeight, maxHeight})
or constrainedBox(BoxConstraints)
These are just a few of the 200+ utilities in exui
. Every method is chainable, production-safe, and built with clarity in mind.
From layout to constraints, visibility to spacing β
exui
keeps your UI code clean, fast, and Flutter-native.
Welcome to exui
, I hope it'll save you time like it did for me (:
Apply padding effortlessly with readable, chainable methods. These extensions wrap your widget in a Padding
widget using concise, expressive syntax.
padding(EdgeInsets)
β Use anyEdgeInsets
object directly.paddingAll(double)
β Uniform padding on all sides.paddingOnly({left, top, right, bottom})
β Custom padding per side.paddingSymmetric({horizontal, vertical})
β Padding for x/y axes.paddingHorizontal(double)
β Shorthand for horizontal-only padding.paddingVertical(double)
β Shorthand for vertical-only padding.- Per-side utilities:
paddingLeft(double)
paddingRight(double)
paddingTop(double)
paddingBottom(double)
All methods return a wrapped Padding
widget and can be freely chained with other extensions.
// 12px padding on all sides
MyWidget().paddingAll(12);
// 16px left and right, 8px top and bottom
MyWidget().paddingSymmetric(horizontal: 16, vertical: 8);
// 10px padding only on the left
MyWidget().paddingLeft(10);
// Custom per-side padding
MyWidget().paddingOnly(left: 8, top: 4, bottom: 12);
π‘ Why use this? Instead of writing:
Padding( padding: EdgeInsets.symmetric(horizontal: 16), child: MyWidget(), )Just write:
MyWidget().paddingHorizontal(16)
exui
Extensions
Effortlessly center any widget with precise control over layout behavior. These extensions wrap your widget in a Center
and offer intuitive access to widthFactor
and heightFactor
when needed.
center({widthFactor, heightFactor})
β Fully customizable centering.centerWidth(double?)
β Center with horizontal shrink-wrapping only.centerHeight(double?)
β Center with vertical shrink-wrapping only.
All methods return a Center
widget and can be seamlessly chained with other extensions.
// Center without size constraints
MyWidget().center();
// Center and shrink-wrap width only
MyWidget().centerWidth(1);
// Center and shrink-wrap height only
MyWidget().centerHeight(1);
// Fully customized centering
MyWidget().center(widthFactor: 0.8, heightFactor: 0.5);
π‘ Instead of writing:
Center( widthFactor: 1, child: MyWidget(), )Just write:
MyWidget().centerWidth(1)
exui
Extensions
Add layout flexibility with zero boilerplate. These extensions wrap your widget in an Expanded
, allowing you to quickly define how much space it should take relative to its siblings.
expandedFlex([int flex = 1])
β Wraps the widget inExpanded
with an optionalflex
.expanded1
β Shorthand forExpanded(flex: 1)
.
- Predefined shorthand getters for fixed flex values:
expanded2
,expanded3
,expanded4
,expanded5
,expanded6
,expanded7
,expanded8
Use them in Row
, Column
, or Flex
to control space distribution without nesting or repetition.
// Flex: 1 (default)
MyWidget().expanded1;
// Flex: 2
MyWidget().expanded2;
// Flex: 5
MyWidget().expandedFlex(5);
π‘ Instead of writing:
Expanded( flex: 3, child: MyWidget(), )Just write:
MyWidget().expanded3
exui
Extensions
Wrap any widget in a Flexible
with full control over flex
and fit
. These extensions reduce verbosity while giving you precise layout behavior in Row
, Column
, or Flex
widgets.
flex({int flex, FlexFit fit})
β Custom flex and fit values (default:flex: 1
,fit: FlexFit.loose
)flexLoose(int)
β Loose-fit shortcutflexTight(int)
β Tight-fit shortcut
- Predefined
flex
shortcuts (default fit:loose
):
flex2()
,flex3()
,flex4()
,flex5()
,flex6()
,flex7()
,flex8()
All methods return a Flexible
widget and are safe to chain with other layout or styling extensions.
// Default: flex 1, loose fit
MyWidget().flex();
// Predefined: flex 3, loose fit
MyWidget().flex3();
// Custom: flex 4, tight fit
MyWidget().flex(flex: 4, fit: FlexFit.tight);
// Loose-fit with custom flex
MyWidget().flexLoose(2);
// Tight-fit with custom flex
MyWidget().flexTight(6);
π‘ Instead of writing:
Flexible( flex: 3, fit: FlexFit.tight, child: MyWidget(), )Just write:
MyWidget().flexTight(3)
exui
Extensions
Align your widget anywhere inside its parent with expressive, chainable methods. These extensions wrap your widget in an Align
widget, giving you fine-grained control over its position.
align({ alignment, widthFactor, heightFactor })
β Custom alignment with optional size factors.alignCenter()
β Center of parent (default).- Top alignment:
alignTopLeft()
alignTopCenter()
alignTopRight()
- Center alignment:
alignCenterLeft()
alignCenterRight()
- Bottom alignment:
alignBottomLeft()
alignBottomCenter()
alignBottomRight()
All methods return an Align
widget and are safe to use inside any layout context.
// Center the widget (default)
MyWidget().alignCenter();
// Align to bottom-right
MyWidget().alignBottomRight();
// Top-left aligned with width factor
MyWidget().alignTopLeft(widthFactor: 2);
π‘ Instead of writing:
Align( alignment: Alignment.bottomRight, child: MyWidget(), )Just write:
MyWidget().alignBottomRight()
π§ When to use
positioned
vsaligned
Use.positioned()
inside aStack
when you need precise pixel placement (top
,left
, etc.).
Use.aligned()
when you want relative alignment (like centering) within any parent.
β οΈ .positioned()
only works inside aStack
..aligned()
works almost anywhere.
exui
Extensions
Easily wrap widgets with Positioned
using expressive, side-specific methods. These extensions let you declare layout constraints cleanly inside a Stack
without cluttering your build logic.
positioned({left, top, right, bottom, width, height})
β FullPositioned
wrapper with optional constraints.positionedTop(double)
β Position from top.positionedBottom(double)
β Position from bottom.positionedLeft(double)
β Position from left.positionedRight(double)
β Position from right.positionedWidth(double)
β Set width directly.positionedHeight(double)
β Set height directly.
All methods return a Positioned
widget and are designed to be composed fluently.
// Position 10px from top and left
MyWidget().positioned(top: 10, left: 10);
// Set only the width, auto-positioned
MyWidget().positionedWidth(200);
// Position from bottom with fixed height
MyWidget().positionedBottom(0).positionedHeight(60);
// Custom full placement
MyWidget().positioned(top: 12, right: 16, width: 150);
π‘ Instead of writing:
Positioned( top: 12, right: 16, width: 150, child: MyWidget(), )Just write:
MyWidget().positioned(top: 12, right: 16, width: 150)
π§ When to use
positioned
vsaligned
Use.positioned()
inside aStack
when you need precise pixel placement (top
,left
, etc.).
Use.aligned()
when you want relative alignment (like centering) within any parent.
β οΈ .positioned()
only works inside aStack
..aligned()
works almost anywhere.
exui
Extensions
Wrap widgets with IntrinsicWidth
or IntrinsicHeight
to size them based on their intrinsic (natural) dimensions. These extensions make it easy to apply intrinsic sizing with expressive, chainable syntax.
intrinsicHeight()
β Wraps inIntrinsicHeight
.intrinsicWidth()
β Wraps inIntrinsicWidth
with default options.intrinsicWidthWith({stepWidth, stepHeight})
β Custom step values.intrinsicWidthStepWidth(double)
β SetstepWidth
only.intrinsicWidthStepHeight(double)
β SetstepHeight
only.
All methods return intrinsic wrappers that measure content and size accordinglyβideal for fine-tuned layout control.
// Wrap with IntrinsicHeight
MyWidget().intrinsicHeight();
// Wrap with IntrinsicWidth (default)
MyWidget().intrinsicWidth();
// Set stepWidth to 100
MyWidget().intrinsicWidthStepWidth(100);
// Set both stepWidth and stepHeight
MyWidget().intrinsicWidthWith(
stepWidth: 80,
stepHeight: 40,
);
π‘ Instead of writing:
IntrinsicWidth( stepHeight: 40, child: MyWidget(), )Just write:
MyWidget().intrinsicWidthStepHeight(40)
β οΈ Use intrinsic widgets with care β they can be expensive to layout and should only be used when needed for dynamic content sizing.
exui
Extensions
Add clean, configurable margins around any widget with chainable extensions. These methods wrap the widget in a Container
with margin
, avoiding cluttered layout nesting and improving code clarity.
margin(EdgeInsets)
β Use anyEdgeInsets
object directly.marginAll(double)
β Equal margin on all sides.marginOnly({left, top, right, bottom})
β Custom margin per side.marginSymmetric({horizontal, vertical})
β Horizontal & vertical margin.marginHorizontal(double)
β Shorthand for horizontal-only margin.marginVertical(double)
β Shorthand for vertical-only margin.- One-sided margin helpers:
marginLeft(double)
marginRight(double)
marginTop(double)
marginBottom(double)
All methods return a wrapped Container
and can be freely chained with other extensions.
// 16px margin on all sides
"Card".text().marginAll(16);
// 24px horizontal, 12px vertical
"Tile".text().marginSymmetric(horizontal: 24, vertical: 12);
// 8px margin only on top
"Header".text().marginTop(8);
// Custom side-by-side margin
"Box".text().marginOnly(left: 6, bottom: 10);
π‘ Why use this? Instead of writing:
Container( margin: EdgeInsets.only(left: 8, top: 4), child: MyWidget(), )Just write:
MyWidget().marginOnly(left: 8, top: 4)
βοΈ Margin vs Padding
Usepadding
to add spacing inside a widget's boundary β like space around text in a button.
Usemargin
to add spacing outside a widget β like separating it from other widgets.π’ For most layout needs,
padding
is the preferred and safer default. Usemargin
when you need to push the widget away from surrounding elements, but be cautious with nesting to avoid layout issues.
exui
Extensions
Use doubles or ints to create SizedBox
widgets with clear, expressive syntax. These extensions turn raw numbers into layout spacingβperfect for columns, rows, and consistent vertical/horizontal gaps.
sizedWidth
βSizedBox(width: this)
sizedHeight
βSizedBox(height: this)
gapHorizontal
/gapRow
/gapWidth
β Aliases for horizontal spacinggapVertical
/gapColumn
/gapHeight
β Aliases for vertical spacing
All extensions return a SizedBox
and are ideal for use in layouts to avoid magic numbers and improve readability.
// Horizontal space of 16
16.gapHorizontal,
// Vertical space of 8
8.gapVertical,
// SizedBox with explicit width
2.5.sizedWidth,
// SizedBox with explicit height
32.sizedHeight,
// Clean Row layout
Row(
children: [
WidgetOne(),
12.gapRow,
WidgetTwo(),
],
)
// Clean Column layout
Column(
children: [
WidgetOne(),
16.gapColumn,
WidgetTwo(),
],
)
π‘ Use
.gapRow
and.gapColumn
when working insideRow
orColumn
widgets for clarity and intent-based naming.
exui
Extensions
Effortlessly create Row
and Column
layouts with readable, inline extensions. Whether you're working with a single widget or a whole list, these helpers make layout structure fast, chainable, and more expressive.
[].row()
/[].column()
β Wrap a list of widgets in aRow
orColumn
..row()
/.column()
β Wrap a single widget in aRow
orColumn
.
β Fully supports all
Row
andColumn
parameters, including:
spacing
,mainAxisAlignment
,crossAxisAlignment
, and more.
// Two widgets in a Row with spacing
[
WidgetOne(),
WidgetTwo(),
].row(spacing: 12);
// Vertical layout with alignment and spacing
[
WidgetOne(),
WidgetTwo(),
].column(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 8,
);
// Single widget inside a Row
MyWidget().row();
// Puts a single widget in a column with center alignment
MyWidget().column(
mainAxisAlignment: MainAxisAlignment.center,
);
π‘ Instead of writing:
Column( spacing: 8, children: [ WidgetOne(), WidgetTwo(), ], )Just write:
[ WidgetOne(), WidgetTwo(), ].column(spacing: 8)
exui
Extensions
Stop repeating alignment boilerplate in your Row
and Column
widgets. These expressive extensions let you instantly apply common combinations of mainAxisAlignment
and crossAxisAlignment
, all while preserving full layout control. They make UI creation dramatically faster and more readable, especially in complex layouts or dynamic widget lists.
All extensions are available for both Row
and Column
, following the same structure:
- β
columnMain*
β SetsmainAxisAlignment
, customize others - β
columnCross*
β SetscrossAxisAlignment
, customize others - β
column<Main><Cross>()
β Aligns both axes (e.g.,columnCenterStart
)
- β
rowMain*
β SetsmainAxisAlignment
, customize others - β
rowCross*
β SetscrossAxisAlignment
, customize others - β
row<Main><Cross>()
β Aligns both axes (e.g.,rowCenterStart
)
All methods accept all standard layout parameters, including:
mainAxisSize
textDirection
verticalDirection
textBaseline
spacing
π‘ Instead of writing:
Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [...], )Just write:
[...].columnCenterStart()
Or instead of:
Row( spacing: 12, mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.end, children: [...], )Just write:
[...].rowSpaceBetweenEnd(spacing: 12)
These shortcuts reduce boilerplate and keep your layout code highly consistent and declarativeβperfect for design systems, builder UIs, and everyday Flutter apps.
columnMainStart()
βMainAxisAlignment.start
columnMainCenter()
βMainAxisAlignment.center
columnMainEnd()
βMainAxisAlignment.end
columnMainSpaceAround()
βMainAxisAlignment.spaceAround
columnMainSpaceBetween()
βMainAxisAlignment.spaceBetween
columnMainSpaceEvenly()
βMainAxisAlignment.spaceEvenly
columnCrossStart()
βCrossAxisAlignment.start
columnCrossCenter()
βCrossAxisAlignment.center
columnCrossEnd()
βCrossAxisAlignment.end
columnCrossBaseline()
βCrossAxisAlignment.baseline
columnCrossStretch()
βCrossAxisAlignment.stretch
columnStartStart()
columnStartCenter()
columnStartEnd()
columnStartBaseline()
columnStartStretch()
columnCenterStart()
columnCenterCenter()
columnCenterEnd()
columnCenterBaseline()
columnCenterStretch()
columnEndStart()
columnEndCenter()
columnEndEnd()
columnEndBaseline()
columnEndStretch()
columnSpaceAroundStart()
columnSpaceAroundCenter()
columnSpaceAroundEnd()
columnSpaceAroundBaseline()
columnSpaceAroundStretch()
columnSpaceBetweenStart()
columnSpaceBetweenCenter()
columnSpaceBetweenEnd()
columnSpaceBetweenBaseline()
columnSpaceBetweenStretch()
columnSpaceEvenlyStart()
columnSpaceEvenlyCenter()
columnSpaceEvenlyEnd()
columnSpaceEvenlyBaseline()
columnSpaceEvenlyStretch()
// A vertically-centered column, aligned to start
[
Icons.image.icon(), // same as Icon(Icons.image)
Text("Profile"),
].columnCenterStart();
// Use main alignment only, keep full control of cross
[
Text("Hello"),
Text("World"),
].columnMainSpaceAround(crossAxisAlignment: CrossAxisAlignment.end);
// Use cross alignment only, keep full control of main
[
Text("Item 1"),
"Item 2".text(), // same as Text("Item 2")
].columnCrossStretch(mainAxisAlignment: MainAxisAlignment.end);
rowMainStart()
rowMainCenter()
rowMainEnd()
rowMainSpaceAround()
rowMainSpaceBetween()
rowMainSpaceEvenly()
rowCrossStart()
rowCrossCenter()
rowCrossEnd()
rowCrossBaseline()
rowCrossStretch()
rowStartStart()
rowStartCenter()
rowStartEnd()
rowStartBaseline()
rowStartStretch()
rowCenterStart()
rowCenterCenter()
rowCenterEnd()
rowCenterBaseline()
rowCenterStretch()
rowEndStart()
rowEndCenter()
rowEndEnd()
rowEndBaseline()
rowEndStretch()
rowSpaceAroundStart()
rowSpaceAroundCenter()
rowSpaceAroundEnd()
rowSpaceAroundBaseline()
rowSpaceAroundStretch()
rowSpaceBetweenStart()
rowSpaceBetweenCenter()
rowSpaceBetweenEnd()
rowSpaceBetweenBaseline()
rowSpaceBetweenStretch()
rowSpaceEvenlyStart()
rowSpaceEvenlyCenter()
rowSpaceEvenlyEnd()
rowSpaceEvenlyBaseline()
rowSpaceEvenlyStretch()
// Centered row, aligned to top
[
WidgetOne(),
WidgetTwo(),
].rowCenterStart();
// Horizontal space between, vertically stretched
[
WidgetOne(),
WidgetTwo(),
].rowSpaceBetweenStretch();
// Apply only main alignment, customize cross
[
WidgetOne(),
WidgetTwo(),
].rowMainEnd(crossAxisAlignment: CrossAxisAlignment.start);
// Apply only cross alignment, keep full control of main
[
WidgetOne(),
WidgetTwo(),
].rowCrossEnd(mainAxisAlignment: MainAxisAlignment.center);
π‘ These extensions make horizontal layout code extremely fast and clean β especially when building repetitive layouts across your app.
β Full support for layout parameters like
spacing
,mainAxisSize
,textDirection
, and more β no flexibility lost.
exui
Extensions
Build layered UI structures with intuitive, chainable extensions. These methods let you create Stack
layouts directly from a List<Widget>
and configure fit modes easily with named variants.
stack()
β Wrap a list in a standardStack
(StackFit.loose
by default).stackLoose()
β Equivalent toStackFit.loose
(default).stackExpand()
β Children fill the available space (StackFit.expand
).stackPassthrough()
β Children keep their intrinsic size (StackFit.passthrough
).
β
Fully supports all native Stack
parameters:
alignment
, textDirection
, fit
, and clipBehavior
.
// Default stack with loose fit
[
WidgetOne(),
WidgetTwo().positionedTop(0),
].stack();
// Stack with expanded fit
[
WidgetOne(),
WidgetTwo().positionedBottom(0),
].stackExpand();
// Stack with passthrough sizing
[
WidgetOne(),
WidgetTwo(),
].stackPassthrough();
// Stack with custom alignment and clip behavior
[
...someWidgets,
].stack(
alignment: Alignment.center,
clipBehavior: Clip.none,
);
// A single widget wrapped in a Stack
MyWidget().stack();
π‘ Instead of writing:
Stack( fit: StackFit.expand, alignment: Alignment.center, children: [ ... ], )Just write:
[...].stackExpand(alignment: Alignment.center)
exui
Extensions
Simplify visibility logic in your widget tree with expressive, chainable methods. These extensions replace repetitive ternary conditions and help keep your UI code declarative and clean.
visibleIf(bool)
β Show this widget only if the condition istrue
; otherwise returns an empty box.visibleIfNot(bool)
β Inverse ofvisibleIf
.visibleIfNull(Object?)
β Show this widget only if the given value isnull
.visibleIfNotNull(Object?)
β Show this widget only if the given value is notnull
.hide()
β Always returns an empty widget (SizedBox.shrink()
).invisible()
β Alias ofhide()
for readability.boxShrink()
β ReturnsSizedBox.shrink()
directly.
All methods return valid widgets and are safe to chain inside build methods.
MyWidget().visibleIf(isLoggedIn); // Show only if condition is true
MyWidget().visibleIfNot(isLoggedIn); // Show only if condition is false
MyWidget().visibleIfNull(user); // Show only if value is null
MyWidget().visibleIfNotNull(user); // Show only if value is not null
MyWidget().hide(); // Always hidden
MyWidget().invisible(); // Same as hide(), for clarity
final emptyBox = MyWidget().boxShrink(); // Returns an empty widget directly
π‘ Why use this? Instead of writing:
condition ? MyWidget() : const SizedBox.shrink()Just write:
MyWidget().visibleIf(condition)
π These methods never break layout structure β they return a valid widget in all cases and help you write safer conditional UI.
exui
Extensions
Quickly apply opacity to any widget using clean, expressive methods. These extensions eliminate the need to wrap widgets manually in Opacity
and support percentage-based and common preset values.
opacity(double)
β Set widget transparency using a0.0β1.0
value.opacityPercent(double)
β Use percentage (0β100
) for readability.opacityHalf()
β Set opacity to 50%.opacityQuarter()
β Set opacity to 25%.opacityZero()
β Set opacity to 0.opacityTransparent()
β Alias ofopacityZero()
.opacityInvisible()
β Alias ofopacityZero()
.
All methods return a wrapped Opacity
widget and are safe to chain with other extensions.
Set to 70%
visible
MyWidget().opacity(0.7);
Set to 40%
using percent
MyWidget().opacityPercent(40);
MyWidget().opacityHalf(); // Half visible (0.5)
MyWidget().opacityQuarter(); // Quarter visible (0.25)
π‘ Instead of writing:
Opacity( opacity: value, child: MyWidget(), )Just write:
MyWidget().opacity(value)Use
.opacityPercent()
when working with designer specs or to make your code more intuitive at a glance.
exui
Extensions
Easily wrap widgets in SafeArea
using expressive, chainable extensions. These methods let you control which sides are paddedβwithout nesting or verbose boilerplate.
safeArea({left, top, right, bottom, minimum, maintainBottomViewPadding})
β Full control over all sides and padding behavior.safeAreaAll()
β Padding on all sides.safeAreaNone()
β No padding at all.safeAreaOnlyTop()
/safeAreaOnlyBottom()
β Top or bottom only.safeAreaOnlyLeft()
/safeAreaOnlyRight()
β Left or right only.safeAreaOnlyHorizontal()
β Left + right.safeAreaOnlyVertical()
β Top + bottom.
All methods return a SafeArea
widget wrapping the original widget.
MyWidget().safeArea(); // Same as wrap in SafeArea;
// Only top padded (e.g., below status bar)
MyWidget().safeAreaOnlyTop();
// Bottom safe area only (e.g., above iPhone home indicator)
MyWidget().safeAreaOnlyBottom();
// Custom safe area: only horizontal
MyWidget().safeAreaOnlyHorizontal();
// No SafeArea applied
MyWidget().safeAreaNone();
π‘ Instead of writing:
SafeArea( child: MyWidget(), )Just write:
MyWidget().safeArea()
exui
Extensions
Eliminate manual GestureDetector
nesting with intuitive, chainable gesture methods. These extensions make it effortless to attach any gesture to any widget.
onTap(VoidCallback)
β Handle basic tap gestures.onDoubleTap(VoidCallback)
β Respond to double-tap gestures.onLongPress(VoidCallback)
β Handle long presses.onTapDown(...)
,onTapUp(...)
,onTapCancel(...)
β Full tap phase handling.onSecondaryTap(...)
,onTertiaryTapDown(...)
, etc. β Full multi-touch support.onVerticalDrag...
,onHorizontalDrag...
,onPan...
β Add drag gestures with full phase support.onScale...
β Handle pinch-to-zoom gestures.onForcePress...
β Support for pressure-sensitive gestures.gestureDetector(...)
β Attach multiple gestures at once in one call.
All methods return a wrapped GestureDetector
and support optional customization of behavior, semantics, and supported devices.
// Basic tap interaction
MyWidget().onTap(() => print("Tapped!"));
// Double tap
MyWidget().onDoubleTap(() => print("Double tapped"));
// Handle tap down position
MyWidget().onTapDown((details) {
print("Tap down at ${details.globalPosition}");
});
// Add vertical drag support
MyWidget().onVerticalDragUpdate((details) {
print("Dragging: ${details.delta.dy}");
});
// Combine multiple gestures
MyWidget().gestureDetector(
onTap: () => print("Tap"),
onLongPress: () => print("Long press"),
onPanUpdate: (details) => print("Panning"),
);
π‘ Instead of writing:
GestureDetector( onTap: () => doSomething(), child: MyWidget(), )Just write:
MyWidget().onTap(() => doSomething())
exui
Extensions
Effortlessly wrap any widget in a Hero
for smooth page-to-page transitions. Customize behavior with optional parameters for animations, flight behavior, and placeholders.
hero(String tag)
β Wraps the widget in aHero
with the given tag.- Optional parameters:
createRectTween(...)
β Customize the transition animation path.flightShuttleBuilder(...)
β Override the animation widget during flight.placeholderBuilder(...)
β Placeholder shown during transition loading.transitionOnUserGestures
β Allow gesture-driven transitions.
All options mirror the native Hero
widget and can be configured inline.
// Basic shared element transition
MyWidget().hero("profile-avatar");
// Custom placeholder
MyWidget()
.hero(
"title-hero",
placeholderBuilder: (context, size, child) =>
SizedBox.fromSize(size: size),
);
// With custom flight behavior
MyWidget().hero(
"star-icon",
flightShuttleBuilder: (context, animation, direction, from, to) {
return ScaleTransition(scale: animation, child: to.widget);
},
);
π‘ Instead of writing:
Hero( tag: "avatar", child: Image.asset("avatar.png"), )Just write:
Image.asset("avatar.png").hero("avatar")
exui
Extensions
Quickly wrap any widget in a SizedBox
with a specified width, height, or both. These extensions improve readability and reduce boilerplate when sizing widgets inline.
sizedBox({width, height})
β Wrap with custom width and/or height.sizedWidth(double)
β Set only the width.sizedHeight(double)
β Set only the height.
All methods return a SizedBox
with your widget as the child, and are safe to chain.
// Fixed width and height
MyWidget().sizedBox(width: 120, height: 40);
// Only width
MyWidget().sizedWidth(200);
// Only height
MyWidget().sizedHeight(60);
π‘ Instead of writing:
SizedBox(width: 120, height: 40, child: MyWidget())Just write:
MyWidget().sizedBox(width: 120, height: 40)
exui
Extensions
Decorate any widget with rich visuals using a clean, expressive API. These extensions wrap your widget in a DecoratedBox
with common presets like gradients, shadows, images, borders, and shapes β no boilerplate required.
decoratedBox(...)
β Core method using anyDecoration
.decoratedBoxDecoration(...)
β ConfigureBoxDecoration
inline with full control.imageBox(...)
β Add a background image with allDecorationImage
options.gradientBox(Gradient)
β Add a linear, radial, or sweep gradient.shadowedBox(...)
β Apply shadow effects with offset, blur, spread.borderedBox(...)
β Add a border (and optional border radius).shapedBox(BoxShape)
β Change the shape (e.g., circle, rectangle).circularBorderBox(...)
β Draw a colored, circular border with width and radius.
All methods return a DecoratedBox
and can be safely combined with padding, opacity, or gesture extensions.
// Apply a gradient background
MyWidget().gradientBox(
LinearGradient(colors: [Colors.purple, Colors.blue]),
);
// Add a background image
MyWidget().imageBox(
image: NetworkImage("https://example.com/image.png"),
fit: BoxFit.cover,
);
// Add a shadow
MyWidget().shadowedBox(
offset: Offset(2, 2),
blurRadius: 6,
);
// Circular border
MyWidget().circularBorderBox(
radius: 12,
color: Colors.red,
width: 2,
);
// Full decorated box manually
MyWidget().decoratedBoxDecoration(
color: Colors.grey.shade100,
border: Border.all(color: Colors.black26),
borderRadius: BorderRadius.circular(8),
);
π‘ Instead of wrapping your widget manually like this:
DecoratedBox( decoration: BoxDecoration( color: Colors.red, borderRadius: BorderRadius.circular(12), ), child: MyWidget(), )You can write:
MyWidget().decoratedBoxDecoration( color: Colors.red, borderRadius: BorderRadius.circular(12), )
exui
Extensions
Apply size constraints directly to any widget without manually wrapping in ConstrainedBox
. These extensions make layout constraints clean, readable, and fully chainable.
constrained({minWidth, maxWidth, minHeight, maxHeight})
β General constraint utility.constrainedBox(BoxConstraints)
β Use customBoxConstraints
directly.constrainedWidth({min, max})
β Horizontal size limits.constrainedHeight({min, max})
β Vertical size limits.minWidth(double)
/maxWidth(double)
β Individual width constraints.minHeight(double)
/maxHeight(double)
β Individual height constraints.
All methods return a ConstrainedBox
and are safe to chain in layout compositions.
// Limit to a width between 100β200
MyWidget().constrainedWidth(min: 100, max: 200);
// Limit to a height between 50β100
MyWidget().constrainedHeight(min: 50, max: 100);
// Only limit max height
MyWidget().maxHeight(120);
// Fully custom constraints
MyWidget().constrained(
minWidth: 80,
maxWidth: 150,
minHeight: 40,
maxHeight: 90,
);
// Using BoxConstraints directly
MyWidget().constrainedBox(
constraints: BoxConstraints.tightFor(width: 100, height: 40),
);
π‘ Instead of writing:
ConstrainedBox( constraints: BoxConstraints( minWidth: 100, maxWidth: 200, ), child: MyWidget(), )Just write:
MyWidget().constrainedWidth(min: 100, max: 200)
exui
Extensions
Wrap any widget with a solid background using ColoredBox
, without nesting manually. This extension is a clean and efficient way to apply color without additional containers.
coloredBox(Color)
β Wraps the widget in aColoredBox
with the given background color.
Use this to apply color styling in layout compositions without using Container
, keeping your UI lightweight.
// Red background
MyWidget().coloredBox(Colors.red);
π‘ Why use this? Instead of writing:
ColoredBox( color: Colors.blue, child: MyWidget(), )Just write:
MyWidget().coloredBox(Colors.blue)
β A minimal, performant way to color backgrounds without unnecessary overhead.
exui
Extensions
Apply colorful backgrounds to any widget using expressive .colorBox()
helpers. These extensions wrap your widget in a ColoredBox
with a specific Color
from the Material palette.
No need to write Container
or manage BoxDecoration
βjust add color directly to any widget in one line.
All extensions return a ColoredBox
.
redBox()
/redAccentBox()
greenBox()
/greenAccentBox()
blueBox()
/blueAccentBox()
yellowBox()
/yellowAccentBox()
orangeBox()
/orangeAccentBox()
purpleBox()
/purpleAccentBox()
deepPurpleBox()
/deepPurpleAccentBox()
pinkBox()
/pinkAccentBox()
brownBox()
tealBox()
/tealAccentBox()
cyanBox()
/cyanAccentBox()
lightBlueBox()
/lightBlueAccentBox()
lightGreenBox()
/lightGreenAccentBox()
limeBox()
/limeAccentBox()
greyBox()
blackBox()
whiteBox()
// Add a red background
MyWidget().redBox();
// Success message with green accent
MyWidget().greenAccentBox();
// Stylized button background
MyWidget().blueBox().paddingAll(12);
π‘ Instead of writing:
ColoredBox( color: Colors.red, child: MyWidget(), )Just write:
MyWidget().redBox()
exui
Extensions
Quickly apply Cupertino-style background colors to any widget using expressive, pre-named methods. These extensions wrap your widget in a ColoredBox
using native CupertinoColors
.
redBox()
/destructiveRedBox()
greenBox()
/activeGreenBox()
blueBox()
/activeBlueBox()
orangeBox()
/orangeAccentBox()
yellowBox()
/purpleBox()
/pinkBox()
tealBox()
/cyanBox()
/brownBox()
greyBox()
/blackBox()
/whiteBox()
darkGrayBox()
/lightGrayBox()
All methods return a ColoredBox
using system-consistent Cupertino color values, ideal for quick prototyping and iOS-native look and feel.
// Apply iOS system red background
MyWidget().redBox();
// Use active Cupertino blue
MyWidget().activeBlueBox();
// Style with light gray background
MyWidget().lightGrayBox();
// Warning or alert color
MyWidget().orangeAccentBox();
π‘ Instead of writing:
ColoredBox( color: CupertinoColors.systemTeal, child: MyWidget(), )Just write:
MyWidget().tealBox()
exui
Extensions
Wrap your widget with a FittedBox
to control how it resizes within its parent. These extensions provide clean, expressive access to BoxFit
options β without the boilerplate.
fittedBox({fit, alignment, clipBehavior})
β Base method with full control.fitContain()
β Preserves aspect ratio, fits within bounds.fitCover()
β Fills bounds, possibly cropping.fitFill()
β Stretches to fill bounds, ignoring aspect ratio.fitScaleDown()
β Only scales down, never up.fitHeight()
β Scales to match parent height.
All methods return a FittedBox
and preserve your widget tree cleanly.
// Scale to fit within constraints
MyWidget().fitContain();
// Fill the available space
MyWidget().fitCover();
// Scale down only if too large
MyWidget().fitScaleDown();
// Stretch to fill all dimensions
MyWidget().fitFill();
π‘ Instead of writing:
FittedBox( fit: BoxFit.cover, child: MyWidget(), )Just write:
MyWidget().fitCover()
exui
Extensions
Easily apply layout, styling, and decoration to any widget by wrapping it in a Container
βwithout cluttering your code. This extension saves you from manual nesting while exposing all the powerful layout features of Container
.
container({...})
β Adds padding, margin, size, decoration, alignment, and more in a single call.
Supports all Container
options:
width
,height
β Size constraintscolor
,decoration
β Background and border stylingpadding
,margin
β Inner and outer spacingalignment
β Align child within the containerclipBehavior
,constraints
β Additional layout control
// Wrap with background color and padding
MyWidget().container(
color: const Color(0xFFE0E0E0),
padding: const EdgeInsets.all(12),
);
// Add margin and align center
MyWidget().container(
margin: const EdgeInsets.symmetric(vertical: 16),
alignment: Alignment.center,
);
// Fully customized container
MyWidget().container(
width: 150,
height: 80,
decoration: BoxDecoration(
color: const Color(0xFF2196F3),
borderRadius: BorderRadius.circular(12),
),
alignment: Alignment.center,
);
π‘ Instead of writing:
Container( padding: EdgeInsets.all(12), color: Colors.grey, child: MyWidget(), )Just write:
MyWidget().container(padding: EdgeInsets.all(12), color: Colors.grey)
exui
Extensions
Easily apply clipping to any widget using expressive, chainable methods. These extensions eliminate boilerplate when working with ClipPath
, ClipRRect
, ClipOval
, and even advanced shapes like squircles.
clipRRect({borderRadius})
β Clip with rounded corners usingClipRRect
.clipCircular([radius])
β Clip into a circle.clipOval()
β Clip into an oval or circle.clipCircle()
β Alias forclipOval()
(semantic clarity).clipPath({clipper})
β General-purpose custom path clipping.clipSquircle([radiusFactor])
β Clip into a modern "squircle" shape.clipContinuousRectangle([radiusFactor])
β Alias forclipSquircle()
.
All methods return wrapped Clip*
widgets and are safe to chain.
// Rounded corners (16 radius)
Container().clipRRect(borderRadius: BorderRadius.circular(16));
// Circular/oval clip
Image.asset("avatar.png").clipCircle();
// Custom path clip (e.g. star shape)
MyWidget().clipPath(clipper: StarClipper());
// Squircle shape (iOS-style corners)
MyWidget().clipSquircle(2.5);
// Same as above but with alternative naming
MyWidget().clipContinuousRectangle(2.5);
π‘ Instead of writing:
ClipRRect( borderRadius: BorderRadius.circular(12), child: MyWidget(), )Just write:
MyWidget().clipRRect(borderRadius: BorderRadius.circular(12))
exui
Extensions
Effortlessly convert a String
into a fully configurable Text
widget. The .text()
and .styledText()
extensions make your UI code clean, readable, and expressive β no more boilerplate, no more clutter.
.text({...})
β Create aText
widget with any nativeText
constructor parameters..styledText({...})
β Configure fullTextStyle
in one place: font, color, spacing, shadows, decoration, and more.
Both methods return a standard Flutter Text
widget β no wrappers, no magic.
"Hello world".text(); // same as Text("Hello world");
Text("Hello world"); // same as "Hello world".text();
"Hello exui".text(
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
);
Text(
"Hello exui",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
);
"Stylish!".styledText(
fontSize: 24,
fontWeight: FontWeight.w600,
color: Colors.purple,
);
Text(
"Stylish!",
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w600,
color: Colors.purple,
),
);
exui
Extensions
Powerfully enhance your Text
widgets with dozens of chainable extensions. Control alignment, overflow, semantics, and apply fine-grained stylingβwithout verbose TextStyle
blocks.
These extensions are non-intrusive, composable, and maintain the immutability of the original widget.
textAlign(...)
textDirection(...)
locale(...)
softWrap(...)
overflow(...)
maxLines(...)
semanticsLabel(...)
widthBasis(...)
heightBehavior(...)
selectionColor(...)
strutStyle(...)
textScaler(...)
Apply full or partial TextStyle
changes with expressive one-liners:
fontSize(...)
fontWeight(...)
fontStyle(...)
letterSpacing(...)
wordSpacing(...)
height(...)
foreground(...)
/background(...)
shadows(...)
fontFeatures(...)
/fontVariations(...)
decoration(...)
decorationColor(...)
decorationStyle(...)
decorationThickness(...)
fontFamily(...)
/fontFamilyFallback(...)
leadingDistribution(...)
debugLabel(...)
Use simple methods for common typography tasks:
bold()
italic()
underline()
strikethrough()
boldItalic()
boldUnderline()
boldStrikethrough()
"Hello World"
.text()
.fontSize(20)
.boldItalic()
.textAlign(TextAlign.center)
.maxLines(2)
.overflow(TextOverflow.ellipsis);
Or, apply full styling in one go:
"Sale!"
.text()
.styled(
fontSize: 24,
fontWeight: FontWeight.w900,
decoration: TextDecoration.lineThrough,
color: Colors.red,
);
exui
Extensions
Easily create and customize Icon
widgets from an IconData
, or update existing Icon
instances with expressive, chainable methods. These extensions support all styling parameters available on Flutter's Icon
.
icon({...})
β Create anIcon
fromIconData
with full styling options.iconSized(double)
β Shorthand for setting size.iconFilled(double)
β Set the fill level (for Material symbols).iconColored(Color)
β Apply color.
.sized(double)
β Change icon size..filled(double)
β Set fill level..weight(double)
/.grade(double)
/.opticalSize(double)
β Fine-tune variable font icons..colored(Color)
β Change icon color..shadowed(List<Shadow>)
β Add text-style shadows..semanticLabeled(String)
β Set semantic label for accessibility..textDirectioned(TextDirection)
β Set directionality..applyTextScaling(bool)
β Respect or ignore text scaling..blendMode(BlendMode)
β Control blend behavior.
All methods return a new Icon
and preserve other properties unless overwritten.
Icons.settings.icon(size: 32, color: Colors.amber,);
// Create a red icon at 24px
Icons.home.iconSized(24).colored(Colors.red);
// Create and fill a Material symbol icon
Icons.favorite.iconFilled(1.0);
// Chain multiple style changes
Icons.star.icon().sized(32).filled(0.8).colored(Colors.amber);
π‘ Instead of:
Icon( Icons.star, size: 32, color: Colors.amber, fill: 0.8, )Just write:
Icons.star.icon(size: 32, color: Colors.amber, fill: 0.8)
exui
Extensions
Transform any widget into a fully styled CupertinoButton
, with fluent syntax and optional variants for filled or tinted styles. These extensions reduce boilerplate while preserving full configurability.
cupertinoButton(...)
β Standard iOS-style button.cupertinoFilledButton(...)
β Filled style, best for emphasis.cupertinoTintedButton(...)
β Tinted (outlined) style with subtle color emphasis.
Each method returns a CupertinoButton
and supports all common options, including pressedOpacity
, autofocus
, borderRadius
, and onLongPress
.
// Basic Cupertino button
Text("Continue").cupertinoButton(onPressed: () {});
// Filled iOS-style button
Text("Submit").cupertinoFilledButton(onPressed: () {});
// Tinted iOS-style button with border and color
Text("Retry").cupertinoTintedButton(
color: CupertinoColors.systemRed,
onPressed: () {},
);
π‘ Instead of writing:
CupertinoButton( child: Text("Continue"), onPressed: () {}, )Just write:
"Continue".text().cupertinoButton(onPressed: () {})
exui
Extensions
Wrap any widget in a Material Design button with a single method. These extensions let you create ElevatedButton
, FilledButton
, OutlinedButton
, and TextButton
variants with or without iconsβwithout boilerplate.
elevatedButton({onPressed})
elevatedIconButton({onPressed, icon})
filledButton({onPressed})
filledTonalButton({onPressed})
filledIconButton({onPressed, icon})
filledTonalIconButton({onPressed, icon})
outlinedButton({onPressed})
outlinedIconButton({onPressed, icon})
textButton({onPressed})
textIconButton({onPressed, icon})
Each method supports full customization via Flutter's native parameters:
style
, focusNode
, clipBehavior
, onHover
, autofocus
, statesController
, and more.
// Basic elevated button
Text("Submit").elevatedButton(onPressed: () => print("Tapped"))
// Filled button with icon
Text("Send").filledIconButton(
onPressed: () => print("Sent"),
icon: const Icon(Icons.send),
)
// Text button, semantic only
Text("Cancel").textButton(onPressed: () => print("Canceled"))
// Outlined button with icon and custom style
Text("Info").outlinedIconButton(
onPressed: () {},
icon: const Icon(Icons.info_outline),
style: OutlinedButton.styleFrom(
foregroundColor: Colors.blue,
),
)
π‘ Instead of writing:
ElevatedButton( onPressed: () {}, child: Text("Confirm"), )Just write:
"Confirm".text().elevatedButton(onPressed: () {})
exui
Extensions