formstack 3.3.0
formstack: ^3.3.0 copied to clipboard
A cross-platform ResearchKit and ODK alternative for Flutter. Build dynamic forms and surveys with 35 input types, 35+ validators, repeat groups, offline save, and multi-language.
Changelog #
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
3.3.0 #
Covering the untested subsystems, and the bugs that were hiding in them. Test coverage went from 58% to 67%; the files with no coverage at all went from 17 to 5, all of which are backed by platform views.
Fixed #
- A form beginning with a
QuestionStepnever reported its results. Reaching the end reset the form to its first step before firing the completion callback — and rendering a step reassignsonFinishfrom that step, so a leading question (carrying no callback of its own) replaced it with null.onFinishsilently never ran and the submission was lost. The callback is now captured before navigating, and receives a copy of the answers rather than the map that is about to be cleared. - The completion callback fired twice when no condition matched. The unmatched-condition path reported, then fell through and reported again — the second time after the answers had been cleared.
- The review step listed every step, including itself. Instructions, the completion step and the review step all appeared as "Not provided", because the flat result map carries an entry per step rather than per answer.
- JSON-defined steps lost their button labels. An absent
nextButtonTextpassed null, wiping the per-type default: a JSONDisplayStepshowed "Next" where a Dart one showed "Start", aReviewStep"Next" instead of "Submit", aCompletionStep"Next" instead of "Finish". - An unknown enum value in JSON threw a bare
StateError.display,selectionType,componentsStyle,inputStyleanddisplayStepTypenow report the field, the value and the permitted set. DynamicConditionalRelevantcrashed on a null callback. The parameter is nullable but was force-unwrapped, so a condition built without one threw during navigation instead of simply not matching.PopStepthrew at the root of an app, where there is nothing to pop, and could touch a defunct context if the view was disposed before its frame.
Added #
PopStepis exported. It was usable from JSON as{"type": "PopStep"}but unreachable from Dart.- Tests for the previously untested subsystems: the expression evaluators,
FormStackLocale,StaticDataProvider,DynamicConditionalRelevant, the consent, review, completion, display and pop step views, and the geotrace coordinate input.
Notes #
Five files remain without coverage — the Google Maps widgets, the map input, the web view and the HTML editor. All are backed by platform views, which a widget test cannot instantiate; they need an integration test on a device.
3.2.0 #
Conditional navigation was broken for most answer types. This release fixes the expression evaluators, a regression shipped in 3.1, and the silent loss of date answers.
Fixed #
- Numeric conditions never matched. The operand was compared as a string
against a number, so
= 5on a rating, slider, NPS or OTP answer was always false and!= 5always true. Comparison is numeric, and<,<=,>and>=are supported. - Decimal answers matched every condition. Dispatch tested
is int, so a slider'sdoublefell through to the catch-all evaluator. - Boolean and other answers matched every condition. The catch-all
evaluator returned
trueunconditionally, so a boolean question always took its first branch and could never reach its "no" path. NOT_INmeant "not all of these".NOT_IN a,bmatched a selection containinga, because it negated "every one is present" rather than "any one is present". It also comparedOptionsobjects against strings, so on a multiple-choice answer it matched whatever was selected.- Operands containing spaces were truncated. Conditions were split on every
space, so
= New Yorkcompared against"New"and never matched. < DAY(01-01-2025)threw. The two-term date form parsed the operator as a date expression and raised aRangeErrorbefore comparing anything. A bare date operand —< 01-01-2025— threw for the same reason.- Date answers were dropped from results.
generateResultonly recorded aDateTimewhen the step carried aDateResultType, so with any other validator — or none — the answer was missing fromexportAsJson, from saved drafts, and from the map handed toonFinish. Dates are now always recorded, formatted when the step says how and ISO-8601 otherwise. - Container steps disposed their children twice. Introduced in 3.1: nested
and repeat steps build child views into the tree, so the framework disposes
each as it unmounts, but they also cascaded from their own
dispose(). Any form containing aNestedSteporRepeatStepasserted with "A TextEditingController was used after being disposed" when it was torn down. - Restoring a draft threw on a repeat group. A JSON round-trip widens the
element type, which the unchecked cast on resume rejected. Rating and NPS
inputs likewise assumed
intwhere a restored draft can hold a double or a numeric string.
Changed #
- Unknown operators now raise
ArgumentErrorconsistently across every answer type, rather than silently matching.
Added #
test/unit/expression_test.dartcovers every operator against every answer type; each case was checked against the behaviour before the fix.test/integration/draft_restore_test.dartround-trips a form through JSON and rebuilds each restored step, which is what surfaced the container double-dispose.
3.1.1 #
A memory audit of the library, and the leaks it found.
Fixed #
- A
WebViewControllerwas created on every rebuild.DisplayStep's web view built its controller in a static method called straight frombuild(), so each rebuild spawned another native web view and issued anotherloadRequest— repeated network loads, and a platform view leaked per rebuild. It now lives in aStateand navigates in place when the URL changes. - Dialogs leaked a text controller per rebuild. The barcode scanner's
manual-entry dialog constructed a
TextEditingControllerinside itsshowDialogbuilder, which Flutter re-invokes on every rebuild of the dialog route; the geotrace coordinate dialog leaked two per point added. Both use the newDialogTextField, which ties the controller to an element the framework disposes. - Base64 images were decoded on every build.
Image.memorykeys its cache entry on theUint8Listinstance, so decoding afresh each build handed it a new key: Flutter re-decoded the image, cached it again, and evicted other entries. The image and signature inputs hold their decoded bytes. - Post-frame focus callbacks could touch a disposed
FocusNode. The text, currency, phone, nested and hidden inputs scheduled work for the next frame without checking whether the view had gone. 3.1 made disposal prompt rather than deferred to a cache eviction, so this became reachable — an auto-advancing step or a fast tap was enough. - An unreadable stored image or signature no longer fails the whole step; the input renders empty.
Added #
test/unit/resource_hygiene_test.dart— source-level rules for the leak shapes this codebase is prone to: every disposable field must be released, no controller may be constructed inside a dialog builder, platform-backed controllers must not be built in a build path, and image bytes must be held rather than re-decoded. There is no way to observe a leakedTextEditingControllerfrom a widget test, so these read the source. Each rule was verified to fail against the defect it guards.DialogTextField, a text field that owns and disposes its own controller.
Notes #
FormStack holds its forms in a static registry, so a form and every answer it
collected — including base64 image and signature data — stay in memory until
cleared. That is the API's shape rather than a defect, but it is now documented
in ARCHITECTURE.md and the FAQ, with the advice to call
FormStack.clearForms(name: ...) once a submission has been sent.
3.1.0 #
Moves step-view lifetime into the widget tree, which is the root cause behind
the disposal bug 3.0 patched over. No breaking changes: the InputRegistry
contract, FormStepView and BaseStepView all keep their shape, and the 30
built-in inputs needed no edits.
Changed #
FormStepViewis aStatefulWidget. It was aStatelessWidgetholding controllers, which has no disposal lifecycle at all — sodispose()was only ever called byFormStackForm's own bookkeeping, and only for views it happened to be holding. ItsStatenow builds the view and disposes it when it leaves the tree. Because the state stays on the widget, every existing subclass compiles unchanged.- Step subtrees are keyed by step. Consecutive steps commonly use the same
view class; without a differing key Flutter reconciles them onto one element,
reuses the
State, and never disposes the outgoing view — leaking its controllers and carrying the previous step's focus into the next step. - The view cache is gone.
FormStackFormkeeps a reference to the view for the step on screen and nothing more. A retained view would be reused after the framework had disposed it, so the cache and framework ownership cannot coexist. Answers are unaffected: they live onformStep.result, which is written before every navigation.
Fixed #
- A captured signature was lost on back navigation.
InputType.signaturenever restored fromformStep.result, so the answer survived only as long as the view cache happened to hold the view. It now restores and shows the captured image. This was already reachable in 3.0 by settingmaxCachedViews: 0, which the docs suggested.
Deprecated #
FormStackForm.maxCachedViews,clearViewCache()anddisposeViews()are no-ops and will be removed in 4.0. Nothing is retained to bound, clear or dispose. Existing calls are harmless.
Added #
- A round-trip test over every buildable input type: set an answer, navigate
away, navigate back, advance, and assert the answer survived. An input that
fails to restore reports an empty
resultValue()on the way forward and destroys what the user entered — exactly what the cache was hiding. Verified to fail when signature restoration is removed.
3.0.0 #
A correctness, extensibility and packaging release. The breaking changes are
confined to the step model; forms defined through FormStack.api().form(...)
or JSON are unaffected.
Fixed #
- Step views were never disposed. Every step view is a
StatelessWidgetholding aTextEditingController,FocusNodeandValueNotifier, and nothing in the framework releases those.FormStackViewnow disposes the form's cached views when it is removed from the tree, andFormStackFormdisposes views it evicts. A form run no longer leaks one controller set per step. Covered by a regression test. - The view cache was unbounded.
FormStackForm.maxCachedViews(default 12) now caps retained step views; the current step is never evicted. A hundred-step survey previously held every view for the lifetime of the form. - The "submitting" spinner never appeared.
isProcessingwas a plain field on aStatelessWidget, so changing it could not repaint. It is now backed by aValueNotifierand the primary button listens to it, which also means the button is genuinely disabled during an asynconBeforeFinish. - JSON parse errors were swallowed.
ParserUtils.buildFormFromJsonwas anasync voidmethod, so itsFormatExceptions escaped to the zone instead of reaching the caller —loadFromAssetreported success on a malformed file. It is synchronous again and errors propagate. ResultFormat.notEmptyrejected every string. It only recognisedList, sonotEmptyon a text field could never pass. It now acceptsString,IterableandMap. This is a widening: nothing that passed before fails.ResultFormat.notBlankaccepted whitespace." "passed anotBlankcheck because the value was never trimmed. It now trims, matching the conventional meaning of "blank".- The form-level JSON
themenever applied. Every step parsed from JSON received a fully-defaultedUIStyle, sostep.style ??= formThemenever fired and the documented"theme"key was silently ignored. Step factories now useUIStyle.maybeFrom, which returns null for an absent style. - A map location callback was an accidental set literal. The
onChangehandler was written(p0) => {formStep.result = p0}, which builds aSetcontaining the assignment's value rather than a block. It worked by accident. - The Places autocomplete built its URLs by string interpolation. The
search text went into the query unescaped, so an
&or=in what the user typed rewrote the request — appending or replacing parameters, including the API key. Both endpoints are now built withUri.httpsand encoded query parameters, and a non-200 response is handled rather than parsed. - Malformed form definitions failed obscurely. A relevant condition without
an
idorexpression, an option that is not an object, or arelevantConditionsvalue that is not a list produced aNoSuchMethodErroron a dynamic call or a condition that could never match. Each is now aFormatExceptionnaming what is wrong. - Numeric values in a JSON
themewritten as strings ("borderRadius": "12") are coerced rather than discarded. - Two
Futures returned from insidetryblocks were not awaited, so their errors bypassed the surroundingcatch. - A failed or cancelled image pick no longer clears an answer the user had
already given, and reports through
FlutterErrorrather thanprint. - The signature pad overflowed its container by 6 pixels on every build. Its wrapper capped height at 200px while the canvas, spacing and Clear button needed ~206px. Found by the new input smoke tests.
Added #
InputRegistry— register application-defined input types, or override a built-in one, without forking the library. Reachable from Dart viaInputType.custom+QuestionStep.customInputType, and from JSON by naming the registered type directly ininputType.StepRegistry— the JSON parser resolves step types through a registry instead of a hard-codedif/elsechain, so a new step type is a registration rather than a change to the parser.ValidatorRegistryand validators in JSON — a JSON-defined step can now declare"validators", unlocking the full validator library to JSON forms. Previously a JSON form could only get the default validator implied by itsinputType; anything more required dropping down to Dart. Applications can register their own named validators.ValidationResultandResultFormat.validate()— validation returns a stablecodeplus constraintparamsalongside the message, so failures can be localized or reported without string-matching.isValid/errorstill work; the new method is expressed in terms of them.FormProgress— position, total and percentage as one value object, computed in a single pass.DeviceCapabilities—BarcodeScannerandAudioRecorderports that makeInputType.barcodeandInputType.audiogenuinely functional. Both previously rendered a UI scaffold with nothing behind it, because the library declares no camera or microphone dependency. Register an adapter backed by the package of your choice and the built-in widgets use it, keeping FormStack's layout, validation and result handling. Without one,barcodefalls back to manual entry andaudiorecords a duration marker, so an app that collects only text and choices still inherits no hardware SDK.FormStackThemeScope—FormStackTheme's instance fields were inert: every call site used the static helpers with hard-coded defaults, so constructing one had no effect.maxContentWidth,contentPadding,borderRadiusandelementSpacingnow apply to the subtree, withFormStackTheme.of(context),copyWithand value equality.FormStackForm.stepAfter/stepBeforefor explicit ordered navigation.- A test suite — 155 tests, from none — covering validators, navigation and branching, JSON parsing and its failure modes, the registries, persistence, the view-disposal chain, and a smoke test that builds every built-in input type, the input registry's resolution order, the theme scope, the device-capability ports, plus a guard that the example app's own JSON assets still parse.
- A CI pipeline covering formatting, analysis with warnings fatal, tests on
Linux, macOS and Windows, the suite again on the oldest supported Flutter, an
example build, and pub.flutter-io.cn publish readiness with a
panascore threshold. ARCHITECTURE.md,SECURITY.md,CODE_OF_CONDUCT.md, issue and pull request templates, and Dependabot configuration.- An Extension Points screen in the example app
(
example/lib/extensibility_demo.dart) showing a registered custom input, a named validator resolved from a JSON spec, a device capability, and a scoped theme — with a test covering the same combination.
Changed #
- The package builds under Dart's full strictness set —
strict-casts,strict-inferenceandstrict-raw-typesare all enabled, and analysis is clean. Turning them on surfaced 251 findings, nearly all implicit downcasts fromdynamicwhere JSON crossed into typed code: they compiled, then threw at the point of use rather than at the malformed field. JsonReadertypes the parsing boundary. Every JSON factory reads through typed accessors that name the field and the step in their error, so{"count": "many"}reportsQuestionStep: "count" should be a number, got "many"instead of failing later as a cast error. Numeric strings and stringified numbers — the shapes people actually write — are coerced rather than rejected, and an unknown enum name lists the permitted values.- Callback types declare their return type.
Function(String)?and friends were bare function types that inferreddynamic; they are nowvoid Function(String)?. - Built-in inputs resolve through
InputRegistrylike everything else.QuestionStep.buildViewwas a 35-armswitch, so the library's own widgets and the extension point had different shapes.BuiltInInputsregisters each built-in withregisterIfAbsent,buildViewis a lookup, and an application override installed first is preserved. FormStepno longer extendsLinkedListEntry. A step described what to ask but was also a node in a list, which meant a step definition could belong to only one form — sharing one threw at runtime. Ordering now lives in the form. This also unblocked the Dart 3 migration:LinkedListEntryisbase, which would otherwise have forcedbaseonto every step subclass including application-defined ones.FormStackForm.stepsisList<FormStep>rather thanLinkedList<FormStep>.FormStepno longer takes a type parameter.Twas declared and never used; its only effect was to make every reference toFormStepa raw type. With it gone,strict-raw-typesis enabled and the package is free of raw types.- JSON decoding in the parser, the step factories and the Google Places models
is typed rather than reaching through
dynamic, so malformed input produces aFormatExceptioninstead of aNoSuchMethodError. getStepandgetCurrentIndexare backed by an index instead of walking the step list. The progress bar previously indexed the list twice per build.- Validator regular expressions are compiled once rather than on every keystroke.
ResultFormat.composeno longer keeps mutable state across calls.- The duplicated
inputBorder()implementation, previously copied into five input widgets, is a singleInputStyle.toInputBorderextension. - SDK floor corrected to Dart 3.10 / Flutter 3.38.2. The package declared
flutter: ">=1.17.0"while usingColor.withValues(3.27) andPopScope.onPopInvokedWithResult(3.24), so the declared floor could never have compiled. The new floor is the lowest combination CI actually builds and tests against, in themin-sdkjob — an untested floor is a guess. The binding constraint isfile_picker12, which requires Dart 3.10. - Removed the root
android/,ios/,linux/,macos/andwindows/folders. This is a pure Dart package with no native code; they wereflutter createleftovers containing only generated plugin registrants, whichflutter pub getrewrote on every machine. Platform support is declared inpubspec.yamland is unchanged at 6 of 6. - Dropped
dioandrxdart. Both existed for one file: the Places autocomplete usedDiofor two GET requests and an rxdartPublishSubjectwith.distinct().debounceTime()for its input debounce. Ahttp.Clientand aTimerdo the same work, so every consumer sheds two dependency trees.http— a far smaller package — takes their place; it was previously declared but never imported. - Upgraded
file_picker(10 → 12) andlocation(8 → 10), which were two and three major versions behind and caused resolution conflicts for applications using either package directly. Thefile_picker12 API made the web-versus-native branch in the image input unnecessary, so image picking no longer reaches fordart:io. UIStyle.maybeFromadded;UIStyle.fromis unchanged.- The public top-level
uuidvariable is gone.identifiers.dartexported a mutable top-leveluuid, so every importer of the library had that very collidable name in scope. It is package-private now. - The published API is fully documented. Every symbol reachable from
package:formstack/formstack.dartcarries a doc comment, including all 37ResultFormatfactories,FormStackForm,QuestionStep,FormStepViewand every step type. InputRegistry.buildno longer stampsResultFormat.none()onto a step that declared no validator and whose type registered no default. Doing so marked the step permanently valid and made a validator assigned later unreachable.
Tooling #
- CI actions updated to current majors:
actions/checkoutv4 → v7 (v4 pins Node 20, which the runners now force onto Node 24 with a deprecation notice on every job),codecov/codecov-actionv4 → v5, andsubosito/flutter-actionpinned tov2.23.0.
Documentation #
MIGRATION.md's v2 → v3 section was written speculatively before 3.0 existed and described a release that does not match this one — it listed OTP, HTML and map inputs as new and stated there were no breaking changes. Rewritten against what actually shipped.FAQ.mdnow covers the registries, validators in JSON, localizing validation throughValidationResult.code, the disposal contract for custom inputs, the bounded view cache, and which platform SDKs the package does and does not pull in.
Migration #
step.next/step.previous→form.stepAfter(step)/form.stepBefore(step).class MyStep extends FormStep<MyStep>→class MyStep extends FormStep.- The top-level
uuidvariable is no longer exported; usepackage:uuiddirectly if you were relying on it. - Code that typed a variable as
LinkedList<FormStep>should useList<FormStep>. - Subclasses of
FormStepneed no changes. - Review any use of
notBlank, which now rejects whitespace-only input, and ofnotEmptyon text fields, which now works.
2.5.0 - 2026-04-02 #
Added #
- 2 new example demo screens: "Data Collection (ODK)" and "Multi-Language & Offline" (total: 12 demos)
- Data Collection demo covers: RepeatStep, calculate fields, hidden fields, cascading selects, barcode, audio, geotrace, geoshape
- Multi-Language demo covers: FormStackLocale with EN/ES/FR, DisplayStep with listTile data, runtime language switching
geotraceandgeoshapeadded to README input type tables- Form-level
defaultStyleparameter for applying UIStyle to all steps at once - JSON
themekey at form level for form-wide styling from JSON
Changed #
- Example app now demonstrates all 35 input types and all 9 step types
- README examples table expanded to 12 demo screens
- Architecture file listing updated with all new files
- Input type count corrected to 36 throughout all docs
2.4.0 - 2026-04-02 #
Added #
FormStackTheme- centralized theme system with responsive sizing, dark/light mode colors, and accessibility helpers- Responsive layout: all widgets adapt to mobile (< 600px), tablet (600-1200px), and desktop (> 1200px) screens
- Dark mode support: all colors resolve from
Theme.of(context).colorSchemeinstead of hardcoded values - Semantics wrappers for accessibility on interactive elements
FormStackTheme.responsiveMaxWidth(),responsiveInputWidth(),responsivePadding(),responsiveIconSize(),responsiveButtonHeight()- Theme-aware NPS colors (
npsDetractorColor,npsPassiveColor,npsPromoterColor) - Canvas colors adapt to dark mode (
canvasStrokeColor,canvasBackgroundColor)
Changed #
- Replaced 47 hardcoded color values with theme-aware alternatives across all view files
- Replaced 45+ hardcoded BoxConstraints with responsive sizing
- Error text uses
Theme.of(context).colorScheme.errorinstead ofColors.red - Input backgrounds use
colorScheme.surfaceContainerHighestinstead of hardcoded grey - Borders use
colorScheme.outlineinstead ofColors.grey - Buttons use responsive heights based on screen size
UIStyleexpanded with 7 new properties:inputBackground,inputTextColor,titleColor,subtitleColor,iconColor,cardBackground,fontSize- all settable from JSON- Form-level
defaultStyleparameter applies to all steps without individual styling - JSON
themekey at form level applies default styling to all steps in that form
2.3.0 - 2026-04-02 #
Added #
- 2 new input types:
geotrace(trace path on map),geoshape(draw polygon on map) - Offline save & resume via
FormPersistenceinterface (enablePersistence,saveDraft,resumeDraft,deleteDraft,listDrafts) InMemoryFormPersistencebuilt-in implementation for testingFormDraftserializable model for draft stateExternalDataProviderinterface for loading options from CSV/API/databaseStaticDataProviderbuilt-in implementation withfromCsvfactoryQuestionStep.optionsProviderandoptionsSourceIdfor external data-backed choices- All new types supported in JSON parser
2.2.0 - 2026-04-02 #
Added #
- 4 new input types:
hidden(data-only, no UI),calculate(auto-computed from other results),barcode(QR/barcode scanner),audio(recording with timer) RepeatStep- dynamic repeating sections where users add/remove entries (modeled after ODKrepeat)- Cascading selects via
QuestionStep.choiceFiltercallback - filter options based on other step results (Country -> State -> City) FormStackLocaleclass for multi-language support with runtime language switching,t()andtf()translation methods, and JSON loadingQuestionStep.calculateCallbackfor auto-computing values from collected resultsQuestionStep.calculateExpressionfor declarative calculate formulas- Step view widget caching to preserve state during navigation
- All new types fully supported in JSON schema parser
Fixed #
2.1.1 - 2026-04-02 #
Fixed #
- Step view widget caching to prevent state loss during navigation (TextEditingController text, slider values, selected choices now preserved when navigating back)
- Controller and FocusNode memory leaks caused by widget recreation on every step change
- Cache cleared on form reset via
clearResult()for clean restarts - Step timestamps (
startTime,endTime) reset properly on form clear
2.1.0 - 2026-04-02 #
Added #
booleaninput type - Yes/No toggle buttonsimageChoiceinput type - select from a grid of images with labelsReviewStep- displays all collected answers for review before submissionConsentStepwithConsentSectionmodel and 8 predefined section types (overview, dataGathering, privacy, dataUse, timeCommitment, studyTasks, withdrawing, custom)- Built-in progress bar UI with step counter ("Step 3 of 10") and percentage
- Result timestamps (
startTime,endTime) recorded per step for analytics - Static image support via
titleIconImagePath(asset or network URL) - Video URL support in
InstructionStepviavideoUrl dateRangevalidator withminDate/maxDateboundsResultFormatpublic constructor for custom validator subclassingBaseStepViewandFormStepViewexported for custom input widget creationStepResultandTaskResultclasses for structured result hierarchy (modeled after ResearchKit's ORKTaskResult)- Step lifecycle callbacks:
onStepWillPresent,onStepDidComplete - API methods:
getStep(),getStepResult(),getTaskResult(),exportAsJson() - ResearchKit migration guide in README with side-by-side Swift/Dart examples
- New exports:
ReviewStep,ConsentStep,ConsentSection,ConsentSectionType,BaseStepView,FormStepView,StepResult,TaskResult
2.0.0 - 2026-04-02 #
Added #
- 8 new input types:
slider,rating,nps,consent,signature,ranking,phone,currency - 12 new validators:
min,max,range,minLength,maxLength,pattern,minSelections,maxSelections,fileSize,iban,consent,compose FormStepproperties:helperText,defaultValue,semanticLabelQuestionStepproperties:minValue,maxValue,stepValue,minSelections,maxSelections,consentText,currencySymbol,phoneCountryCode,ratingCount- New exports:
RelevantCondition,ExpressionRelevant,DynamicConditionalRelevant,NestedStep,DisplayStep,UIStyle - Full JSON schema support for all new input types and properties
- Comprehensive example app with 10 demo screens
- Dartdoc comments on all public API classes and members
Changed #
- Renamed
formKitFormtoformStackFormacross codebase - Renamed
TextFeildWidgetViewtoTextFieldWidgetView - Renamed
inputBoder()toinputBorder()across all input fields - Renamed
intputtoinputin expression evaluators - Renamed
htm_field.darttohtml_input_field.dart - Renamed
mapview_field.darttomap_input_field.dart - Replaced
GlobalKeyanti-pattern withValueNotifierinBaseStepViewandCompletionStepView - Updated
index.htmlto modernFlutterLoader.loadinitialization - Updated README with complete documentation, screenshots, and JSON schema reference
Fixed #
- Memory leak: OTP field controllers recreated on every build
- Memory leak: verification code list growing indefinitely on rebuilds
- Memory leak:
addPostFrameCallbackfiring on every rebuild in text, nested, and completion views - Performance: image memory bloat from full-resolution decoding (added
cacheWidth/cacheHeight) - Performance: dynamic key-value controller text resetting on every build
- Fixed typos in class names (
_TextesultType,_MultipleChoiceesultType) - Fixed
nextFormSatcktypo informstack_form.dart - Cleaned up dead commented-out code in
htm_field.dartandweb_view.dart - Removed unnecessary imports across all internal files
- Added
cacheExtentto allListViewwidgets for smoother scrolling
Updated #
google_maps_flutter: ^2.14.0 -> ^2.17.0google_maps_flutter_web: ^0.5.14+3 -> ^0.6.2dio: ^5.9.0 -> ^5.9.2uuid: ^4.5.2 -> ^4.5.3file_picker: ^10.3.7 -> ^10.3.10webview_flutter: ^4.13.0 -> ^4.13.1google_maps: ^8.1.1 -> ^8.2.0flutter_lints: ^2.0.0 -> ^6.0.0 (example)
1.1.1 - 2024-11-15 #
Fixed #
- Memory leak in Google Places autocomplete stream subscription
- Component disposal in nested step views
- Undefined
mountedproperty in completion step view - Auto-trigger callback firing multiple times
Changed #
- Added cache size limit (50 items) to prevent unbounded memory growth
- Optimized image memory usage by clearing file results after encoding
- Optimized controller recreation to only occur when form step result changes
- Reduced unnecessary setState calls in base step view
- Added proper GoogleMapController disposal
- Added lazy loading for background animations
Updated #
webview_flutter: ^4.9.0 -> ^4.13.0dio: ^5.8.0+1 -> ^5.9.0uuid: ^4.5.1 -> ^4.5.2lottie: ^3.3.1 -> ^3.3.2file_picker: ^8.3.7 -> ^10.3.7google_maps_flutter_web: ^0.5.12+2 -> ^0.5.14+3google_maps_flutter: ^2.12.3 -> ^2.14.0http: ^1.4.0 -> ^1.6.0
0.7.4 - 2023-10-15 #
Added #
- Dropdown button with component styles (minimal and basic)
- Dynamic key-value widget
