Flutter SDK reference
Пара: shopstory_player_flutter 0.2.0-preview.3 и native ShopStoryPlayerSDK 0.2.0-preview.2. Описание сверено с immutable tags. Установка, toolchain и starter: Flutter на iOS. Android и Flutter Web в этот adapter contract не входят.
Entry point и конфигурация
Используйте ShopStoryPlayerFlutter.instance. Перед конфигурацией проверяйте isSupportedPlatform: он возвращает true только на iOS вне Web. На других платформах методы конфигурации и player завершаются UnsupportedError.
await ShopStoryPlayerFlutter.instance.configure(
applicationId: applicationId,
hostAppVersion: hostAppVersion,
isEnabled: true,
followMePlayerEnabled: true,
manualMinimizeEnabled: true,
);
applicationId обязателен. hostAppVersion — необязательная строка; три boolean-параметра по умолчанию равны true. Это локальные разрешения; remote policy может дополнительно запретить SDK UI. Успешное завершение configure не подтверждает доступность видео. Policy, её cache и refresh принадлежат native SDK.
Публичные методы
| API | Результат | Назначение |
|---|---|---|
configure(...) | Future<void> | Конфигурация native SDK |
setCurrentSurface(surface) | Future<void> | Передача реально видимого commerce-экрана |
showProductDetailPreview(feedProductId: ...) | Future<ShopStoryProductStreamResult> | Низкоуровневый запрос и показ preview; заменяет предыдущую preview session |
clearProductDetailPreview() | Future<void> | Снятие активного preview |
closePlayer() | Future<void> | Закрытие SDK player surfaces |
registerProductActionHandlers(...) | ShopStoryProductActionHandlerRegistration | Регистрация host callbacks для product actions |
lifecycleEvents | Stream<ShopStoryLifecycleEvent> | Поток локальной диагностики |
Для обычной PDP используйте ShopStoryProductDetailScope: он управляет preview session за widget. Не дублируйте его запрос вызовом showProductDetailPreview для того же экрана.
ShopStoryProductDetailScope
ShopStoryProductDetailScope(
feedProductId: product.feedProductId,
onResult: handlePreviewResult,
onError: reportPreviewError,
child: ProductDetailView(product: product),
)
ProductDetailView, handlePreviewResult и reportPreviewError принадлежат приложению. feedProductId и child обязательны; player, onResult и onError необязательны.
Scope запрашивает preview после первого frame, обновляет его при смене товара и снимает свою session при dispose. Освобождение старого scope не должно снимать preview нового. Scope не вызывает setCurrentSurface: видимый route по-прежнему сообщает host app.
Результаты preview
| Тип | Поле | Действие приложения |
|---|---|---|
ShopStoryProductStreamActive | streamId | Сохранить ID для диагностики; SDK показывает preview |
ShopStoryProductStreamUnavailable | reason | Оставить PDP без SDK placeholder |
ShopStoryProductStreamFailure | error | Сохранить рабочую PDP и записать техническую причину |
Unavailable reasons: noActiveStream, scheduled, archived, disabled, notEligible. Эти варианты существуют в SDK для разбора состояния; опубликованный HTTP-контракт сейчас возвращает no_active_stream для недоступного видео.
Technical errors: sdkNotConfigured, runtimePolicyDisabled, invalidProductIdentifier, networkFailure, invalidResponse, decodingFailed, requestSuperseded, presentationUnavailable. Первые семь соответствуют native lookup errors. presentationUnavailable означает, что native UI не удалось представить; проверьте текущий экран и lifecycle приложения.
Типизированный ShopStoryProductStreamFailure приходит через результат. Ошибки вызова или platform channel обрабатываются через onError; без этого callback scope использует FlutterError.reportError.
Navigation и surface
ShopStorySurface содержит home, liveList, catalog, search, productDetail, cart, checkout, payment, auth, profile, unknown.
ShopStoryNavigatorObserver(surfaceForRoute: ...) поддерживает один root Navigator. Callback получает PageRoute<dynamic> и возвращает ShopStorySurface?:
- обычный экран — его surface;
- неизвестный экран —
ShopStorySurface.unknown; - page-style overlay, сохраняющий нижележащий commerce-экран, —
null.
Observer обрабатывает push, pop, replace и remove, пропускает pageless overlays и подавляет повтор одинаковой surface. Необязательный onError(Object, StackTrace) получает ошибки mapping и channel; без него используется FlutterError.
Для declarative router, tabs и nested navigators вызывайте setCurrentSurface из существующего owner видимого состояния. Observer не определяет бизнес-смысл маршрутов за приложение.
Product actions
ShopStoryProductAction содержит kind и payload. Kind: openProduct, addProduct, unknown. Payload содержит необязательные name, feedProductId, feedProductGroupId, url, vendorCode, source. Строковый feedProductId должен совпадать с ID в каталоге и фиде; url не управляет native routing.
Регистрация принимает onOpenProduct и onAddProduct. Нужен хотя бы один callback; иначе возникает ArgumentError. Callback имеет тип FutureOr<ShopStoryProductActionResult> Function(ShopStoryProductAction).
| Результат callback | Когда возвращать |
|---|---|
ShopStoryProductActionOpenedProduct() | Router принял переход на native PDP; adapter запускает handoff к Follow Me |
ShopStoryProductActionSucceeded() | Host подтвердил действие без открытия PDP, например успешную cart operation |
ShopStoryProductActionFailed(reason) | Действие отклонено или не выполнено |
Не ожидайте завершения Navigator.push: его Future обычно завершается после возврата с открытой страницы. Результат OpenedProduct возвращается после принятого перехода.
Failure reasons: invalidPayload, unsupportedAction, unavailable, rejected, operationFailed. Неизвестное действие или отсутствие нужного handler не разрешает переход и изменение корзины. У onAddProduct нет автоматического fallback на onOpenProduct.
Root integration owner удерживает регистрацию и вызывает dispose() при освобождении. Новая регистрация заменяет callbacks для будущих действий; уже начавшееся действие использует прежний callback.
Timeout и результат корзины
iOS plugin ограничивает срок ожидания product action 30 секундами. Истечение этого срока не отменяет cart operation магазина. При потерянном ответе сначала выясните её результат по operation ID; не повторяйте запись только из-за timeout.
Результат Flutter callback управляет host-side lifecycle adapter. Он не передаётся embedded player как подтверждение корзины. Merchant UI и аналитика магазина используют подтверждённый ответ Cart API независимо от состояния кнопки player.
Lifecycle diagnostics
final subscription = ShopStoryPlayerFlutter.instance.lifecycleEvents.listen(
(event) => reportShopStoryDiagnostic(event.kind, event.rawKind),
);
// При освобождении integration owner:
await subscription.cancel();
reportShopStoryDiagnostic — функция приложения. kind содержит типизированную категорию, rawKind сохраняет исходное имя, включая неизвестное новой версии. Необязательный message предназначен только для согласованной диагностики; не показывайте его пользователю и не отправляйте целиком в стороннюю аналитику.
| Группа | Примеры ShopStoryLifecycleEventKind | Реакция |
|---|---|---|
| Player | playerOpened, playerRestored, playerClosed, pageLoaded | Наблюдать lifecycle; pageLoaded не подтверждает video frame |
| Background | backgroundPaused, foregroundRestored | Сверить видимую surface и текущую policy |
| Follow Me | followMeShown, followMeHidden, followMeRestored, followMeSuppressed, followMeUnavailable | Проверить route, контейнер и разрешение policy |
| Preview | productPreviewShown, productPreviewHidden, presentationUnavailable | Проверить актуальный товар и экран |
| Ошибки | navigationFailed, playerLoadFailed, navigationBlocked, playerLoadBlocked, bridgeMessageBlocked, runtimePolicyDisabled | Сохранить очищенную причину; не обходить origin и bridge checks |
| Product action | productActionSuccess, productActionFailure, productActionTimeout, productActionIgnored, productActionDecodeFailed, callbackError | Проверить handler и подтверждённый результат магазина |
Lifecycle diagnostics не заменяют события аналитики. Для несовместимого bridge adapter может вызвать ShopStoryBridgeContractException; проверьте согласованную пару Flutter/native tags и чистую сборку.
Migration notes
preview.2→preview.3: rootNavigatorможет использоватьShopStoryNavigatorObserverвместо собственного observer. Сохраните route-to-surface mapping; для tabs и nested navigation остаётся явныйsetCurrentSurface.preview.1→preview.2: обновите обе зависимости до соответствующих exact tags и lockfiles. Native entrypoint перестал раскрывать player URL; Dart API этой пары не изменился.
После обновления выполните приёмку перед выпуском. При проблеме используйте диагностику Mobile SDK.