From 779bb20f8872ee2fd8e365cb4e21eb218a16f7db Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Oct 2025 22:47:25 +0000 Subject: [PATCH 1/2] Initial plan From 3a0d12e0594f6bdc1d2188853c5d0c0639d41d18 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Oct 2025 22:57:41 +0000 Subject: [PATCH 2/2] Reorganize documentation into docs/ directory Co-authored-by: YoungMayor <47315212+YoungMayor@users.noreply.github.com> --- README.md | 663 ++----------------------------- docs/README.md | 48 +++ docs/bool_extensions.md | 33 ++ docs/build_context_extensions.md | 42 ++ docs/datetime_extensions.md | 58 +++ docs/datetime_format.md | 34 ++ docs/duration_extensions.md | 31 ++ docs/dynamic_extensions.md | 26 ++ docs/humanize_extensions.md | 55 +++ docs/image_extensions.md | 26 ++ docs/list_extensions.md | 111 ++++++ docs/map_extensions.md | 62 +++ docs/number_extensions.md | 89 +++++ docs/object_extensions.md | 25 ++ docs/set_extensions.md | 31 ++ docs/string_extensions.md | 96 +++++ docs/widget_extensions.md | 66 +++ 17 files changed, 865 insertions(+), 631 deletions(-) create mode 100644 docs/README.md create mode 100644 docs/bool_extensions.md create mode 100644 docs/build_context_extensions.md create mode 100644 docs/datetime_extensions.md create mode 100644 docs/datetime_format.md create mode 100644 docs/duration_extensions.md create mode 100644 docs/dynamic_extensions.md create mode 100644 docs/humanize_extensions.md create mode 100644 docs/image_extensions.md create mode 100644 docs/list_extensions.md create mode 100644 docs/map_extensions.md create mode 100644 docs/number_extensions.md create mode 100644 docs/object_extensions.md create mode 100644 docs/set_extensions.md create mode 100644 docs/string_extensions.md create mode 100644 docs/widget_extensions.md diff --git a/README.md b/README.md index 467ac08..73ea6fd 100644 --- a/README.md +++ b/README.md @@ -35,650 +35,51 @@ import 'package:mayr_extensions/mayr_extensions.dart'; ## ๐Ÿš€ Features -### ๐Ÿงฑ BuildContext Extensions +This package provides a comprehensive set of extension methods for Flutter and Dart development: -- `form` โ€“ Easily access the nearest `FormState` using `context.form`. +### ๐Ÿ“ฆ Available Extensions -- `navigator` โ€“ Shorthand for `Navigator.of(context)`. -- `overlay` โ€“ Access the current `OverlayState` from the context. -- `scaffold` โ€“ Retrieve the nearest `ScaffoldState` with `context.scaffold`. +- **[๐Ÿงฑ BuildContext Extensions](docs/build_context_extensions.md)** - Easy access to form, navigator, scaffold, media query, and device type checks +- **[โœ… Bool Extensions](docs/bool_extensions.md)** - Boolean utilities including `choose()`, `toInt()`, `toYesNo()` +- **[๐Ÿ—“๏ธ DateTime Extensions](docs/datetime_extensions.md)** - Date manipulation, time checks, age calculations, and formatting +- **[โณ Duration Extensions](docs/duration_extensions.md)** - Delay execution, readable strings, and duration comparisons +- **[๐Ÿ”ท Object Extensions](docs/object_extensions.md)** - Kotlin-inspired `let()` and `also()` for elegant chaining +- **[๐ŸŒ€ Dynamic Extensions](docs/dynamic_extensions.md)** - Nullable utilities and debug helpers +- **[๐Ÿ–ผ๏ธ Image Extensions](docs/image_extensions.md)** - Quick `CircleAvatar` creation from `ImageProvider` +- **[๐Ÿ”ข Number Extensions](docs/number_extensions.md)** - Numeric utilities, formatting, random generators, and duration conversions +- **[๐Ÿ”ค String Extensions](docs/string_extensions.md)** - String manipulation, casing, pattern matching, and pretty printing +- **[๐Ÿงฉ Iterable/List Extensions](docs/list_extensions.md)** - Safe access, transformations, aggregations, and query operations +- **[๐Ÿ—บ๏ธ Map Extensions](docs/map_extensions.md)** - Map transformations, merging, and filtering +- **[๐Ÿ”ข Set Extensions](docs/set_extensions.md)** - Set operations like toggle, intersects, subset checks +- **[๐ŸŽฏ Humanize Extensions](docs/humanize_extensions.md)** - Convert values to human-readable strings +- **[๐Ÿงฉ Widget Extensions](docs/widget_extensions.md)** - Widget transformations for padding, positioning, clipping, and tap handling +- **[๐Ÿ“œ DateTimeFormat](docs/datetime_format.md)** - Pre-defined date/time format strings -#### ScaffoldMessenger - -- `scaffoldMessenger` โ€“ Quickly get the `ScaffoldMessengerState` for showing snackbars and more. - -- `void showSnackBar(String content, {Duration? duration, SnackBarBehavior behavior = SnackBarBehavior.fixed})` - Quickly show a SnackBar without manually accessing ScaffoldMessenger. - -#### Media Query -- `mediaQuery` โ€“ Access `MediaQueryData` from context. - -- `platformBrightness` โ€“ Get the system's brightness setting (`Brightness.dark` or `Brightness.light`). -- `platformInDarkMode` | `platformInLightMode` โ€“ Returns `true` based on the app's current brightness mode. -- `widgetSize` โ€“ Get the rendered size of the widget associated with the context. -- `widgetHeight` โ€“ Convenience getter for just the height of the widget. -- `widgetWidth` โ€“ Convenience getter for just the width. - -#### Media Query Orientation -- `orientation` โ€“ Access the current screen orientation (`portrait` or `landscape`). - -- `isLandscape` / `isPortrait` โ€“ Easy checks for current orientation. -- `widgetShortestSide` โ€“ Useful for responsive layouts based on the device's shortest screen edge. -- `isPhone` โ€“ Returns `true` if the device is considered a phone. -- `isSmallTablet`, `isLargeTablet` โ€“ Classify tablets based on width. -- `isTablet` โ€“ Shortcut combining both small and large tablets. -- `isDesktop` โ€“ Detects larger screens, typically desktops. - -------------------------------------------------------------------------------- - -### โœ… Bool Extensions - -- `choose(trueValue, falseValue)` โ€“ Returns `trueValue` if the boolean is true, otherwise returns `falseValue`. -- `toInt()` โ€“ Converts the boolean to an integer (1 for true, 0 for false). -- `toYesNo({trueString, falseString})` โ€“ Converts to a string representation with customizable values. -- `not` โ€“ Returns the negation of the boolean (equivalent to `!this`). - -```dart -// Examples -true.choose('Active', 'Inactive'); // 'Active' -false.choose('Active', 'Inactive'); // 'Inactive' -true.toInt(); // 1 -false.toInt(); // 0 -true.toYesNo(); // 'Yes' -false.toYesNo(trueString: 'On', falseString: 'Off'); // 'Off' -true.not; // false -``` - -------------------------------------------------------------------------------- - -### ๐Ÿ—“๏ธ DateTime Extensions - -#### โœ… Checkers -- `isAfternoon` โ€“ Checks if the time is between 12:00 PM and 5:59 PM. - -- `isMorning` โ€“ Checks if the time is before 12:00 PM. -- `isEvening` โ€“ Checks if the time is between 6:00 PM and 11:59 PM. -- `isNight` โ€“ Checks if the time is between midnight and 5:59 AM. -- `isToday` / `isTomorrow` / `isYesterday` โ€“ Quickly check the relation to the current day. -- `isSameDay(DateTime other)` โ€“ Returns `true` if the date is the same calendar day as `other`. -- `isInPast` / `isInFuture` โ€“ Check if the datetime is before or after now. - -#### ๐Ÿ”ง Utilities -- `startOfDay()` โ€“ Returns the start of the day (midnight) for the datetime. - -#### ๐Ÿ”ง Manipulators -- `addDays(int)` / `addMonths(int)` / `addYears(int)` โ€“ Add to the datetime. - -- `addHours(int)` / `addMinutes(int)` / `addSeconds(int)` โ€“ Add smaller units. -- `subDays(int)` / `subMonths(int)` / `subYears(int)` โ€“ Subtract from the datetime. -- `subHours(int)` / `subMinutes(int)` / `subSeconds(int)` โ€“ Subtract smaller units. - -#### ๐Ÿ”ข Age -- `toAge()` โ€“ Convert the date to an age in years. - -- `isAgeOlder(age)` / `isAgeYounger(age)` / `isAgeEqualTo(age)` โ€“ Check against an age. -- `isAgeBetween(min, max)` โ€“ Check if the age is within a given range. - -#### ๐Ÿง  Time to String - -- `format(String format)` โ€“ Fully custom format using `intl`. - > Popular date and time formats included in the [MayrDateTimeFormats] class. - > - > Currently includes: - > - `MayrDateTimeFormats.ukDate` - dd/MM/yyyy - > - `MayrDateTimeFormats.ukDateTime` - dd/MM/yyyy HH:mm:ss - > - `MayrDateTimeFormats.usDate` - yyyy-MM-dd - > - `MayrDateTimeFormats.usDateTime` - yyyy-MM-dd HH:mm:ss - > - `MayrDateTimeFormats.time` - HH:mm:ss - > - `MayrDateTimeFormats.timeNoSecs` - HH:mm - -- `toDayOrdinal()` โ€“ Get the day of the month with ordinal (e.g. `1st`, `22nd`, `31st`). -- `toTimeAgoString()` โ€“ Human-readable "time ago" format (e.g. "2 days ago"). -- `toTimeString()` โ€“ Convert to time only (e.g. `14:35` or `14:35:59`). -- `toShortDate()` โ€“ Returns a short formatted date string (e.g. `Wed 15th Jan`). - -------------------------------------------------------------------------------- - -### โณ Duration Extensions - -- `delay([callback])` โ€“ Delays execution for the given duration. Optionally accepts a callback to run after the delay. -- `toReadableString()` โ€“ Returns a human-readable string representation (e.g., '2h 30m', '1d 5h 30m'). -- `isLongerThan(other)` โ€“ Checks if this duration is longer than another duration. -- `isShorterThan(other)` โ€“ Checks if this duration is shorter than another duration. - -```dart -// Example -await 2.seconds.delay(() { - print('Delayed by 2 seconds'); -}); - -final duration = Duration(hours: 2, minutes: 30); -print(duration.toReadableString()); // '2h 30m' - -5.seconds.isLongerThan(3.seconds); // true -3.seconds.isShorterThan(5.seconds); // true -``` - -------------------------------------------------------------------------------- - -### ๐Ÿ”ท Object Extensions - -- `let(transform)` โ€“ Executes a function with this object as its argument and returns the result. Useful for chaining operations or transforming values inline. -- `also(action)` โ€“ Executes a function with this object and returns this object. Useful for performing side effects while maintaining the original value for further chaining. - -```dart -// Examples -final result = 'hello'.let((it) => it.toUpperCase()); // 'HELLO' -final length = 'test'.let((it) => it.length); // 4 - -final user = User('John') - .also((it) => print('Created user: ${it.name}')) - .also((it) => log.info('User created')); -``` - -------------------------------------------------------------------------------- - -### ๐ŸŒ€ Dynamic Extensions - -- `nullOnDebug()` โ€“ Returns `null` **only in debug mode**; retains value in release/profile. Useful for testing nullable flows. - -- `onlyOnDebug()` โ€“ Returns the value **only in debug mode**, otherwise `null`. -- `maybe({double probability = 0.5})` โ€“ Randomly returns `null` based on the given probability (between 0.0 and 1.0). Great for simulating unreliable data in tests or dev mode. - - ```dart - final value = 'Simulate me'.maybe(probability: 0.3); - // Has a 30% chance of being null - ``` -- `orDefault(T fallback)` - Returns the fallback value if the provided value is null - -------------------------------------------------------------------------------- - -### ๐Ÿ–ผ๏ธ Image Extensions - -- `circleAvatar({ ... })` โ€“ Quickly convert an `ImageProvider` to a `CircleAvatar` widget with full customisation options. +### โœจ Quick Examples ```dart -// Example -NetworkImage('https://example.com/pic.jpg').circleAvatar(radius: 40); -``` - -#### Parameters: - -- backgroundColor โ€“ Background colour of the avatar (default is transparent). -- radius โ€“ Sets the circular radius of the avatar. -- minRadius / maxRadius โ€“ Optional constraints. -- foregroundColor โ€“ Colour for the foreground image. -- onBackgroundImageError / onForegroundImageError โ€“ Handle image load failures. - -------------------------------------------------------------------------------- - -### ๐Ÿ”ข Number Extensions +// Duration helpers +await 2.seconds.delay(() => print('Done!')); -#### ๐Ÿงฎ General Num Extensions -- `isEqual(otherNum)` โ€“ Checks if two numbers are exactly equal. +// String utilities +'hello world'.camelCase; // helloWorld +'user@example.com'.isEmail; // true -- `isGreaterThan(otherNum)` โ€“ Returns `true` if the number is greater. -- `isLessThan(otherNum)` โ€“ Returns `true` if the number is less. -- `clampMin(min)` โ€“ Clamps the number to a minimum value. -- `clampMax(max)` โ€“ Clamps the number to a maximum value. -- `isBetween(min, max)` โ€“ Checks if the number is within a range (inclusive). -- `isPositive` โ€“ Returns `true` if the number is greater than zero. -- `isNegativeNumber` โ€“ Returns `true` if the number is less than zero. -- `isZero` โ€“ Returns `true` if the number equals zero. - -#### ๐Ÿ”ข Integer Extensions -- `isEvenNumber` โ€“ Checks if the integer is even. -- `isOddNumber` โ€“ Checks if the integer is odd. -- `times(action)` โ€“ Repeats an action n times. -- `timesIndexed(action)` โ€“ Repeats an action n times with the current index. - -```dart -// Example -5.isBetween(1, 10); // true -3.times(() => print('Hello')); // Prints 'Hello' 3 times -3.timesIndexed((i) => print('Index: $i')); // Prints indices 0, 1, 2 -``` - -#### ๐ŸŽฒ Random Generators -- `randomLess({min = 1.0})` โ€“ For `int` or `double`, generates a random value **less than** the current one, starting from the `min`. - -- `randomMore(max)` โ€“ Generates a random value **greater than** the current one, up to `max`. - -```dart -10.randomLess(); // e.g. returns 3, 7, etc. -5.5.randomMore(10.0); // e.g. returns 6.23, etc. -``` - -#### ๐ŸŽฏ Double Extensions -- `toDecimalPlaces(places)` โ€“ Rounds the double to a specified number of decimal places. - -```dart -3.14159.toDecimalPlaces(2); // 3.14 -3.14159.toDecimalPlaces(4); // 3.1416 -``` - -#### ๐Ÿ’ฐ Number Formatting -- `formatAsCurrency({locale, symbol, decimalDigits})` โ€“ Formats the number as currency. - -- `formatAsDecimal({locale, decimalDigits})` โ€“ Formats the number as a decimal with specified precision. -- `formatAsNumber({locale})` โ€“ Formats as a regular number string. - -```dart -1234.5.formatAsCurrency(locale: 'en_NG', symbol: 'โ‚ฆ'); // โ‚ฆ1,234.50 -``` - -#### โฑ๏ธ Number to Duration -- `days`, `hours`, `minutes`, `seconds`, `milliseconds`, `microseconds` โ€“ Shorthand for converting numbers to Duration. - -```dart -// Example -await 2.seconds.delay(); // Waits for 2 seconds -``` - -------------------------------------------------------------------------------- - -### ๐Ÿ”ค String Extensions - -#### โœ… Utilities -- `copyToClipboard()` - Copies the string to clipboard. - -- `matchesRegExp(regex)` โ€“ Checks if the string matches a given regular expression. -- `toBool` โ€“ Converts `"true"` or `"false"` to a boolean. -- `toDateTime()` โ€“ Parses the string into a `DateTime` object. Returns null if parse fails -- `toRegExp()` โ€“ Converts the string into a `RegExp`. -- `toUri()` - Attempts to parse the string to a `Uri` -- `limit(maxLength, [overflow = "โ€ฆ"])` โ€“ Limits string length with optional overflow characters. -- `mask({start = 2, end = 2, maskChar = '*', maskLength})` โ€“ Masks the middle of the string, leaving edges visible. - ```dart - '08012345678'.mask(); // 08*******78 - '08012345678'.mask(maskLength: 2); // 08**78 - ``` -- `reverse()` โ€“ Reverses the string. -- `isBlank` / `isNotBlank` โ€“ Checks if the string is empty or contains only whitespace. -- `removeWhitespace()` โ€“ Removes all whitespace from the string. -- `countOccurrences(substring)` โ€“ Counts how many times a substring appears. -- `truncate(maxLength, {ellipsis})` โ€“ Truncates the string with word boundary awareness. -- `wrap(prefix, [suffix])` โ€“ Wraps the string with a prefix and optional suffix. -- `removePrefix(prefix)` โ€“ Removes a prefix if it exists. -- `removeSuffix(suffix)` โ€“ Removes a suffix if it exists. - -```dart -// Examples -'hello'.reverse(); // 'olleh' -' '.isBlank; // true -'hello world'.removeWhitespace(); // 'helloworld' -'hello world'.countOccurrences('l'); // 3 -'The quick brown fox'.truncate(10); // 'The quick...' -'text'.wrap('"'); // '"text"' -'Hello World'.removePrefix('Hello '); // 'World' -``` - -#### ๐ŸŽ€ Pretty Printing - -- `prettyJson()` โ€“ Formats a raw JSON string. - -- `prettyXml()` โ€“ Formats raw XML into readable indents. -- `prettyYaml()` โ€“ Formats YAML strings prettily. - -#### ๐Ÿ”  Casing - -- `camelCase` โ€“ Converts string to camelCase. - -- `capitalised` โ€“ Capitalises the first letter of each word. -- `kebabCase` โ€“ Converts string to kebab-case. -- `pascalCase` โ€“ Converts string to PascalCase. -- `snakeCase` โ€“ Converts string to snake_case. -- `titleCase` โ€“ Converts string to Title Case. - -```dart -'the big brown fox'.camelCase; // theBigBrownFox -'the big brown fox'.capitalised; // The big brown fox -'the big brown fox'.pascalCase; // TheBigBrownFox -'the big brown fox'.kebabCase; // the-big-brown-fox -'the big brown fox'.snakeCase; // the_-_big_-_brown_-_fox -'the big brown fox'.titleCase; // The Big Brown Fox -``` - -#### ๐Ÿงช Case and Pattern Checkers - -##### Case Checkers - -- `isCamelCase` - -- `isPascalCase` -- `isSnakeCase` -- `isKebabCase` -- `isTitleCase` -- `isCapitalised` -- `isUpperCase` -- `isLowerCase` - -##### Pattern Checkers - -- `isEmail` - -- `isURL` -- `isUlid` -- `isUuid` -- `isSlug` -- `isHexColor` -- `isIPAddress` -- `isNum โ€“ Validates numeric string` -- `isAlphabetOnly` -- `isNumericOnly` - - -------------------------------------------------------------------------------- - -### ๐Ÿงฉ Iterable / List Extensions - -#### Query & Search - -- `firstOrNull()` โ†’ Returns first element or `null` if empty -- `lastOrNull()` โ†’ Returns last element or `null` if empty -- `singleWhereOrNull(predicate)` โ†’ Returns match or `null` -- `containsWhere(predicate)` โ†’ Boolean check -- `indexWhereOrNull(predicate)` โ†’ Returns index or `null` - -```dart -// Examples +// List operations [1, 2, 3].firstOrNull(); // 1 -[].firstOrNull(); // null -[1, 2, 3].singleWhereOrNull((e) => e == 2); // 2 -[1, 2, 3].containsWhere((e) => e > 2); // true -``` +[1, null, 2].whereNotNull(); // [1, 2] -#### Safe Access +// Number formatting +1234.5.formatAsCurrency(locale: 'en_US', symbol: '\$'); // \$1,234.50 -- `getOrNull(index)` โ†’ Returns element at index or `null` -- `getOrDefault(index, defaultValue)` โ†’ Returns element or default value +// Widget helpers +Text('Hello').paddingAll(16).center(); -```dart -// Examples -[1, 2, 3].getOrNull(1); // 2 -[1, 2, 3].getOrNull(5); // null -[1, 2, 3].getOrDefault(5, 0); // 0 +// Boolean utilities +true.choose('Yes', 'No'); // 'Yes' ``` -#### Transformations - -- `chunked(size)` โ†’ Splits into chunks -- `mapIndexed((index, item) => ...)` โ†’ Maps with index -- `whereNotNull()` โ†’ Filters out nulls -- `distinctBy(keySelector)` โ†’ Unique items by property -- `flatten()` โ†’ Flattens nested lists -- `sortedBy(keySelector)` / `sortedByDesc(keySelector)` โ†’ Sort by property -- `flip()` โ†’ Reverses the list - -```dart -// Examples -[1, 2, 3, 4, 5].chunked(2); // [[1, 2], [3, 4], [5]] -['a', 'b', 'c'].mapIndexed((i, e) => '$i: $e'); // ['0: a', '1: b', '2: c'] -[1, null, 2, null, 3].whereNotNull(); // [1, 2, 3] -[[1, 2], [3, 4]].flatten(); // [1, 2, 3, 4] -[1, 2, 3].flip(); // [3, 2, 1] -``` - -#### Aggregations (Only available on list of numbers) - -- `sumBy(num Function(T))` โ†’ Sum elements by selector -- `averageBy(num Function(T))` โ†’ Average by selector -- `min()` โ†’ Minimum value -- `max()` โ†’ Maximum value -- `countWhere(predicate)` โ†’ Count matching elements - -```dart -// Examples -[1, 2, 3, 4, 5].sumBy((e) => e); // 15 -[1, 2, 3, 4, 5].averageBy((e) => e); // 3.0 -[3, 1, 4, 1, 5].min(); // 1 -[3, 1, 4, 1, 5].max(); // 5 -[1, 2, 3, 4, 5].countWhere((e) => e > 3); // 2 -``` - -#### Mutation Helpers (returns new copy) - -- `insertIf(condition, value)` โ†’ Insert conditionally -- `replaceWhere(predicate, newValue)` โ†’ Replace matching elements -- `removeWhereNot(predicate)` โ†’ Keep only matching elements -- `updateWhere(predicate, updater)` โ†’ Update matching elements -- `addIf(value)` / `addAllIf(values)` โ†’ Add conditionally -- `append(value)` / `appendAll(values)` โ†’ Append elements -- `appendIf(value)` / `appendAllIf(values)` โ†’ Append conditionally -- `pop()` โ†’ Remove and return last element -- `fliter(predicate)` โ†’ Filter elements -- `unique()` โ†’ Get unique elements - -```dart -// Examples -[1, 2, 3].insertIf(true, 4); // [1, 2, 3, 4] -[1, 2, 3, 2].replaceWhere((e) => e == 2, 5); // [1, 5, 3, 5] -[1, 2, 3, 4, 5].removeWhereNot((e) => e > 2); // [3, 4, 5] -[1, 2, 2, 3, 3, 4].unique(); // [1, 2, 3, 4] -``` - -#### Utility - -- `isNullOrEmpty()` โ†’ Check if empty -- `joinToString(separator, transform)` โ†’ Join with custom format -- `forEachIndexed()` โ†’ Iterate with index - -```dart -// Examples -[].isNullOrEmpty(); // true -[1, 2, 3].joinToString(separator: ', '); // '1, 2, 3' -['a', 'b'].forEachIndexed((i, e) => print('$i: $e')); -``` - -------------------------------------------------------------------------------- - -### ๐Ÿ—บ๏ธ Map Extensions - -#### Safe Access - -- `getOrNull(key)` โ†’ Get value or null -- `getOrDefault(key, defaultValue)` โ†’ Get value or default - -```dart -// Examples -{'a': 1, 'b': 2}.getOrNull('a'); // 1 -{'a': 1, 'b': 2}.getOrNull('c'); // null -{'a': 1, 'b': 2}.getOrDefault('c', 0); // 0 -``` - -#### Transformations - -- `mapKeys((k, v) => newKey)` โ†’ Transform keys -- `mapValues((k, v) => newValue)` โ†’ Transform values -- `filterKeys(predicate)` โ†’ Filter by keys -- `filterValues(predicate)` โ†’ Filter by values -- `invert()` โ†’ Swap keys and values - -```dart -// Examples -{'a': 1, 'b': 2}.mapKeys((k, v) => k.toUpperCase()); // {'A': 1, 'B': 2} -{'a': 1, 'b': 2}.mapValues((k, v) => v * 2); // {'a': 2, 'b': 4} -{'a': 1, 'b': 2}.invert(); // {1: 'a', 2: 'b'} -``` - -#### Merge & Combine - -- `merge(otherMap)` โ†’ Merge with precedence -- `mergeIfAbsent(otherMap)` โ†’ Merge without overriding -- `combine(other, (k, v1, v2) => mergedValue)` โ†’ Custom merge - -```dart -// Examples -{'a': 1, 'b': 2}.merge({'b': 3, 'c': 4}); // {'a': 1, 'b': 3, 'c': 4} -{'a': 1, 'b': 2}.mergeIfAbsent({'b': 3, 'c': 4}); // {'a': 1, 'b': 2, 'c': 4} -``` - -#### Utility - -- `keysWhere(predicate)` โ†’ Get keys matching predicate -- `valuesWhere(predicate)` โ†’ Get values matching predicate -- `toQueryString()` โ†’ Convert to URL query string - -```dart -// Examples -{'a': 1, 'b': 2, 'c': 3}.keysWhere((v) => v > 1); // ['b', 'c'] -{'name': 'John', 'age': '30'}.toQueryString(); // 'name=John&age=30' -``` - -------------------------------------------------------------------------------- - -### ๐Ÿ”ข Set Extensions - -- `toggle(element)` โ†’ Adds if missing, removes if present -- `intersects(otherSet)` โ†’ Check for intersection -- `isSubsetOf(otherSet)` โ†’ Check if subset -- `isSupersetOf(otherSet)` โ†’ Check if superset -- `unionAll(sets)` โ†’ Union of multiple sets -- `without(element)` โ†’ Remove element - -```dart -// Examples -{1, 2, 3}.toggle(2); // {1, 3} -{1, 2, 3}.toggle(4); // {1, 2, 3, 4} -{1, 2, 3}.intersects({2, 3, 4}); // true -{1, 2}.isSubsetOf({1, 2, 3}); // true -{1, 2}.unionAll([{2, 3}, {3, 4}]); // {1, 2, 3, 4} -``` - -------------------------------------------------------------------------------- - -### ๐ŸŽฏ Humanize Extensions - -The goal of `humanize` is simple: - -> Convert technical or numeric values into **readable, natural, human-friendly strings**. - -Where computers speak in seconds, bytes, and counts, `humanize` translates them into something that sounds like it came from a person. - -#### Durations - -- `.humanize(locale)` โ†’ "2 hours, 3 minutes" - -```dart -// Examples -Duration(hours: 2, minutes: 3).humanize(); // '2 hours, 3 minutes' -Duration(days: 1).humanize(); // '1 day' -Duration(seconds: 45).humanize(); // '45 seconds' -``` - -#### Time (DateTime) - -- `.humanize(locale)` โ†’ "just now", "3 hours ago", "yesterday", "last week", "3 days from now", "2 weeks ago" - -```dart -// Examples -DateTime.now().humanize(); // 'just now' -DateTime.now().subtract(Duration(hours: 3)).humanize(); // '3 hours ago' -DateTime.now().subtract(Duration(days: 1)).humanize(); // 'yesterday' -DateTime.now().add(Duration(days: 2)).humanize(); // 'in 2 days' -``` - -#### Numbers - -- `humanizeNumber()` โ†’ "15.3k", "1.5M" -- `humanizeOrdinal()` โ†’ "1st", "2nd", "3rd" -- `humanizeCount('item')` โ†’ "1 item" / "3 items" -- `humanizePercentage(max, min)` โ†’ "74%" -- `humanizeFileSize()` โ†’ "1.0 MB", "520.3 KB" - -```dart -// Examples -1234.humanizeNumber(); // '1.2k' -1500000.humanizeNumber(); // '1.5M' -1.humanizeOrdinal(); // '1st' -21.humanizeOrdinal(); // '21st' -3.humanizeCount('item'); // '3 items' -0.75.humanizePercentage(); // '75%' -1024.humanizeFileSize(); // '1.0 KB' -520300.humanizeFileSize(); // '508.1 KB' -``` - -------------------------------------------------------------------------------- - -### ๐Ÿงฉ Widget Extensions - -#### ๐Ÿช„ Basic Transformations - -- `center({heightFactor, widthFactor})` โ€“ Wraps widget in a `Center`. - -- `expanded([flex = 1])` โ€“ Wraps widget in an `Expanded`. -- `flexible({flex = 1, fit = FlexFit.loose})` โ€“ Wraps widget in a `Flexible`. -- `opacity(opacity)` โ€“ Wraps widget with an `Opacity` widget. -- `sizedBox({width, height})` โ€“ Wraps widget with a `SizedBox`. -- `constrained({maxHeight, maxWidth, minHeight, minWidth})` โ€“ Wraps widget with a `ConstrainedBox`. - ---- - -#### โœ‚๏ธ Clipping -- `clipRect()` โ€“ Clips widget to a rectangle. - -- `clipRRect(borderRadius)` โ€“ Clips widget with rounded corners. -- `clipRounded([radius = 12])` โ€“ Quickly clip widget with a uniform rounded border. - ---- - -#### ๐Ÿงน Padding -- `paddingAll(padding)` โ€“ Adds equal padding on all sides. - -- `paddingSymmetric({horizontal, vertical})` โ€“ Adds symmetric horizontal and vertical padding. -- `paddingOnly({left, top, right, bottom})` โ€“ Custom padding for specific sides. -- `paddingZero()` โ€“ Adds zero padding. - ---- - -#### ๐Ÿงญ Positioning -- `positionAlign(alignment)` โ€“ Aligns widget using `Align`. - -- `positionedFill()` โ€“ Fills parent constraints using `Positioned.fill`. - ---- - -#### ๐Ÿ‘ป Visibility Helpers -- `hideIf(condition)` โ€“ Hides widget (returns `SizedBox.shrink()`) if `condition` is true. - -- `hideUnless(condition)` โ€“ Hides widget unless `condition` is true. -- `showIf(condition)` โ€“ Shows widget if `condition` is true, otherwise hides. -- `showUnless(condition)` โ€“ Shows widget unless `condition` is true. - ---- - -#### ๐Ÿ”˜ inkwellManager on Widget -A helper class for managing taps on a widget in a cleaner way. - -- `inkWellManager(callback, {color = Colors.transparent})` โ€“ Wraps widget with an `InkWell` for tap detection. - -- `onTap()` โ€“ Wraps child with `InkWell` for tap gesture. -- `onDoubleTap()` โ€“ Wraps child with `InkWell` for double-tap gesture. -- `onLongPress()` โ€“ Wraps child with `InkWell` for long-press gesture. - -> **Tip:** Used alongside the `inkWellManager` extension to easily attach tap interactions without boilerplate. - -```dart -Text('Click Me') - .inkWellManager(() => print('Tapped'), color: Colors.black) - .onTap(); -``` - -##### ๐Ÿ’ก Why InkWellManager? - -Normally, to make a widget respond to taps, you must manually wrap it inside an InkWell every time, setting colours and callbacks. -InkWellManager simplifies this by providing quick `.onTap()`, `.onDoubleTap()`, and `.onLongPress()` methods โ€” making your code shorter, cleaner, and more maintainable. - -It also auto-applies the same splash, hover, and focus colours without extra setup. - -### ๐Ÿ“œ DateTimeFormat - -This package also include some common date time formats. These inlude: -- `MayrDateTimeFormats.ukDate` -- `MayrDateTimeFormats.usDate` -- `MayrDateTimeFormats.time` -- `MayrDateTimeFormats.timeNoSecs` -- `MayrDateTimeFormats.ukDateTime` -- `MayrDateTimeFormats.usDateTime` +For detailed documentation on each extension type, click the links above or visit the [docs](docs/) directory. ## Usage diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..4ddb13b --- /dev/null +++ b/docs/README.md @@ -0,0 +1,48 @@ +# ๐Ÿ“š Documentation + +Welcome to the comprehensive documentation for **mayr_extensions**! + +This directory contains detailed guides for all available extensions. + +## ๐Ÿ“– Extension Guides + +### Core Extensions +- [๐Ÿงฑ BuildContext Extensions](./build_context_extensions.md) - Navigation, media query, scaffold, and more +- [โœ… Bool Extensions](./bool_extensions.md) - Boolean utilities and transformations +- [๐Ÿ—“๏ธ DateTime Extensions](./datetime_extensions.md) - Date manipulation, formatting, and utilities +- [โณ Duration Extensions](./duration_extensions.md) - Duration helpers and formatting +- [๐Ÿ”ท Object Extensions](./object_extensions.md) - Object transformation utilities +- [๐ŸŒ€ Dynamic Extensions](./dynamic_extensions.md) - Nullable and debug utilities + +### Type Extensions +- [๐Ÿ”ข Number Extensions](./number_extensions.md) - Numeric utilities, formatting, and conversions +- [๐Ÿ”ค String Extensions](./string_extensions.md) - String manipulation, parsing, and formatting +- [๐Ÿงฉ Iterable/List Extensions](./list_extensions.md) - List operations and utilities +- [๐Ÿ—บ๏ธ Map Extensions](./map_extensions.md) - Map transformations and utilities +- [๐Ÿ”ข Set Extensions](./set_extensions.md) - Set operations + +### UI Extensions +- [๐Ÿ–ผ๏ธ Image Extensions](./image_extensions.md) - Image widget helpers +- [๐Ÿงฉ Widget Extensions](./widget_extensions.md) - Widget transformations and utilities + +### Other Extensions +- [๐ŸŽฏ Humanize Extensions](./humanize_extensions.md) - Human-readable formatting +- [๐Ÿ“œ DateTimeFormat](./datetime_format.md) - Pre-defined date/time formats + +## ๐Ÿš€ Quick Links + +- [Installation & Setup](../README.md#-installation--setup) +- [Usage Examples](../README.md#usage) +- [Contributing Guidelines](../README.md#-contributing) +- [Changelog](../CHANGELOG.md) + +## ๐Ÿ’ก Need Help? + +If you can't find what you're looking for: +- Check the [main README](../README.md) for a quick overview +- Browse the individual extension documentation files above +- [Open an issue](https://github.com/YoungMayor/flutter_utils_extensions/issues) if you have questions + +--- + +**Made with โค๏ธ by the mayr_extensions community** diff --git a/docs/bool_extensions.md b/docs/bool_extensions.md new file mode 100644 index 0000000..c794b84 --- /dev/null +++ b/docs/bool_extensions.md @@ -0,0 +1,33 @@ +# โœ… Bool Extensions + +Utilities for working with boolean values. + +## Methods + +- `choose(trueValue, falseValue)` โ€“ Returns `trueValue` if the boolean is true, otherwise returns `falseValue` +- `toInt()` โ€“ Converts the boolean to an integer (1 for true, 0 for false) +- `toYesNo({trueString, falseString})` โ€“ Converts to a string representation with customizable values +- `not` โ€“ Returns the negation of the boolean (equivalent to `!this`) + +## Examples + +```dart +// Choose between values +true.choose('Active', 'Inactive'); // 'Active' +false.choose('Active', 'Inactive'); // 'Inactive' + +// Convert to integer +true.toInt(); // 1 +false.toInt(); // 0 + +// Convert to Yes/No strings +true.toYesNo(); // 'Yes' +false.toYesNo(trueString: 'On', falseString: 'Off'); // 'Off' + +// Negation +true.not; // false +``` + +## Back to Documentation + +[โ† Back to main documentation](./README.md) diff --git a/docs/build_context_extensions.md b/docs/build_context_extensions.md new file mode 100644 index 0000000..e3fef94 --- /dev/null +++ b/docs/build_context_extensions.md @@ -0,0 +1,42 @@ +# ๐Ÿงฑ BuildContext Extensions + +Extensions that make working with `BuildContext` easier and more expressive. + +## Quick Access Properties + +### Form & Navigation + +- `form` โ€“ Easily access the nearest `FormState` using `context.form` +- `navigator` โ€“ Shorthand for `Navigator.of(context)` +- `overlay` โ€“ Access the current `OverlayState` from the context +- `scaffold` โ€“ Retrieve the nearest `ScaffoldState` with `context.scaffold` + +## ScaffoldMessenger + +- `scaffoldMessenger` โ€“ Quickly get the `ScaffoldMessengerState` for showing snackbars and more +- `void showSnackBar(String content, {Duration? duration, SnackBarBehavior behavior = SnackBarBehavior.fixed})` - Quickly show a SnackBar without manually accessing ScaffoldMessenger + +## Media Query + +Access media query information with ease: + +- `mediaQuery` โ€“ Access `MediaQueryData` from context +- `platformBrightness` โ€“ Get the system's brightness setting (`Brightness.dark` or `Brightness.light`) +- `platformInDarkMode` | `platformInLightMode` โ€“ Returns `true` based on the app's current brightness mode +- `widgetSize` โ€“ Get the rendered size of the widget associated with the context +- `widgetHeight` โ€“ Convenience getter for just the height of the widget +- `widgetWidth` โ€“ Convenience getter for just the width + +## Media Query Orientation + +- `orientation` โ€“ Access the current screen orientation (`portrait` or `landscape`) +- `isLandscape` / `isPortrait` โ€“ Easy checks for current orientation +- `widgetShortestSide` โ€“ Useful for responsive layouts based on the device's shortest screen edge +- `isPhone` โ€“ Returns `true` if the device is considered a phone +- `isSmallTablet`, `isLargeTablet` โ€“ Classify tablets based on width +- `isTablet` โ€“ Shortcut combining both small and large tablets +- `isDesktop` โ€“ Detects larger screens, typically desktops + +## Back to Documentation + +[โ† Back to main documentation](./README.md) diff --git a/docs/datetime_extensions.md b/docs/datetime_extensions.md new file mode 100644 index 0000000..b719dfa --- /dev/null +++ b/docs/datetime_extensions.md @@ -0,0 +1,58 @@ +# ๐Ÿ—“๏ธ DateTime Extensions + +Comprehensive utilities for working with dates and times. + +## โœ… Checkers + +- `isAfternoon` โ€“ Checks if the time is between 12:00 PM and 5:59 PM +- `isMorning` โ€“ Checks if the time is before 12:00 PM +- `isEvening` โ€“ Checks if the time is between 6:00 PM and 11:59 PM +- `isNight` โ€“ Checks if the time is between midnight and 5:59 AM +- `isToday` / `isTomorrow` / `isYesterday` โ€“ Quickly check the relation to the current day +- `isSameDay(DateTime other)` โ€“ Returns `true` if the date is the same calendar day as `other` +- `isInPast` / `isInFuture` โ€“ Check if the datetime is before or after now + +## ๐Ÿ”ง Utilities + +- `startOfDay()` โ€“ Returns the start of the day (midnight) for the datetime + +## ๐Ÿ”ง Manipulators + +### Adding Time + +- `addDays(int)` / `addMonths(int)` / `addYears(int)` โ€“ Add to the datetime +- `addHours(int)` / `addMinutes(int)` / `addSeconds(int)` โ€“ Add smaller units + +### Subtracting Time + +- `subDays(int)` / `subMonths(int)` / `subYears(int)` โ€“ Subtract from the datetime +- `subHours(int)` / `subMinutes(int)` / `subSeconds(int)` โ€“ Subtract smaller units + +## ๐Ÿ”ข Age + +- `toAge()` โ€“ Convert the date to an age in years +- `isAgeOlder(age)` / `isAgeYounger(age)` / `isAgeEqualTo(age)` โ€“ Check against an age +- `isAgeBetween(min, max)` โ€“ Check if the age is within a given range + +## ๐Ÿง  Time to String + +- `format(String format)` โ€“ Fully custom format using `intl` + + > Popular date and time formats included in the [MayrDateTimeFormats] class. + > + > Currently includes: + > - `MayrDateTimeFormats.ukDate` - dd/MM/yyyy + > - `MayrDateTimeFormats.ukDateTime` - dd/MM/yyyy HH:mm:ss + > - `MayrDateTimeFormats.usDate` - yyyy-MM-dd + > - `MayrDateTimeFormats.usDateTime` - yyyy-MM-dd HH:mm:ss + > - `MayrDateTimeFormats.time` - HH:mm:ss + > - `MayrDateTimeFormats.timeNoSecs` - HH:mm + +- `toDayOrdinal()` โ€“ Get the day of the month with ordinal (e.g. `1st`, `22nd`, `31st`) +- `toTimeAgoString()` โ€“ Human-readable "time ago" format (e.g. "2 days ago") +- `toTimeString()` โ€“ Convert to time only (e.g. `14:35` or `14:35:59`) +- `toShortDate()` โ€“ Returns a short formatted date string (e.g. `Wed 15th Jan`) + +## Back to Documentation + +[โ† Back to main documentation](./README.md) diff --git a/docs/datetime_format.md b/docs/datetime_format.md new file mode 100644 index 0000000..25ad066 --- /dev/null +++ b/docs/datetime_format.md @@ -0,0 +1,34 @@ +# ๐Ÿ“œ DateTimeFormat + +Pre-defined date and time format strings for common use cases. + +This package includes some common date time formats that you can use with the `format()` extension method on `DateTime` objects. + +## Available Formats + +- `MayrDateTimeFormats.ukDate` - **dd/MM/yyyy** (e.g., 15/01/2025) +- `MayrDateTimeFormats.ukDateTime` - **dd/MM/yyyy HH:mm:ss** (e.g., 15/01/2025 14:30:45) +- `MayrDateTimeFormats.usDate` - **yyyy-MM-dd** (e.g., 2025-01-15) +- `MayrDateTimeFormats.usDateTime` - **yyyy-MM-dd HH:mm:ss** (e.g., 2025-01-15 14:30:45) +- `MayrDateTimeFormats.time` - **HH:mm:ss** (e.g., 14:30:45) +- `MayrDateTimeFormats.timeNoSecs` - **HH:mm** (e.g., 14:30) + +## Usage + +```dart +import 'package:mayr_extensions/mayr_extensions.dart'; + +final now = DateTime.now(); + +// Use pre-defined formats +print(now.format(MayrDateTimeFormats.ukDate)); // 15/01/2025 +print(now.format(MayrDateTimeFormats.usDateTime)); // 2025-01-15 14:30:45 +print(now.format(MayrDateTimeFormats.time)); // 14:30:45 + +// Or use custom formats +print(now.format('EEEE, MMMM d, yyyy')); // Wednesday, January 15, 2025 +``` + +## Back to Documentation + +[โ† Back to main documentation](./README.md) diff --git a/docs/duration_extensions.md b/docs/duration_extensions.md new file mode 100644 index 0000000..2c568f1 --- /dev/null +++ b/docs/duration_extensions.md @@ -0,0 +1,31 @@ +# โณ Duration Extensions + +Extensions for working with `Duration` objects. + +## Methods + +- `delay([callback])` โ€“ Delays execution for the given duration. Optionally accepts a callback to run after the delay +- `toReadableString()` โ€“ Returns a human-readable string representation (e.g., '2h 30m', '1d 5h 30m') +- `isLongerThan(other)` โ€“ Checks if this duration is longer than another duration +- `isShorterThan(other)` โ€“ Checks if this duration is shorter than another duration + +## Examples + +```dart +// Delay execution +await 2.seconds.delay(() { + print('Delayed by 2 seconds'); +}); + +// Human-readable string +final duration = Duration(hours: 2, minutes: 30); +print(duration.toReadableString()); // '2h 30m' + +// Compare durations +5.seconds.isLongerThan(3.seconds); // true +3.seconds.isShorterThan(5.seconds); // true +``` + +## Back to Documentation + +[โ† Back to main documentation](./README.md) diff --git a/docs/dynamic_extensions.md b/docs/dynamic_extensions.md new file mode 100644 index 0000000..a2d0c2f --- /dev/null +++ b/docs/dynamic_extensions.md @@ -0,0 +1,26 @@ +# ๐ŸŒ€ Dynamic Extensions + +Extensions for nullable types and debug utilities. + +## Methods + +- `nullOnDebug()` โ€“ Returns `null` **only in debug mode**; retains value in release/profile. Useful for testing nullable flows +- `onlyOnDebug()` โ€“ Returns the value **only in debug mode**, otherwise `null` +- `maybe({double probability = 0.5})` โ€“ Randomly returns `null` based on the given probability (between 0.0 and 1.0). Great for simulating unreliable data in tests or dev mode +- `orDefault(T fallback)` - Returns the fallback value if the provided value is null + +## Examples + +```dart +// Simulate unreliable data +final value = 'Simulate me'.maybe(probability: 0.3); +// Has a 30% chance of being null + +// Provide fallback values +String? name = null; +print(name.orDefault('Anonymous')); // 'Anonymous' +``` + +## Back to Documentation + +[โ† Back to main documentation](./README.md) diff --git a/docs/humanize_extensions.md b/docs/humanize_extensions.md new file mode 100644 index 0000000..4ad20bf --- /dev/null +++ b/docs/humanize_extensions.md @@ -0,0 +1,55 @@ +# ๐ŸŽฏ Humanize Extensions + +Convert technical or numeric values into readable, natural, human-friendly strings. + +> Where computers speak in seconds, bytes, and counts, `humanize` translates them into something that sounds like it came from a person. + +## Durations + +- `.humanize(locale)` โ†’ "2 hours, 3 minutes" + +### Examples + +```dart +Duration(hours: 2, minutes: 3).humanize(); // '2 hours, 3 minutes' +Duration(days: 1).humanize(); // '1 day' +Duration(seconds: 45).humanize(); // '45 seconds' +``` + +## Time (DateTime) + +- `.humanize(locale)` โ†’ "just now", "3 hours ago", "yesterday", "last week", "3 days from now", "2 weeks ago" + +### Examples + +```dart +DateTime.now().humanize(); // 'just now' +DateTime.now().subtract(Duration(hours: 3)).humanize(); // '3 hours ago' +DateTime.now().subtract(Duration(days: 1)).humanize(); // 'yesterday' +DateTime.now().add(Duration(days: 2)).humanize(); // 'in 2 days' +``` + +## Numbers + +- `humanizeNumber()` โ†’ "15.3k", "1.5M" +- `humanizeOrdinal()` โ†’ "1st", "2nd", "3rd" +- `humanizeCount('item')` โ†’ "1 item" / "3 items" +- `humanizePercentage(max, min)` โ†’ "74%" +- `humanizeFileSize()` โ†’ "1.0 MB", "520.3 KB" + +### Examples + +```dart +1234.humanizeNumber(); // '1.2k' +1500000.humanizeNumber(); // '1.5M' +1.humanizeOrdinal(); // '1st' +21.humanizeOrdinal(); // '21st' +3.humanizeCount('item'); // '3 items' +0.75.humanizePercentage(); // '75%' +1024.humanizeFileSize(); // '1.0 KB' +520300.humanizeFileSize(); // '508.1 KB' +``` + +## Back to Documentation + +[โ† Back to main documentation](./README.md) diff --git a/docs/image_extensions.md b/docs/image_extensions.md new file mode 100644 index 0000000..7ddd8a4 --- /dev/null +++ b/docs/image_extensions.md @@ -0,0 +1,26 @@ +# ๐Ÿ–ผ๏ธ Image Extensions + +Extensions for working with `ImageProvider` objects. + +## Methods + +- `circleAvatar({ ... })` โ€“ Quickly convert an `ImageProvider` to a `CircleAvatar` widget with full customisation options + +## Parameters + +- `backgroundColor` โ€“ Background colour of the avatar (default is transparent) +- `radius` โ€“ Sets the circular radius of the avatar +- `minRadius` / `maxRadius` โ€“ Optional constraints +- `foregroundColor` โ€“ Colour for the foreground image +- `onBackgroundImageError` / `onForegroundImageError` โ€“ Handle image load failures + +## Examples + +```dart +// Create a circle avatar from a network image +NetworkImage('https://example.com/pic.jpg').circleAvatar(radius: 40); +``` + +## Back to Documentation + +[โ† Back to main documentation](./README.md) diff --git a/docs/list_extensions.md b/docs/list_extensions.md new file mode 100644 index 0000000..64b0550 --- /dev/null +++ b/docs/list_extensions.md @@ -0,0 +1,111 @@ +# ๐Ÿงฉ Iterable / List Extensions + +Comprehensive extensions for working with lists and iterables. + +## Query & Search + +- `firstOrNull()` โ†’ Returns first element or `null` if empty +- `lastOrNull()` โ†’ Returns last element or `null` if empty +- `singleWhereOrNull(predicate)` โ†’ Returns match or `null` +- `containsWhere(predicate)` โ†’ Boolean check +- `indexWhereOrNull(predicate)` โ†’ Returns index or `null` + +### Examples + +```dart +[1, 2, 3].firstOrNull(); // 1 +[].firstOrNull(); // null +[1, 2, 3].singleWhereOrNull((e) => e == 2); // 2 +[1, 2, 3].containsWhere((e) => e > 2); // true +``` + +## Safe Access + +- `getOrNull(index)` โ†’ Returns element at index or `null` +- `getOrDefault(index, defaultValue)` โ†’ Returns element or default value + +### Examples + +```dart +[1, 2, 3].getOrNull(1); // 2 +[1, 2, 3].getOrNull(5); // null +[1, 2, 3].getOrDefault(5, 0); // 0 +``` + +## Transformations + +- `chunked(size)` โ†’ Splits into chunks +- `mapIndexed((index, item) => ...)` โ†’ Maps with index +- `whereNotNull()` โ†’ Filters out nulls +- `distinctBy(keySelector)` โ†’ Unique items by property +- `flatten()` โ†’ Flattens nested lists +- `sortedBy(keySelector)` / `sortedByDesc(keySelector)` โ†’ Sort by property +- `flip()` โ†’ Reverses the list + +### Examples + +```dart +[1, 2, 3, 4, 5].chunked(2); // [[1, 2], [3, 4], [5]] +['a', 'b', 'c'].mapIndexed((i, e) => '$i: $e'); // ['0: a', '1: b', '2: c'] +[1, null, 2, null, 3].whereNotNull(); // [1, 2, 3] +[[1, 2], [3, 4]].flatten(); // [1, 2, 3, 4] +[1, 2, 3].flip(); // [3, 2, 1] +``` + +## Aggregations (Only available on list of numbers) + +- `sumBy(num Function(T))` โ†’ Sum elements by selector +- `averageBy(num Function(T))` โ†’ Average by selector +- `min()` โ†’ Minimum value +- `max()` โ†’ Maximum value +- `countWhere(predicate)` โ†’ Count matching elements + +### Examples + +```dart +[1, 2, 3, 4, 5].sumBy((e) => e); // 15 +[1, 2, 3, 4, 5].averageBy((e) => e); // 3.0 +[3, 1, 4, 1, 5].min(); // 1 +[3, 1, 4, 1, 5].max(); // 5 +[1, 2, 3, 4, 5].countWhere((e) => e > 3); // 2 +``` + +## Mutation Helpers (returns new copy) + +- `insertIf(condition, value)` โ†’ Insert conditionally +- `replaceWhere(predicate, newValue)` โ†’ Replace matching elements +- `removeWhereNot(predicate)` โ†’ Keep only matching elements +- `updateWhere(predicate, updater)` โ†’ Update matching elements +- `addIf(value)` / `addAllIf(values)` โ†’ Add conditionally +- `append(value)` / `appendAll(values)` โ†’ Append elements +- `appendIf(value)` / `appendAllIf(values)` โ†’ Append conditionally +- `pop()` โ†’ Remove and return last element +- `fliter(predicate)` โ†’ Filter elements (alias for where) +- `unique()` โ†’ Get unique elements + +### Examples + +```dart +[1, 2, 3].insertIf(true, 4); // [1, 2, 3, 4] +[1, 2, 3, 2].replaceWhere((e) => e == 2, 5); // [1, 5, 3, 5] +[1, 2, 3, 4, 5].removeWhereNot((e) => e > 2); // [3, 4, 5] +[1, 2, 2, 3, 3, 4].unique(); // [1, 2, 3, 4] +``` + +## Utility + +- `isNullOrEmpty()` โ†’ Check if empty +- `joinToString(separator, transform)` โ†’ Join with custom format +- `forEachIndexed()` โ†’ Iterate with index + +### Examples + +```dart +[].isNullOrEmpty(); // true +[1, 2, 3].joinToString(separator: ', '); // '1, 2, 3' +['a', 'b'].forEachIndexed((i, e) => print('$i: $e')); +``` + +## Back to Documentation + +[โ† Back to main documentation](./README.md) diff --git a/docs/map_extensions.md b/docs/map_extensions.md new file mode 100644 index 0000000..ea6d9cd --- /dev/null +++ b/docs/map_extensions.md @@ -0,0 +1,62 @@ +# ๐Ÿ—บ๏ธ Map Extensions + +Extensions for working with `Map` objects. + +## Safe Access + +- `getOrNull(key)` โ†’ Get value or null +- `getOrDefault(key, defaultValue)` โ†’ Get value or default + +### Examples + +```dart +{'a': 1, 'b': 2}.getOrNull('a'); // 1 +{'a': 1, 'b': 2}.getOrNull('c'); // null +{'a': 1, 'b': 2}.getOrDefault('c', 0); // 0 +``` + +## Transformations + +- `mapKeys((k, v) => newKey)` โ†’ Transform keys +- `mapValues((k, v) => newValue)` โ†’ Transform values +- `filterKeys(predicate)` โ†’ Filter by keys +- `filterValues(predicate)` โ†’ Filter by values +- `invert()` โ†’ Swap keys and values + +### Examples + +```dart +{'a': 1, 'b': 2}.mapKeys((k, v) => k.toUpperCase()); // {'A': 1, 'B': 2} +{'a': 1, 'b': 2}.mapValues((k, v) => v * 2); // {'a': 2, 'b': 4} +{'a': 1, 'b': 2}.invert(); // {1: 'a', 2: 'b'} +``` + +## Merge & Combine + +- `merge(otherMap)` โ†’ Merge with precedence +- `mergeIfAbsent(otherMap)` โ†’ Merge without overriding +- `combine(other, (k, v1, v2) => mergedValue)` โ†’ Custom merge + +### Examples + +```dart +{'a': 1, 'b': 2}.merge({'b': 3, 'c': 4}); // {'a': 1, 'b': 3, 'c': 4} +{'a': 1, 'b': 2}.mergeIfAbsent({'b': 3, 'c': 4}); // {'a': 1, 'b': 2, 'c': 4} +``` + +## Utility + +- `keysWhere(predicate)` โ†’ Get keys matching predicate +- `valuesWhere(predicate)` โ†’ Get values matching predicate +- `toQueryString()` โ†’ Convert to URL query string + +### Examples + +```dart +{'a': 1, 'b': 2, 'c': 3}.keysWhere((v) => v > 1); // ['b', 'c'] +{'name': 'John', 'age': '30'}.toQueryString(); // 'name=John&age=30' +``` + +## Back to Documentation + +[โ† Back to main documentation](./README.md) diff --git a/docs/number_extensions.md b/docs/number_extensions.md new file mode 100644 index 0000000..c046685 --- /dev/null +++ b/docs/number_extensions.md @@ -0,0 +1,89 @@ +# ๐Ÿ”ข Number Extensions + +Comprehensive extensions for numeric types (`num`, `int`, `double`). + +## ๐Ÿงฎ General Num Extensions + +- `isEqual(otherNum)` โ€“ Checks if two numbers are exactly equal +- `isGreaterThan(otherNum)` โ€“ Returns `true` if the number is greater +- `isLessThan(otherNum)` โ€“ Returns `true` if the number is less +- `clampMin(min)` โ€“ Clamps the number to a minimum value +- `clampMax(max)` โ€“ Clamps the number to a maximum value +- `isBetween(min, max)` โ€“ Checks if the number is within a range (inclusive) +- `isPositive` โ€“ Returns `true` if the number is greater than zero +- `isNegativeNumber` โ€“ Returns `true` if the number is less than zero +- `isZero` โ€“ Returns `true` if the number equals zero + +## ๐Ÿ”ข Integer Extensions + +- `isEvenNumber` โ€“ Checks if the integer is even +- `isOddNumber` โ€“ Checks if the integer is odd +- `times(action)` โ€“ Repeats an action n times +- `timesIndexed(action)` โ€“ Repeats an action n times with the current index + +### Examples + +```dart +5.isBetween(1, 10); // true +3.times(() => print('Hello')); // Prints 'Hello' 3 times +3.timesIndexed((i) => print('Index: $i')); // Prints indices 0, 1, 2 +``` + +## ๐ŸŽฒ Random Generators + +- `randomLess({min = 1.0})` โ€“ For `int` or `double`, generates a random value **less than** the current one, starting from the `min` +- `randomMore(max)` โ€“ Generates a random value **greater than** the current one, up to `max` + +### Examples + +```dart +10.randomLess(); // e.g. returns 3, 7, etc. +5.5.randomMore(10.0); // e.g. returns 6.23, etc. +``` + +## ๐ŸŽฏ Double Extensions + +- `toDecimalPlaces(places)` โ€“ Rounds the double to a specified number of decimal places + +### Examples + +```dart +3.14159.toDecimalPlaces(2); // 3.14 +3.14159.toDecimalPlaces(4); // 3.1416 +``` + +## ๐Ÿ’ฐ Number Formatting + +- `formatAsCurrency({locale, symbol, decimalDigits})` โ€“ Formats the number as currency +- `formatAsDecimal({locale, decimalDigits})` โ€“ Formats the number as a decimal with specified precision +- `formatAsNumber({locale})` โ€“ Formats as a regular number string + +### Examples + +```dart +1234.5.formatAsCurrency(locale: 'en_NG', symbol: 'โ‚ฆ'); // โ‚ฆ1,234.50 +``` + +## โฑ๏ธ Number to Duration + +Shorthand for converting numbers to `Duration`: + +- `days` +- `hours` +- `minutes` +- `seconds` +- `milliseconds` +- `microseconds` + +### Examples + +```dart +// Create durations easily +await 2.seconds.delay(); // Waits for 2 seconds +final timeout = 5.minutes; +final period = 30.days; +``` + +## Back to Documentation + +[โ† Back to main documentation](./README.md) diff --git a/docs/object_extensions.md b/docs/object_extensions.md new file mode 100644 index 0000000..4525c58 --- /dev/null +++ b/docs/object_extensions.md @@ -0,0 +1,25 @@ +# ๐Ÿ”ท Object Extensions + +Generic extensions that work on any object. + +## Methods + +- `let(transform)` โ€“ Executes a function with this object as its argument and returns the result. Useful for chaining operations or transforming values inline +- `also(action)` โ€“ Executes a function with this object and returns this object. Useful for performing side effects while maintaining the original value for further chaining + +## Examples + +```dart +// Transform objects inline +final result = 'hello'.let((it) => it.toUpperCase()); // 'HELLO' +final length = 'test'.let((it) => it.length); // 4 + +// Execute side effects while chaining +final user = User('John') + .also((it) => print('Created user: ${it.name}')) + .also((it) => log.info('User created')); +``` + +## Back to Documentation + +[โ† Back to main documentation](./README.md) diff --git a/docs/set_extensions.md b/docs/set_extensions.md new file mode 100644 index 0000000..e81d3e2 --- /dev/null +++ b/docs/set_extensions.md @@ -0,0 +1,31 @@ +# ๐Ÿ”ข Set Extensions + +Extensions for working with `Set` objects. + +## Methods + +- `toggle(element)` โ†’ Adds if missing, removes if present +- `intersects(otherSet)` โ†’ Check for intersection +- `isSubsetOf(otherSet)` โ†’ Check if subset +- `isSupersetOf(otherSet)` โ†’ Check if superset +- `unionAll(sets)` โ†’ Union of multiple sets +- `without(element)` โ†’ Remove element + +## Examples + +```dart +// Toggle elements +{1, 2, 3}.toggle(2); // {1, 3} +{1, 2, 3}.toggle(4); // {1, 2, 3, 4} + +// Check relationships +{1, 2, 3}.intersects({2, 3, 4}); // true +{1, 2}.isSubsetOf({1, 2, 3}); // true + +// Union operations +{1, 2}.unionAll([{2, 3}, {3, 4}]); // {1, 2, 3, 4} +``` + +## Back to Documentation + +[โ† Back to main documentation](./README.md) diff --git a/docs/string_extensions.md b/docs/string_extensions.md new file mode 100644 index 0000000..070cfe9 --- /dev/null +++ b/docs/string_extensions.md @@ -0,0 +1,96 @@ +# ๐Ÿ”ค String Extensions + +Comprehensive utilities for string manipulation, parsing, and formatting. + +## โœ… Utilities + +- `copyToClipboard()` - Copies the string to clipboard +- `matchesRegExp(regex)` โ€“ Checks if the string matches a given regular expression +- `toBool` โ€“ Converts `"true"` or `"false"` to a boolean +- `toDateTime()` โ€“ Parses the string into a `DateTime` object. Returns null if parse fails +- `toRegExp()` โ€“ Converts the string into a `RegExp` +- `toUri()` - Attempts to parse the string to a `Uri` +- `limit(maxLength, [overflow = "โ€ฆ"])` โ€“ Limits string length with optional overflow characters +- `mask({start = 2, end = 2, maskChar = '*', maskLength})` โ€“ Masks the middle of the string, leaving edges visible +- `reverse()` โ€“ Reverses the string +- `isBlank` / `isNotBlank` โ€“ Checks if the string is empty or contains only whitespace +- `removeWhitespace()` โ€“ Removes all whitespace from the string +- `countOccurrences(substring)` โ€“ Counts how many times a substring appears +- `truncate(maxLength, {ellipsis})` โ€“ Truncates the string with word boundary awareness +- `wrap(prefix, [suffix])` โ€“ Wraps the string with a prefix and optional suffix +- `removePrefix(prefix)` โ€“ Removes a prefix if it exists +- `removeSuffix(suffix)` โ€“ Removes a suffix if it exists + +### Examples + +```dart +'hello'.reverse(); // 'olleh' +' '.isBlank; // true +'hello world'.removeWhitespace(); // 'helloworld' +'hello world'.countOccurrences('l'); // 3 +'The quick brown fox'.truncate(10); // 'The quick...' +'text'.wrap('"'); // '"text"' +'Hello World'.removePrefix('Hello '); // 'World' + +// Masking +'08012345678'.mask(); // 08*******78 +'08012345678'.mask(maskLength: 2); // 08**78 +``` + +## ๐ŸŽ€ Pretty Printing + +- `prettyJson()` โ€“ Formats a raw JSON string +- `prettyXml()` โ€“ Formats raw XML into readable indents +- `prettyYaml()` โ€“ Formats YAML strings prettily + +## ๐Ÿ”  Casing + +Transform strings to different cases: + +- `camelCase` โ€“ Converts string to camelCase +- `capitalised` โ€“ Capitalises the first letter of each word +- `kebabCase` โ€“ Converts string to kebab-case +- `pascalCase` โ€“ Converts string to PascalCase +- `snakeCase` โ€“ Converts string to snake_case +- `titleCase` โ€“ Converts string to Title Case + +### Examples + +```dart +'the big brown fox'.camelCase; // theBigBrownFox +'the big brown fox'.capitalised; // The big brown fox +'the big brown fox'.pascalCase; // TheBigBrownFox +'the big brown fox'.kebabCase; // the-big-brown-fox +'the big brown fox'.snakeCase; // the_-_big_-_brown_-_fox +'the big brown fox'.titleCase; // The Big Brown Fox +``` + +## ๐Ÿงช Case and Pattern Checkers + +### Case Checkers + +- `isCamelCase` +- `isPascalCase` +- `isSnakeCase` +- `isKebabCase` +- `isTitleCase` +- `isCapitalised` +- `isUpperCase` +- `isLowerCase` + +### Pattern Checkers + +- `isEmail` +- `isURL` +- `isUlid` +- `isUuid` +- `isSlug` +- `isHexColor` +- `isIPAddress` +- `isNum` โ€“ Validates numeric string +- `isAlphabetOnly` +- `isNumericOnly` + +## Back to Documentation + +[โ† Back to main documentation](./README.md) diff --git a/docs/widget_extensions.md b/docs/widget_extensions.md new file mode 100644 index 0000000..5cbcf39 --- /dev/null +++ b/docs/widget_extensions.md @@ -0,0 +1,66 @@ +# ๐Ÿงฉ Widget Extensions + +Extensions for transforming and enhancing Flutter widgets. + +## ๐Ÿช„ Basic Transformations + +- `center({heightFactor, widthFactor})` โ€“ Wraps widget in a `Center` +- `expanded([flex = 1])` โ€“ Wraps widget in an `Expanded` +- `flexible({flex = 1, fit = FlexFit.loose})` โ€“ Wraps widget in a `Flexible` +- `opacity(opacity)` โ€“ Wraps widget with an `Opacity` widget +- `sizedBox({width, height})` โ€“ Wraps widget with a `SizedBox` +- `constrained({maxHeight, maxWidth, minHeight, minWidth})` โ€“ Wraps widget with a `ConstrainedBox` + +## โœ‚๏ธ Clipping + +- `clipRect()` โ€“ Clips widget to a rectangle +- `clipRRect(borderRadius)` โ€“ Clips widget with rounded corners +- `clipRounded([radius = 12])` โ€“ Quickly clip widget with a uniform rounded border + +## ๐Ÿงน Padding + +- `paddingAll(padding)` โ€“ Adds equal padding on all sides +- `paddingSymmetric({horizontal, vertical})` โ€“ Adds symmetric horizontal and vertical padding +- `paddingOnly({left, top, right, bottom})` โ€“ Custom padding for specific sides +- `paddingZero()` โ€“ Adds zero padding + +## ๐Ÿงญ Positioning + +- `positionAlign(alignment)` โ€“ Aligns widget using `Align` +- `positionedFill()` โ€“ Fills parent constraints using `Positioned.fill` + +## ๐Ÿ‘ป Visibility Helpers + +- `hideIf(condition)` โ€“ Hides widget (returns `SizedBox.shrink()`) if `condition` is true +- `hideUnless(condition)` โ€“ Hides widget unless `condition` is true +- `showIf(condition)` โ€“ Shows widget if `condition` is true, otherwise hides +- `showUnless(condition)` โ€“ Shows widget unless `condition` is true + +## ๐Ÿ”˜ inkwellManager on Widget + +A helper class for managing taps on a widget in a cleaner way. + +- `inkWellManager(callback, {color = Colors.transparent})` โ€“ Wraps widget with an `InkWell` for tap detection +- `onTap()` โ€“ Wraps child with `InkWell` for tap gesture +- `onDoubleTap()` โ€“ Wraps child with `InkWell` for double-tap gesture +- `onLongPress()` โ€“ Wraps child with `InkWell` for long-press gesture + +> **Tip:** Used alongside the `inkWellManager` extension to easily attach tap interactions without boilerplate. + +### Example + +```dart +Text('Click Me') + .inkWellManager(() => print('Tapped'), color: Colors.black) + .onTap(); +``` + +### ๐Ÿ’ก Why InkWellManager? + +Normally, to make a widget respond to taps, you must manually wrap it inside an InkWell every time, setting colours and callbacks. InkWellManager simplifies this by providing quick `.onTap()`, `.onDoubleTap()`, and `.onLongPress()` methods โ€” making your code shorter, cleaner, and more maintainable. + +It also auto-applies the same splash, hover, and focus colours without extra setup. + +## Back to Documentation + +[โ† Back to main documentation](./README.md)