Vai al contenuto

L’SDK Flutter di Hooky

La conversazione Hooky nella tua app Flutter: un’API Dart e un widget della bolla, costruiti sugli SDK nativi di Hooky. La conversazione stessa è quella degli SDK iOS (SwiftUI) e Android (Jetpack Compose): nativa, senza WebView, con i colori del tuo sito.

Un’app Flutter su Android con il widget HookyBubble in basso a destra. La conversazione Hooky aperta da un’app Flutter su iPhone, in modalità scura.
Il widget HookyBubble su Android, e la conversazione nativa su iPhone, in modalità scura.

Requisiti

  • Flutter 3.35 o successivo (Dart 3.9).
  • Android 7.0 o successivo (minSdk 24, il minimo di Flutter stesso), compilato con compileSdk 36.
  • iOS 15 o successivo.
  • Solo Android e iOS: sul web e sul desktop, le chiamate non fanno nulla.
  • Un account Hooky e un sito: la sua chiave pubblica è in Siti › Installa, la stessa del data-site-key della bolla web.

Installazione

Il pacchetto si installa dal suo repository GitHub, come dipendenza git. Nel tuo pubspec.yaml:

dependencies:
  hooky_sdk:
    git:
      url: https://github.com/Defdjamel/hooky-flutter
      ref: 1.0.0

poi flutter pub get. Gli SDK nativi iOS e Android sono inclusi nel pacchetto: nient’altro da aggiungere, nessun altro repository.

iOS

  • Il plugin funziona sia con Swift Package Manager (attivo per impostazione predefinita nelle versioni recenti di Flutter) sia con CocoaPods: compila l’SDK iOS che include, con il suo manifesto sulla privacy.
  • Porta il target di distribuzione a iOS 15.0: in Xcode, target Runner › General › Minimum Deployments.
  • Per proporre la fotocamera, aggiungi NSCameraUsageDescription a ios/Runner/Info.plist. Senza, l’opzione è semplicemente nascosta.

Android

Niente da dichiarare: l’SDK Android è incluso, con la sua schermata di conversazione, il suo FileProvider e l’autorizzazione INTERNET. Le sue dipendenze (AndroidX, Compose, OkHttp) arrivano da google() e mavenCentral(), già presenti nei progetti Flutter.

Il codice sorgente è sotto licenza MIT: github.com/Defdjamel/hooky-flutter.

Iniziare in 3 righe

import 'package:hooky_sdk/hooky_sdk.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  Hooky.configure('LA_TUA_CHIAVE_PUBBLICA');                     // 1. all’avvio
  runApp(const MiaApp());
}

Scaffold(
  floatingActionButton: const HookyBubble(),      // 2. la bolla fluttuante
  …
);

FilledButton(onPressed: Hooky.open, child: const Text('Scrivici'));   // 3. o il tuo pulsante

configure carica il nome, il colore e il messaggio di benvenuto del sito, riprende la conversazione salvata e tiene aperta la connessione in diretta finché l’app è in primo piano. I suoi parametri con nome: translation: false rimuove la traduzione dei messaggi del team (vedi Traduzione dei messaggi), apiUrl serve solo per fare test sul tuo server.

Tutte le chiamate restituiscono un Future e non generano mai errori: gli errori vengono scritti solo nella console, in debug. Prima di configure, identify, setAttributes e track aspettano, mentre open, close e reset non fanno nulla.

La bolla

HookyBubble somiglia alla bolla nativa di ogni piattaforma: un quadrato arrotondato con ombre morbide su iOS (60 punti), la scheda con bordo e ombra netta della bolla web su Android (64 dp). Prende il colore del sito, mostra un badge per i non letti e compare appena configure è stato chiamato.

// Come pulsante fluttuante dello Scaffold
Scaffold(floatingActionButton: const HookyBubble(), …)

// Ovunque, in uno Stack
Stack(children: [
  const MiaSchermata(),
  const Positioned(right: 20, bottom: 20, child: SafeArea(child: HookyBubble())),
])

// La tua azione, un’altra dimensione, uno stile imposto
HookyBubble(onPressed: () => analytics.log('chat'), size: 52, style: HookyBubbleStyle.material)

Aprire e chiudere la conversazione

Hooky.open();    // a schermo intero: un foglio su iOS, una Activity su Android
Hooky.close();   // la richiude

La conversazione è disegnata dall’SDK nativo: le pagine iOS e Android descrivono cosa fa (foto, PDF, bozza, errori).

