Skip to content
Back to archipelago.banuacoder.com

Feature SDKs

How to scroll to a widget from anywhere in a Flutter app ​

Free Reference for autoscroll_sdk: every variable, option and package it generates. Command: archipelago generate autoscroll_sdk

Generate the Autoscroll SDK, register a GlobalKey during build, scroll to it on demand, and learn why unregistered ids do nothing.

What you'll learn ​

  • Generating the Autoscroll SDK
  • Registering a widget location's GlobalKey during 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 ​

Step 1: Generate the Autoscroll SDK ​

The Autoscroll SDK is a Free tier brick.

bash
archipelago generate autoscroll_sdk

There are no generation variables. The SDK is string-keyed at runtime, so there is no fixed list of screen names to maintain.

Step 2: Understand the Generated Structure ​

The Autoscroll SDK uses the API/Impl split pattern:

text
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/             # AutoscrollSdkImplPackageModule

Features that register or scroll to a location depend only on autoscroll_sdk_api.

The post-generation hook adds both packages to the workspace and the impl to the app pubspec.yaml. It registers AutoscrollSdkImplPackageModule in lib/di/injector.dart. The SDK is a lazy singleton that holds an in-memory map.

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:

dart
// 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, call scrollTo. A deep-link handler or a notification tap handler is a typical caller. Call it once the owning screen is built:

dart
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:

dart
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:

  • id was never registered via getOrCreateKey, or
  • id is registered but its widget is not currently mounted (e.g. the owning screen has not been built yet, or has already been disposed).
dart
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 must not be reused, call release:

dart
@override
void dispose() {
  getIt<AutoscrollSdk>().release('transaction-42');
  super.dispose();
}

Call reset() to clear every registration at once. This helps in tests and after a 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 Column wrapped in a SingleChildScrollView

It does not support scrolling to an item far outside the built range of a virtualized list. There is no height cache for that case, which keeps 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 ​

GoalWhat to do
Scroll instantly, no animationPass duration: Duration.zero
Pin the target to the top of the viewportPass alignment: 0
Pin the target to the bottom of the viewportPass alignment: 1
Clear a stale registration on screen disposalCall release(id) in dispose()
Reset all registrations (e.g. sign-out)Call reset()

Next Steps ​

  • Wire scrollTo into your deep-link handler alongside route navigation
  • Configure push notifications to land users on a specific transaction or section via a registered id