Preskočiť na obsah

Ako vytvoriť dopytový formulár pre Android v Kotline

Dopytový formulár v Jetpack Compose s výberom služby, rozpočtu a termínu, ktorý vám každý dopyt pošle e-mailom a zákazníkovi automaticky pošle potvrdenie.

Aktualizované:

Video je v angličtine; nižšie nájdete celý návod v slovenčine.

V tomto návode vytvoríte obrazovku na dopyt v Jetpack Compose. Zákazník vyberie službu a rozpočet, zadá termín a popis; vy dostanete štruktúrovaný e-mail a zákazník automatické potvrdenie.

Potrebujete: bezplatný účet Frontmail, projekt pre Android s Compose a zhruba dvadsať minút.

1. Pripojte schránku a vytvorte šablóny

  1. E-mailové služby → Pridať službu: pripojte svoju schránku alebo poskytovateľa, Nastaviť ako predvolenú, Overiť a uložiť.
  2. Šablóny → Z galérie → Dopyt (kategória Biznis) → Použiť šablónu.
  3. Z galérie → Automatická odpoveď: ďakujeme za správu → Použiť šablónu; nastavte predvolené hodnoty pre company_name a response_time a potom ju vyberte v šablóne dopytu v Nastavenia → Doručovanie → Šablóna automatickej odpovede.

Šablóna dopytu potrebuje name, email, service a details; phone, company, budget a deadline (YYYY-MM-DD) sú nepovinné. service a budget prijímajú iba hodnoty z karty Parametre. Príklad kódu používa hodnoty anglickej šablóny; keď šablónu vytvoríte v slovenčine, hodnoty budú slovenské (Web, E-shop, Mobilná aplikácia, Konzultácie, Iné a Do 5 000 €, 5 000 – 20 000 €, 20 000 – 50 000 €, Nad 50 000 €) – v aplikácii posielajte presne tie isté reťazce.

2. Povoľte mobilné aplikácie a zapnite CAPTCHA

  • Zabezpečenie → Mobilné aplikácie → Povoliť mobilné aplikácie → Uložiť.
  • Šablóna dopytu → Nastavenia → Ochrana → Vyžadovať CAPTCHA (Turnstile) (zdieľaný mobilný widget Frontmailu, žiadne kľúče nepotrebujete).

3. Pridajte SDK

// app/build.gradle.kts
dependencies {
    implementation("dev.frontmail:frontmail:0.1.0")
}

Pridajte oprávnenie INTERNET a skopírujte pomocné funkcie showTurnstile / resetTurnstile z časti o Turnstile v dokumentácii Android SDK.

4. Vytvorte 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
import java.time.LocalDate

val frontmail = Frontmail("pk_…")

val services = listOf("Website", "E-shop", "Mobile app", "Consulting", "Other")
val budgets = listOf("Under $5k", "$5k–20k", "$20k–50k", "Over $50k")

@OptIn(ExperimentalLayoutApi::class)
@Composable
fun Choices(options: List<String>, selected: String?, onSelect: (String?) -> Unit) {
    FlowRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
        options.forEach { option ->
            FilterChip(
                selected = option == selected,
                onClick = { onSelect(if (option == selected) null else option) },
                label = { Text(option) },
            )
        }
    }
}

@Composable
fun QuoteScreen() {
    var name by remember { mutableStateOf("") }
    var email by remember { mutableStateOf("") }
    var service by remember { mutableStateOf<String?>(services.first()) }
    var budget by remember { mutableStateOf<String?>(null) }
    var deadline by remember { mutableStateOf("") }
    var details 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())
        Text("What do you need?")
        Choices(services, service) { service = it }
        Text("Budget (optional)")
        Choices(budgets, budget) { budget = it }
        OutlinedTextField(deadline, { deadline = it }, label = { Text("Deadline (YYYY-MM-DD, optional)") }, modifier = Modifier.fillMaxWidth())
        OutlinedTextField(details, { details = it }, label = { Text("Project details") }, 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 && service != null,
            onClick = {
                scope.launch {
                    try {
                        frontmail.send(
                            templateId = "tpl_…",
                            params = mapOf(
                                "name" to name,
                                "email" to email,
                                "service" to service,
                                "budget" to budget,
                                "deadline" to runCatching { LocalDate.parse(deadline).toString() }.getOrNull(),
                                "details" to details,
                            ),
                            options = SendOptions(turnstileToken = token, turnstileKey = widget?.turnstileKey),
                        )
                        info = "Thanks! We’ve emailed you a confirmation."
                    } catch (e: FrontmailException) {
                        info = e.message ?: "Sending failed"
                    } finally {
                        token = null // a token works only once
                        webView?.resetTurnstile()
                    }
                }
            },
        ) { Text("Request a quote") }
        Text(info)
    }
}

Hodnoty null (bez rozpočtu, bez platného termínu) sa v e-maili vynechajú. Ak chcete, nahraďte textové pole komponentom DatePicker z Material 3 – len dátum posielajte ako YYYY-MM-DD.

5. Vyskúšajte to

Odošlite dopyt s vlastným e-mailom: dostanete dopyt a o chvíľu aj potvrdenie. Oba sa zobrazia v História aj s časovou osou doručenia.

Dopyt aj s potvrdením stojí dva kredity. Ak vám kredity dôjdu, dopyty sa prijmú so stavom HELD a po dobití sa automaticky odošlú.

Ďalšie kroky

  • V nastaveniach šablóny zapnite Zber kontaktov a budete mať zoznam všetkých, ktorí žiadali o ponuku.
  • Prikladajte súbory (napríklad zadanie v PDF) cez SendOptions(attachments = …) – pozrite si dokumentáciu SDK.
Všetky návody
  • Video, 42 s

    Android (Kotlin)Kontaktný formulár

    Ako vytvoriť kontaktný formulár pre Android v Kotline a Jetpack Compose

    Kontaktná obrazovka v Jetpack Compose, ktorá vám každú správu pošle e-mailom cez vašu vlastnú schránku – bez backendu a bez SMTP hesla v APK. S Kotlin SDK od Frontmailu.

  • Video, 42 s

    WebDopytový formulár

    Ako vytvoriť dopytový formulár na web

    Dopytový formulár so službou, rozpočtom a termínom, ktorý vám príde ako prehľadný e-mail – a zákazníkovi automaticky pošle potvrdenie. Bez backendu, v čistom HTML.

  • Video, 42 s

    FlutterDopytový formulár

    Ako vytvoriť dopytový formulár vo Flutteri

    Dopytový formulár vo Flutteri s výberom služby, rozpočtu a termínu, ktorý vám každý dopyt pošle e-mailom a zákazníkovi automaticky potvrdí jeho prijatie – bez backendu.