whats_new_kit_flutter 0.2.1
whats_new_kit_flutter: ^0.2.1 copied to clipboard
An Apple-style What's New sheet for Flutter — a faithful port of SvenTiigi/WhatsNewKit, themed from your app's ColorScheme.
0.2.1 #
Documentation only. No code changed, so there is nothing to upgrade for unless you read the README on pub.dev, which serves it from the published archive and so still showed 0.1.0's account of several things 0.2.0 replaced.
Fixed #
- The FAQ and the "how it decides what to show" notes both said the package
deliberately does not merge entries, and told the reader to build a combined
sheet themselves.
WhatsNewPresentationPolicy.allMissedSinceandmergeMissedEntrieshave done that since 0.2.0. - Quick start said the returned future completes when the sheet is dismissed.
It completes with a
WhatsNewDismissal. scrollBottomContentInsetwas documented as the inset. It is a floor — the footer's real height is measured and wins when larger, so lowering it below that height does nothing.WhatsNewVersion.parseCompatwas described as reproducing WhatsNewKit exactly, without the trap that comes with it: it does not strip+build, so'1.2.3+45'parses to1.2.0.
Added #
minTapTargetSize,respectHighContrastandshowDragHandle, threeWhatsNewLayoutproperties missing from the layout table.- The
textDirectionoverride,onErrorandreportWhatsNewError,WhatsNewLaunchRecordStore, andWhatsNewController.innerVersionStore. - An FAQ entry for the
MaterialLocalizationserror aCupertinoApphits, with the delegates taken from the package's ownErrorHint.
0.2.0 #
Bug fixes, a release gate, and the things a shipping app needed that the package could not do: localize its copy, take content from a server, tell an upgrader from a first-time installer, show what a reader missed across skipped releases, and report any of it to analytics.
Fixed #
- Content could sit permanently behind the pinned footer. The scroll body
reserved a fixed 150pt for a footer whose height was never measured. A
compact phone with a secondary action needs 197pt, so the last 47pt of
content could not be scrolled clear — 241pt at 300% text scale, and more
again with
WhatsNewSafeAreaBehavior.add. The footer is now measured, andWhatsNewLayout.scrollBottomContentInsetis a floor rather than the inset. - A concurrent
load()silently suppressed the sheet. The second caller returned early withisLoadedstill false, sopendingWhatsNewanswered null and nothing was presented. Concurrent callers now join the in-flight load. presentIfNeededcould present twice. The once-per-session guard was set after an await, so two callers could both pass it. This was masked by theload()bug above — fixing that alone would have exposed it.- Disposing a controller mid-flight threw.
notifyListeners()ran after awaits with no disposal guard, so disposing while a sheet was open crashed withused after being disposed. - Store failures were swallowed or escaped into the zone. A failed write no
longer eats
onDismiss, an unreadable store presents the sheet rather than silently withholding it, and the fire-and-forget write inWhatsNewView.disposeis caught. All of it routes through a newonErrorhook, defaulting toFlutterError.reportError. - The cache could disagree with disk.
CachingWhatsNewVersionStoreupdated memory before writing, so a failed write left the sheet suppressed for the session and back on the next launch. WhatsNewThemehad no==, despite being aThemeExtension. Any rebuild constructing a fresh instance made the wholeThemeDataunequal and invalidated everyTheme.ofdependent. Value equality added there and onWhatsNew,WhatsNewLayout, both action types,WhatsNewResolvedThemeandInlineMarkdownStyle.WhatsNewAutoSheet.enabledwas read once. Flipping it false-to-true — the usual "wait until login finishes" pattern — never presented anything.- A custom primary-button background could render an invisible label. Setting
only
backgroundColorkept the theme'sonPrimarylabel colour; the foreground is now derived from the background it actually sits on. WhatsNewVersion.tryParseonly validated the first component, so a strayWhatsNewKit.1.zzpreference key read back as1.0.0presented and permanently suppressed that sheet. Every component is checked, and more than three is rejected.stripInlineMarkdowndisagreed with the renderer. Screen readers heard"2 * 3 * 4"as"2 3 4"and lost backslash escapes. It is now built on the parser rather than onreplaceAll.- Links failed silently. Both launch sites guarded with
canLaunchUrl, which returns false on Android 11+ for any scheme missing from the app's<queries>entry and loses the user-gesture context on web. The guard is gone, the launch is attempted and every failure is reported. - Haptics could crash the zone on Windows and Linux, where
HapticFeedbackis unimplemented and the returned future rejects.
Added #
- Localization. Leave
WhatsNewPrimaryAction.titleunset and the button says "Continue" in the reader's language, taken fromMaterialLocalizations— no.arbfiles needed for the one string the package owns. SetWhatsNewController.collectionBuilderto supply entries perLocale. - Remote content.
WhatsNewCodecreads and writes entries as JSON, so release notes can come from remote config, a CMS or a hosted file instead of being compiled into the binary. Icons resolve through aWhatsNewIconsregistry, which keeps them referenced from Dart and safe from tree-shaking. Anything that is code rather than data —WhatsNewText.rich,WhatsNewText.builder,WhatsNewImage.widget, callbacks — is refused with a message naming the path, rather than silently flattened. WhatsNewLinkPolicy, restricting which URL schemes may open. Defaults tohttps,httpandmailto;javascript:,file:,intent:andmarket:are refused. This matters the moment content comes from anywhere but your own source, andWhatsNewLinkFailurereports every refusal and platform failure.- Presentation policies.
WhatsNewPresentationPolicy.allMissedSincemerges every release a reader skipped into one surface — the "everything you missed" case, which previously had no primitive to build it from.latestOnlycovers a running version no entry matches.exactThenMinorremains the default and is unchanged.WhatsNewController.entriesSinceis public for building your own summary, and is the first thing in the package to useWhatsNewVersion's comparison operators rather than equality. - First-install suppression.
suppressOnFirstInstallpresents nothing to a reader who has never been here — "what's new" implies a "before", and a fresh installer has none. Needs a store implementing the newWhatsNewLaunchRecordStore, which records the version at last launch; the bundled stores all do. WhatsNewObserverandWhatsNewDismissal.WhatsNewSheet.showandshowWhatsNewSheetnow complete with how the surface closed, and the observer reports open and close. The information always existed; it was discarded.CompactWhatsNewVersionStore, keeping every presented version under one preference key instead of one per release. The bundled default reproduces WhatsNewKit's format, which means callinggetKeys()— materialising every preference the host app owns — and growing a key per release forever. Existing records are imported on first run, once.WhatsNewController.innerVersionStore, for handing your own store to a second controller without wrapping the cache in another cache, andisDisposed.- A clear error when a sheet or dialog is presented without
MaterialLocalizations— aCupertinoAppneedsDefaultMaterialLocalizations.delegateadded, and Flutter's own assertion never says so. Everything else works inside aCupertinoAppalready, including dark mode.
Changed #
showWhatsNewSheetandWhatsNewSheet.showreturnFuture<WhatsNewDismissal>rather thanFuture<void>. Existingawaitcalls keep working.WhatsNewPrimaryAction.titleis now nullable, defaulting to the localized label.showWhatsNewSheet'scontinueLabelis likewiseString?.WhatsNewVersion.parseCompatis unchanged, but now documents the trap it always had: it does not strip+buildor-prerelease, because WhatsNewKit does not either, so'1.2.3+45'parses to1.2.0and'2.0.0-beta.1'to2.0.1. UseWhatsNewVersion.parsefor anything the running app supplies.- CI runs format, analyze, test and a WebAssembly build of the example on every
pull request, and the publish workflow will not release a tag that fails them
or whose version disagrees with the pubspec. Golden files are tagged
goldenand run on their own macOS job, because they only match on the platform that generated them. - The published archive drops tests and the example's platform scaffolding, 167 files down to 54.
- The example app gained a web target, so the WebAssembly claim is something a machine checks.
Test suite #
165 tests to 271, including the first coverage of url_launcher (through its
platform interface), the dialog and page presentations, and every new surface
above.
0.1.0 #
Initial release — a Flutter port of WhatsNewKit.
Presenting #
showWhatsNewSheetfor the common case,WhatsNewSheet.showfor a full model, andWhatsNewViewto embed the content in a page of your own.WhatsNewPresentationpicks a bottom sheet, dialog or full page, and adapts between them by window size.- Secondary actions:
openUrl,dismiss,present, or your own callback. Haptics on either action.
Deciding what to show #
WhatsNewController,WhatsNewScopeandWhatsNewAutoSheetshow one sheet per release, once — WhatsNewKit's algorithm ported exactly, including themajor.minor.0fallback and recording a swipe-down as seen.WhatsNewVersionwith positional parsing,+build/-prereleasestripping, and storage keys byte-identical to the Swift package's, so a migrating app keeps its history.- Version stores backed by
shared_preferencesor memory, a caching decorator for synchronous decisions, and a base class to back it with anything else.
Looking right #
- Every geometry constant from
WhatsNew.Layout, the pinned blur-backed footer with its 10pt overhang, and the size-class padding branches mapped onto Flutter breakpoints. Verified against the original to the pixel on a phone. - Responsive beyond the original: the content column is capped and centred so
tablet text keeps a readable line length, and a wide, short surface — a
landscape phone, a landscape tablet — splits into a title column beside a
scrolling feature column. Both are configurable, and
maxContentWidth: double.infinitywithcontentLayout: WhatsNewContentLayout.singlerestores WhatsNewKit exactly. - Sizing is measured from the surface, not the screen, so split views, resized desktop windows and embedded use all lay out correctly.
- All colours derived from
ColorScheme, overridable per call or app-wide via aWhatsNewThemeextension. - Rich text through inline Markdown or explicit spans, with recognizer lifetimes managed so links do not leak.
Accessibility and internationalisation #
- Every control clears 48pt, so Apple's 44pt minimum, Material's 48dp and WCAG 2.2 target size all pass. The bare text link is padded out without changing how it looks.
- Screen readers get one element per feature, a heading that names the route, and buttons announced once with the correct traits — a URL action carries the link trait, an in-app action does not.
- Dynamic Type is tested to 300%, Bold Text thickens every weight, and a high contrast preference replaces the footer blur with an opaque fill.
- The suite asserts all four of Flutter's accessibility guidelines.
- Right-to-left works without configuration: the icon column, the feature text
and the two-column arrangement all mirror.
showWhatsNewSheetandWhatsNewSheet.showtake an optionaltextDirectionfor a single sheet whose copy is in a different script from the rest of the app.
Notes #
- Two plugin dependencies,
shared_preferencesandurl_launcher, neither of whose behaviour is required — supply your own store and link handler and nothing calls them. WASM-compatible; all six Flutter platforms. - Supply the app version yourself — see
WhatsNewAppVersion. The package takes nopackage_info_plusdependency.
