O SDK Flutter do Hooky
A conversa do Hooky no seu app Flutter: uma API Dart e um widget de balão, construídos sobre os SDKs nativos do Hooky. A conversa em si é a dos SDKs iOS (SwiftUI) e Android (Jetpack Compose): nativa, sem WebView, com as cores do seu site.
HookyBubble no Android, e a conversa nativa no iPhone, no modo escuro.Requisitos
- Flutter 3.35 ou mais recente (Dart 3.9).
- Android 7.0 ou mais recente (
minSdk 24, o mínimo do próprio Flutter), compilado comcompileSdk 36. - iOS 15 ou mais recente.
- Só Android e iOS: na web e no desktop, as chamadas não fazem nada.
- Uma conta Hooky e um site: a chave pública dele fica em Sites › Instalar, a mesma do
data-site-keydo balão na web.
Instalação
O pacote é instalado a partir do repositório no GitHub, como dependência git. No seu pubspec.yaml:
dependencies:
hooky_sdk:
git:
url: https://github.com/Defdjamel/hooky-flutter
ref: 1.0.0e depois flutter pub get. Os SDKs nativos de iOS e Android já vêm no pacote: nada mais a adicionar, nenhum outro repositório.
iOS
- O plugin funciona com o Swift Package Manager (ativado por padrão nas versões recentes do Flutter) e também com o CocoaPods: ele compila o SDK iOS que traz junto, com o manifesto de privacidade.
- Suba o target de implantação para iOS 15.0: no Xcode, target Runner › General › Minimum Deployments.
- Para oferecer a câmera, adicione
NSCameraUsageDescriptionaoios/Runner/Info.plist. Sem ela, a opção simplesmente fica escondida.
Android
Nada a declarar: o SDK Android já vem incluído, com a tela de conversa, o FileProvider e a permissão INTERNET. As dependências dele (AndroidX, Compose, OkHttp) vêm do google() e do mavenCentral(), já presentes nos projetos Flutter.
O código-fonte está sob licença MIT: github.com/Defdjamel/hooky-flutter.
Comece em 3 linhas
import 'package:hooky_sdk/hooky_sdk.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
Hooky.configure('SUA_CHAVE_PUBLICA'); // 1. na inicialização
runApp(const MeuApp());
}
Scaffold(
floatingActionButton: const HookyBubble(), // 2. o balão flutuante
…
);
FilledButton(onPressed: Hooky.open, child: const Text('Fale com a gente')); // 3. ou o seu próprio botãoconfigure carrega o nome, a cor e a mensagem de boas-vindas do site, retoma a conversa salva e mantém a conexão ao vivo aberta enquanto o app está em primeiro plano. Os parâmetros nomeados dele: translation: false remove a tradução das mensagens da equipe (veja Tradução das mensagens), apiUrl só serve para testar com o seu próprio servidor.
Todas as chamadas retornam um Future e nunca geram erro: as falhas só aparecem no console, em debug. Antes de configure, identify, setAttributes e track ficam esperando, enquanto open, close e reset não fazem nada.
O balão
HookyBubble se parece com o balão nativo de cada plataforma: um quadrado arredondado com sombras suaves no iOS (60 pontos), o card com borda e sombra marcada do balão da web no Android (64 dp). Ele usa a cor do site, mostra um contador de não lidas e aparece assim que configure é chamado.
// Como botão flutuante do Scaffold
Scaffold(floatingActionButton: const HookyBubble(), …)
// Em qualquer lugar, dentro de um Stack
Stack(children: [
const MinhaTela(),
const Positioned(right: 20, bottom: 20, child: SafeArea(child: HookyBubble())),
])
// A sua própria ação, outro tamanho, um estilo forçado
HookyBubble(onPressed: () => analytics.log('chat'), size: 52, style: HookyBubbleStyle.material)Abrir e fechar a conversa
Hooky.open(); // tela cheia: uma folha no iOS, uma Activity no Android
Hooky.close(); // fecha a conversaA conversa é desenhada pelo SDK nativo: as páginas iOS e Android descrevem o que ela faz (fotos, PDFs, rascunho, erros).
Mensagens não lidas
O balão já mostra o próprio contador. Para os seus botões, Hooky.unreadCount é ao mesmo tempo um ValueListenable<int> e um Stream<int> (o valor atual, depois cada mudança). Ele volta a 0 quando a conversa é aberta.
// Um contador em uma aba
ValueListenableBuilder<int>(
valueListenable: Hooky.unreadCount,
builder: (context, n, _) => Badge(isLabelVisible: n > 0, label: Text('$n'), child: const Icon(Icons.chat)),
)
// Ou como stream
Hooky.unreadCount.listen((n) => print('$n não lidas'));
final agora = Hooky.unreadCount.value;Hooky.state (um ValueListenable<HookyState>) também traz isConfigured, siteName, siteColor e sitePosition, para a sua própria interface. As respostas chegam ao vivo enquanto o app está em primeiro plano; a versão 1.0.0 não envia notificações push aos seus usuários.
Identificar o usuário
O seu app sabe quem está logado: conte isso ao Hooky. Nos detalhes da conversa, a sua equipe vê o nome, o e-mail, o seu próprio identificador do usuário, o aparelho e os dados que você anexa. Com um e-mail, a conversa não pede mais nome nem e-mail.
// Depois do login
Hooky.identify(
name: user.nome,
email: user.email,
userId: user.id, // o seu próprio identificador
attributes: {'plano': 'Pro', 'pedidos': 3, 'newsletter': true},
);
// Mais tarde: mesclados com os anteriores
Hooky.setAttributes({'carrinho': 49.9, 'cupom': null}); // null apaga uma chave
// No logout
Hooky.reset();Como funciona
- Ainda sem conversa: tudo fica na memória (mesmo antes de
configure) e é enviado com a primeira mensagem. Depois, cada mudança é enviada na hora, agrupada (um único envio para tudo o que muda no mesmo segundo). Sem conexão, ela é enviada mais tarde. nameeemailsubstituem os da conversa (nullos apaga no aparelho);userId: nullo deixa como está.- Um
userIddiferente do da conversa é outra conta no mesmo aparelho: a conversa é esquecida, como comreset(). O novo usuário não vê a conversa do anterior. Hooky.reset()fecha a conversa e esquece o histórico e a identidade. Chame no logout.
Regras dos dados
| Campo | Regra |
|---|---|
| Dados livres | Um Map sem aninhamento: 50 chaves no máximo, 8 KB no total. As chaves novas substituem as antigas, as outras ficam. |
| Chave | De 1 a 64 caracteres: letras, algarismos, espaços, _, . e -. Senão, é ignorada. |
| Valor | Uma String (cortada em 500 caracteres), um num ou um bool. null apaga a chave. O resto é descartado, com um aviso em debug. |
userId | 200 caracteres no máximo. |
name, email | O nome: 100 caracteres no máximo. O e-mail: um endereço válido, senão é ignorado. |
O Hooky limpa o que passar do limite, sem nunca recusar a mensagem do seu usuário.
Esses dados são declarativos: a chave do site é pública, então qualquer pessoa pode usá-la para enviar o nome, o e-mail ou o userId que quiser. Eles ajudam a sua equipe a situar a pessoa, não provam quem ela é. Nunca coloque um segredo neles (senha, token, número de cartão) e nunca use esses dados para liberar um acesso ou agir sobre uma conta: sempre confira do seu lado.
Estatísticas
Conte o que acontece no seu app, como com Hooky.track na web: os cards aparecem em Estatísticas, junto com os do site. Chame logo depois que a ação der certo.
Hooky.track('cadastro');
Hooky.track('pedido', value: 49.9, properties: {'plano': 'pro', 'assentos': 3, 'teste': false});value: um valor em dinheiro ou uma quantidade, somado no card. As propriedades não têm aninhamento (String,num,bool).- Chamado antes de
configure, o evento fica esperando (100 no máximo). As falhas são silenciosas; sem conexão, o evento se perde. - As mesmas regras da web (nome, 20 propriedades no máximo, limite): veja Estatísticas na página.
Nunca envie dados pessoais (e-mail, nome, telefone) em um evento. Conte ações, não pessoas.
Tradução das mensagens
O seu usuário e a sua equipe escrevem cada um no seu idioma, como com o balão na web (veja Tradução das mensagens).
- Abaixo de uma mensagem da equipe escrita em outro idioma que o da conversa (o do aparelho), aparece um link discreto Traduzir. Traduzida, a mensagem aparece no idioma do usuário, com “Traduzido do inglês · Ver o original” para voltar ao texto original.
- O botão de tradução do cabeçalho oferece Traduzir sempre: as mensagens da equipe passam a chegar já traduzidas. A escolha fica salva no aparelho, para cada chave de site.
- Uma tradução que falha mostra um erro curto com Tentar de novo. As mensagens do usuário nunca são traduzidas do lado dele; a sua equipe tem o próprio botão Traduzir, no painel e no app Hooky.
- Só o texto é traduzido, não os anexos. A tradução vem ativada por padrão; para removê-la do seu app:
Hooky.configure('SUA_CHAVE_PUBLICA', translation: false).
O que o SDK envia
O que o balão da web envia, e nada mais: as mensagens e os arquivos do usuário, o nome, o e-mail, o identificador e os dados que você informa, o idioma e o fuso horário do aparelho, e o modelo dele com a versão do sistema (“iPhone 17 Pro · iOS 27.0”, “Pixel 8 · Android 15”). Sem identificador de publicidade nem rastreamento.
- Cada requisição leva
X-Hooky-Client: hooky-flutter/1.0.0eX-Hooky-App: <bundle id ou package> <version> (<build>): a sua equipe vê que a conversa vem de “Flutter · com.example.app 1.0 (1)”. - O token da conversa é guardado pelo SDK nativo: no Keychain no iOS (só neste aparelho), em um arquivo privado nunca salvo em backup no Android.
- O plugin traz um manifesto de privacidade iOS (
PrivacyInfo.xcprivacy) que não declara nenhum rastreamento. - Para as páginas das suas lojas, os dados são os dos SDKs nativos: veja a Privacidade do app na App Store (página iOS) e a seção Segurança dos dados do Google Play (página Android).
Idioma, modo escuro, acessibilidade
- Idioma
- A interface existe em francês, inglês, espanhol, português, italiano e alemão. Ela segue o idioma do aparelho, com inglês por padrão. O idioma vai com a primeira mensagem, para que a sua equipe responda no idioma certo e na hora certa.
- Modo escuro
- A conversa e o balão seguem o modo claro ou escuro do aparelho, com a cor do seu site.
- Acessibilidade
- O balão é um botão para os leitores de tela (“Abrir o chat com …”, e o número de não lidas); a conversa nativa mantém VoiceOver e Dynamic Type no iOS, TalkBack e o tamanho do texto no Android.
R8 e ProGuard
Nada a adicionar no Android: o SDK não usa reflexão e traz as próprias regras.
Solução de problemas
- O meu site cadastrou os endereços dele: o app é recusado?
- Não. Um app não tem endereço: ele se apresenta pelo cabeçalho
X-Hooky-Cliente funciona mesmo quando o site cadastrou os domínios dele. Não adicione nada em Sites. - “the Hooky plugin is not available on this platform”
- Depois de adicionar o pacote, reinicie o app por completo (
flutter run): um hot reload não basta. Na web e no desktop, o plugin não existe. - A compilação iOS falha
- Confira o target de implantação (iOS 15.0): o Swift Package Manager e o CocoaPods recusam um menor. Depois de mudar a versão do pacote, rode
flutter cleane depoisflutter pub get. - O balão fica com as cores padrão
- Confira a chave pública do site (não a chave secreta
hks_…, nem uma chave de APIhka_…) e a rede. - Sem conexão
- A mensagem mostra o erro e fica no campo;
identifyesetAttributessão enviados mais tarde. Os eventostrackenviados sem conexão se perdem.
Histórico de versões
- 1.0.0
- Primeira versão:
Hooky.configure,identify(comuserIde dados livres),setAttributes,open,close,track,reset,unreadCountestateao vivo, o widgetHookyBubble, a tradução das mensagens da equipe (translation), sobre os SDKs nativos iOS 1.0.0 e Android 1.0.0.