appcraft_list_loading_flutter 1.0.0
appcraft_list_loading_flutter: ^1.0.0 copied to clipboard
Dispatcher for paginated list loading with offset/cursor support, debounced search and pluggable cancellation strategies.
appcraft_list_loading_flutter #
1.0.0 #
Migration from 0.2.0 → 1.0.0: see MIGRATION.md.
BREAKING CHANGE #
- Removed deprecated parser-based API:
ACDispatcher,ACDefaultDispatcher,ACCustomDispatcher,ACParser,ACDefaultParser,ACResultParser,ACResult,ACDebouncedSearchStrategy. Migrate to the self-contained dispatchers — see MIGRATION.md.
0.3.0 #
Migration from 0.2.0 → 0.3.0: see MIGRATION.md.
- Added: new self-contained
ACAnchoredDispatcher<P extends ACParamsMixin, R extends ACPage<T>, T>— a bidirectional, anchor-centred dispatcher for chat/feed pagination, built as a composition of twoACPageDispatchers (one per side), so each side reuses the full loading engine (cancellation, staleness guards,isLoading,lastResult, error channels,retry,mutate).loadAroundseeds the window around an anchor (cancelling any previous around-load and both sides);loadOlder/loadNewergrow each side independently vialoadMore. Exposes the separateitemsOlder/itemsNewer(for aCustomScrollView(center:)without scroll jumps) and a merged read-onlyitems(reverse(itemsOlder) ++ itemsNewer), fully per-side state (isLoadingOlder/isLoadingNewer/isLoadingAround,hasMoreOlder/hasMoreNewer,lastErrorOlder/Newer/Aroundwith matchingerror*Listenableandloading*Listenable,lastResultOlder/Newer,lastAround,retryOlder/retryNewer), realtimemutateOlder/mutateNewer, pluscanceland an idempotentdispose. Cursors are managed manually by the consumer (initial ones fromlastAround, subsequent ones fromlastResult*). - Added: new around-page contract
ACAnchoredPage<T>(mixin withitems,hasMoreOlder,hasMoreNewer) — returned byACAnchoredDispatcher.loadAround, it carries both directional flags at once (unlike the per-sideACPage, whosehasMoremeans "more on this side"). The per-sideloadOlder/loadNewerkeep using the existingACPagemodel. - Additive only — a new composition on top of two existing
ACPageDispatchers; no existing class is changed and no migration is required. - Added: new standalone
ACDebouncer— an action-neutral debounce utility that collapses a burst of calls into a single trailing-edge invocation (run(void Function()),isActive,cancel(),dispose()), with a non-negativeduration(default300ms;Duration.zerofires on the next tick). It knows nothing about loading or search, so it is reusable for any debounced behaviour (read-tracking, autosave, analytics throttling, ...).dispose()is mandatory — it cancels the pending timer so a deferred action never fires after teardown. - Added: new
ACSearchDebouncer implements ACSearchStrategy— the new default search strategy on both dispatchers (debounce: 300ms,minLength: 3), built by composing anACDebouncerfor timing. Search behaviour (empty /< minLength/ equal / zero / debounce gating) is unchanged from the previous default. TheACSearchStrategycontract is kept for custom strategies. - Deprecated:
ACDebouncedSearchStrategy— will be removed in 1.0.0; useACSearchDebouncerinstead (samedebounce/minLengthparameters and identical behaviour — a straight rename). It keeps working and is still exported; it was moved tolib/src/deprecated/. Additive change — the search behaviour of the dispatchers is unchanged and no migration is required beyond the optional rename. - Changed: BEHAVIOR CHANGE — a fresh
ACListDispatcher/ACPageDispatchernow reportshasMore == falsebefore the first load (wastrue), sohasMorealone already gates a bottom loader without the extralastResult != null && hasMorecheck. As a result,loadMore()before the firstreloadis a no-op (thehasMore == falseguard) — usereloadfor the first load, orloadMore(force: true)to force it (forcestill bypasses thehasMore == falseguard, now including this initial state). ThehasMorecomputation after a load is unchanged (offset rule /page.hasMore), and thehasMoresetter andminLengthare unaffected. Migration: if you relied on a fresh dispatcher reportinghasMore == true(e.g. callingloadMorefirst), switch toreloadorloadMore(force: true). Deprecated dispatchers are not affected. - Added: built-in error state on
ACLoadingDispatcher—Object? lastError(the last thrown object, read synchronously) andValueListenable<Object?> errorListenable(its reactive channel, deduplicated by value and released indispose). A failingloadstill propagates the exception exactly as before — the error state is additive, so existingtry/catcharoundreload/loadMorekeeps working. The error clears on the next successful load, and changinglastErrordoes not firenotifyListeners(). - Added:
Future<void> retry()onACLoadingDispatcher— repeats the last operation (reloadorloadMore) through the public methods with the originalparams/loadand, forloadMore, the originalforce. A no-op when there is no prior operation or afterdispose. - Added: read-only
ACDispatcherOperation<Params, T>? lastOperationonACLoadingDispatcherfor introspecting the captured operation, plus the new public sealed modelACDispatcherOperationwith the variantsACReloadOperationandACLoadMoreOperation(the latter carryingforce); both expose the commonparamsandload. An exhaustiveswitchneeds no default clause. - All of the above live in the base
ACLoadingDispatcher, soACListDispatcher,ACPageDispatcherand third-party subclasses inherit them. Additive only — the exception propagation is unchanged (no breaking changes, no migration). - Added: optional
bool forceparameter (defaultfalse) onACLoadingDispatcher.loadMore— force a load past the end of the list by bypassing only thehasMore == falseguard, without a fullreloadand without losing the accumulated list. It is one-shot (hasMoreis recomputed after a successful load) and keeps every other guard:forcehas no effect during an active load or afterdispose. Additive only — the defaultfalsepreserves the previous behaviour, no breaking changes and no migration. Lives in the baseACLoadingDispatcher, soACListDispatcher,ACPageDispatcherand third-party subclasses inherit it. - Added: public
ValueListenable<bool> loadingListenableonACLoadingDispatcher— a reactive channel that mirrors the overallisLoadingflag, notifying on everyfalse ↔ truetransition (deduplicated by value). Drive a spinner reactively viaValueListenableBuilderor anaddListenerin a Cubit/Bloc, without manualsetStateand without subscribing to list changes. Released indispose. - Added: public synchronous flags
bool isReloadingandbool isLoadingMoreonACLoadingDispatcher— tell areload(full-screen spinner) apart from aloadMore(bottom spinner). Exactly one istrueduring an active load; both arefalseotherwise.isLoadingis now the derivedisReloading || isLoadingMore, so its semantics are unchanged. - All of the above live in the base
ACLoadingDispatcher, soACListDispatcher,ACPageDispatcherand third-party subclasses inherit them. Additive only —isLoadingis unchanged and no existing behaviour changes (no breaking changes, no migration; there is no separate listenable for the granular flags). - Added: new public abstract
ACLoadingDispatcher<Params extends ACParamsMixin, T>— the shared loading engine that owns the whole lifecycle (search scheduling, cancellation of a previous load, staleness guards, theisLoadingflag and the typedlastResult) behindreload/loadMore/cancel/dispose. It is also a public extension point: subclass it to implement non-standard pagination by providing only the collection state and thehasMore/onLoadSuccess/onLoadRejectedhooks. - Changed:
ACListDispatcherandACPageDispatcherare now thin subclasses ofACLoadingDispatcher— the duplicated loading state machine lives in one place. Internal refactor only: their public API and behaviour are unchanged, no breaking changes and no migration required. - Added: new self-contained
ACPageDispatcher<P extends ACParamsMixin, R extends ACPage<T>, T>for DTO/cursor pagination where the loader returns a page model. It works directly with the model —itemsandhasMoreare read from it — without extendingACDispatcherand without a parser. Full behavioural parity with the deprecatedACCustomDispatcher, plus themutatemethod and thehasMoresetter (same semantics as onACListDispatcher). - Added: new page-model contract
ACPage<T>(mixin withitemsandhasMore) — the renamedACResult<T>. DTOs mix inACPageinstead. - Deprecated: the parser architecture and the old model —
ACDispatcher,ACCustomDispatcher,ACParser,ACResultParserandACResult— will be removed in 1.0.0. Use the self-containedACListDispatcher(plainList) orACPageDispatcher(ACPagemodel) instead; migrate DTOs fromACResulttoACPage. All keep working and are still exported; they were moved tolib/src/deprecated/. - Added:
ACListDispatcher.mutate(void Function(List<T> items) update)— the sanctioned way to mutate the accumulated list from outside (realtime append, optimistic update, seed). The callback receives the mutable list; a singlenotifyListeners()fires on successful completion (batched — many operations, one notification). No-op afterdispose; a callback exception propagates without notifying;lastResultis untouched. Theitemsgetter stays unmodifiable — directitems.add(...)still throws. - Added: public setter
ACListDispatcher.hasMore— manually control whetherloadMoremay fetch (seed / end-of-list). Does not notify listeners (parity with the existing "notify only on items change" rule). - Added: new self-contained
ACListDispatcher<P extends ACOffsetParamsMixin, T>for the most common scenario — offset pagination over a plainList<T>. It does the job directly, without extendingACDispatcherand without a parser: item extraction andhasMore(result.length >= limit, ortruewhenlimit == null) are built in. Public API is a drop-in match forACDefaultDispatcher(per-callload, search viaACSearchStrategy, per-callcancelStrategy,List<T>? lastResult, identical notify/cancel/error/stale-result/guard semantics). - Deprecated:
ACDefaultDispatcherandACDefaultParser— will be removed in 1.0.0; useACListDispatcherinstead. Both keep working and are still exported; they were moved tolib/src/deprecated/. - No breaking changes — all 0.2.0 public names remain available.
0.2.0 #
-
Added: public
R? lastResultgetter onACDispatcher— stores the last successfully loadedRreturned by the loader (the same reference, no defensive copy). Updated after every successfulreload/loadMore; not reset onminLengthrejection, loader/parser exception, orcancel(). Reset tonullbydispose(). Useful for cursor pagination and DTO scenarios where the response carries metadata beyonditems/hasMore— for example, a server-sidenextCursortoken that the consumer feeds into the nextloadMorecall. -
BREAKING CHANGE: all 9 public types renamed — the
ListLoadingprefix was dropped to keep names short. Search/Cancel strategies kept their names (noListLoadingprefix to begin with). No deprecatedtypedefaliases — migrate via find-replace.Old name New name ACListLoadingDispatcherACDispatcherACDefaultListLoadingDispatcherACDefaultDispatcherACCustomListLoadingDispatcherACCustomDispatcherACListLoadingParamsMixinACParamsMixinACOffsetListLoadingParamsMixinACOffsetParamsMixinACListLoadingParserACParserACDefaultListLoadingParserACDefaultParserACResultListLoadingParserACResultParserACListLoadingResultACResult -
BREAKING CHANGE: flat
lib/src/layout. The intermediatelib/src/list_loading_dispatcher/list_loading_dispatcher.dartbarrel was removed; the six implementation files now live directly inlib/src/. The public entry pointlib/appcraft_list_loading_flutter.dartexports them directly. Consumers that import via the public entry are unaffected; deep imports throughlib/src/list_loading_dispatcher/...must be updated tolib/src/.... -
Removed:
ACCursorListLoadingParamsMixin— empty marker mixin not read by the dispatcher. Cursor-style params now declare a plaincursorfield on the params class withACParamsMixin. Carry the next-page cursor between calls throughdispatcher.lastResult?.<your_field>. -
Migration guide:
- Find-replace the 9 type names per the table above.
- Drop
ACCursorListLoadingParamsMixinfrom your params classes; declare a plainString? cursor(or any type) field instead. - (Optional) Use
dispatcher.lastResultto access the raw last loader response — for cursor pagination or DTO metadata. - If you used deep imports (
package:appcraft_list_loading_flutter/src/list_loading_dispatcher/...), either switch to the public entrypackage:appcraft_list_loading_flutter/appcraft_list_loading_flutter.dartor update paths to the flatpackage:appcraft_list_loading_flutter/src/....
0.1.0 #
- BREAKING CHANGE: removed
finalmodifier from public concrete classes, allowing bothextendsandimplementsfor downstream consumers. Affected classes:ACDefaultListLoadingDispatcher,ACCustomListLoadingDispatcher,ACDefaultListLoadingParser,ACResultListLoadingParser,ACDebouncedSearchStrategy,ACOperationCancelStrategy. For consumers that did not rely onfinalguarantees, this is a non-breaking relaxation; no migration is required. - Added "Extending the API" section to README with subclassing example.
- Added inheritance/implementation note to dartdoc of all open classes.
0.0.1 #
- Initial package release.
- Added
ACListLoadingDispatcher<P, R, T>— a dispatcher for paginated list loading with support for offset/cursor pagination, search and cancellation. - Added facade dispatchers
ACDefaultListLoadingDispatcher<P, T>andACCustomListLoadingDispatcher<P, R, T>with pre-configured parsers. - Added parsers
ACListLoadingParser,ACDefaultListLoadingParser,ACResultListLoadingParser. - Added
ACListLoadingResult<T>mixin for response DTOs. - Added parameter mixins
ACListLoadingParamsMixin,ACOffsetListLoadingParamsMixin,ACCursorListLoadingParamsMixin. - Added search strategy
ACSearchStrategywith implementationACDebouncedSearchStrategy(debounce + minLength). - Added cancellation strategy
ACCancelStrategy(contract + implementation based onCancelableOperation).