Jak udělat kontaktní formulář v Androidu v Kotlinu a Jetpack Compose
Kontaktní obrazovka v Jetpack Compose, která vám každou zprávu pošle e-mailem přes vaši vlastní schránku – bez backendu a bez SMTP hesla v APK. Používá Kotlin SDK Frontmailu.
Aktualizováno:
Do aplikace pro Android v Kotlinu a Jetpack Compose přidáte kontaktní obrazovku. Aplikace obsahuje jen veřejný klíč – e-mail odešle Frontmail přes vaši schránku, takže v APK neskončí žádné SMTP heslo, které by si z něj mohl kdokoli vytáhnout.
Budete potřebovat: bezplatný účet Frontmail, projekt pro Android s Compose a zhruba patnáct minut.
1. Připojte schránku a vytvořte šablonu
- E-mailové služby → Přidat službu: připojte Gmail, Outlook, SMTP nebo transakční službu, zaškrtněte Nastavit jako výchozí a klikněte na Ověřit a uložit.
- Šablony → Z galerie → Kontaktní formulář → Použít šablonu. Povinné parametry:
name,email,message; nepovinné:phone,topic. - Na záložce Kód pro web zkopírujte ID šablony (
tpl_…).
2. Povolte mobilní aplikace
- Zabezpečení → Mobilní aplikace → Povolit mobilní aplikace → Uložit. Aplikace
neposílají hlavičku
Origin, takže je do té doby API odmítá s chybouorigin_not_allowed. - Šablona → Nastavení → Ochrana → Vyžadovat CAPTCHA (Turnstile). Aplikace používají sdílený widget Turnstile od Frontmailu – klíče od Cloudflaru nepotřebujete.
3. Přidejte SDK
// app/build.gradle.kts
dependencies {
implementation("dev.frontmail:frontmail:0.1.0")
}
Do AndroidManifest.xml přidejte <uses-permission android:name="android.permission.INTERNET" />.
Pak si z části o Turnstile v dokumentaci Android SDK zkopírujte do projektu pomocné funkce
WebView.showTurnstile(…) a WebView.resetTurnstile() – je to jeden soubor, který patří vám.
4. Postavte obrazovku
import android.webkit.WebView
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import dev.frontmail.Frontmail
import dev.frontmail.FrontmailException
import dev.frontmail.SendOptions
import dev.frontmail.Turnstile
import dev.frontmail.TurnstileWidget
import kotlinx.coroutines.launch
val frontmail = Frontmail("pk_…") // create once, reuse (thread-safe)
@Composable
fun ContactScreen() {
var name by remember { mutableStateOf("") }
var email by remember { mutableStateOf("") }
var message by remember { mutableStateOf("") }
var widget by remember { mutableStateOf<TurnstileWidget?>(null) }
var token by remember { mutableStateOf<String?>(null) }
var webView by remember { mutableStateOf<WebView?>(null) }
var info by remember { mutableStateOf("") }
val scope = rememberCoroutineScope()
LaunchedEffect(Unit) { widget = Turnstile.sharedWidget() }
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
OutlinedTextField(name, { name = it }, label = { Text("Name") }, modifier = Modifier.fillMaxWidth())
OutlinedTextField(email, { email = it }, label = { Text("Email") }, modifier = Modifier.fillMaxWidth())
OutlinedTextField(message, { message = it }, label = { Text("Message") }, minLines = 4, modifier = Modifier.fillMaxWidth())
widget?.let { w ->
AndroidView(
factory = { context ->
WebView(context).apply {
showTurnstile(w, onToken = { token = it }, onExpire = { token = null })
webView = this
}
},
modifier = Modifier.fillMaxWidth().height(70.dp),
)
}
Button(
enabled = token != null,
onClick = {
scope.launch {
try {
frontmail.send(
templateId = "tpl_…",
params = mapOf("name" to name, "email" to email, "message" to message),
options = SendOptions(turnstileToken = token, turnstileKey = widget?.turnstileKey),
)
info = "Thank you! We’ll get back to you soon."
} catch (e: FrontmailException) {
info = e.message ?: "Sending failed"
} finally {
token = null // a token works only once
webView?.resetTurnstile()
}
}
},
) { Text("Send") }
Text(info)
}
}
Chyby sítě, vypršení časového limitu a odpovědi 5xx se automaticky opakují se stejným
idempotency key, takže ani při slabém signálu vám tatáž zpráva nepřijde dvakrát.
5. Vyzkoušejte to
Spusťte aplikaci na zařízení nebo v emulátoru, vyplňte formulář a klepněte na Send. E-mail dorazí do vaší schránky a uvidíte ho v sekci Historie v dashboardu.
origin_not_allowed– volba Povolit mobilní aplikace je vypnutá nebo se neuložila.captcha_required– zatím chybí token Turnstile; tlačítko zůstane neaktivní, dokud kontrola nedoběhne.invalid_template_params– povinné pole je prázdné nebo e-mail není platný.
Došly kredity? Zpráva se přijme se stavem
HELDa odešle se automaticky po dobití – pokud to chcete uživateli sdělit, zkontrolujteresult.status.
Další kroky
- Přidejte z galerie šablonu Automatická odpověď: díky za zprávu, která uživateli potvrdí přijetí zprávy.
- Předejte
SendOptions(locale = Locale.getDefault().toLanguageTag()), aby se použila jazyková verze šablony podle jazyka telefonu.


