Flutter
O Flutter usa um sistema flexível que permite chamar APIs específicas da plataforma, estejam elas disponíveis em código Kotlin ou Java no Android ou em código Swift ou Objective-C no iOS.
Este guia ensina como integrar o AllowMe SDK ao seu aplicativo Flutter utilizando o mecanismo de Platform Channel.
Importante!
O tutorial abaixo possui simplificações por se tratar do código de um app de exemplo. Os resultados das chamadas são mostrados na tela para facilitar o entendimento. Você deverá fazer as modificações necessárias para a sua implementação real. Além disso, o código completo de exemplo está disponível. Entre em contato com [email protected] para ter acesso.
1. Implementação Nativa Android
Antes de prosseguir nessa seção é recomendado a leitura da integração nativa para Android.
O primeiro passo para integração do AllowMe com o seu aplicativo é verificar se a pasta responsável por conter os módulos nativos já existe em seu projeto:

Caso não exista você pode criá-la usando o seguinte comando na raíz do projeto:
$ flutter create -a kotlin .
1.1 Adicionando o AllowMeSDK como dependência
Essa seção se refere à implementação em Flutter do que é mostrado com mais detalhes em Primeiros Passos - Adicionando AllowMeSDK como dependência da documentação nativa.Uma vez criada a pasta responsável por conter os código nativos Android você deve adicionar o SDK e suas dependências nos arquivos build.gradle do projeto.
O AllowMeSDK está hospedado em um repositório privado Maven, acessível via Gradle. Por isso, você deve adicionar ao build.gradle do seu projeto as seguintes linhas, contendo o username, password e URL:
allprojects {
repositories {
google()
jcenter()
maven {
authentication {
basic(BasicAuthentication)
}
credentials {
username 'SEU_USERNAME_AQUI'
password 'SEU_PASSWORD_AQUI'
}
url "SUA_URL_DO_REPOSITORIO_AQUI"
}
}
}
Em seguida, você deve adicionar essas dependências ao build.gradle do seu módulo:
dependencies {
// Para produção, utilize:
releaseImplementation "br.com.allowme.android:allowme-sdk:3.4.9"
// Para homologação, utilize:
debugImplementation "br.com.allowme.android:allowme-sdk:3.4.9-stage"
}
Importante - Ofuscação!
O tópico a seguir se faz necessário apenas se a sua aplicação utiliza ofuscação no projeto. Caso não utilize pode passar para o próximo tópico desta integração
1.2 Configuração de Ofuscação
Caso o seu projeto se utilize de alguma feature de ofuscação de código (como Dexguard / Proguard ou outro), será necessário manter algumas classes que o SDK se utiliza. Isto se deve ao fato de que o mecanísmo de build do Flutter não ter a visibilidade completa sobre o grafo de dependências como no Android Nativo e caso esta configuração não seja feita é provável que erros em Runtime, ou seja, que acontecem em tempo de execução irão fazer o aplicativo quebrar.

