Zum Inhalt springen

Das Flutter-SDK von Hooky

Das Hooky-Gespräch in deiner Flutter-App: eine Dart-API und ein Bubble-Widget, aufgesetzt auf die nativen SDKs von Hooky. Das Gespräch selbst ist das der SDKs für iOS (SwiftUI) und Android (Jetpack Compose): nativ, ohne WebView, in den Farben deiner Website.

Eine Flutter-App auf Android mit dem Widget HookyBubble unten rechts. Das Hooky-Gespräch, aus einer Flutter-App auf dem iPhone geöffnet, im Dark Mode.
Das Widget HookyBubble auf Android, und das native Gespräch auf dem iPhone, im Dark Mode.

Voraussetzungen

  • Ab Flutter 3.35 (Dart 3.9).
  • Ab Android 7.0 (minSdk 24, das Minimum von Flutter selbst), kompiliert mit compileSdk 36.
  • Ab iOS 15.
  • Nur Android und iOS: Im Web und auf dem Desktop tun die Aufrufe nichts.
  • Ein Hooky-Konto und eine Website: Ihr öffentlicher Schlüssel steht unter Websites › Bubble installieren, derselbe wie das data-site-key der Web-Bubble.

Installation

Das Paket wird aus seinem GitHub-Repository installiert, als Git-Abhängigkeit. In deiner pubspec.yaml:

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

dann flutter pub get. Die nativen SDKs für iOS und Android sind im Paket enthalten: nichts weiter hinzuzufügen, kein weiteres Repository.

iOS

  • Das Plugin funktioniert mit Swift Package Manager (in neueren Flutter-Versionen standardmäßig aktiv) und mit CocoaPods: Es baut das mitgelieferte iOS-SDK samt Privacy Manifest.
  • Setz das Deployment-Target auf iOS 15.0: in Xcode, Target Runner › General › Minimum Deployments.
  • Um die Kamera anzubieten, füg NSCameraUsageDescription zu ios/Runner/Info.plist hinzu. Ohne den Eintrag ist die Option einfach ausgeblendet.

Android

Nichts zu deklarieren: Das Android-SDK ist enthalten, mit seinem Gesprächsbildschirm, seinem FileProvider und der Berechtigung INTERNET. Seine Abhängigkeiten (AndroidX, Compose, OkHttp) kommen von google() und mavenCentral(), in Flutter-Projekten schon eingebunden.

Der Quellcode steht unter MIT-Lizenz: github.com/Defdjamel/hooky-flutter.

Loslegen in 3 Zeilen

import 'package:hooky_sdk/hooky_sdk.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  Hooky.configure('DEIN_OEFFENTLICHER_SCHLUESSEL');                     // 1. beim Start
  runApp(const MeineApp());
}

Scaffold(
  floatingActionButton: const HookyBubble(),      // 2. die schwebende Bubble
  …
);

FilledButton(onPressed: Hooky.open, child: const Text('Schreib uns'));   // 3. oder dein eigener Button

configure lädt Name, Farbe und Begrüßungsnachricht der Website, nimmt das gespeicherte Gespräch wieder auf und hält die Live-Verbindung offen, solange die App im Vordergrund ist. Seine benannten Parameter: translation: false entfernt die Übersetzung der Nachrichten des Teams (siehe Nachrichten übersetzen), apiUrl dient nur zum Testen gegen deinen eigenen Server.

Alle Aufrufe geben ein Future zurück und werfen nie einen Fehler: Fehlschläge landen nur in der Konsole, im Debug-Modus. Vor configure warten identify, setAttributes und track, während open, close und reset nichts tun.

Die Bubble

HookyBubble sieht aus wie die native Bubble der jeweiligen Plattform: ein abgerundetes Quadrat mit weichen Schatten auf iOS (60 Punkte), die umrandete Karte mit dem harten Schatten der Web-Bubble auf Android (64 dp). Sie nimmt die Farbe der Website an, trägt ein Badge für Ungelesene und erscheint, sobald configure aufgerufen wurde.

// Als schwebender Button des Scaffold
Scaffold(floatingActionButton: const HookyBubble(), …)

// Irgendwo, in einem Stack
Stack(children: [
  const MeinBildschirm(),
  const Positioned(right: 20, bottom: 20, child: SafeArea(child: HookyBubble())),
])

// Deine eigene Aktion, eine andere Größe, ein festgelegter Stil
HookyBubble(onPressed: () => analytics.log('chat'), size: 52, style: HookyBubbleStyle.material)

Gespräch öffnen und schließen

Hooky.open();    // Vollbild: ein Sheet auf iOS, eine Activity auf Android
Hooky.close();   // schließt es wieder

Das Gespräch wird vom nativen SDK gezeichnet: Die Seiten iOS und Android beschreiben, was es kann (Fotos, PDFs, Entwurf, Fehler).

Ungelesene Nachrichten

