Setting Up the Autoscroll SDK
What you'll learn
- Generating the Autoscroll SDK
- Registering a widget location's
GlobalKeyduring build - Scrolling to a registered location from anywhere in the app
- Understanding the no-op behavior for unregistered or unmounted ids
- Why this SDK does not support scrolling deep into virtualized lists
Prerequisites
- An existing Archipelago monorepo (see Monorepo Scaffolding)
Step 1: Generate the Autoscroll SDK
The Autoscroll SDK is a Free tier brick.
archipelago generate autoscroll_sdkThere are no generation variables — the SDK is entirely string-keyed at runtime, with no fixed enum of screen names to maintain.
Step 2: Understand the Generated Structure
The Autoscroll SDK uses the API/Impl split pattern:
features/
└── autoscroll_sdk/
├── autoscroll_sdk_api/ # AutoscrollSdk contract — no models, no plugin
└── autoscroll_sdk_impl/ # In-memory Map<String, GlobalKey> registry
└── lib/src/
├── autoscroll_sdk_impl.dart
└── di/ # AutoscrollSdkImplPackageModuleFeatures that register or scroll to a location depend only on autoscroll_sdk_api.
Step 3: Register a Widget Location
Call getOrCreateKey(id) once per build, on the widget you want to be able to scroll back to. It is safe to call on every build — repeated calls with the same id return the same key instance:
// A transaction list item, keyed by its own id:
final autoscroll = getIt<AutoscrollSdk>();
ListTile(
key: autoscroll.getOrCreateKey('transaction-$transactionId'),
title: Text(transaction.title),
);Step 4: Scroll to a Registered Location
From anywhere else in the app — typically a deep-link handler or a notification tap handler — call scrollTo once the owning screen is expected to already be built:
final autoscroll = getIt<AutoscrollSdk>();
await autoscroll.scrollTo('transaction-42');scrollTo accepts optional duration, curve, alignment, and alignmentPolicy parameters, all forwarded to Scrollable.ensureVisible. The defaults animate a short scroll that centers the target in the viewport:
await autoscroll.scrollTo(
'transaction-42',
duration: const Duration(milliseconds: 500),
curve: Curves.easeOutCubic,
alignment: 0, // pin to the leading edge instead of centering
);Step 5: Handle the No-op Case
scrollTo never throws. It returns false when:
idwas never registered viagetOrCreateKey, oridis registered but its widget is not currently mounted (e.g. the owning screen has not been built yet, or has already been disposed).
final scrolled = await autoscroll.scrollTo('transaction-42');
if (!scrolled) {
// Fall back to a default destination, or do nothing — a bad deep link
// should never crash the app.
await context.router.push(const TransactionListRoute());
}Step 6: Release Stale Registrations (Optional)
If a screen that owns a registered location is being permanently disposed and its id should not be reused stale, call release:
@override
void dispose() {
getIt<AutoscrollSdk>().release('transaction-42');
super.dispose();
}Call reset() to clear every registration at once — useful for tests or a full state reset (e.g. sign-out).
Understanding the Virtualized-List Limitation
scrollTo resolves the target through its GlobalKey's current BuildContext, which must already exist. This works reliably for:
- Content currently on screen
- Content within a lazy list's cache extent
- Any non-lazy scrollable, such as a
Columnwrapped in aSingleChildScrollView
It does not support scrolling to an item far outside a virtualized list's currently-built range — there is deliberately no height-preservation cache for that case, keeping the SDK simple. Reserve registered ids for content that is realistically already laid out, such as sections of a single screen. For guaranteed reachability deep into a long ListView.builder, use a dedicated package such as scrollable_positioned_list instead.
Common Customizations
| Goal | What to do |
|---|---|
| Scroll instantly, no animation | Pass duration: Duration.zero |
| Pin the target to the top of the viewport | Pass alignment: 0 |
| Pin the target to the bottom of the viewport | Pass alignment: 1 |
| Clear a stale registration on screen disposal | Call release(id) in dispose() |
| Reset all registrations (e.g. sign-out) | Call reset() |
Next Steps
- Wire
scrollTointo your deep-link handler alongside route navigation - Configure push notifications to land users on a specific transaction or section via a registered id