Skip to content
This repository was archived by the owner on Oct 17, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 100 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,105 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.0] - Major Release with New Extensions

### Added

#### 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`

- **Safe Access:**
- `getOrNull(index)` - Returns element at index or `null`
- `getOrDefault(index, defaultValue)` - Returns element or default value

- **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

- **Aggregations (for numbers):**
- `sumBy(num Function(T))` - Sum elements by selector
- `averageBy(num Function(T))` - Average by selector
- `countWhere(predicate)` - Count matching elements

- **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

- **Utility:**
- `isNullOrEmpty()` - Check if empty
- `joinToString(separator, transform)` - Join with custom format
- `forEachIndexed()` - Iterate with index

#### Map Extensions
- **Safe Access:**
- `getOrNull(key)` - Get value or null
- `getOrDefault(key, defaultValue)` - Get value or default

- **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

- **Merge & Combine:**
- `merge(otherMap)` - Merge with precedence
- `mergeIfAbsent(otherMap)` - Merge without overriding
- `combine(other, (k, v1, v2) => mergedValue)` - Custom merge

- **Utility:**
- `keysWhere(predicate)` - Get keys matching predicate
- `valuesWhere(predicate)` - Get values matching predicate
- `toQueryString()` - Convert to URL query string

#### Set Extensions
- `toggle(element)` - Add if missing, remove 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

#### Humanize Extensions
- **Duration:**
- `humanize(locale)` - Convert to "2 hours, 3 minutes" format

- **DateTime:**
- `humanize(locale)` - Convert to relative time ("just now", "3 hours ago", "yesterday", "last week", etc.)

- **Numbers:**
- `humanizeNumber()` - Format as "15.3k", "1.5M", etc.
- `humanizeOrdinal()` - Format as "1st", "2nd", "3rd", etc.
- `humanizeCount('item')` - Format as "1 item" / "3 items"
- `humanizePercentage(max, min)` - Format as "74%"
- `humanizeFileSize()` - Format as "1.0 MB", "520.3 KB", etc.

### Changed
- Updated package version to 1.0.0 marking stable release

### Improved
- Added comprehensive test coverage for all new extensions
- Complete documentation with examples for all methods
- Enhanced type safety with proper generic constraints

## [Unreleased]

### Improved
Expand All @@ -26,7 +125,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `truncate(maxLength)` - Truncates with word boundary awareness
- `wrap(prefix, suffix)` - Wraps string with prefix and suffix
- `removePrefix(prefix)` and `removeSuffix(suffix)` - Remove specific prefix or suffix

- **Number Extensions:**
- `toDecimalPlaces(places)` - Rounds double to specified decimal places
- `isBetween(min, max)` - Checks if number is within range
Expand Down
230 changes: 230 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,236 @@ await 2.seconds.delay(); // Waits for 2 seconds
- `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
[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

```dart
// Examples
[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

```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
Expand Down
4 changes: 4 additions & 0 deletions lib/src/extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ part './extensions/build_context.dart';
part './extensions/date_time.dart';
part './extensions/duration.dart';
part './extensions/dynamic.dart';
part './extensions/humanize.dart';
part './extensions/image_widget.dart';
part './extensions/list.dart';
part './extensions/map.dart';
part './extensions/number.dart';
part './extensions/object.dart';
part './extensions/set.dart';
part './extensions/stateless_widget.dart';
part './extensions/string.dart';
part './extensions/text_style.dart';
Expand Down
Loading