Die Bubble zeigt ihr Badge schon selbst. Für deine eigenen Buttons ist Hooky.unreadCount zugleich ein ValueListenable<int> und ein Stream<int> (der aktuelle Wert, dann jede Änderung). Er fällt auf 0, sobald sich das Gespräch öffnet.

// Ein Badge auf einem Tab
ValueListenableBuilder<int>(
  valueListenable: Hooky.unreadCount,
  builder: (context, n, _) => Badge(isLabelVisible: n > 0, label: Text('$n'), child: const Icon(Icons.chat)),
)

// Oder als Stream
Hooky.unreadCount.listen((n) => print('$n ungelesen'));
final jetzt = Hooky.unreadCount.value;

Hooky.state (ein ValueListenable<HookyState>) liefert außerdem isConfigured, siteName, siteColor und sitePosition, für deine eigene Oberfläche. Antworten kommen live an, solange die App im Vordergrund ist; Version 1.0.0 schickt deinen Nutzern keine Push-Benachrichtigungen.

Nutzer identifizieren

Deine App weiß, wer angemeldet ist: Sag es Hooky. Dein Team sieht in den Details des Gesprächs den Namen, die E-Mail, deine eigene Nutzer-ID, das Gerät und die Daten, die du mitgibst. Mit einer E-Mail fragt das Gespräch weder nach Vornamen noch nach E-Mail.

// Nach der Anmeldung
Hooky.identify(
  name: user.vorname,
  email: user.email,
  userId: user.id,                                     // deine eigene ID
  attributes: {'tarif': 'Pro', 'bestellungen': 3, 'newsletter': true},
);

// Später: mit den bisherigen zusammengeführt
Hooky.setAttributes({'warenkorb': 49.9, 'gutschein': null}); // null löscht einen Schlüssel

// Bei der Abmeldung
Hooky.reset();

So funktioniert es

  • Noch kein Gespräch: Alles bleibt im Speicher (sogar vor configure) und wird mit der ersten Nachricht gesendet. Danach wird jede Änderung sofort gesendet, gebündelt (eine einzige Anfrage für alles, was sich in derselben Sekunde ändert). Offline geht sie später raus.
  • name und email ersetzen die des Gesprächs (null löscht sie auf dem Gerät); userId: null lässt sie unverändert.
  • Eine userId, die sich von der des Gesprächs unterscheidet, bedeutet ein anderes Konto auf demselben Gerät: Das Gespräch wird vergessen, wie bei reset(). Der neue Nutzer sieht den Verlauf des vorherigen nicht.
  • Hooky.reset() schließt das Gespräch und vergisst Verlauf und Identität. Ruf es bei der Abmeldung auf.

Regeln für die Daten

FeldRegel
Freie DatenEine flache Map: höchstens 50 Schlüssel, insgesamt 8 KB. Neue Schlüssel ersetzen die alten, die übrigen bleiben.
Schlüssel1 bis 64 Zeichen: Buchstaben, Ziffern, Leerzeichen, _, . und -. Sonst wird er ignoriert.
WertEin String (nach 500 Zeichen abgeschnitten), ein num oder ein bool. null löscht den Schlüssel. Alles andere wird verworfen, mit einer Warnung im Debug-Modus.
userIdHöchstens 200 Zeichen.
name, emailDer Name: höchstens 100 Zeichen. Die E-Mail: eine gültige Adresse, sonst wird sie ignoriert.

Hooky räumt auf, was darüber hinausgeht, und lehnt die Nachricht deines Nutzers deswegen nie ab.

Diese Daten sind Angaben, keine Beweise: Der Schlüssel der Website ist öffentlich, und jeder kann ihn nutzen, um einen beliebigen Namen, eine E-Mail oder eine userId seiner Wahl zu senden. Sie helfen deinem Team, die Person einzuordnen, beweisen aber nicht, wer sie ist. Leg dort nie etwas Geheimes ab (Passwort, Token, Kartennummer) und nutze sie nie, um Zugriff zu gewähren oder an einem Konto etwas zu ändern: Prüf immer auf deiner Seite nach.

Stats

Zähl, was in deiner App passiert, wie mit Hooky.track im Web: Die Kacheln erscheinen unter Statistiken, zusammen mit denen der Website. Ruf es direkt auf, nachdem die Aktion geklappt hat.

Hooky.track('anmeldung');
Hooky.track('bestellung', value: 49.9, properties: {'tarif': 'pro', 'plaetze': 3, 'testphase': false});
  • value: ein Betrag oder eine Menge, in der Kachel summiert. Die Properties sind flach (String, num, bool).
  • Vor configure aufgerufen, wartet das Event (höchstens 100). Fehler bleiben stumm; offline geht das Event verloren.
  • Dieselben Regeln wie im Web (Name, höchstens 20 Properties, Limit): siehe Stats in der Seite.

Schick nie personenbezogene Daten (E-Mail, Name, Telefon) in einem Event. Zähl Aktionen, keine Personen.

Nachrichten übersetzen