Messaggi non letti

La bolla mostra già il suo badge. Per i tuoi pulsanti, Hooky.unreadCount è insieme un ValueListenable<int> e uno Stream<int> (il valore attuale, poi ogni modifica). Torna a 0 quando la conversazione si apre.

// Un badge su una scheda
ValueListenableBuilder<int>(
  valueListenable: Hooky.unreadCount,
  builder: (context, n, _) => Badge(isLabelVisible: n > 0, label: Text('$n'), child: const Icon(Icons.chat)),
)

// O come flusso
Hooky.unreadCount.listen((n) => print('$n non letti'));
final adesso = Hooky.unreadCount.value;

Hooky.state (un ValueListenable<HookyState>) fornisce anche isConfigured, siteName, siteColor e sitePosition, per la tua interfaccia. Le risposte arrivano in diretta finché l’app è in primo piano; la versione 1.0.0 non invia notifiche push ai tuoi utenti.

Identificare l’utente

La tua app sa chi ha effettuato l’accesso: dillo a Hooky. Nella scheda della conversazione, il tuo team legge il nome, l’email, il tuo identificativo dell’utente, il dispositivo e i dati che aggiungi. Con un’email, la conversazione non chiede più né il nome né l’email.

// Dopo l’accesso
Hooky.identify(
  name: user.nome,
  email: user.email,
  userId: user.id,                                     // il tuo identificativo
  attributes: {'piano': 'Pro', 'ordini': 3, 'newsletter': true},
);

// Più tardi: uniti ai precedenti
Hooky.setAttributes({'carrello': 49.9, 'coupon': null}); // null cancella una chiave

// All’uscita
Hooky.reset();

Come funziona

  • Nessuna conversazione ancora: tutto resta in memoria (anche prima di configure) e parte con il primo messaggio. Dopo, ogni modifica parte subito, raggruppata (un solo invio per tutto ciò che cambia nello stesso secondo). Offline, riparte più tardi.
  • name ed email sostituiscono quelli della conversazione (null li cancella sul dispositivo); userId: null lo lascia invariato.
  • Uno userId diverso da quello della conversazione significa un altro account sullo stesso dispositivo: la conversazione viene dimenticata, come con reset(). Il nuovo utente non vede la conversazione del precedente.
  • Hooky.reset() chiude la conversazione e dimentica i messaggi e l’identità. Chiamalo all’uscita.

Le regole dei dati

CampoRegola
Dati liberiUna Map piatta: 50 chiavi al massimo, 8 KB in tutto. Le nuove chiavi sostituiscono le vecchie, le altre restano.
ChiaveDa 1 a 64 caratteri: lettere, cifre, spazi, _, . e -. Altrimenti viene ignorata.
ValoreUna String (tagliata a 500 caratteri), un num o un bool. null cancella la chiave. Il resto viene scartato, con un avviso in debug.
userId200 caratteri al massimo.
name, emailIl nome: 100 caratteri al massimo. L’email: un indirizzo valido, altrimenti viene ignorata.

Hooky ripulisce ciò che supera i limiti, senza mai rifiutare il messaggio del tuo utente.

Questi dati sono dichiarativi: la chiave del sito è pubblica, e chiunque può usarla per inviare un nome, un’email o uno userId a sua scelta. Aiutano il tuo team a inquadrare la persona, non provano chi è. Non metterci mai segreti (password, token, numero di carta) e non usarli mai per concedere un accesso o agire su un account: verifica sempre dalla tua parte.

Statistiche

Conta quello che succede nella tua app, come Hooky.track sul web: le schede compaiono in Statistiche, insieme a quelle del sito. Chiamalo subito dopo che l’azione è andata a buon fine.

Hooky.track('iscrizione');
Hooky.track('ordine', value: 49.9, properties: {'piano': 'pro', 'posti': 3, 'prova': false});
  • value: un importo o una quantità, sommato nella scheda. Le proprietà sono piatte (String, num, bool).
  • Chiamato prima di configure, l’evento aspetta (100 al massimo). Gli errori sono silenziosi; offline, l’evento va perso.
  • Stesse regole del web (nome, 20 proprietà al massimo, limite): vedi Statistiche nella pagina.

Non inviare mai dati personali (email, nome, telefono) in un evento. Conta le azioni, non le persone.

Traduzione dei messaggi

