fluiver 5.0.1
fluiver: ^5.0.1 copied to clipboard
Flutter SDK gap-fillers — non-scrolling Grid, TickerBuilder, Debounce/Throttle, LRUCache, DisposableBag, Object.into, DateTime/Iterable/Color extensions. Ships an agent skill.
Changelog #
5.0.1 - 2026-10-16 #
Changed #
- The README opens with what the package does, why to use it and a short example. Every Dart sample in the README and the agent skill is analyzed with the package's lints and fits pub.dev without scrolling sideways.
5.0.0 - 2026-10-15 #
Breaking #
TimeOfDayOnDate(onDate) now extendspackage:material_ui/material_ui.dart'sTimeOfDay. Callers still usingpackage:flutter/material.dart'sTimeOfDaywith explicitshow TimeOfDayOnDateorTimeOfDayOnDate(tod)overrides should switch toLegacyTimeOfDayOnDate(plain barrel imports with implicit.onDateresolve automatically).- Added
material_ui: ^1.6.0as a runtime dependency. - Removed deprecated
DateTime.withTimeOfDay(usetime.onDate(date)). - Extension getters are now methods:
DateTime.isToday,isTomorrow,isYesterday,inThisYear,ScrollController.atTop,atBottom, andColor.contrastText. Migration: add()at each call site (date.isToday(),controller.atTop(),color.contrastText()). Debounce,ThrottleFirst,ThrottleLast, andThrottleLatestthrowStateErrorin all build modes when called afterdispose()(previously a debug-onlyFlutterError; release builds silently scheduled the task).DisposableBagException.errorsandstackTracesare unmodifiable.DisposableBag.addafterdispose()has settled throwsStateError(previously it ran the disposer immediately and dropped its error into the zone). Everydispose()call returns the same future, so a second call during an in-flight dispose waits for the release and sees itsDisposableBagException(previously it returned at once). Callingdispose()from one of the bag's own disposers, directly or through another bag's disposer, throwsStateErrorinstead of waiting on itself.Debounce,ThrottleFirst,ThrottleLast, andThrottleLatestthrowArgumentErrorfor a negative duration (previously treated as zero).Color.darkenandColor.lightenthrowRangeErrorwhenamountis outside[0, 1]orNaN(previously clamped), andArgumentErrorwhen a channel is outside[0, 1](previously clipped); map extended-sRGB colors into gamut first.TextEditingController.setTextAndCaretthrowsRangeErrorwhencaretis outside0..text.length(previously clamped).ScrollController.atTop(),atBottom(),animateToTop, andanimateToBottomthrowStateErrorwhen no scroll view is attached, likeScrollController.position(previouslyfalse/ a no-op).Future.timeoutOrNullis only available onFuture<T>with a non-nullableT, sonullalways means the timeout elapsed.NetworkProbe.checkConnectionthrowsUnsupportedErroron web (previously returnednavigator.onLine); useNetworkProbe.isBrowserOnline().Gridreports an unbounded cross axis in every build mode (previously a debug-only assert).Iterable.windowedis nowslidingWindows(extensionIterableSlidingWindows), andObject.letis nowinto(extensionObjectInto): the old names clashed withpackage:dartx'swindowedand other packages'.let. Migration: rename the call sites.LocaleListenerandBrightnessListenerregister themselves withWidgetsBindingon construction and stop ondispose(), likeAppLifecycleListener; a seconddispose()throwsStateError. They are no longerWidgetsBindingObservers, so a leftoverWidgetsBinding.instance.addObserver(listener)fails to compile instead of double-firing. Migration: delete theaddObserver/removeObservercalls and calllistener.dispose().Debounce,ThrottleFirst,ThrottleLast,ThrottleLatest,LRUCache,DisposableBag,DisposableBagException,LocaleListener, andBrightnessListenerarefinalclasses: they can't be extended or implemented outside fluiver.
Added #
LegacyTimeOfDayOnDate(onDate) forpackage:flutter/material.dart'sTimeOfDay.NetworkProbe.isBrowserOnline()returns the browser'snavigator.onLineon web and throwsUnsupportedErrorelsewhere.
Changed #
- Decoupled
Color,DateTime,Iterable,TextFieldBuilders, andDisposableBagfrompackage:flutter/material.dartruntime imports.
Fixed #
DateTime.isWithinFromNowthrowsArgumentErrorwhendurationis negative.DisposableBag.disposeruns and awaits disposers registered re-entrantly during an in-flightdispose()call inline.NetworkProbe.checkConnectioncallssocket.destroy()instead ofawait socket.close(), preventing hangs when a peer accepts the TCP handshake and never sendsFIN.ScrollTrackingExpandablecoalesces post-frame scroll callbacks to at most one per frame and uses an animation status listener so rapid collapse/re-expand toggles andduration: Duration.zerocollapses reliably update offstage state.
4.2.0 - 2026-10-04 #
Added #
- Web and Wasm support:
NetworkProbesits behind a conditional import, sopackage:fluiver/fluiver.dartno longer reachesdart:io. DateTime.age({DateTime? at})takes an optional reference date.LRUCache.peek,valuesandentries.DisposableBagException.stackTraces.TickerBuilder(enabled:)pauses and resumes the ticker.
Changed #
- Requires Dart 3.13 / Flutter 3.47 (was Dart 3.10, no Flutter floor).
- Sources are per-file libraries re-exported by
package:fluiver/fluiver.dart; that import is unaffected. DateTime.withTimeOfDayis deprecated; useTimeOfDay.onDate(date).- Agent skill renamed
skills/flutter-fluiver→skills/fluiver-usage, sodart run skills@ get --package fluiver --allinstalls it. - pubspec drops the dead
homepageand the redundantdocumentationlink. age()reads each date's calendar fields as stored: a local date of birth saved with.toUtc()now reads as the previous day east of UTC, so it counts the new year on the eve of the birthday. Call.toLocal()on it first.
Fixed #
NetworkProbe.checkConnectionon web returnsnavigator.onLineinstead of alwaystrue.Color.contrastTextreturns the higher-contrast of black/white; the old 0.5 luminance cutoff picked the worse one for mid tones (e.g.Colors.bluenow gets black text).darken/lightenkeep the colour space and sub-8-bit channel precision instead of rounding to 8 bits; channels outside[0, 1](extended sRGB) are still clipped.age()no longer counts a UTC date of birth a day early west of UTC.LRUCache.keysreturns a snapshot: reading each value while iterating it no longer throwsConcurrentModificationError.ThrottleFirstandThrottleLatestopen the window before running the task, so re-entrant or throwing tasks can't bypass it andThrottleLatest.disposeleaves no timer pending.ThrottleLastkeeps a call made while its task runs.ScrollControlleredge helpers work with several attached scroll views.windowedthrowsRangeErrorat the call, not on first iteration.- A collapsed
ScrollTrackingExpandablechild is offstage: not focusable, hidden from semantics, tickers muted. - README, skill and API-doc snippets compile;
FastHashdocuments its UTF-16 input.
4.1.0 #
- Changed —
DisposableBag.disposeinvokes disposers in registration order but awaitsFuture-returning ones together, so disposal takes as long as the slowest disposer instead of the sum, a stalled async disposer no longer starves the ones after it, and synchronous disposers queued behind an async one now run insidedispose()rather than after the async one settles. Steps that must wait for a previous async step (flush, then close) belong in one closure.DisposableBagException.errorsstays in registration order. - Changed —
LRUCachehits cost one hash lookup instead of two (redundantcontainsKeyprobe removed).
4.0.0 #
Strips every API whose job the framework or core libraries already do — fluiver keeps only genuine gaps.
-
Removed —
AppLifecycleObserver. The framework'sAppLifecycleListeneris the same wrapper with more granular callbacks:AppLifecycleListener(onStateChange: (s) => ...). -
Removed —
DateTime.toTimeOfDay(). Identical to the framework'sTimeOfDay.fromDateTime(dt). -
Removed —
Map.any/Map.every/Map.firstWhereOrNull. Sugar overmap.entries:entries.any((e) => ...),entries.every((e) => ...), andentries.firstWhereOrNull(...)viapackage:collection. -
Renamed —
LocaleObserver→LocaleListener,BrightnessObserver→BrightnessListener, matching the framework'sAppLifecycleListenernaming. -
Breaking —
FlexGridis nowGrid(RenderFlexGrid→RenderGrid), configured by the sameSliverGridDelegatefamily asGridViewfor DX and agent predictability:Grid(gridDelegate: ...)takes any delegate (custom ones included),Grid.count(...)/Grid.extent(...)mirrorGridView.count/GridView.extent. MigrateFlexGrid(crossAxisCount: 3, ...)→Grid.count(crossAxisCount: 3, ...); the delegate'smainAxisExtentoverrideschildAspectRatio(theGridViewcontract, replacing the old additive behavior). -
Added —
Gridcovers whatGridView(shrinkWrap: true)cannot: real intrinsics (no hard-coded estimates; unbounded extents shrink-wrap to child intrinsics), exactcomputeDryLayoutandcomputeDryBaseline, first-child baseline,debugFillProperties, RTL cross-axis reversal matchingGridView, a descriptiveFlutterErrorunder an unbounded cross axis, and degenerate constraints clamping cells to zero instead of negative. -
Breaking —
platformDispatch'smacOSparameter is nowmacos, matching its all-lowercase siblings (android,ios,linux, …) — the name agents and autocomplete-blind humans guess first. -
Breaking —
NetworkProbe.hasConnection→checkConnection(verb — it's an async probe, not a cheap property). Gainshost/port(default unchanged:1.0.0.1:53) for self-hosted probe endpoints, its first tests, and the defaulttimeoutrises 2s → 3s so a lossy mobile network surviving two SYN retransmits still reportstrue. -
Changed —
RenderGridandTickerBuilderinternals follow framework conventions: getter/field/setter property clusters, delegate relayout viaSliverGridDelegate.shouldRelayout, named tick handler created ininitState(side effects moved out of thesetStateclosure). -
Changed —
unawaited_futures/discarded_futureslints disabled package-wide: fire-and-forget futures in sync lifecycle methods are dropped bare, framework-style;NetworkProbenow awaitssocket.close()before reporting reachability. -
Fixed —
TextEditingController.setTextAndCaretnow clampscaretto0..text.lengthinstead of producing an out-of-range selection. -
Changed —
LRUCachethrowsArgumentErrorformaxEntries <= 0in all build modes (was a debug-only assert). -
Changed —
FastHash.fnv1aiterates code units by index, avoiding an iterator allocation per call. -
Changed —
FastHash.fnv1anow runs on Wasm builds (native 64-bit ints there); only JS web throwsUnsupportedError. -
Changed —
Debounce/Throttle*dispose()drops pending task references (no closure retention), and calling a disposed instance throwsFlutterErrorin debug mode. -
Changed — a
GridgivenEdgeInsetsDirectionalpadding with no ambientDirectionalitynow asserts in debug mode (framework behavior) instead of silently resolving as LTR. -
Fixed — an empty
Grid's cross-axis intrinsics no longer include phantomcrossAxisSpacing; they report padding only. -
Fixed —
windowed(0)(andstep: 0) now throwsRangeError: size: must be > 0instead of a misleading-1message. -
Changed — dropped the unused
metadependency.
Kept deliberately, verified against Flutter 3.41 / Dart 3.11: truncateTime()
(unlike DateUtils.dateOnly it preserves UTC-ness), windowed
(package:collection only has non-overlapping slices), timeoutOrNull
(package:async has no equivalent), Map.where/whereKeyType/whereValueType
(return a Map, narrow the static type), separated (Flex spacing only
covers blank gaps, not separator widgets), byNameOrNull (core's
asNameMap()[name] allocates a map per lookup and spells a simple scan
sideways).
3.3.1 #
- Not documented.
3.3.0 #
- Added —
ScrollTrackingExpandablewidget: animated expand/collapse that keeps the growing bottom edge visible in the nearestScrollablewhile expanding, withscrollOffsetfor extra breathing room below.
3.2.3 #
- Fixed — SDK compatibility fix in
NetworkProbeby replacing type-directed enum shorthand.IPv4with fully qualified enum reference. - Fixed — Rescheduling timing bug in
ThrottleLatest. - Fixed — Memory leak in
ThrottleLastby clearing callback references post-execution. - Fixed — Native resource leak in
TickerBuilderby disposing of ticker binding resources properly. - Fixed — Exception safety in
DisposableBagto prevent a single failing disposer from blocking subsequent disposers, raisingDisposableBagExceptioncontaining all errors at the end. - Fixed — Timezone-safety alignment in
DateTimeCheck.age()andTimeOfDayOnDate.onDate(). - Added — Intrinsic height and width measurement calculations on
FlexGrid's customRenderBox(RenderFlexGrid). - Changed — Optimized lookup efficiency in
Map.entryOfand generic compile-safety inEnumIndexComparable.
3.2.2 #
- Fixed —
FastHash.fnv1a's 64-bit offset-basis literal broke compilation on web targets (includingflutter widget-preview). Now split into web-representable halves (bit-exact on the VM); throwsUnsupportedErroron web instead of corrupting hashes.
3.2.1 #
- Changed — Dropped
@useResultfromObject.let; the result can now be safely ignored (e.g. side-effecting transforms) without an analyzer warning. - Changed — LLM artifact moved from a path-scoped rule to a
description-triggered skill (
tool/claude/flutter-fluiver/SKILL.md); loaded by relevance instead of on everylib//test/file.
3.2.0 #
- Added —
LRUCache.putIfAbsent(K key, V Function() ifAbsent). Lazy compute on miss; hit promotes the entry to most-recently-used. - Added —
DisposableBag.addAll(Iterable<FutureOr<void> Function()>). Bulk-register disposers; same semantics as repeatedadd. - Changed — Rule file no-overlap callout narrowed to
package:collectionandpackage:async.
3.1.0 #
- Added —
Color.darken([amount]),Color.lighten([amount]),Color.contrastText. HSL-based;contrastTextpicks black or white via luminance. - Added —
ScrollController.atTop,.atBottom,.animateToTop({duration, curve}),.animateToBottom({duration, curve}).hasClients-safe. - Added —
Future<T>.timeoutOrNull(Duration)→Future<T?>. Null on timeout, errors still propagate. - Added —
Iterable<E>.windowed(int size, {int step = 1}). Stepped sliding window; drops partial trailing window. - Added —
TextEditingController.setTextAndCaret(String, {int? caret}). Avoids thecontroller.text = ...caret-reset papercut. - Changed — LLM artifact moved from
rules/fluiver.mdtorules/flutter-fluiver.md. Namespaced with theflutter-prefix. - Removed —
PaddedFlex/PaddedRow/PaddedColumnwidgets. UsePadding(padding: ..., child: Column(children: [...]))/Row— LLMs reach for this natively. - Removed —
IterableEnum.byNameOrElse(name, orElse: ...). UseEnum.values.byNameOrNull(name) ?? .fallback— Dart shorthand handles the fallback.
3.0.0 (not published) #
Breaking — utility-library pivot. The "single-dot shortcut" framing collided
with LLM-assisted development: agents do not know the extensions exist, default
to stdlib forms, and teaching the API costs input tokens on every turn. The
sugar layer had to go. What remains is substance — things the SDK is genuinely
missing. Ships a compact rule file at rules/fluiver.md for consumer projects
to load into their LLM agent.
- Removed —
extensions/build_context.dart(context.primaryColor,context.titleLargeTextStyle,context.screenWidth,context.isThemeDark, …). UseTheme.of(context).colorScheme.primary,Theme.of(context).textTheme.titleLarge,MediaQuery.sizeOf(context).width,Theme.of(context).brightness == Brightness.dark. - Removed —
extensions/text_style.dart(withColor,withWeight400,withSize,withUnderline, …). Usestyle.copyWith(color: ..., fontWeight: FontWeight.w400, fontSize: ...). - Removed —
extensions/edge_insets.dart(addLeft,onlyTop,setRight,withStatusBarMargin, …). UseEdgeInsets.only(...),.copyWith(...),MediaQuery.viewPaddingOf(context).top. - Removed —
extensions/border_radius.dart(addAll,onlyTopLeft,setBottomRight, …). UseBorderRadius.only(...),.copyWith(...). - Removed —
extensions/bool.dart(toInt). Useb ? 1 : 0. - Removed —
extensions/date_time.dartarithmetic (addYears,addMonths,addWeeks,addDays,addHours,addMinutes,addSeconds). Usedt.add(Duration(days: N))/DateTime(y + n, m, d). - Removed —
extensions/key.dart(validateAndSaveonGlobalKey<FormFieldState<T>>). Useif (k.currentState!.validate()) k.currentState!.save();. - Removed —
extensions/flutter_enums.dart(Axis.reverse,TextDirection.reverse,Brightness.reverse,Orientation.reverse). Use inline ternary. - Removed —
extensions/string.dart(capitalize,capitalizeAll,initials,safeSubstring). Inline. - Renamed —
fastHash(s)→FastHash.fnv1a(s). Namespace, algorithm-named, future-proof for other hashes. - Renamed —
hasDeviceConnection()→NetworkProbe.hasConnection({timeout}). Namespaced;timeoutis now configurable. - Renamed —
platformSpecific<T>(...)→platformDispatch<T>(...). Clearer name; matchescompute/dispatchmental model. - Renamed —
disabledInputCounterBuilder→TextFieldBuilders.disabledCounter. Namespaced; room for moreTextFieldhelpers. - Renamed —
TimeOfDay.todayAt()→TimeOfDay.onDate(DateTime date). Takes the calendar day explicitly — testable, no hiddenDateTime.now(). - Fixed —
Map.entryOf(k)distinguishes "key absent" from "key present with null value"; previously returnednullfor both.{'a': null}.entryOf('a')now returnsMapEntry('a', null). - Fixed —
DateTime.isTomorrow/isYesterdayusedDuration(days: 1)(24h), which crosses DST fall-back days incorrectly. Now uses calendar-day arithmetic (DateTime(y, m, d ± 1)). - Fixed —
NetworkProbe.hasConnectionshort-circuits totrueon web (previousdart:io.Socketcall would have thrown at runtime), narrows its catch toSocketException/TimeoutException(no more swallowing programming bugs), and exposes the timeout as a parameter. - Fixed —
PaddedFlex.textDirection/textBaselinedefault tonull—Flexresolves from ambientDirectionality. Previously hardcodedTextDirection.ltrbroke RTL layouts. - Fixed —
TickerBuilder.initStatecallssuper.initState()before starting the ticker. - Fixed —
ThrottleLast._lastinitialized to a no-op closure instead oflate VoidCallback. - Fixed —
Let<T>extension is now bounded toT extends Object, so.letno longer pollutes autocomplete on nullable receivers. Use?.let(...)for null-aware chaining. - Added —
LRUCache<K, V>(maxEntries: N). Bounded least-recently-used cache; O(1) reads / writes / eviction; reads / writes promote. - Added —
DisposableBag. Collectdispose/cancel/closeclosures and flush in registration order with a singledispose(). Idempotent; adding after dispose runs the closure immediately. - Added —
rules/fluiver.md. Compact rule file for LLM agents; documents the surviving API surface and steers away from removed identifiers.
2.4.3 #
- Removed
StreamWhereType(duplicatesdart:async). - Renamed
inToday→todayAt, fixedBuildContextsColorSchemetypo. - Doc fixes.
2.4.2 #
- Removed
is*enum getters; use shorthand comparisons (x == .ltr). - Improved
hasDeviceConnection(socket + timeout). - Added
disabledInputCounterBuilder.
2.4.1 #
- Broadened SDK constraint to
>=3.10.0 <4.0.0.
2.4.0 #
- Added
platformSpecificutility. - Modernized observers to use
PlatformDispatcher. - Adopted shorthand enum values throughout.
- Fixed
ThrottleLasttask execution.
2.3.0 #
- Added
indexparameter toIterable.separated. - Added
letscope extension onObject. - Renamed
deviceHasConnection→hasDeviceConnection. - Added
fastHash.
2.2.0 #
- Removed
Iterableextensions that duplicatepackage:collection. - Renamed
Iterable.separate→Iterable.separated.
2.1.0 #
- Added
PaddedFlex,PaddedRow,PaddedColumn.
2.0.0 #
- Breaking:
FlexGridrewritten asMultiChildRenderObjectWidget. - Added
FlexGrid.padding, all M3ColorSchemecontext getters. - Removed
BezierSquircleBorder. Min SDK Dart 3.0.0.
1.2.0 (not published) #
- Added
ColorSchemecontext getters,BezierSquircleBorder,StreamWhereType.
1.1.1 #
- Added
TickerBuilder.onTick.
1.1.0 #
- Added
TickerBuilder.
1.0.1 #
- Updated
MediaQuerylookups; removedelementAtOrNull,singleOrNull(now in core).
1.0.0 #
- Initial release.