Dein Nutzer und dein Team schreiben jeweils in ihrer Sprache, wie mit der Web-Bubble (siehe Nachrichten übersetzen).

  • Unter einer Nachricht des Teams in einer anderen Sprache als der des Gesprächs (der des Geräts) steht ein dezenter Link Übersetzen. Übersetzt erscheint die Nachricht in der Sprache des Nutzers, mit „Übersetzt aus dem Englischen · Original anzeigen“, um zum ursprünglichen Text zurückzukehren.
  • Der Übersetzungsknopf in der Kopfzeile bietet Immer übersetzen an: Die Nachrichten des Teams kommen dann schon übersetzt an. Die Wahl wird auf dem Gerät gespeichert, pro Website-Schlüssel.
  • Schlägt eine Übersetzung fehl, erscheint ein kurzer Fehler mit Erneut versuchen. Die Nachrichten des Nutzers werden bei ihm nie übersetzt; dein Team hat seinen eigenen Knopf Übersetzen, im Dashboard und in der Hooky-App.
  • Nur Text wird übersetzt, keine Anhänge. Die Übersetzung ist standardmäßig aktiv; um sie aus deiner App zu entfernen: Hooky.configure('DEIN_OEFFENTLICHER_SCHLUESSEL', translation: false).

Was das SDK sendet

Was die Web-Bubble sendet, und sonst nichts: die Nachrichten und Dateien des Nutzers, Name, E-Mail, ID und die Daten, die du mitgibst, Sprache und Zeitzone des Geräts sowie sein Modell mit der Systemversion („iPhone 17 Pro · iOS 27.0“, „Pixel 8 · Android 15“). Ohne Werbe-ID und ohne Tracking.

  • Jede Anfrage trägt X-Hooky-Client: hooky-flutter/1.0.0 und X-Hooky-App: <bundle id oder package> <version> (<build>): Dein Team sieht, dass das Gespräch von „Flutter · com.example.app 1.0 (1)“ kommt.
  • Das Gesprächs-Token wird vom nativen SDK gespeichert: im Schlüsselbund auf iOS (nur dieses Gerät), in einer privaten, nie gesicherten Datei auf Android.
  • Das Plugin bringt ein iOS-Datenschutzmanifest (PrivacyInfo.xcprivacy) mit, das kein Tracking angibt.
  • Für deine Store-Einträge gelten die Daten der nativen SDKs: siehe das App Store-Profil (iOS-Seite) und den Abschnitt Datensicherheit bei Google Play (Android-Seite).

Sprache, Dark Mode, Barrierefreiheit

Sprache
Die Oberfläche gibt es auf Französisch, Englisch, Spanisch, Portugiesisch, Italienisch und Deutsch. Sie folgt der Sprache des Geräts, standardmäßig Englisch. Die Sprache wird mit der ersten Nachricht gesendet, damit dein Team in der richtigen Sprache und zur richtigen Uhrzeit antwortet.
Dark Mode
Gespräch und Bubble folgen dem hellen oder dunklen Modus des Geräts, mit der Farbe deiner Website.
Barrierefreiheit
Die Bubble ist für Screenreader ein Button („Chat mit … öffnen“, und die Zahl der ungelesenen Nachrichten); das native Gespräch behält VoiceOver und Dynamic Type auf iOS, TalkBack und die Schriftgröße auf Android.

R8 und ProGuard

Auf Android nichts hinzuzufügen: Das SDK nutzt keine Reflection und bringt eigene Regeln mit.

Fehlerbehebung

Meine Website hat ihre Adressen eingetragen: Wird die App abgelehnt?
Nein. Eine App hat keine Adresse: Sie weist sich mit ihrem Header X-Hooky-Client aus und kommt auch dann durch, wenn die Website ihre Domains eingetragen hat. Trag unter Websites nichts ein.
„the Hooky plugin is not available on this platform“
Starte die App nach dem Hinzufügen des Pakets komplett neu (flutter run), ein Hot Reload reicht nicht. Im Web und auf dem Desktop gibt es das Plugin nicht.
Der iOS-Build schlägt fehl
Prüf das Deployment-Target (iOS 15.0): Swift Package Manager und CocoaPods lehnen ein niedrigeres ab. Nach einem Versionswechsel des Pakets: flutter clean, dann flutter pub get.
Die Bubble bleibt in den Standardfarben
Prüf den öffentlichen Schlüssel der Website (nicht den geheimen Schlüssel hks_… und keinen API-Schlüssel hka_…) und das Netz.
Offline
Die Nachricht zeigt den Fehler und bleibt im Eingabefeld; identify und setAttributes werden später gesendet. Offline gesendete track-Events gehen verloren.

Versionsverlauf

1.0.0
Erste Version: Hooky.configure, identify (mit userId und freien Daten), setAttributes, open, close, track, reset, unreadCount und state live, das Widget HookyBubble, die Übersetzung der Nachrichten des Teams (translation), auf den nativen SDKs iOS 1.0.0 und Android 1.0.0.