Il tuo utente e il tuo team scrivono ognuno nella propria lingua, come con la bolla web (vedi Traduzione dei messaggi).

  • Sotto un messaggio del team scritto in una lingua diversa da quella della conversazione (quella del dispositivo), un link discreto Traduci. Una volta tradotto, il messaggio compare nella lingua dell’utente, con «Tradotto dall’inglese · Mostra l’originale» per tornare al testo di partenza.
  • Il pulsante di traduzione nell’intestazione propone Traduci sempre: i messaggi del team arrivano allora già tradotti. La scelta viene conservata sul dispositivo, per ogni chiave del sito.
  • Una traduzione non riuscita mostra un breve errore con Riprova. I messaggi dell’utente non vengono mai tradotti dalla sua parte; il tuo team ha il suo pulsante Traduci, nella dashboard e nell’app Hooky.
  • Si traduce solo il testo, non gli allegati. La traduzione è attiva per impostazione predefinita; per toglierla dalla tua app: Hooky.configure('LA_TUA_CHIAVE_PUBBLICA', translation: false).

Cosa invia l’SDK

Quello che invia la bolla web, e nient’altro: i messaggi e i file dell’utente, il nome, l’email, l’identificativo e i dati che fornisci, la lingua e il fuso orario del dispositivo, e il suo modello con la versione del sistema («iPhone 17 Pro · iOS 27.0», «Pixel 8 · Android 15»). Nessun identificativo pubblicitario né tracciamento.

  • Ogni richiesta porta X-Hooky-Client: hooky-flutter/1.0.0 e X-Hooky-App: <bundle id o package> <version> (<build>): il tuo team vede che la conversazione arriva da «Flutter · com.example.app 1.0 (1)».
  • Il token della conversazione è conservato dall’SDK nativo: nel Portachiavi su iOS (solo questo dispositivo), in un file privato mai salvato nel backup su Android.
  • Il plugin include un manifest della privacy iOS (PrivacyInfo.xcprivacy) che non dichiara alcun tracciamento.
  • Per le tue schede negli store, i dati sono quelli degli SDK nativi: vedi la scheda App Store (pagina iOS) e la sezione Sicurezza dei dati di Google Play (pagina Android).

Lingua, modalità scura, accessibilità

Lingua
L’interfaccia esiste in francese, inglese, spagnolo, portoghese, italiano e tedesco. Segue la lingua del dispositivo, l’inglese per impostazione predefinita. La lingua parte con il primo messaggio, perché il tuo team risponda nella lingua giusta e all’ora giusta.
Modalità scura
La conversazione e la bolla seguono la modalità chiara o scura del dispositivo, con il colore del tuo sito.
Accessibilità
La bolla è un pulsante per gli screen reader («Apri la chat con …», e il numero di non letti); la conversazione nativa mantiene VoiceOver e Dynamic Type su iOS, TalkBack e la dimensione del testo su Android.

R8 e ProGuard

Niente da aggiungere su Android: l’SDK non usa la reflection e include le sue regole.

Risoluzione dei problemi

Il mio sito ha dichiarato i suoi indirizzi: l’app viene rifiutata?
No. Un’app non ha un indirizzo: si presenta con la sua intestazione X-Hooky-Client e funziona anche quando il sito ha dichiarato i suoi domini. Non aggiungere nulla in Siti.
«the Hooky plugin is not available on this platform»
Dopo aver aggiunto il pacchetto, riavvia completamente l’app (flutter run): un hot reload non basta. Sul web e sul desktop, il plugin non esiste.
La compilazione iOS fallisce
Controlla il target di distribuzione (iOS 15.0): Swift Package Manager e CocoaPods ne rifiutano uno più basso. Dopo aver cambiato la versione del pacchetto, esegui flutter clean e poi flutter pub get.
La bolla resta con i colori predefiniti
Controlla la chiave pubblica del sito (non la chiave segreta hks_…, né una chiave API hka_…), e la rete.
Offline
Il messaggio mostra l’errore e resta nel campo; identify e setAttributes ripartono più tardi. Gli eventi track inviati offline vanno persi.

Cronologia delle versioni

1.0.0
Prima versione: Hooky.configure, identify (con userId e dati liberi), setAttributes, open, close, track, reset, unreadCount e state in diretta, il widget HookyBubble, la traduzione dei messaggi del team (translation), sugli SDK nativi iOS 1.0.0 e Android 1.0.0.