📦 BeMall SDK
Este pacote Flutter fornece uma interface para integração com o WebApp da plataforma BeMall.
📁 Instalação (via ZIP local)
- Extraia o arquivo
.zipque você recebeu do BeMall para dentro do seu projeto.
Sugerimos a pastavendor/bemall-sdk/:
seu_app/
lib/
pubspec.yaml
vendor/
promotion/
- Referencie os pacotes por caminho no
pubspec.yaml:
dependencies:
# WebApp de Promoções
bemall_promotion:
path: ./vendor/bemall-sdk/promotion
Se preferir manter os pacotes fora do projeto, também funciona:
bemall_promotion: path: /caminho/absoluto/para/bemall-sdk/promotion
- iOS – Podfile
No arquivoios/Podfile, defina a plataforma mínima:
platform :ios, '15.5'
- Instale as dependências:
flutter pub get
🛡️ Configuração de Build (Android)
Para evitar problemas de ofuscação/reflexão, adicione android/app/proguard-rules.pro:
# === ML Kit - Manter opções de reconhecimento ===
-keep class com.google.mlkit.vision.text.** { *; }
-dontwarn com.google.mlkit.vision.text.**
# === JAI ImageIO (usado por image_picker e ML Kit) ===
-keep class javax.imageio.** { *; }
-dontwarn javax.imageio.**
-keep class com.github.jaiimageio.impl.** { *; }
-dontwarn com.github.jaiimageio.impl.**
# === Prevenir remoção de classes acessadas via reflexão ===
-keep class * extends java.util.List
-keep class * extends java.util.Map
# === Evitar remoção de métodos dinâmicos ===
-keepattributes *Annotation*,EnclosingMethod,InnerClasses,Signature
# === Opcional: Manter ML Kit Commons ===
-keep class com.google_mlkit_commons.** { *; }
-dontwarn com.google_mlkit_commons.**
-dontwarn com.github.jaiimageio.**
-dontwarn java.awt.**
Adicione no app/build.gradle
ndkVersion = "27.0.12077973"
buildTypes {
release {
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
🧪 Exemplo de uso — WebApp de Promoções
import 'package:flutter/material.dart';
import 'package:bemall_promotion/bemall_sdk.dart';
void main() async {
runApp(const MyApp()
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
void _handleWebViewError(dynamic error) {
switch (error.errorType) {
case WebResourceErrorType.authentication:
debugPrint("Falha de autenticação no servidor");
break;
case WebResourceErrorType.badUrl:
debugPrint("URL malformada");
break;
case WebResourceErrorType.connect:
debugPrint("Falha ao conectar ao servidor");
break;
case WebResourceErrorType.failedSslHandshake:
debugPrint("Falha na negociação SSL/TLS");
break;
case WebResourceErrorType.file:
debugPrint("Erro genérico de arquivo");
break;
case WebResourceErrorType.fileNotFound:
debugPrint("Arquivo não encontrado");
break;
case WebResourceErrorType.hostLookup:
debugPrint("DNS não encontrado");
break;
case WebResourceErrorType.io:
debugPrint("Erro de leitura/escrita (I/O)");
break;
case WebResourceErrorType.proxyAuthentication:
debugPrint("Falha de autenticação em proxy");
break;
case WebResourceErrorType.redirectLoop:
debugPrint("Loop de redirecionamento detectado");
break;
case WebResourceErrorType.timeout:
debugPrint("Conexão expirada");
break;
case WebResourceErrorType.tooManyRequests:
debugPrint("Muitas requisições");
break;
case WebResourceErrorType.unknown:
debugPrint("Erro genérico/desconhecido");
break;
case WebResourceErrorType.unsafeResource:
debugPrint("Recurso inseguro bloqueado");
break;
case WebResourceErrorType.unsupportedAuthScheme:
debugPrint("Esquema de autenticação não suportado");
break;
case WebResourceErrorType.unsupportedScheme:
debugPrint("Esquema de URI não suportado");
break;
case WebResourceErrorType.webContentProcessTerminated:
debugPrint("Processo da WebView encerrado");
break;
case WebResourceErrorType.webViewInvalidated:
debugPrint("WebView inválida");
break;
case WebResourceErrorType.javaScriptExceptionOccurred:
debugPrint("Erro em execução de JavaScript");
break;
case WebResourceErrorType.javaScriptResultTypeIsUnsupported:
debugPrint("Resultado JS não suportado");
break;
default:
debugPrint("❌ Erro não tratado: ${error.errorType}");
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'SDK BeMall',
home: Scaffold(
body: Center(
child: Builder(
builder: (context) => Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: const Text('Abrir WebApp'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => WebAppViewPromotion(
customerId: '<CUSTOMER_ID>',
campaignHash: '<CAMPAIGN_HASH>',
customerAccessToken:
'<CUSTOMER_ACCESS_TOKEN>',
onClose: () => Navigator.of(context).pop(),
pageErrorNotifier: (error) =>{
debugPrint('error: ${error}'),
debugPrint('error description: ${error.description}'),
debugPrint('error errorCode: ${error.errorCode}'),
debugPrint('error errorType: ${error.errorType}')
},
pageLoadedNotifier: (loading) =>
debugPrint('booelan loading: $loading'),
showDebugButton: true,
),
);
},
),
],
),
),
),
),
);
}
}
WebResourceError – Guia de Erros retornados pelo SDK
Quando ocorre um erro de carregamento no WebView, o Flutter retorna um objeto WebResourceError.
Esse objeto contém informações sobre o problema, como:
errorCode: código cru vindo da plataforma (Android ou iOS).description: texto descritivo do erro.errorType: enum genérico fornecido pelo Flutter para categorizar o erro.url: URL que falhou.
🔹 Propriedades principais
class WebResourceError {
final int errorCode; // Código da plataforma (Android/iOS)
final String description; // Descrição do erro
final WebResourceErrorType? errorType; // Tipo categorizado
final String? url; // URL que falhou
}
🔹 Enum WebResourceErrorType
O Flutter mapeia alguns erros comuns para valores genéricos:
authentication→ Falha de autenticação no servidor.badUrl→ URL malformada.connect→ Falha ao conectar ao servidor.failedSslHandshake→ Falha na negociação SSL/TLS.file→ Erro genérico de arquivo.fileNotFound→ Arquivo não encontrado.hostLookup→ DNS não encontrado.io→ Erro de leitura/escrita.proxyAuthentication→ Falha de autenticação em proxy.redirectLoop→ Loop de redirecionamento.timeout→ Conexão expirada.tooManyRequests→ Muitas requisições.unknown→ Erro genérico/desconhecido.unsafeResource→ Recurso inseguro bloqueado.unsupportedAuthScheme→ Esquema de autenticação não suportado.unsupportedScheme→ Esquema de URI não suportado.webContentProcessTerminated→ Processo da WebView encerrado.webViewInvalidated→ WebView inválida.javaScriptExceptionOccurred→ Erro em execução JS.javaScriptResultTypeIsUnsupported→ Resultado JS não suportado.
🔹 Códigos Android (WebView)
Os erros seguem as constantes de WebViewClient (ERROR_*):
| Código | Constante | Significado |
|---|---|---|
| -1 | ERROR_UNKNOWN |
Erro desconhecido. |
| -2 | ERROR_HOST_LOOKUP |
DNS/host não encontrado. |
| -3 | ERROR_UNSUPPORTED_AUTH_SCHEME |
Autenticação não suportada. |
| -4 | ERROR_AUTHENTICATION |
Falha de autenticação no servidor. |
| -5 | ERROR_PROXY_AUTHENTICATION |
Falha de autenticação no proxy. |
| -6 | ERROR_CONNECT |
Não conseguiu conectar. |
| -7 | ERROR_IO |
Erro de I/O. |
| -8 | ERROR_TIMEOUT |
Tempo limite esgotado. |
| -9 | ERROR_REDIRECT_LOOP |
Loop de redirecionamento. |
| -10 | ERROR_UNSUPPORTED_SCHEME |
Esquema de URI não suportado. |
| -11 | ERROR_FAILED_SSL_HANDSHAKE |
Falha no handshake SSL/TLS. |
| -12 | ERROR_BAD_URL |
URL malformada. |
| -13 | ERROR_FILE |
Erro de arquivo. |
| -14 | ERROR_FILE_NOT_FOUND |
Arquivo não encontrado. |
| -15 | ERROR_TOO_MANY_REQUESTS |
Muitas requisições. |
🔹 Códigos iOS (WKWebView)
No iOS os erros seguem os códigos de NSURLErrorDomain:
| Código | Constante iOS | Significado |
|---|---|---|
| -1000 | NSURLErrorBadURL |
URL inválida. |
| -1001 | NSURLErrorTimedOut |
Tempo limite esgotado. |
| -1002 | NSURLErrorUnsupportedURL |
Esquema não suportado. |
| -1003 | NSURLErrorCannotFindHost |
Host não encontrado. |
| -1004 | NSURLErrorCannotConnectToHost |
Falha ao conectar ao host. |
| -1005 | NSURLErrorNetworkConnectionLost |
Conexão perdida. |
| -1009 | NSURLErrorNotConnectedToInternet |
Sem internet. |
| -1012 | NSURLErrorUserCancelledAuthentication |
Autenticação cancelada. |
| -1013 | NSURLErrorUserAuthenticationRequired |
Autenticação necessária. |
| -1200 | NSURLErrorSecureConnectionFailed |
Falha SSL/TLS. |
| -1202 | NSURLErrorServerCertificateUntrusted |
Certificado não confiável. |
| -1203 | NSURLErrorServerCertificateHasBadDate |
Certificado expirado. |
| -1204 | NSURLErrorServerCertificateNotYetValid |
Certificado ainda não válido. |
| -1205 | NSURLErrorServerCertificateHasUnknownRoot |
Root desconhecida. |
| -1206 | NSURLErrorServerCertificateRejected |
Certificado rejeitado. |
🔹 Dicas práticas
- Use sempre
errorTypepara lógica genérica entre Android/iOS. - Logue
errorCode+descriptionpara debug detalhado. - No Android, espere -1 a -15.
- No iOS, os erros ficam geralmente entre -1000 e -1200.
📌 Observações
- Garanta as permissões de internet e câmera:
- Android:
AndroidManifest.xml+ runtime permissions. - iOS: adicionar
NSCameraUsageDescriptionnoInfo.plist.
- Android:
WebAppViewPromotionabre o WebApp BeMall em uma WebView embutida.onClosedeve executar o retorno à tela anterior (por exemplo,Navigator.pop()).
🔁 Atualizações do SDK
Para atualizar:
- Substitua o conteúdo da pasta
vendor/bemall-sdk/pelo novo.zipextraído. - Rode:
flutter clean
flutter pub get