flutter_adaptive_cards_host_fs

Backend invoke bridge for pub.dev and on GitHub at flutter_adaptive_cards_fs — serialize host callbacks, POST to your flow-service, parse responses, and apply patches to the rendered card.

Published on pub.dev and on gitub at flutter_adaptive_cards_host_fs.

Usage

Import the barrel:

import 'package:flutter_adaptive_cards_host_fs/flutter_adaptive_cards_host_fs.dart';

Full guide: docs/backend-host-integration.md

Package structure

Wires core invoke callbacks to your flow-service. Parsing and overlay application delegate to flutter_adaptive_cards_fs (RawAdaptiveCardState.applyUpdates, document notifier).

flowchart TB
  subgraph host_pkg["flutter_adaptive_cards_host_fs"]
    BH["AdaptiveCardBackendHandlers\nwrap() · onSubmit · onExecute · onChange · onRefresh"]
    REQ["models/\nAdaptiveCardInvokeRequest · Response · Effect · Kind"]
    subgraph adapters["adapters/"]
      PJ["PlainJsonInvokeAdapter + response parser"]
      TM["TeamsInvokeAdapter"]
      EU["element_update_json.dart"]
    end
    subgraph client["client/"]
      BC["AdaptiveCardBackendClient"]
      HTTP["HttpAdaptiveCardBackendClient"]
    end
    BH --> REQ
    REQ --> PJ
    REQ --> TM
    PJ --> BC
    TM --> BC
    BC --> HTTP
    REQ --> EU
  end

  subgraph core["flutter_adaptive_cards_fs"]
    IH["InheritedAdaptiveCardHandlers"]
    RS["RawAdaptiveCardState.applyTo / applyUpdates"]
    DOC["Document overlays · onCardReplaced"]
  end

  UI["User Submit · Execute · Refresh · input change"] --> BH
  BH -. replaces handlers .-> IH
  BH -->|"POST serialized body"| HTTP
  HTTP -->|"effects in order"| RS
  RS --> DOC

Quick start

import 'dart:developer';

import 'package:flutter/material.dart';
import 'package:flutter_adaptive_cards_fs/flutter_adaptive_cards_fs.dart';
import 'package:flutter_adaptive_cards_host_fs/flutter_adaptive_cards_host_fs.dart';

final cardKey = GlobalKey<RawAdaptiveCardState>();

AdaptiveCardBackendHandlers(
  client: HttpAdaptiveCardBackendClient(
    endpoint: Uri.parse('https://api.example.com/adaptive-card/invoke'),
  ),
  cardKey: cardKey,
  onError: (error) => log('invoke failed', error: error),
).wrap(
  RawAdaptiveCard.fromMap(
    key: cardKey,
    map: cardJson,
    hostConfigs: HostConfigs(),
  ),
  onCardReplaced: (map) => setState(() => cardJson = map),
);

Assign the same GlobalKey<RawAdaptiveCardState> to both AdaptiveCardBackendHandlers and RawAdaptiveCard.fromMap. InputChangeInvoke uses invoke.cardState directly; Submit, Execute, and Refresh resolve state from cardKey.

Wired callbacks

Handler Invoked when
onSubmit Action.Submit
onExecute Action.Execute
onRefresh Root card refresh (manual affordance or auto-expire)
onChange Input value changes (includes Data.Query with associatedInputs)

Pass onOpenUrl / onOpenUrlDialog on AdaptiveCardBackendHandlers when you need non–backend URL handling (defaults to no-op).

PlainJson request shape

{
  "kind": "execute",
  "verb": "saveProfile",
  "actionId": "act1",
  "data": { "email": "user@example.com" }
}

Input changes include inputId, value, and optional dataQuery (Teams Data.Query shape with merged parameters when associatedInputs is "auto").

Refresh requests use kind: execute with the nested refresh action's verb and merged input data.

PlainJson response contract

Patches + validation errors:

{
  "type": "adaptiveCard.invokeResponse",
  "effects": [
    {
      "type": "applyPatches",
      "elements": [
        {
          "id": "city",
          "choices": [{ "title": "Paris", "value": "paris" }]
        }
      ]
    },
    {
      "type": "setInputErrors",
      "errors": { "email": "Invalid format" }
    }
  ]
}

Full card replacement:

{
  "type": "adaptiveCard.invokeResponse",
  "card": { "type": "AdaptiveCard", "version": "1.5", "body": [] }
}

Effect apply order

Effects run in JSON array order. Recommended server order:

  1. applyPatchesRawAdaptiveCardState.applyUpdates (choices, visibility, text, …)
  2. setInputErrors — validation overlays on input ids
  3. replaceCard — calls onCardReplaced with full card JSON (required when this effect is present)

Error handling

Case Behavior
Network failure onError; card unchanged
Parse failure AdaptiveCardInvokeResponseParseExceptiononError
Unknown effect type Skipped (debug log in debug builds)
replaceCard without onCardReplaced StateError from applyTo

Always implement onError in production hosts.

Teams adapter

Use TeamsInvokeAdapter.toMap / TeamsInvokeAdapter.responseFromMap for Bot Framework–shaped invoke activities:

AdaptiveCardBackendHandlers(
  client: client,
  cardKey: cardKey,
  requestAdapter: TeamsInvokeAdapter.toMap,
  responseParser: TeamsInvokeAdapter.responseFromMap,
  ...
)

Custom client

Implement AdaptiveCardBackendClient for gRPC, WebSocket, or in-memory mocks:

class MyBackendClient implements AdaptiveCardBackendClient {
  @override
  Future<Map<String, dynamic>> post(Map<String, dynamic> body) async {
    // ...
  }
}

Libraries

flutter_adaptive_cards_host_fs
Backend invoke bridge for Flutter Adaptive Cards.