Manage pages and asynchronous state in Flutter
Build a teaching library around Flutter widget identity, navigation, and asynchronous request ordering, with a persistent repository and explicit stale-response, failure, and disposal handling.
Implement library and detail screens, reject stale results during rapid query changes, and recover saved content after an offline restart.
A teaching library has categories, search results, and document pages. When a user changes a query, the previous request may still be running. A newer request can finish first and then be overwritten by an older response. This case connects Flutter component state with asynchronous boundaries.
Use synthetic document IDs and a network adapter returning controlled test data. The initial version includes lists, details, local copies, and retries. Account synchronization and recommendations can follow after these behaviors are verified.
Separate widget description from document identity
Widgets are immutable interface descriptions. Elements retain identity and associations in the tree; render objects participate in layout and painting. Rebuilding widgets does not imply repainting every pixel. Actual layout and painting depend on the change, so rebuild counts alone are not a performance result.
StatefulWidget State has a managed lifecycle. Business documents must not depend on one component instance staying alive. Identify list rows by document ID, because titles and positions can change. When keys are needed, use values reflecting logical identity rather than random keys that discard retained state.
Represent loading, empty results, success, and failure explicitly. Refreshing an existing list can retain its content with a refresh indicator. Selection refers to an ID, and a body-read failure should affect its detail view without removing other saved documents.
Give the repository asynchronous responsibilities
A Dart Future represents one eventual value or error. A Stream represents multiple events. Requests commonly fit Futures, while ongoing library changes may fit streams. Neither automatically parallelizes CPU-intensive parsing. Assess isolates separately when computation warrants them.
A repository reads lists and bodies, saves copies, and coordinates retries. It owns persistent data and request information while screens emit intent and display results. Avoid independently mutable copies of one document in several screens; deleting through one copy can leave another falsely marked offline-ready.
Cancellation, errors, and completion need a common commit policy. A failed request must remain distinguishable from a successful empty query. Handle State disposal and request relevance before updating the interface. A result can be obsolete even when its screen still exists.
Establish a runnable Flutter project
Install a toolchain compatible with the target platform. Run flutter doctor to find missing dependencies, create the project with flutter create reading_library, and use flutter run to verify the starter. Record SDK and device information. These are instructions for the reader, not claims that this application has been executed here.
Separate document models, repository, library screen, and detail screen. Add two text resources through project asset configuration. Return a synthetic catalogue and implement list selection and return navigation. Pass the document ID to the detail screen, which resolves the current record from the repository.
Choose a persistence solution supported on the target platforms and record plugin and native configuration. Save the body and index, then restore from that index on relaunch. Verify resource loading and persistence before adding asynchronous search. Platform adapters must handle sandboxes, file selection, and permissions; desktop paths cannot simply be reused everywhere.
Reject responses that arrive late
Increment a sequence for each query and allow only the newest sequence to commit. Debouncing reduces requests but does not eliminate out-of-order responses. Network cancellation can save resources, yet result validation remains necessary when cancellation arrives after completion.
The offline Python example assumes request 2 is current. It rejects request 1 and accepts request 2. It runs no Dart Future, cancels no HTTP operation, and creates no Flutter interface.
latest_request = 2
library = []
def finish(request_id, items):
global library
if request_id != latest_request:
return False
library = list(items)
return True
assert not finish(1, ["old"])
assert finish(2, ["new"])
assert library == ["new"]
print(library)
The output is ['new']. The rejected response does not modify the library. Apply the same sequence rule to errors and loading completion. Otherwise an obsolete failure can replace a newer successful state.
Before updating a disposed screen, verify lifecycle validity. Repository-owned persistence may continue after navigation. A mounted check addresses part of lifecycle handling, but cannot identify obsolete queries. A mounted screen may already be showing a different search.
Persist state and adapt platforms
The local index needs ID, title, content version, and copy location. Mark a document offline-ready only after a complete, readable write. Separate catalogue loading from body reading so one corrupt document does not block the whole library.
On network failure, saved content remains readable and unsaved content shows an explicit offline limitation. A failed download preserves an existing version with a retry action. Removing a copy updates the repository before the screen derives its new status.
Desktop, mobile, and browser targets differ in window sizing, navigation, keyboards, and file capabilities. Shared widgets still require testing with long titles, text scaling, keyboard obstruction, and return behavior. Plugin platform support does not remove permission and build configuration work.
Check scrolling responsibilities for long bodies. Build catalogue entries as needed and retain a recoverable reading position in detail. Do not give document fragments random identities on every refresh. Preserve the query after search failure so the user can edit or retry.
Define stable ordering in the adapter, distinguishing same-titled documents by a deterministic field. Pagination needs query and page identity. Old pages must not append after refresh. Test final, empty, and retried pages, retaining each document once under an explicit client-server contract.
Exercise: verify ordering and recovery
Introduce controlled delays that make an old query finish after a new one, then make the old query fail. Rapid query changes must show only the current query's result or error. Obsolete events must not close its loading indicator.
Persist two documents, exit, and reopen offline. Remove one local copy and confirm its row explains that it is unavailable offline. Navigate away before a delayed response returns and check for disposal errors. Record observed behavior before extending categories and pagination. The Python model validates only the stated ordering contract.
Sources & further reading
Sources checked on 2026-11-10. Original engineering explanation; examples are teaching data, not production measurements.