buildAdapter<T extends NutrientController> static method
T
buildAdapter<T extends NutrientController>()
Build the controller for a NutrientDocumentView<T>.
Resolution order:
- a builder registered via addAdapterClass for
T(fresh instance); - when no specific type was requested (
T == NutrientController, i.e. a bareNutrientDocumentView()), a fresh platform default viewer per view — resolved before the shared slot so concurrent bare views never collide on one controller; - otherwise, a typed instance registered through the deprecated global
slots (initialize's
androidAdapter:/etc.) when it matchesT.
Throws StateError with an actionable message when T is a specific type
with no registration — surfacing the misconfiguration instead of a silent
null. Intended for the SDK's views; apps use addAdapterClass +
NutrientDocumentView<T>.
Implementation
static T buildAdapter<T extends NutrientController>() {
final factory = _factories[T];
if (factory != null) return factory() as T;
// No specific controller type requested (a bare `NutrientDocumentView()`):
// build a FRESH platform default viewer per view. Resolve this BEFORE the
// shared global slot so two simultaneous bare views each own their own
// controller (multi-view safe + disposed by the view) instead of clobbering
// the one process-shared default — the A1 collision this design prevents.
if (T == NutrientController) {
final fallback = _makeDefaultAdapter();
if (fallback is T) return fallback as T;
}
// Same, for a bare `NutrientInstantView()`: build a FRESH platform default
// Instant controller per view (implements NutrientInstantController, so the
// Instant sync methods are available on the surfaced controller).
if (T == NutrientInstantController) {
final fallback = _makeDefaultInstantAdapter();
if (fallback is T) return fallback as T;
}
// Back-compat: a *typed* controller registered through the (deprecated)
// global slots — only reached for a concrete T (a bare view took the
// fresh-default path above). The slot's static type (NutrientPlatformAdapter)
// is unrelated to T so `is T` can't promote; the real adapters implement
// both, so the runtime check + cast is sound.
final slot = _adapterSlotForCurrentPlatform();
if (slot is T) return slot as T;
throw StateError(
'No adapter registered for $T. Pass `adapter:` to NutrientDocumentView, '
'or register a factory with Nutrient.addAdapterClass<$T>(() => ...).',
);
}