As configurações necessárias são encontradas abaixo e devem fazer parte da configuração de sua ferramenta de ofuscação. Caso a ferramenta de ofuscação seja Proguard, pode-se adicionar estas configurações no arquivo utilizado pelo projeto.
Configuração no arquivo build.gradle
# modificar o buildType para adicionar o proguard no buildType correto, caso não exista.
buildTypes {
release {
# Adicionar as linhas abaixo para configurar o proguard para utilizar o arquivo proguard-rules.pro
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro', 'retrofit2.pro'
}
}
Configurações do proguard-rules.pro
# Adicionar as Linhas Abaixo
-keep @androidx.room.Entity class *
-keep class com.facetec.sdk.** { *; }
-keepresourcefiles assets/cache_uyertuazlkxcjrlwkejr/**
-keep class kotlin.** { *; }
-keep class kotlinx.** { *; }
-keep class kotlinx.coroutines** { *; }
-keep class androidx.** { *; }
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-keep class io.jsonwebtoken.** { *; }
-keep class com.google.** { *; }
-keep class com.scottyab.** { *; }
-keep class javax.** { *; }
-keep class androidx.lifecycle.** { *; }
# Work manager
-keep public class androidx.work.** { *; }
1.3 Criando Canal de Comunicação
Essa seção se refere à implementação em Flutter do que é mostrado com mais detalhes em Primeiros Passos - Inicializar o SDK da documentação nativa.Para que a aplicação Flutter consiga enviar e receber comandos nativos é necessário criar um canal de comunicação. No exemplo abaixo nós criamos o canal "br.com.samples.allowme/sdk" e sobrescrevemos o método configureFlutterEngine da classe FlutterFragmentActivity.
FlutterActivity vs FlutterFragmentActivity
O AllowMeSDK utiliza algumas features mais recentes do Android como Lifecycle Aware Components. Por este motivo utilizamos a classe FlutterFragmentActivity no lugar da classe FlutterActivity para fazer o processo de comunicação entre a aplicação em Flutter e o código nativo do AllowMeSDK.
Observe que o exemplo mostra um código inicial de implementação que implementa apenas o método setup do AllowMeSDK.
Verifique os produtos contratados para seguir na implementação mostrada nas próximas seções!
import {...}
class MainActivity : FlutterFragmentActivity() {
private val CHANNEL = "br.com.samples.allowme/sdk"
lateinit var mAllowMe: AllowMe
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Criação da instância AllowMe
mAllowMe = AllowMe.getInstance(
applicationContext,
applicationContext.getString(R.string.api_key)
)
}
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
when (call.method) {
"setup" -> setupAllowMe(result)
else -> result.notImplemented()
}
}
}
private fun setupAllowMe(result: MethodChannel.Result){
mAllowMe.setup(object: SetupCallback{
override fun success(){
result.success("Setup AllowMe success")
}
override fun error(throwable: Throwable){
result.error("Setup failed: ", throwable.message, null)
}
})
}
}
Configurações Adicionais
Mesmo sua aplicação se tratando de um projeto Flutter ainda é necessário registrar a activity no AndroidManifest.xml além de adicionar as permissões recomendadas para o funcionamento do AllowMe. Para mais informações você pode consultar a documentação oficial Android (opens new window)ou a documentação AllowMe nativa.
1.4 Implementando o Device Intelligence (Contextual)
Essa seção se refere à implementação em Flutter do que é mostrado com mais detalhes em Device Intelligence (Contextual) da documentação nativa.Com base no exemplo mostrado na seção anterior, podemos adicionar o código de exemplo abaixo para adicionar o produto Device Intelligence (Contextual) do AllowMeSDK com uma chamada ao método collect.
{...}
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
when (call.method) {
"setup" -> setupAllowMe(result)
"collect" -> getContextual(result)
else -> result.notImplemented()
}
}
}
private fun getContextual(result: MethodChannel.Result){
mAllowMe.collect(object: CollectCallback{
override fun success(collect: String){
result.success("Collect Contextual Success: ", collect)
}
override fun error(throwable: Throwable){
result.error("Collect Contextual Failed: ", throwable.message,null)
}
})
}
{...}
'
1.5 Implementando Validação de Endereço
Essa seção se refere à implementação em Flutter do que é mostrado com mais detalhes em Validação de Endereço da documentação nativa.Com base no exemplo mostrado na seção anterior, podemos adicionar o código de exemplo abaixo para adicionar a função Validação de Endereço do AllowMeSDK. Observe que o método start tem como objetivo realizar o agendamento das coletas periódicas enquando o addPerson faz o envio das informações do usuário para o backend AllowMe para realização da validação de endereço.
{...}
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
when (call.method) {
{...}
"start" -> startValidação de Endereço(result)
"addPerson" -> addPerson(result)
else -> result.notImplemented()
}
}
}
private fun startOnboarding(result: MethodChannel.Result) {
mAllowMe.start(object: StartCallback{
override fun success() {
result.success("Start Onboarding Success!")
}
override fun error(throwable: Throwable) {
result.error("Start Onboarding Failed: ", throwable.message,null)
}
})
}
private fun addPerson(result: MethodChannel.Result) {
// Veja a documentação nativa do Android para ter mais informações sobre
// os dados que podem ser enviados para o método addPerson
val address = Address(
"Nome da Rua",
"city",
"state",
100,
"neighbourhood",
"zipCode",
"unit",
"country",
null
)
val person = Person(
"Nome Completo",
"00000000000",
address,
"email",
"phone"
)
mAllowMe.addPerson(person, object : AddPersonCallback {
override fun success() {
result.success("Person added!")
}
override fun error(throwable: Throwable) {
result.error("addPerson failed", throwable.message, null)
}
})
}
{...}
Neste exemplo, o método addPerson que criamos instancia as classes Person e Address com valores pré-definidos. Esses valores, porém, devem ser fornecidos pela sua aplicação Flutter. Mais informações sobre os parâmetros aceitos pelo método podem ser encontradas em Cadastrando uma pessoa.
1.6 Implementando a Biometria Facial
Essa seção se refere à implementação em Flutter do que é mostrado com mais detalhes em Biometria Facial da documentação nativa.Como mostrado na documentação nativa, a biometria facial requer uma permissão extra para o uso da câmera do celular. Certifique-se de pedir a permissão necessária antes de continuar.
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), 1)
Após a adição da permissão de acesso à câmera, você está pronto para chamar a tela de captura da Biometria Facial conforme mostrado abaixo:
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
when (call.method) {
{...}
"setup" -> setupAllowMe(result)
"biometrics" -> startBiometrics()
else -> result.notImplemented()
}
}
}
private fun startBiometrics() {
val intent = Intent(this@MainActivity, AllowMeBiometricsActivity::class.java)
startActivityForResult(intent, 999)
}
Observe que para receber o resultado da Biometria Facial você deve inicializar a activiy com o método startActivityForResult. Esse método demanda uma implementação adicional na sua classe da função onActivityResult conforme mostrado abaixo:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == 999) {
val biometricsResult = data?.extras?.getParcelable<BiometricsResult>(AllowMeBiometricsActivity.BIOMETRICS_RESULT_KEY)
val result = verify(biometricsResult)
// é necessário checar as fotos e/ou o erro retornados para
// enviá-los para o backend conforme detalhado na documentação oficial
methodChannel?.invokeMethod("didReceiveBiometrics", result)
}
}
O resultado acima é transformado em String para ser mostrado na tela a partir do código em dart. Faça as modificações necessárias para a sua implementação!
2.Implementação Nativa iOS
Antes de prosseguir nessa seção é recomendado a leitura da integração nativa para iOS.
O primeiro passo para integração do AllowMe dentro do seu projeto é verificar se a pasta responsável por conter os módulos nativos já existe em seu projeto:

Caso não exista você pode criá-la usando o seguinte comando na raíz do seu projeto:
$ flutter create -i swift .
2.1. Adicionando o AllowMeSDK como dependência
Essa seção se refere à implementação em Flutter do que é mostrado com mais detalhes em Primeiros Passos - Adicionando AllowMeSDK como dependência da documentação nativa.Antes de começar, certifique-se de que esteja de posse das credenciais de acesso aos nossos repositórios!
WARNING
Caso não faça ideia de onde estejam essas credenciais, entre em contato com o nosso suporte em [email protected].
O nosso SDK tem suporte para Swift Package Manager e Cocoapods. Escolha o que for melhor para o seu projeto, acesse o arquivo xcworkspace no Xcode os passos descritos na documentação nativa:
- Swift Package Manager
- Cocoapods
2.2. Criando o Canal de Comunicação
Essa seção se refere à implementação em Flutter do que é mostrado com mais detalhes em Primeiros Passos - Importar e Inicializar o SDK da documentação nativa.Antes de tudo, importe o AllowMe de acordo com o target usado. Lembre-se que você deve adicionar o AllowMeSDKHomolog ao seu target de debug/homologação e o AllowMeSDK ao target de release/produção.
#if DEBUG
import AllowMeSDKHomolog
let ALLOWE_API_KEY = "your-homolog-api-key-here"
#else
import AllowMeSDK
let ALLOWE_API_KEY = "your-prod-api-key-here"
#endif
Após isso, para que a aplicação flutter consiga enviar e receber comandos nativos é necessário criar um canal de comunicação. No exemplo abaixo nós criamos o canal "br.com.samples.allowme/sdk" e através da sobrescrita do método application da classe FlutterAppDelegate conforme pode ser visto no exemplo abaixo:
import {...}
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
var allowMe: AllowMe?
var methodChannel: FlutterMethodChannel?
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
methodChannel = FlutterMethodChannel(name: "br.com.samples.allowme/sdk", binaryMessenger: controller.binaryMessenger)
// Você pode chamar o setup sempre na inicialização do aplicativo iOS (recomendado).
// Nesse exemplo chamaremos apenas ao clicar no botão setup
do {
self.allowMe = try AllowMe.getInstance(withApiKey: "your-api-key-here")
} catch let error {
// tratar erro
}
methodChannel?.setMethodCallHandler({(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
if self.allowMe != nil {
if (call.method == "setup") {
self.allowMe?.setup { (error) in
if error != nil {
// tratar erro
result(FlutterError(code: "SetupError", message: error?.localizedDescription, details: error))
} else {
// sucesso
result("Setup Success!!")
}
}
} else {
result(FlutterMethodNotImplemented)
}
} else {
result(FlutterMethodNotImplemented)
}
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
Note que antes do "methodChannel?.setMethodCallHandler" pode ser chamado o setup do SDK. Conforme falado na documentação nativa, o setup faz a troca de chaves para que a criptografia esteja preparada antes da chamada nos outros métodos, aumentando a velocidade de resposta do SDK. Você pode chamar o setup apenas na entrada do AppDelegate do código nativo para que ele seja chamado sempre que o aplicativo iOS inicializar.
Agora, verifique os produtos contratados para seguir na implementação mostrada nas próximas seções!
2.3. Implementando o Device Intelligence (Contextual)
Essa seção se refere à implementação em Flutter do que é mostrado com mais detalhes em Device Intelligence (Contextual) da documentação nativa.Tomando o código acima como base, vamos adicionar ao setMethodCallHandler a chamada ao método collect que retorna uma string criptografada contendo o fingerprint gerado pelo AllowMe. Observe o exemplo com o código abaixo:
{...}
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
{...}
methodChannel?.setMethodCallHandler({(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
if self.allowMe != nil {
{...}
if (call.method == "collect") {
self.allowMe?.collect(onSuccess: { (collect) in
result("Collect Success: \(collect)")
}, onError: { (error) in
result("collect error: \(String(describing: error?.localizedDescription))")
})
} else {
result(FlutterMethodNotImplemented)
}
} else {
}
result(FlutterMethodNotImplemented)
return
})
{...}
}
2.4. Implementando Validação de Endereço
Essa seção se refere à implementação em Flutter do que é mostrado com mais detalhes em Validação de Endereço da documentação nativa.Ao contrário dos métodos mencionados anteriormente, o método start de agendamento de coletas para a função de Validação de Endereço deve apenas ser chamado durante a inicialização do app iOS conforme mostrado abaixo.
{...}
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
do {
self.allowMe = try AllowMe.getInstance(withApiKey: ALLOWME_API_KEY)
if let error = self.allowme.start() {
// tratar erro
} else {
// sucesso
}
} catch let error {
// tratar erro
}
{...}
}
IMPORTANTE!!!
Verifique a documentação nativa para entender porque o start deve ser chamado sempre durante a inicialização e realizar a configuração necessária de Background Modes. Observe também na documentação nativa a necessidade de sobrescrever, ou não, o método abaixo conforme fizemos com o método application
public override func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void)
Após isso, implemente a função addPerson:
{...}
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
{...}
methodChannel?.setMethodCallHandler({(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
if self.allowMe != nil {
{...}
if (call.method == "addPerson") {
let adddress = AddressModel(street: "Rua",
number: "000",
neighbourhood: "Bairro",
city: "Cidade",
state: "AA",
zipCode: "00000000")
let person = PersonModel(name: "Nome",
nationalId: "00000000000",
address: adddress)
self.allowMe?.addPerson(person: person, completion: { (error) in
if error == nil {
result("AddPerson Success!")
} else {
result("addPerson error: \(String(describing: error?.localizedDescription))")
}
})
} else {
result(FlutterMethodNotImplemented)
}
} else {
result(FlutterMethodNotImplemented)
}
})
{...}
}
Veja a documentação nativa da Validação de Endereço para ver todos os dados que podem ser passados para o método addPerson a partir de sua aplicação Flutter
2.5. Implementando a Biometria Facial
Essa seção se refere à implementação em Flutter do que é mostrado com mais detalhes em Biometria Facial da documentação nativa. Do mesmo modo visto nas seções anteriores, a biometria facial deverá ser chamada dentro do setMethodCallHandler do canal de comunicação criado.
{...}
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
methodChannel = FlutterMethodChannel(name: "br.com.samples.allowme/sdk", binaryMessenger: controller.binaryMessenger)
{...}
methodChannel?.setMethodCallHandler({(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
if self.allowMe != nil {
{...}
if (call.method == "biometrics") {
do {
let config = AllowMeBiometricsConfig()
try self.allowMe?.startBiometrics(viewController: controller,
delegate: self, config: config)
} catch let initError {
result("Erro ao inicializar: \(initError.localizedDescription)")
}
} else {
result(FlutterMethodNotImplemented)
}
} else {
result(FlutterMethodNotImplemented)
}
})
{...}
}
Observe a documentação nativa da Biometria Facial para verificar todas as possibilidades de customização disponíveis com a AllowMeBiometricsConfig.
O método startBiometrics recebe, além da ViewController declarada no início da função, um delegate. Você pode passar a classe que quiser como delegate desde que ela herde o protocolo AllowMeBiometricsDelegate. Nesse exemplo, passamos self, ou seja, a própria classe AppDelegate. Para isso, adicionamos o protocolo e a função biometricsDidFinish na classe conforme mostrado abaixo:
{...}
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate, AllowMeBiometricsDelegate {
{...}
func biometricsDidFinish(biometricsObject: AllowMeBiometricsResult?, error: Error?) {
let result = verify(biometricsObject, error)
// é necessário checar o objeto e o erro retornados para
// enviá-los para o backend conforme detalhado na documentação oficial
methodChannel?.invokeMethod("didReceiveBiometrics", arguments: result)
}
}
TIP
Para informações de como implementar a Biometria Facial com provider Facetec, veja as seções 4.3. Telas de Captura FaceTec e providerError na seção 5. Implementação do AllowMeBiometricsDelegate.
3. Integrando com o código dart
Agora que criamos os canais de comunicação no Android e iOS precisamos chamá-los através da nossa aplicação Flutter em dart. Você deve fazer isso usando platform.invokeMethod('nome-do-método') para invocar o método correspondente de cada plataforma.
Para cada botão do nosso aplicativo Flutter, vamos chamar cada uma das funções abaixo conforme a necessidade dos produtos contratados e implementados anteriormente
class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
{...}
// AllowMe Geral - Primeiros Passos
Future<void> _setupSDK() async {
String setupResult = "Not started yet";
try {
setupResult = await platform.invokeMethod('setup');
} on PlatformException catch (e) {
setupResult = "Failed to setup AllowMe '${e.message}'.";
}
_setAllowMeResult(setupResult);
}
// AllowMe Device Intelligence (Contextual)
Future<void> _collect() async {
String collect = "Not started yet";
try {
collect = await platform.invokeMethod('collect');
} on PlatformException catch (e) {
collect = "Failed to get collect '${e.message}'.";
}
_setAllowMeResult(collect);
}
// AllowMe Validação de Endereço
Future<void> _startSDK() async {
String startResult = "Not started yet";
try {
startResult = await platform.invokeMethod('start');
} on PlatformException catch (e) {
startResult = "Failed to start SDK '${e.message}'.";
}
_setAllowMeResult(startResult);
}
Future<void> _addPerson() async {
String addPersonResult = "No result yet";
try {
await platform.invokeMethod('addPerson');
addPersonResult = "Add Person with success";
} on PlatformException catch (e) {
addPersonResult = "Failed to addPerson '${e.message}'.";
}
_setAllowMeResult(addPersonResult);
}
3.1 Implementando a Biometria
A chamada da Biometria Facial trabalha de maneira semelhante aos outros métodos mostrados anteriormente.
import {...}
{...}
class _MyHomePageState extends State<MyHomePage> {
static const platform = const MethodChannel("br.com.samples.allowme/sdk");
{...}
Future<void> _biometrics() async {
String biometrics = "No Context yet";
try {
biometrics = await platform.invokeMethod('biometrics');
} on PlatformException catch (e) {
biometrics = "'${e.message}'.";
}
_setAllowMeResult(biometrics);
}
{...}
}
Para receber o resultado da Biometria Facial, o canal de comunicação seguirá o caminho inverso ao que estamos acostumados. Assim, devemos também implementar a função que será chamada pelas implementações nativas do Android e do iOS. Para isso, adicionaremos um novo callHandler mostrado abaixo:
@override
Widget build(BuildContext context) {
platform.setMethodCallHandler(this._didReceiveBiometrics);
{...}
}
e implementaremos a função chamada pelas plataformas. No nosso caso, as duas plataformas invocam a função didReceiveBiometrics.
{...}
Future<void> _didReceiveBiometrics(MethodCall call) async {
final String result = call.arguments;
switch (call.method) {
case "didReceiveBiometrics":
if (result != null) {
setState(() {
_allowMeResult = result;
});
}
}
}
TIP
Em caso dúvidas ou para ter acesso ao código fonte desse exemplo entre em contato com [email protected].
Updated 1 day ago