Ako vytvoriť kontaktný formulár vo Flutteri, ktorý posiela e-maily
Kontaktný formulár vo Flutteri, ktorý vám každú správu pošle e-mailom – bez backendu a bez SMTP hesla v aplikácii. Funguje na Androide, iOS, webe aj desktope s balíčkom Frontmail pre Dart.
Aktualizované:
Do aplikácie vo Flutteri pridáte kontaktnú obrazovku, ktorá každú správu pošle do vašej schránky. Aplikácia obsahuje iba verejný kľúč; Frontmail odošle e-mail cez vašu vlastnú schránku, takže sa do balíčka aplikácie nedostane žiadne SMTP heslo ani adresa servera.
Potrebujete: bezplatný účet Frontmail, projekt vo Flutteri 3.24+ a zhruba pätnásť minút.
1. Pripojte schránku a vytvorte šablónu
- E-mailové služby → Pridať službu: pripojte Gmail, Outlook, SMTP alebo transakčného poskytovateľa, zaškrtnite Nastaviť ako predvolenú, Overiť a uložiť.
- Šablóny → Z galérie → Kontaktný formulár → Použiť šablónu. Očakáva
name,emailamessage(povinné) aphone,topic(nepovinné). - Z karty Kód skopírujte ID šablóny (
tpl_…).
2. Povoľte mobilné aplikácie
Aplikácie neposielajú hlavičku Origin ako weby, preto ich API odmieta, kým ich nepovolíte:
Zabezpečenie → Mobilné aplikácie → zapnite Povoliť mobilné aplikácie → Uložiť.
Potom v šablóne otvorte Nastavenia → Ochrana a zapnite Vyžadovať CAPTCHA (Turnstile). Mobilné aplikácie používajú zdieľaný widget Turnstile od Frontmailu, takže nepotrebujete žiadne vlastné kľúče od Cloudflare.
3. Nainštalujte balíček
flutter pub add frontmail webview_flutter url_launcher
webview_flutter zobrazuje overenie Turnstile. Na Androide potrebujú release buildy aj
<uses-permission android:name="android.permission.INTERNET"/> v AndroidManifest.xml.
Z časti o Turnstile v dokumentácii Flutter SDK si skopírujte widget TurnstileView do
lib/turnstile_view.dart – je to zhruba šesťdesiat riadkov, ktoré patria vám a môžete ich
ľubovoľne upraviť.
4. Vytvorte formulár
import 'package:flutter/material.dart';
import 'package:frontmail/frontmail.dart';
import 'turnstile_view.dart';
final frontmail = Frontmail(publicKey: 'pk_…'); // create once, share
class ContactScreen extends StatefulWidget {
const ContactScreen({super.key});
@override
State<ContactScreen> createState() => _ContactScreenState();
}
class _ContactScreenState extends State<ContactScreen> {
final _name = TextEditingController();
final _email = TextEditingController();
final _message = TextEditingController();
TurnstileSite? _site;
String? _token;
int _resetId = 0;
bool _sending = false;
String _info = '';
@override
void initState() {
super.initState();
Turnstile.site(client: frontmail).then((site) => setState(() => _site = site));
}
Future<void> _send() async {
final site = _site, token = _token;
if (site == null || token == null) return;
setState(() {
_sending = true;
_token = null; // a token works only once
_resetId++;
});
try {
await frontmail.send(
templateId: 'tpl_…',
params: {'name': _name.text, 'email': _email.text, 'message': _message.text},
options: SendOptions(turnstileToken: token, turnstileKey: site.turnstileKey),
);
setState(() => _info = 'Thank you! We’ll get back to you soon.');
} on FrontmailException catch (e) {
setState(() => _info = e.message);
} finally {
setState(() => _sending = false);
}
}
@override
Widget build(BuildContext context) {
final site = _site;
return Scaffold(
appBar: AppBar(title: const Text('Contact us')),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
TextField(controller: _name, decoration: const InputDecoration(labelText: 'Name')),
TextField(controller: _email, decoration: const InputDecoration(labelText: 'Email'), keyboardType: TextInputType.emailAddress),
TextField(controller: _message, decoration: const InputDecoration(labelText: 'Message'), maxLines: 5),
const SizedBox(height: 16),
if (site != null)
TurnstileView(
site: site,
resetId: _resetId,
onMessage: (m) => setState(() => _token = m is TurnstileToken ? m.token : null),
),
FilledButton(onPressed: _token == null || _sending ? null : _send, child: const Text('Send')),
Text(_info),
],
),
);
}
}
SDK samo opakuje pokusy pri sieťových chybách a výpadkoch servera s jedným idempotency kľúčom, takže ani nestabilné mobilné pripojenie nevytvorí duplicitné e-maily.
5. Vyskúšajte to
Spustite aplikáciu, vyplňte formulár a ťuknite na Send. E-mail vám príde do schránky a zobrazí sa v dashboarde v časti História.
origin_not_allowed– Povoliť mobilné aplikácie je vypnuté (alebo neuložené).captcha_required– chýba token Turnstile; počkajte, kým sa overenie dokončí.invalid_template_params– povinné pole je prázdne alebo e-mailová adresa nie je platná.
Ak vám dôjdu kredity, správa sa prijme so stavom
helda po dobití sa automaticky odošle – podľaresult.status == DeliveryStatus.heldto môžete oznámiť používateľovi.
Ďalšie kroky
- Pošlite používateľovi potvrdenie pomocou šablóny Automatická odpoveď: ďakujeme za správu z galérie.
- Ak má šablóna jazykové verzie, odovzdajte
SendOptions(locale: …)a e-mail odíde v jazyku používateľa.


