save_points_extensions_utils 1.0.2+1
save_points_extensions_utils: ^1.0.2+1 copied to clipboard
Extensions and reusable widgets. Provides 100+ widget extensions, data type extensions, form validators, spacing utilities, and more. Zero dependencies, fully documented, production-ready.
Save Points Extensions #
A comprehensive Flutter library of extensions and reusable widgets designed to enhance developer productivity and code quality. This library provides a collection of well-organized, type-safe extensions and widgets following Flutter best practices.
A powerful, modular, zero-dependency Extensions library for Flutter
Pure Dart • Zero Dependencies • 100+ Extensions • Fully Modular • Production Ready
🚀 Live Demo • Features • Installation • Quick Start • Documentation • Examples
✨ Features #
- 100+ Widget Extensions - Streamline widget composition with chainable methods (padding, margin, center, clip, transform, gestures, and more)
- Comprehensive Data Type Extensions - Enhance String, Number, List, Map, DateTime, Color, Context, and Animation with 50+ utility methods
- Professional Code Quality - Fully documented with examples, type-safe, null-safe, and following Flutter best practices
- Form Validation - Comprehensive validators for common use cases (email, phone, URL, required, length, etc.)
- Utility Helpers - Spacing constants, app constants, debounce/throttle helpers, and reusable utilities
- Zero Dependencies - Pure Flutter/Dart, no external dependencies
- Well Documented - Every extension includes comprehensive documentation with usage examples
📦 Installation #
Add this to your pubspec.yaml:
dependencies:
flutter:
sdk: flutter
save_points_extensions_utils: ^1.0.2
Then import the extensions:
// Option 1: Import everything from main library
import 'package:save_points_extensions_utils/save_points_extensions_utils.dart';
// Option 2: Import specific parts
import 'package:save_points_extensions_utils/extensions/logics/extensions.dart';
import 'package:save_points_extensions_utils/utils/utils.dart';
⚠️ Avoiding Conflicts with Other Packages #
If you're using multiple packages that might have naming conflicts (e.g., save_points_extensions_utils and save_points_chart), here are strategies to avoid conflicts:
Strategy 1: Use Library Prefixes (Recommended) #
import 'package:save_points_extensions_utils/save_points_extensions_utils.dart' as utils;
import 'package:save_points_chart/save_points_chart.dart' as chart;
// Use with prefix
utils.Spacing.md;
utils.Validators.email('test@example.com');
chart.ChartConfig();
Strategy 2: Use Explicit Imports with show #
// Import only what you need
import 'package:save_points_extensions_utils/utils/utils.dart'
show Spacing, Validators, AppConstants;
import 'package:save_points_chart/chart_utils.dart'
show ChartConfig, ChartType;
Strategy 3: Use hide to Exclude Conflicting Names #
import 'package:save_points_extensions_utils/utils/utils.dart'
hide SpacingConfig; // Hide if it conflicts
import 'package:save_points_chart/chart_utils.dart';
Strategy 4: Import Extensions Separately #
Extensions on built-in types (String, List, Widget, etc.) won't conflict if they have different method names. If they do conflict, use prefixes:
import 'package:save_points_extensions_utils/save_points_extensions_utils.dart' as utils;
import 'package:save_points_chart/save_points_chart.dart' as chart;
// Extensions work normally
'hello'.capitalize(); // Uses utils extension
// If methods conflict, use explicit extension
utils.StringExtensions('hello').capitalize();
Common Conflict Scenarios #
- Utility Classes:
Spacing,Validators,AppConstants- Use prefixes - Config Classes:
SpacingConfig,ValidatorConfig- Use prefixes orshow/hide - Extension Methods: Usually fine unless same method name - Use prefixes if needed
- Static Methods: Use prefixes or explicit class names
Best Practice #
For projects using multiple save_points_* packages, always use library prefixes:
import 'package:save_points_extensions_utils/save_points_extensions_utils.dart' as utils;
import 'package:save_points_chart/save_points_chart.dart' as chart;
import 'package:save_points_other/save_points_other.dart' as other;
// Clear and conflict-free
utils.Spacing.md;
chart.ChartConfig();
other.OtherUtility();
📖 For more details, see:
- Conflict Prevention Guide - Detailed guide on avoiding conflicts
- Best Practices - Comprehensive best practices guide
🚀 Quick Start #
Using Widget Extensions #
Chain multiple extensions together for clean, readable code:
import 'package:save_points_extensions_utils/extensions/extensions.dart';
// Chain widget modifications
const Text('Chained Extensions')
.padding(all: 20)
.center()
.clipRRect(borderRadius: 16)
.decoratedBox(
color: context.colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: context.colorScheme.primary.withOpacity(0.3),
blurRadius: 8,
offset: Offset(0, 4),
),
],
)
.onTap(() => print('Tapped!'))
.tooltip('Tap me!');
// Transform widgets
const Icon(Icons.star, size: 48)
.rotate(0.2)
.scale(1.5)
.translate(x: 10, y: 5);
// Clip widgets
Container(
width: 100,
height: 100,
color: Colors.blue,
)
.clipRRect(borderRadius: 16)
.clipOval()
.onTap(() => print('Tapped'));
Using Context Extensions #
Easy access to theme, media query, and navigation:
import 'package:save_points_extensions_utils/extensions/extensions.dart';
// Theme access
context.colorScheme.primary
context.textTheme.headlineLarge
context.isDarkMode
context.isLightMode
// Screen dimensions
context.screenWidth
context.screenHeight
context.screenSize
context.isMobile
context.isTablet
context.isDesktop
// Navigation helpers
context.push(MyPage());
context.pop();
context.showSnackBar('Hello!');
context.hideKeyboard();
Using Data Extensions #
String Extensions
// Text manipulation
'hello world'.capitalize() // "Hello world"
'hello world'.capitalizeWords() // "Hello World"
'very long text'.truncate(10) // "very long..."
'hello world'.removeWhitespace() // "helloworld"
'hello world'.reverse() // "dlrow olleh"
'hello world'.toSlug() // "hello-world"
// Validation
'email@test.com'.isValidEmail // true
'https://example.com'.isValidUrl // true
'+1234567890'.isValidPhone // true
'12345'.isNumeric // true
'Hello'.isAlpha // true
'Hello123'.isAlphanumeric // true
// Utilities
'hello world'.startsWithAny(['he', 'hi']) // true
'hello world'.endsWithAny(['ld', 'la']) // true
'hello world'.findAll(RegExp(r'\w+')) // ['hello', 'world']
Number Extensions
// Formatting
42.format(prefix: 'Count: ') // "Count: 42"
1234567.formatWithSeparator() // "1,234,567"
1234.formatCurrency() // "$1,234.00"
1500000.toHumanReadable() // "1.5M"
// Validation
42.isEven // true
42.isOdd // false
5.isPositive // true
(-5).isNegative // true
5.isBetween(1, 10) // true
// Duration helpers
60.seconds // Duration(seconds: 60)
5.minutes // Duration(minutes: 5)
2.hours // Duration(hours: 2)
7.days // Duration(days: 7)
// Ranges
1.rangeTo(5) // [1, 2, 3, 4]
1.rangeToInclusive(5) // [1, 2, 3, 4, 5]
DateTime Extensions
// Date checks
DateTime.now().isToday // true
DateTime.now().isYesterday // false
DateTime.now().isTomorrow // false
DateTime(2020, 1, 1).isPast // true
DateTime(2030, 1, 1).isFuture // true
// Formatting
date.format('yyyy-MM-dd') // "2024-01-15"
date.format('HH:mm:ss') // "14:30:45"
date.toFormattedString() // "January 15, 2024"
date.toShortFormattedString() // "Jan 15, 2024"
// Relative time
date.timeAgo // "2 hours ago"
date.age // 24
// Date manipulation
date.addDays(5) // Add 5 days
date.subtractDays(5) // Subtract 5 days
date.addMonths(2) // Add 2 months
date.addYears(1) // Add 1 year
// Date ranges
date.startOfDay // 00:00:00.000
date.endOfDay // 23:59:59.999
date.startOfWeek // Monday 00:00:00
date.endOfWeek // Sunday 23:59:59
date.startOfMonth // First day of month
date.endOfMonth // Last day of month
// Comparisons
date.isSameDay(otherDate) // true/false
date.isSameWeek(otherDate) // true/false
date.isBetween(startDate, endDate) // true/false
date.daysUntil(otherDate) // 5
List Extensions
// Safe access
list.firstOrNull // First element or null
list.lastOrNull // Last element or null
list.elementAtOrNull(5) // Element at index or null
list.elementAtOrDefault(5, defaultValue) // Element or default
// Manipulation
[1, 2, 2, 3].distinct // [1, 2, 3]
list.distinctBy((item) => item.id) // Remove duplicates by key
list.chunk(3) // Split into chunks of 3
list.shuffled // New shuffled list
// Querying
list.firstWhereOrNull((x) => x > 3) // First matching or null
list.lastWhereOrNull((x) => x > 3) // Last matching or null
list.any((x) => x > 5) // true if any matches
list.all((x) => x > 0) // true if all match
// Aggregation
list.sum((x) => x.value) // Sum of values
list.average((x) => x.value) // Average of values
list.maxBy((x) => x.value) // Max element
list.minBy((x) => x.value) // Min element
// Grouping
list.groupBy((item) => item.category) // Map<K, List<T>>
// Slicing
list.drop(2) // Remove first 2
list.dropLast(2) // Remove last 2
list.take(3) // Take first 3
list.takeLast(3) // Take last 3
Map Extensions
// Safe access
map.getOrNull('key') // Value or null
map.getOrDefault('key', defaultValue) // Value or default
map.getOrPut('key', () => defaultValue) // Value or compute and store
// Transformation
map.mapKeys((k) => k.toUpperCase()) // Transform keys
map.mapValues((v) => v * 2) // Transform values
map.filter((k, v) => v > 10) // Filter entries
// Utilities
map.firstEntry // First entry or null
map.lastEntry // Last entry or null
map.merge(otherMap) // Merge maps
map.mergeWith(otherMap, (a, b) => a + b) // Merge with function
map.invert() // Swap keys and values
map.sortedByKey() // Sort by keys
map.sortedByValue() // Sort by values
Color Extensions
// Manipulation
color.darken(0.2) // Darker color
color.lighten(0.2) // Lighter color
color.saturate(0.3) // More saturated
color.desaturate(0.3) // Less saturated
color.blend(otherColor, 0.5) // Blend colors
color.adjustBrightness(0.2) // Adjust brightness
color.adjustHue(120) // Shift hue
color.complementary // Complementary color
// Conversion
color.hexString // "#FF2196F3"
color.hexStringNoAlpha // "#2196F3"
color.toRgb() // {'r': 255, 'g': 0, 'b': 0}
color.toHsl() // {'h': 0.0, 's': 1.0, 'l': 0.5}
// Utilities
color.isDark // true if dark
color.isLight // true if light
color.contrastingColor // Black or white for contrast
// Static factories
ColorExtensions.fromHex('#FF0000') // From hex string
ColorExtensions.fromRgb(255, 0, 0) // From RGB
ColorExtensions.fromHsl(0, 1.0, 0.5) // From HSL
Animation Extensions
// Animation status
animation.isCompleted // true if completed
animation.isDismissed // true if dismissed
animation.isForward // true if forward
animation.isReverse // true if reverse
animation.isAnimating // true if animating
animation.clampedValue // Value clamped 0.0-1.0
animation.reversedValue // 1.0 - value
// Controller utilities
controller.toggle() // Toggle animation
controller.resetAndForward() // Reset and forward
controller.resetAndReverse() // Reset and reverse
controller.isNotAnimating // true if not animating
// Tween utilities
tween.reverse // Reverse tween
tween.evaluate(0.5) // Value at 0.5
tween.lerp(0.5) // Value at 0.5
Using Utilities #
Validators
import 'package:save_points_extensions_utils/utils/utils.dart';
// In form validation
TextFormField(
validator: (value) => Validators.required(value),
)
TextFormField(
validator: (value) => Validators.email(value),
)
// Available validators
Validators.required(value)
Validators.email(value)
Validators.phone(value)
Validators.url(value)
Validators.numeric(value)
Validators.minLength(value, 8)
Validators.maxLength(value, 100)
Validators.match(value, otherValue)
Spacing
import 'package:save_points_extensions_utils/utils/utils.dart';
// Constants
Spacing.xs // 4.0
Spacing.sm // 8.0
Spacing.md // 16.0
Spacing.lg // 24.0
Spacing.xl // 32.0
// Helpers
Spacing.vertical(16)
Spacing.horizontal(8)
Spacing.all(16)
Spacing.symmetric(horizontal: 8, vertical: 16)
Spacing.only(top: 8, bottom: 16)
Constants
import 'package:save_points_extensions_utils/utils/utils.dart';
AppConstants.defaultPadding
AppConstants.defaultBorderRadius
AppConstants.shortAnimation
Debounce & Throttle
import 'package:save_points_extensions_utils/utils/utils.dart';
final debounce = Debounce(const Duration(milliseconds: 300));
final throttle = Throttle(const Duration(milliseconds: 500));
// Runs once after rapid triggers settle
debounce(() {
// Your code here
});
// Runs at most once in the given interval
throttle(() {
// Your code here
});
📚 Available Extensions #
Widget Extensions #
Located in lib/extensions/widgets/:
- Core Extensions (
widget_core_extensions.dart) - padding, margin, center, expanded, flexible, sizedBox, container, opacity, visible, safeArea, decoratedBox - Gesture Extensions (
widget_gesture_extensions.dart) - onTap, onLongPress, onDoubleTap, inkWell - Clip Extensions (
widget_clip_extensions.dart) - clipRRect, clipOval, clipRect - Transform Extensions (
widget_transform_extensions.dart) - rotate, scale, translate, transform - Layout Extensions (
widget_layout_extensions.dart) - align, positioned, aspectRatio, constrained, fittedBox, fractionallySizedBox - Material Extensions (
widget_material_extensions.dart) - material, card - Animation Extensions (
widget_animation_extensions.dart) - hero, animatedSwitcher - Scroll Extensions (
widget_scroll_extensions.dart) - scrollable - Sizing Extensions (
widget_sizing_extensions.dart) - intrinsicHeight, intrinsicWidth, unconstrained, limitedBox, overflowBox - Semantics Extensions (
widget_semantics_extensions.dart) - semantics, tooltip - Text Extensions (
widget_text_extensions.dart) - TextExtensions.bold, TextExtensions.italic - Icon Extensions (
widget_icon_extensions.dart) - IconExtensions.themed - Image Extensions (
widget_image_extensions.dart) - ImageExtensions.networkWithFallback - List Extensions (
widget_list_extensions.dart) - ListViewExtensions.withPadding, GridViewExtensions.withDefaults - Stack Extensions (
widget_stack_extensions.dart) - StackExtensions.centered - Column/Row Extensions (
widget_column_row_extensions.dart) - ColumnExtensions.centered, RowExtensions.centered - Wrap Extensions (
widget_column_row_extensions.dart) - WrapExtensions.withSpacing
Data Type Extensions #
- String Extensions (
string_extensions.dart) - 30+ methods for text manipulation, validation, and formatting - Number Extensions (
number_extensions.dart) - Formatting, validation, duration helpers, ranges, and more - DateTime Extensions (
datetime_extensions.dart) - Date manipulation, formatting, relative time, age calculation, date ranges - Duration Extensions (
datetime_extensions.dart) - Human-readable strings, formatted output - List Extensions (
list_extensions.dart) - 40+ methods for safe access, manipulation, querying, aggregation, grouping - Map Extensions (
map_extensions.dart) - Safe access, transformation, filtering, merging, sorting - Color Extensions (
color_extensions.dart) - Color manipulation, conversion, blending, HSL/RGB conversion - Context Extensions (
context_extensions.dart) - Theme access, media query, navigation, snackbar, keyboard, screen size detection - Animation Extensions (
animation_extensions.dart) - Animation status checks, controller utilities, tween helpers
🛠️ Utilities #
Validators #
Comprehensive form validation utilities:
import 'package:save_points_extensions_utils/utils/utils.dart';
Validators.required(value)
Validators.email(value)
Validators.phone(value)
Validators.url(value)
Validators.numeric(value)
Validators.minLength(value, 8)
Validators.maxLength(value, 100)
Validators.match(value, otherValue)
Spacing #
Consistent spacing constants and helpers:
import 'package:save_points_extensions_utils/utils/utils.dart';
// Constants
Spacing.xs // 4.0
Spacing.sm // 8.0
Spacing.md // 16.0
Spacing.lg // 24.0
Spacing.xl // 32.0
// Helpers
Spacing.vertical(16)
Spacing.horizontal(8)
Spacing.all(16)
Spacing.symmetric(horizontal: 8, vertical: 16)
Spacing.only(top: 8, bottom: 16)
Constants #
Application-wide constants:
import 'package:save_points_extensions_utils/utils/utils.dart';
AppConstants.defaultPadding
AppConstants.defaultBorderRadius
AppConstants.shortAnimation
Debounce & Throttle #
Control how frequently callbacks execute to prevent spamming expensive work:
import 'package:save_points_extensions_utils/utils/utils.dart';
final debounce = Debounce(const Duration(milliseconds: 300));
final throttle = Throttle(const Duration(milliseconds: 500));
// Runs once after rapid triggers settle
debounce(() {
// Your code here
});
// Runs at most once in the given interval
throttle(() {
// Your code here
});
📁 Project Structure #
lib/
├── app/
├── extensions/
│ ├── widgets/
│ │ ├── widget_core_extensions.dart
│ │ ├── widget_gesture_extensions.dart
│ │ ├── widget_clip_extensions.dart
│ │ ├── widget_transform_extensions.dart
│ │ ├── widget_layout_extensions.dart
│ │ ├── widget_material_extensions.dart
│ │ ├── widget_animation_extensions.dart
│ │ ├── widget_scroll_extensions.dart
│ │ ├── widget_sizing_extensions.dart
│ │ ├── widget_semantics_extensions.dart
│ │ ├── widget_text_extensions.dart
│ │ ├── widget_icon_extensions.dart
│ │ ├── widget_image_extensions.dart
│ │ ├── widget_list_extensions.dart
│ │ ├── widget_stack_extensions.dart
│ │ ├── widget_column_row_extensions.dart
│ │ └── widgets.dart
│ ├── animation_extensions.dart
│ ├── color_extensions.dart
│ ├── context_extensions.dart
│ ├── datetime_extensions.dart
│ ├── list_extensions.dart
│ ├── map_extensions.dart
│ ├── number_extensions.dart
│ ├── string_extensions.dart
│ ├── widget_extensions.dart
│ └── extensions.dart
├── pages/
├── utils/
│ ├── constants.dart
│ ├── spacing.dart
│ ├── validators.dart
│ ├── debounce.dart
│ ├── date_formatters.dart
│ ├── helpers.dart
│ ├── url_utils.dart
│ ├── string_formatters.dart
│ └── utils.dart
├── widgets/
│ ├── components/
│ │ └── copy_button.dart
│ └── sections/
│ ├── animation_extensions_examples_section.dart
│ ├── debounce_throttle_section.dart
│ └── widget_extensions_section.dart
└── main.dart
💡 Best Practices #
Chaining Extensions #
Extensions can be chained for cleaner, more readable code:
const Text('Hello World')
.padding(all: 16)
.margin(all: 12)
.center()
.clipRRect(borderRadius: 8)
.decoratedBox(
color: Colors.blue,
borderRadius: BorderRadius.circular(8),
)
.onTap(() => print('Tapped!'))
.tooltip('Click me');
Safe State Updates #
Always check if the widget is mounted before updating state:
void _incrementCounter() {
if (mounted) {
setState(() {
_counter++;
});
}
}
Using Context Extensions #
Leverage context extensions for cleaner code:
// Instead of
Theme.of(context).colorScheme.primary
MediaQuery.of(context).size.width
// Use
context.colorScheme.primary
context.screenWidth
Const Constructors #
Use const constructors where possible for better performance:
const Text('Hello')
.padding(all: 16)
.center();
🎯 Examples #
🚀 Try the Live Demo to see all features in action!
See lib/main.dart for a comprehensive demo showcasing:
- Widget Extensions Chaining - Multiple extensions chained together
- Transform Extensions - Rotate, scale, and translate widgets
- Clip Extensions - ClipRRect, ClipOval, ClipRect
- Data Extensions - String, Number, DateTime, and List operations
- Color Extensions - Color manipulation (darken, lighten)
- Gesture Extensions - onTap, onLongPress, inkWell
- Animation Extensions - Hero animations plus clamped/reversed values, curves, mirror, and controller helpers
- Form Validation - Using validators utility
- Context Extensions - Theme access, navigation, snackbar
- Debounce & Throttle - Limit rapid calls with debounce/throttle helpers
- Copyable Snippets -
CopyButtonwidget to quickly copy example code
📝 License #
This project is open source and available for use in your Flutter projects.
🤝 Contributing #
Contributions are welcome! Feel free to:
- Add new extensions
- Improve existing ones
- Fix bugs
- Enhance documentation
- Add more examples
📧 Support #
For issues, questions, or suggestions, please open an issue in the repository.
Built with ❤️ for the Flutter community
