Přeskočit na obsah

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:

Video je v angličtině; text níže obsahuje celý návod.

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

  1. 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.
  2. Šablony → Z galerie → Kontaktní formulář → Použít šablonu. Povinné parametry: name, email, message; nepovinné: phone, topic.
  3. 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 chybou origin_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 HELD a odešle se automaticky po dobití – pokud to chcete uživateli sdělit, zkontrolujte result.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.
Všechny návody
  • Video, 42 s

    Android (Kotlin)Poptávkový formulář

    Jak udělat poptávkový formulář v Androidu v Kotlinu

    Poptávkový formulář v Jetpack Compose s výběrem služby a rozpočtu a termínem, který vám každou poptávku pošle e-mailem a zákazníkovi automaticky potvrdí přijetí.

  • Video, 42 s

    WebKontaktní formulář

    Jak přidat kontaktní formulář na web bez backendu

    Obyčejný HTML kontaktní formulář, který vám každou zprávu pošle e-mailem – bez serveru, bez PHP a bez pluginu. Funguje na každém statickém webu, od ručně psaného HTML po Astro, Hugo nebo WordPress.

  • Video, 42 s

    FlutterKontaktní formulář

    Jak ve Flutteru udělat kontaktní formulář, který odesílá e-maily

    Kontaktní formulář ve Flutteru, který vám každou zprávu pošle e-mailem – bez backendu a bez SMTP hesla v aplikaci. Funguje na Androidu, iOS, webu i desktopu s Dart balíčkem Frontmail.