cool_ext 3.0.0
cool_ext: ^3.0.0 copied to clipboard
Cool Flutter Package with cool Utilities and Extensions for Easy and Pretty Coding
cool_ext #
A comprehensive Flutter package with powerful extensions, utility classes, and widgets to make your Flutter development easier and more productive.
Features #
This package provides lots of small utility classes, methods, and properties for day-to-day Flutter coding. It includes:
- 🔧 Extensions for BuildContext, String, Widget, num, Iterable, MediaQueryData, and more
- 🛠️ Utility Classes for colors, dates, type conversions, snackbars, and general helpers
- 🎨 Pre-built Widgets like LoadingButton, ChoiceChips, MenuBoxes, and more
- 📅 Date Formatting utilities with common format patterns
This is an opinionated package with utilities behaving in their own style. Many methods offer customization options.
Installation #
Add this to your package's pubspec.yaml file:
dependencies:
cool_ext: ^2.23.1
Then run:
flutter pub get
Import #
import 'package:cool_ext/cool_ext.dart';
📱 Extensions #
BuildContext Extensions #
Convenient shortcuts for common BuildContext operations:
// Instead of MediaQuery.of(context)
context.mediaQuery
// Instead of Theme.of(context)
context.theme
// Instead of Navigator.of(context)
context.navigator
// Instead of ScaffoldMessenger.of(context)
context.scaffoldMessenger
String Extensions #
Nullable String Extensions:
String? text = "hello";
text.hasValue // true if not null and not empty
text.isNullOrEmpty // true if null or empty
text.mapIfEmpty("default") // returns "default" if null/empty
String Extensions:
String text = "Hello World";
text.takeLeft(5) // "Hello"
text.takeLeft(5, addEllipses: true) // "Hello..."
text.takeAfter("Hello ") // "World"
text.takeAfterLast("/") // Gets text after last occurrence
// Validation
"12345".containsOnlyNumbers // true
"+1234567890".startsWithPlusAndContainsOnlyNumbers // true
"red".isAnyOf(["red", "blue", "green"]) // true
Widget Extensions #
Powerful widget extension methods for padding, layout, and decoration:
Padding:
Widget child = Text("Hello");
child.paddingAll(10)
child.paddingHorizontal(20)
child.paddingVertical(15)
child.paddingTop(10)
child.paddingBottom(10)
child.paddingLeft(10)
child.paddingRight(10)
child.paddingTLBR(10, 5, 10, 5) // top, left, bottom, right
child.paddingVH(10, 20) // vertical, horizontal
child.paddingCustom(EdgeInsets.only(top: 5))
Layout:
child.expanded() // Wraps in Expanded
child.expanded(flex: 2) // With flex parameter
child.flexible() // Wraps in Flexible
child.onCenter() // Wraps in Center
child.onScroll() // Wraps in SingleChildScrollView
child.onSafeArea() // Wraps in SafeArea
Decoration:
child.onWhiteContainer() // White background
child.onRoundedWhiteContainer(bgColor: Colors.blue)
child.border(context, radius: 5, borderColor: Colors.grey)
child.tooltip("Tooltip text")
child.tooltipOnTap("Click me")
num Extensions #
double value = 123.456;
value.asTwoDecimals // "123.46" (formatted with 2 decimal places)
Iterable Extensions #
Nullable Iterable Extensions:
List<int>? numbers = [1, 2, 3];
numbers.hasValue // true if not null and not empty
numbers.isNullOrEmpty // true if null or empty
// Map to list
numbers.mapToList((e) => e * 2) // [2, 4, 6]
numbers.mapToNullableList((e) => e.toString()) // ["1", "2", "3"] or null
// Group by key
var items = [Item(type: "A"), Item(type: "B"), Item(type: "A")];
items.group((item) => item.type) // {A: [item1, item3], B: [item2]}
// Convert to map
items.toMap((item) => item.id) // {id1: item1, id2: item2}
Widget List Extensions:
List<Widget> widgets = [Text("A"), Text("B"), Text("C")];
widgets.joinWith(Divider()) // Inserts divider between each widget
MediaQueryData Extensions #
MediaQueryData mq = MediaQuery.of(context);
mq.isLandscape // true if width > height
mq.isPortrait // true if height >= width
mq.shorterSize // Minimum of width and height
mq.largerSize // Maximum of width and height
mq.halfScreenWidth // width / 2
mq.halfScreenHeight // height / 2
Navigator Extensions #
// Navigate with widget directly
context.navigator.pushWidget(NextScreen())
context.navigator.pushReplacementWidget(NextScreen())
context.navigator.popUntilFirst()
context.navigator.removeUntilFirstAndPushReplacementWidget(NextScreen())
// Configure animation style
NavUtil.pushRouteAnimation = PushRouteAnimation.rightToLeft;
Icon Extensions #
Icon icon = Icon(Icons.home);
icon.withSize(24.0)
icon.withColor(Colors.blue)
Dynamic Extensions #
dynamic value = someNullableValue;
value.mapIfNotNull<int, String>(
(input) => input.toString(),
defaultValue: "N/A"
)
ScaffoldMessengerState Extensions #
context.scaffoldMessenger.coolSuccessSnackBar("Success!");
context.scaffoldMessenger.coolFailureSnackBar("Error occurred");
Int Extensions (Date Formatting) #
Convert milliseconds to formatted date strings:
int timestamp = DateTime.now().millisecondsSinceEpoch;
timestamp.asDDMMMYHHMMSSAAA // "02-Nov-2025 14:30:45:123"
timestamp.asDDMMMYHHMMSS // "02-Nov-2025 14:30:45"
timestamp.asHHMMSS // "14:30:45"
timestamp.asHHMMA // "02:30 PM"
timestamp.asDDMMYYYY // "02-Nov-2025"
timestamp.asDDMMM // "02-Nov"
timestamp.asDDMMYYYYHHMMA // "02-Nov-2025 2:30 PM"
timestamp.asYYYYMMM // "2025-Nov"
timestamp.asYYYYMM // "2025-11"
timestamp.asEEEhhmma // "Sat 02:30 PM"
timestamp.asEEE // "Sat"
🛠️ Utility Classes #
CoolUtil #
A comprehensive utility class with many helper methods:
UI Helpers:
// Pre-configured input border
CoolUtil.outlinedRounded25rNoneBorder
// Network error page
CoolUtil.networkErrorPage(errorMsg: "Failed to load", icon: Icon(Icons.error))
// Text widgets
CoolUtil.boldText(context, "Bold Text")
CoolUtil.disabledText(context, "Disabled")
CoolUtil.captionText(context, "Caption", tooltip: "Info")
CoolUtil.centerText("Centered")
// Text on colored background
CoolUtil.textOnColor(context,
text: "Label",
backgroundColor: Colors.blue,
textColor: Colors.white
)
// Widget on colored background
CoolUtil.widgetOnColor(context,
widget: Text("Content"),
backgroundColor: Colors.grey.shade200
)
// Inkwell card
CoolUtil.onInkCard(
onTap: () {},
child: Text("Tap me"),
radius: 10,
elevation: 2
)
// Clipboard helpers
CoolUtil.clipboardCopyButton(context, "Text to copy")
CoolUtil.clipboardCopyableText(context, "Text with copy button")
List Builders:
// Build and join widgets with interleaved widget
CoolUtil.buildAndJoinWidgets(
items: ["A", "B", "C"],
widgetBuilder: (item) => Text(item),
interleaved: Divider(),
startWithInterleaver: true,
endWithInterleaver: false
)
// Build with dividers
CoolUtil.buildAndJoinDivider(
items: myList,
widgetBuilder: (item) => ListTile(title: Text(item))
)
Dialogs:
// Input dialog
String? result = await CoolUtil.inputDialog(
context: context,
title: "Enter Name",
defaultValue: "John",
hintText: "Type your name",
validator: (value) => value?.isEmpty == true ? "Required" : null
);
// Confirmation dialog
CoolUtil.wrapInYesCancelConfirmDialog(
context,
"Delete Item",
contentString: "Are you sure?",
action1Text: "Delete",
action1: () => deleteItem(),
action1ButtonFgColor: Colors.red
);
Form Helpers:
CoolUtil.wrapInFormField(
validator: (value) => value == null ? "Required" : null,
child: MyCustomWidget(),
labelText: "Field Label",
filled: true
)
Navigation Routes:
CoolUtil.defaultRoute(NextScreen()) // Material page route
CoolUtil.animatedRoute(NextScreen()) // Animated slide transition
Converters:
CoolUtil.yesNoToBool("Yes") // true
CoolUtil.boolToYesNo(true) // "Yes"
Theme Size Helpers:
CoolUtil.bodyLargeSize(context)
CoolUtil.headlineLargeSize(context)
CoolUtil.displayMediumSize(context)
DateUtil #
Comprehensive date formatting and manipulation utilities:
Pre-defined Formats:
DateUtil.hh24mmss // 24-hour format: "14:30:45"
DateUtil.hh12mma // 12-hour format: "02:30 PM"
DateUtil.ddmmyyyy // "02-Nov-2025"
DateUtil.ddmmmyyyy // "02-Nov-2025"
DateUtil.yyyymmm // "2025-Nov"
DateUtil.ddmmyhhmmssaaa // "02-Nov-2025 14:30:45"
DateUtil.yyyymmddhhmmssForFileName // "2025-11-02-14-30-45"
DateUtil.eeehhmma // "Sat 02:30 PM"
DateUtil.eee // "Sat"
Format Methods:
// Format TimeOfDay
TimeOfDay time = TimeOfDay(hour: 14, minute: 30);
DateUtil.formatTimeOfDay(time) // "02:30 PM"
DateUtil.formatTimeOfDay(time, formatTo12Hr: false) // "14:30"
// Format timestamp
DateUtil.formatDateDDMMMYHHMMSS(timestamp)
DateUtil.formatDateFriendlyDateAndTimeUptoMillis(timestamp)
Parsing:
TimeOfDay time = DateUtil.parseHHMMToTimeOfDay("14:30");
TimeOfDay time2 = DateUtil.parseHHColonMMToTimeOfDay("14:30");
Date Manipulation:
DateTime now = DateTime.now();
DateUtil.addDaysAndSetHHMM(now, 5, "09:00") // Add 5 days, set to 9 AM
DateUtil.reduceDaysAndSetHHMM(now, 2, "18:00") // Subtract 2 days, set to 6 PM
Duration Formatting:
Duration duration = Duration(hours: 2, minutes: 30, seconds: 45);
DateUtil.getElapsedStr(duration) // "2hr 30m 45s"
DateUtil.getElapsedStr(duration, showMilliSeconds: true) // "2hr 30m 45s 500ms"
ColorUtil #
Color color = ColorUtil.colorFromHex("#FF5733");
Color color2 = ColorUtil.colorFromHex("FF5733"); // Works without #
SnackBarUtil #
Easy snackbar display with colored backgrounds:
// With context
SnackBarUtil.showSuccess(context, "Operation successful!");
SnackBarUtil.showError(context, "Something went wrong");
// With ScaffoldMessengerState
SnackBarUtil.showSuccessWithMessenger(scaffoldMessenger, "Success!");
SnackBarUtil.showFailureWithMessenger(scaffoldMessenger, "Failed");
// Customize colors
SnackBarUtil.successColor = Colors.green.shade900;
SnackBarUtil.failureColor = Colors.red.shade900;
TypeConversionUtil #
// Convert dynamic list to typed lists
List<dynamic> dynamicList = [1, 2, 3];
List<String> strings = TypeConversionUtil.dynamicListToStringList(dynamicList);
List<int> ints = TypeConversionUtil.dynamicListToIntList(dynamicList);
Set<String> stringSet = TypeConversionUtil.dynamicListToStringSet(dynamicList);
DevConsole #
Debug printing utilities:
DevConsole.printIfDebug("Debug message"); // Only in debug mode
DevConsole.printAlways("Always printed"); // Always prints
DevConsole.printCaught("Error occurred", error, stackTrace); // For caught exceptions
GeneralUtil Classes #
StringIdText - Represents an item with ID and text:
var item = StringIdText("id1", "Display Text");
item.toJson();
StringIdText.fromJson(json);
StringIdText.listFrom(jsonList);
StringIdText.findById(items, "id1");
StringIdText.findAllById(items, ["id1", "id2"]);
TypeIdText - Item with type, ID, and text:
var item = TypeIdText("user", "123", "John Doe");
item.toJson();
Result - Simple success/failure result:
var result = Result.successMsg("Operation completed");
var result2 = Result.failureMsg("Operation failed");
ProcessResult - Result with optional object:
var result = ProcessResult<User>.successMsg("User loaded");
var result2 = ProcessResult.failureMsg("Failed to load");
OneTimeWorker - Execute code only once:
OneTimeWorker.work(
jobName: "initializeApp",
action: () => print("This runs only once")
);
MsgDisplay - Widget to display ProcessResult messages:
ValueNotifier<ProcessResult<User>> notifier = ValueNotifier(ProcessResult.empty());
MsgDisplay(valueListenable: notifier)
🎨 Widgets #
LoadingButton #
A button that shows a loading indicator while executing an async operation:
LoadingButton(
buttonType: LoadingButtonType.elevatedButton,
onPressed: () async {
await Future.delayed(Duration(seconds: 2));
},
child: Text("Submit"),
tooltip: "Click to submit",
style: ElevatedButton.styleFrom(backgroundColor: Colors.blue)
)
// Available button types:
// LoadingButtonType.elevatedButton
// LoadingButtonType.textButton
// LoadingButtonType.iconButton
// LoadingButtonType.filledButton
ChoiceChips #
Single or multi-select chip widget:
// Single select
ChoiceChips(
items: [
StringIdText("1", "Option 1"),
StringIdText("2", "Option 2"),
StringIdText("3", "Option 3"),
],
onSelectionChanged: (selected) {
print("Selected: ${selected.map((e) => e.text).join(', ')}");
},
selectedColor: Colors.blue,
selectedFgColor: Colors.white,
showCheckmark: true
)
// Multi-select
ChoiceChips(
items: items,
multiSelect: true,
initialSelection: [items[0], items[1]],
onSelectionChanged: (selected) => handleSelection(selected)
)
MultiSelectChips #
Convenience wrapper for multi-select chips:
MultiSelectChipsController controller = MultiSelectChipsController();
MultiSelectChips(
items: chipItems,
initialSelection: selectedItems,
controller: controller
)
// Get current selection
List<StringIdText> selected = controller.currentSelections();
CoolList #
Enhanced ListView with dividers, reordering, and empty state:
CoolList<User>(
items: users,
widgetBuilder: (user) => ListTile(title: Text(user.name)),
msgOnEmpty: "No users found",
title: "User List",
addDivider: true,
dividerColor: Colors.grey,
shrinkWrap: true,
listReOrderHelper: ListReOrderHelper(
keyProvider: (user) => user.id,
onReOrder: (oldIndex, newIndex, reorderedList) {
// Handle reorder
}
)
)
MenuBoxes #
Grid menu with icon boxes:
MenuBoxes(
minCrossAxisCount: 2,
menus: [
MenuBoxItem(
icon: Icon(Icons.home, size: 40),
labelText: "Home",
bgColor: Colors.white,
onPressed: () => navigateToHome()
),
MenuBoxItem(
icon: Icon(Icons.settings, size: 40),
labelText: "Settings",
bgColor: Colors.blue.shade50,
onPressed: () => navigateToSettings()
),
]
)
ChipFilterableListView #
ListView with filtering chips:
ChipFilterableListView(
filters: [
StringIdText("active", "Active"),
StringIdText("inactive", "Inactive"),
],
items: [
ChipFilterableListItem(
{"active"},
UserTile(user: user1)
),
ChipFilterableListItem(
{"inactive"},
UserTile(user: user2)
),
],
allFilter: true,
allFilterText: "All Users",
addDivider: true
)
OrientationLocker #
Lock screen orientation for a specific screen:
OrientationLocker(
permittedOrientations: [
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
],
child: VideoPlayerScreen()
)
// When widget is disposed, orientation is reset to default
Blur #
Apply blur effect to any widget:
Blur(
child: Image.network("https://example.com/image.jpg")
)
📋 Example Usage #
import 'package:cool_ext/cool_ext.dart';
import 'package:flutter/material.dart';
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text("cool_ext Demo")),
body: Column(
children: [
// String extensions
Text("Hello World".takeLeft(5)),
// Widget extensions
Text("Padded Text")
.paddingAll(20)
.onRoundedWhiteContainer()
.tooltip("This is a tooltip"),
// LoadingButton
LoadingButton(
buttonType: LoadingButtonType.elevatedButton,
onPressed: () async {
await Future.delayed(Duration(seconds: 2));
SnackBarUtil.showSuccess(context, "Done!");
},
child: Text("Click Me")
),
// Date formatting
Text(DateTime.now().millisecondsSinceEpoch.asDDMMYYYY),
// Choice chips
ChoiceChips(
items: [
StringIdText("1", "Option 1"),
StringIdText("2", "Option 2"),
],
onSelectionChanged: (selected) {
print("Selected: $selected");
}
),
].joinWith(SizedBox(height: 10))
).paddingAll(16).onScroll()
),
);
}
}
🤝 Contributing #
This is an opinionated package with utilities behaving in their own style. Many methods offer customization, but some may not.
If you find issues or want new features:
- Create an issue at GitLab Issues
- Submit a merge request with your improvements
📄 License #
Please check the repository for license information.
🔗 Links #
- Homepage: https://gitlab.com/adithyank/flutter_cool_ext
- Repository: https://gitlab.com/adithyank/flutter_cool_ext
- Issue Tracker: https://gitlab.com/adithyank/flutter_cool_ext/-/issues
📝 Changelog #
See CHANGELOG.md for version history.