cool_ext 3.5.0 copy "cool_ext: ^3.5.0" to clipboard
cool_ext: ^3.5.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.

Company powering the development and continuous maintenance of this package is https://www.techsukras.com

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.

8
likes
160
points
457
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

Documentation

API reference

License

MIT (license)

Dependencies

flutter, intl

More

Packages that depend on cool_ext