flumpose 0.0.2
flumpose: ^0.0.2 copied to clipboard
A Flutter package for declarative, const-safe UI composition.
Flumpose #
Flumpose is a Flutter package that brings declarative, chainable widget composition to your UI code. Say goodbye to deeply nested widget trees and hello to clean, readable, and maintainable Flutter UI.
Transform verbose code like this:
Container(
color: Colors.blue,
child: Padding(
padding: EdgeInsets.all(16),
child: Align(
alignment: Alignment.center,
child: Text('Hello World'),
),
),
)
Into this:
Text('Hello World')
.pad(16)
.backgroundColor(Colors.blue)
.alignCenter()
β¨ Features #
Core Extensions #
- π Chainable Extensions: Fluent API for widget composition
- π¨ Background & Decoration: Colors, gradients, images, and custom decorations
- π Layout Control: Padding, margin, alignment, sizing, and constraints
- π Transform & Clip: Rotate, scale, translate, and clip with ease
- βοΈ Text Styling: Font size, color, weight, and style helpers
- βΏ Accessibility: Semantic label extensions for better a11y
- π¬ Animations: Fade and other animation helpers
- π§ Parent Wrapping: Custom parent widget injection
New Feature Extensions #
- ποΈ Visibility Control: Show/hide, opacity, and pointer events
- π Flex Layouts: Expanded, flexible, and fractional sizing
- π Stack & Positioning: Absolute positioning and layering
- π± Responsive Design: Breakpoints, adaptive sizing, and screen-aware helpers
- π Advanced Gestures: Pan, drag, scale, and draggable widgets
- π‘οΈ Utilities: SafeArea, Hero, Material, Card, Sliver helpers, and more
π¦ Installation #
Add flumpose to your pubspec.yaml:
dependencies:
flumpose: ^0.0.1
Then run:
flutter pub get
π Quick Start #
Import the package:
import 'package:flumpose/flumpose.dart';
Now you can chain extensions on any widget:
import 'package:flutter/material.dart';
import 'package:flumpose/flumpose.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text('Hello, Flumpose!')
.bold()
.fontSize(24)
.color(Colors.white)
.pad(20)
.backgroundColor(Colors.blue)
.borderRadius(BorderRadius.circular(12))
.onTap(() => print('Tapped!'))
.alignCenter();
}
}
π Usage Examples #
Layout & Spacing #
// Padding
Text('Padded').pad(16) // All sides
Text('Padded').pad(EdgeInsets.symmetric(horizontal: 20))
// Margin
Container().margin(12)
Container().margin(EdgeInsets.only(top: 8))
// Alignment
Text('Centered').alignCenter()
Text('Top Left').alignTopLeft()
Text('Bottom Right').alignBottomRight()
Text('Custom').align(Alignment.topCenter)
// Sizing
Container().width(200).height(100)
Text('Constrained').constrained(BoxConstraints(maxWidth: 300))
// Scrollable
Column(children: [...]).scrollable()
Row(children: [...]).scrollable(axis: Axis.horizontal)
Background & Decoration #
// Solid color background
Text('Blue BG').backgroundColor(Colors.blue)
// Linear gradient
Container()
.backgroundLinearGradient(
colors: [Colors.purple, Colors.blue],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
)
// Radial gradient
Container()
.backgroundRadialGradient(
colors: [Colors.yellow, Colors.orange],
radius: 0.8,
)
// Background image
Container()
.backgroundImage(
NetworkImage('https://example.com/image.png'),
fit: BoxFit.cover,
)
// Custom decoration
Container().decorated(
BoxDecoration(
color: Colors.white,
boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 8)],
),
)
Borders & Clipping #
// Border
Container()
.border(Border.all(color: Colors.red, width: 2))
// Border radius with clip
Container()
.borderRadius(BorderRadius.circular(16))
// Clip rounded rectangle
Image.network('url').clipRRect(BorderRadius.circular(12))
// Clip oval
Image.network('url').clipOval()
// Box shadow
Container().boxShadow(
color: Colors.black26,
blurRadius: 10,
offset: Offset(0, 4),
)
// Elevation (Material)
Container().elevation(8, color: Colors.white, borderRadius: BorderRadius.circular(8))
Transform & Animation #
// Rotate (angle in radians)
Icon(Icons.refresh).rotate(0.5)
// Scale
Container().scaleWidget(1.5)
// Translate
Text('Shifted').translate(x: 10, y: 20)
// Custom transform
Container().transform(Matrix4.skewX(0.2))
// Fade animation
Container().fade(duration: Duration(milliseconds: 500))
Gestures & Interaction #
// Simple tap
Text('Click me').onTap(() => print('Tapped'))
// Double tap
Text('Double click').onDoubleTap(() => print('Double tapped'))
// Long press
Text('Hold me').onLongPress(() => print('Long pressed'))
// Material ripple effect
Container()
.ripple(
() => print('Ripple tapped'),
borderRadius: BorderRadius.circular(8),
)
// Multiple gestures
Container().gestures(
onTap: () => print('Tap'),
onLongPress: () => print('Long press'),
)
Text Styling #
Text('Styled Text')
.fontSize(20)
.color(Colors.blue)
.bold()
.italic()
// Chain multiple styles
Text('Hello')
.fontSize(18)
.textColor(Colors.red)
.bold()
Visibility Control #
// Conditional visibility
Text('Visible').visible(true)
Text('Hidden').hide()
Text('Shown').show()
Text('Conditional').showIf(userLoggedIn)
// Opacity control
Container().opacity(0.5)
Text('Semi-transparent').semiTransparent() // 50% opacity
Image.asset('logo.png').mostlyTransparent() // 25% opacity
// Pointer events
Container().ignorePointer() // Non-interactive
Container().absorbPointer() // Blocks events from widgets behind
Flex & Responsive Layouts #
// Expanded and Flexible
Container().expanded() // flex: 1
Container().expanded(flex: 2)
Container().flexible(flex: 2, fit: FlexFit.loose)
// Fractional sizing
Container().fractionalSize(widthFactor: 0.5, heightFactor: 0.8)
// Aspect ratios
Image.network('photo.jpg').square() // 1:1
Video().aspect16x9() // 16:9 widescreen
Image.asset('portrait.jpg').aspect4x3() // 4:3
// Fitted boxes
Image.network('large.jpg').fitContain() // Fit within bounds
Container().fitCover() // Cover entire area
Widget().fitScaleDown() // Scale down only
Stack & Positioning #
// Positioned within Stack
Stack(
children: [
Background(),
Overlay().positionedTopRight(top: 16, right: 16),
Logo().positionedBottomLeft(bottom: 20, left: 20),
Badge().positioned(left: 100, top: 50),
FullscreenOverlay().positionedFill(),
],
)
// Stack builders
Container(color: Colors.blue).stack(
children: [
Icon(Icons.star).positionedTopRight(),
Text('Overlay').positionedCenter(),
],
)
// Layer helpers
Background().withOverlay(Foreground())
Logo().onTopOf(BackgroundImage())
// IndexedStack (for tabs)
[HomePage(), ProfilePage(), SettingsPage()]
.indexedStack(index: currentTabIndex)
Responsive Design #
// Breakpoint-based visibility
Widget().onlyMobile() // Shows only on mobile (<600dp)
Widget().onlyTablet() // Shows only on tablet (600-900dp)
Widget().onlyDesktop() // Shows only on desktop (>=900dp)
// Adaptive sizing
Container().adaptiveSize(
mobile: 100,
tablet: 150,
desktop: 200,
)
// Screen scaling
Logo().scaleWithScreen(
baseWidth: 375,
maxScale: 1.5,
minScale: 0.8,
)
// Constrained layouts
Content().maxWidthBox(600) // Max 600dp width, centered
// Responsive padding
Container().responsivePadding(
mobilePadding: 16,
tabletPadding: 32,
desktopPadding: 64,
)
// BuildContext helpers
Builder(builder: (context) {
final isMobile = context.isMobile;
final screenWidth = context.screenWidth;
final fontSize = context.responsiveValue(
mobile: 14.0,
tablet: 16.0,
desktop: 18.0,
);
return Text('Responsive').fontSize(fontSize);
})
Advanced Gestures #
// Pan gestures
Container().onPan(
onPanStart: (details) => print('Pan started'),
onPanUpdate: (details) => print('Panning: ${details.delta}'),
onPanEnd: (details) => print('Pan ended'),
)
// Drag gestures
Container().onHorizontalDrag(
onUpdate: (details) => print('Dragging X: ${details.delta.dx}'),
)
Container().onVerticalDrag(
onUpdate: (details) => print('Dragging Y: ${details.delta.dy}'),
)
// Scale gestures (pinch to zoom)
Image.asset('photo.jpg').onScale(
onScaleUpdate: (details) {
print('Scale: ${details.scale}');
print('Rotation: ${details.rotation}');
},
)
// Draggable
Container().draggable<String>(
data: 'item-id',
feedback: Container(color: Colors.blue.withOpacity(0.5)),
onDragStarted: () => print('Drag started'),
)
// Tap with position
Widget().onTapWithPosition(
onTapDown: (details) {
print('Tapped at: ${details.localPosition}');
},
)
Utilities #
// SafeArea
Content().safeArea() // All edges
Header().safeTop() // Top only (status bar)
NavBar().safeBottom() // Bottom only (nav bar)
Content().safeHorizontal() // Left and right only
// Hero animations
Image.network('photo.jpg').hero(tag: 'photo-hero')
// Material wrappers
Widget().material(color: Colors.white, elevation: 4)
Content().card(elevation: 2, margin: EdgeInsets.all(8))
// Slivers
Container().toSliverBox() // SliverToBoxAdapter
[Widget1(), Widget2()].toSliverList()
[Item1(), Item2()].toSliverGrid(crossAxisCount: 2)
// Scaffold
Content().scaffold(
appBar: AppBar(title: Text('Title')),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
),
)
// Keyboard dismissal
Form().dismissKeyboard() // Tap anywhere to dismiss
// Rotation
Icon(Icons.arrow_forward).rotatedBox(1) // 90Β° clockwise
Accessibility #
// Add semantic label
Icon(Icons.home)
.semanticsLabel('Home button')
// Exclude child semantics
Container()
.semanticsLabel('Custom label', excludeSemantics: true)
Custom Parent Wrapping #
// Wrap with custom parent
Text('Child').parent((child) =>
Card(child: Padding(padding: EdgeInsets.all(8), child: child))
)
π API Reference #
Layout Extensions #
pad(dynamic)- Add padding (double or EdgeInsets)margin(dynamic)- Add margin (double or EdgeInsets)alignCenter(),alignTopLeft(),alignBottomRight()- Quick alignmentsalign(Alignment)- Custom alignmentwidth(double),height(double)- Set dimensionsconstrained(BoxConstraints)- Apply constraintsscrollable({Axis, ScrollController})- Make scrollableoverflow({Clip})- Clip overflow
Background Extensions #
backgroundColor(Color)- Solid color backgroundbackgroundGradient(Gradient)- Custom gradientbackgroundLinearGradient({colors, begin, end, ...})- Linear gradientbackgroundRadialGradient({colors, center, radius, ...})- Radial gradientbackgroundImage(ImageProvider, {fit, alignment})- Background imagedecorated(BoxDecoration)- Custom decoration
Border & Clip Extensions #
border(Border)- Add borderborderRadius(BorderRadius)- Rounded corners with clipclipRRect(BorderRadius)- Clip as rounded rectangleclipOval()- Clip as ovalelevation(double, {color, borderRadius, ...})- Material elevationboxShadow({color, blurRadius, offset})- Box shadow
Transform Extensions #
rotate(double, {alignment})- Rotate (radians)scaleWidget(double, {alignment})- Scaletranslate({x, y})- Translatetransform(Matrix4, {alignment})- Custom transform
Gesture Extensions #
onTap(VoidCallback)- Tap handleronDoubleTap(VoidCallback)- Double tap handleronLongPress(VoidCallback)- Long press handlerripple(VoidCallback?, {borderRadius})- Material ripplegestures({onTap, onLongPress})- Multiple gestures
Text Extensions #
fontSize(double)- Set font sizecolor(Color)- Set text colortextColor(Color)- Set text color (alias)bold()- Make text bolditalic()- Make text italic
Animation Extensions #
fade({duration})- Animated opacity
Semantics Extensions #
semanticsLabel(String, {excludeSemantics})- Add semantic label
Parent Extensions #
parent(Widget Function(Widget))- Wrap with custom parent
Visibility Extensions #
visible(bool)- Control visibilityhide()- Hide widget (invisible)show()- Show widget (visible)showIf(bool)- Conditional visibilityopacity(double)- Set opacitysemiTransparent()- 50% opacitymostlyTransparent()- 25% opacityignorePointer({ignoring})- Ignore pointer eventsabsorbPointer({absorbing})- Absorb pointer events
Flex & Layout Extensions #
expanded({flex})- Wrap in Expandedflexible({flex, fit})- Wrap in FlexiblefractionalSize({widthFactor, heightFactor})- Fractional sizingaspectRatio(double)- Set aspect ratiosquare()- 1:1 aspect ratioaspect16x9()- 16:9 aspect ratioaspect4x3()- 4:3 aspect ratiofitted({fit, alignment})- Wrap in FittedBoxfitContain()- BoxFit.containfitCover()- BoxFit.coverfitFill()- BoxFit.fillfitScaleDown()- BoxFit.scaleDown
Stack & Positioned Extensions #
positioned({left, top, right, bottom, width, height})- Position in StackpositionedTopLeft({top, left})- Position at top-leftpositionedTopRight({top, right})- Position at top-rightpositionedBottomLeft({bottom, left})- Position at bottom-leftpositionedBottomRight({bottom, right})- Position at bottom-rightpositionedFill()- Fill entire StackpositionedDirectional(...)- RTL-aware positioningpositionedCenter()- Center in Stackstack({children, alignment, ...})- Create StackwithOverlay(Widget)- Add overlay on toponTopOf(Widget)- Place on top of backgroundindexedStack({index, alignment, ...})- Create IndexedStack (on List
Responsive Extensions #
responsive({builder, maxWidth, maxHeight})- Responsive renderingonlyMobile()- Show only on mobile (<600dp)onlyTablet()- Show only on tablet (600-900dp)onlyDesktop()- Show only on desktop (>=900dp)adaptiveSize({mobile, tablet, desktop, ...})- Adaptive sizingscaleWithScreen({baseWidth, maxScale, minScale})- Scale with screenmaxWidthBox(double, {alignment})- Constrain max widthresponsivePadding({mobilePadding, tabletPadding, desktopPadding, ...})- Adaptive paddingfillWithAspectRatio(double)- Fill with aspect ratio
BuildContext Extensions:
context.screenWidth- Get screen widthcontext.screenHeight- Get screen heightcontext.isMobile- Check if mobilecontext.isTablet- Check if tabletcontext.isDesktop- Check if desktopcontext.responsiveValue<T>({mobile, tablet, desktop})- Select value by screen size
Advanced Gesture Extensions #
onPan({onPanStart, onPanUpdate, onPanEnd, ...})- Pan gesturesonHorizontalDrag({onStart, onUpdate, onEnd})- Horizontal dragonVerticalDrag({onStart, onUpdate, onEnd})- Vertical dragonScale({onScaleStart, onScaleUpdate, onScaleEnd})- Scale gestures (pinch)draggable<T>({data, feedback, ...})- Make draggabledragTarget<T>({builder, onWillAccept, onAccept, ...})- Drag targetonLongPressWithDuration(...)- Long press with detailsonTapWithPosition({onTapDown, ...})- Tap with position
Utility Extensions #
safeArea({top, bottom, left, right, minimum})- SafeArea wrappersafeTop()- SafeArea for top onlysafeBottom()- SafeArea for bottom onlysafeHorizontal()- SafeArea for sideshero({tag, createRectTween, ...})- Hero animationmaterial({color, elevation, borderRadius, ...})- Material wrappercard({color, elevation, shape, ...})- Card wrapperbaseline({baseline, baselineType})- Baseline alignmenttoSliverBox()- Convert to SliverToBoxAdaptersliverFillRemaining({hasScrollBody, ...})- SliverFillRemainingsliverPadding(EdgeInsets)- SliverPaddingtoSliverList()- Convert ListtoSliverGrid({crossAxisCount, ...})- Convert Listscaffold({appBar, floatingActionButton, ...})- Scaffold wrapperdismissKeyboard()- Tap to dismiss keyboardrotatedBox(int)- Rotate by quarter turns
πΊοΈ Roadmap #
Version 0.1.0 (Upcoming) #
- β
Visibility extensions (
visible(),hide(),opacity()) β - β Flex/Expanded helpers β
- β Hero animation support β
- β SafeArea and MediaQuery helpers β
- β Positioned and Stack helpers β
- β Responsive layout helpers β
- β Advanced gesture types (pan, drag, scale) β
- β Sliver extensions β
- β Material/Card wrappers β
Version 0.2.0 (Planned) #
- β AnimatedContainer extensions
- β Theme-aware helpers
- β Form and input extensions
- β Custom animation builders
Future Considerations #
- β More layout helpers (Wrap, Flow, etc.)
- β Enhanced accessibility features
π€ Contributing #
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
π License #
This project is licensed under the MIT License - see the LICENSE file for details.
π Acknowledgments #
Inspired by SwiftUI's modifier pattern and other declarative UI frameworks. Built with β€οΈ for the Flutter community.
π Support #
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Documentation: pub.dev
Made with Flutter οΏ½οΏ½