cool_ext 3.0.0 copy "cool_ext: ^3.0.0" to clipboard
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
// 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
    }
  )
)

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.



📝 Changelog #

See CHANGELOG.md for version history.

9
likes
0
points
31
downloads

Publisher

verified publisheradithyan.dev

Weekly Downloads

Cool Flutter Package with cool Utilities and Extensions for Easy and Pretty Coding

Repository (GitLab)
View/report issues

License

unknown (license)

Dependencies

flutter, intl

More

Packages that depend on cool_ext