Send a mailable from the PostPublished listener:
await Mail.to(author.email).send(PostPublishedMail(post));Amarna Mail (amarna) builds messages as mailables and sends them through
SMTP, the application log, or an in-memory array transport. New Maat projects
register MailServiceProvider and include the configuration below.
Configuration
config/mail.dart selects the default named mailer and sender:
final Map<String, dynamic> mail = { 'default': env('MAIL_MAILER', 'log'), 'mailers': { 'smtp': { 'transport': 'smtp', 'host': env('MAIL_HOST', '127.0.0.1'), 'port': envInt('MAIL_PORT', 587), 'username': env('MAIL_USERNAME'), 'password': env('MAIL_PASSWORD'), 'encryption': env('MAIL_ENCRYPTION', 'tls'), }, 'log': {'transport': 'log'}, 'array': {'transport': 'array'}, }, 'from': { 'address': env('MAIL_FROM_ADDRESS', 'hello@example.com'), 'name': env('MAIL_FROM_NAME', 'Example'), },};Use log locally to inspect messages without delivering them. Use array in
tests that need the assembled Email, and smtp for delivery. tls upgrades
the SMTP connection with STARTTLS; ssl opens an SSL connection immediately;
an empty encryption value allows an insecure connection.
Mailables
Generate a mailable:
maat make:mail PostPublishedMailDefine its envelope, content, and attachments:
import 'package:amarna/amarna.dart';
class PostPublishedMail extends Mailable { PostPublishedMail(this.post);
final Post post;
@override Envelope envelope() => const Envelope( from: Address('hello@example.com', 'Maat Blog'), subject: 'Your post is live', replyTo: [Address('support@example.com')], );
@override Content content() => Content( view: 'mail.post_published', text: 'Your post "${post.title}" is now live.', data: {'post': post}, );
@override List<Attachment> attachments() => const [];}view renders through Khnum and becomes the HTML body. text is the plain
text alternative. Without an explicit subject, the class name becomes a
headline: PostPublishedMail becomes Post Published Mail.
Sending
await Mail.to(author.email).send(PostPublishedMail(post));
// Uses recipients declared by Envelope.to/cc/bcc.await Mail.send(WeeklyDigestMail());
await Mail.raw('Service restored', (email) { email ..to.add(const Address('ops@example.com')) ..subject = 'Status update';});
// Select a configured mailer instead of the default.await Mail.mailer('smtp').to('ada@example.com').send(WelcomeMail());Recipients may be strings, Address values, or application objects exposing a
String email field and optional String name field.
Transports
The built-in transport names are smtp, log, and array. Add a custom one
before its mailer is first resolved:
MailManager.extend('archive', (settings) => ArchiveTransport(settings));The custom class implements Transport.send(Email email). Named mailers are
created lazily and cached by MailManager.
Delivery Events
MessageSending runs before transport I/O. Return false to cancel delivery:
Event.listen<MessageSending>((event) { if (event.email.hasTo('blocked@example.com')) return false;});
Event.listen<MessageSent>((event) { Log.info('Sent ${event.email.subject}');});MessageSent fires only after the selected transport succeeds.
Testing
final mail = Mail.fake();
await Mail.to(author.email).send(PostPublishedMail(post));
mail.assertSent<PostPublishedMail>();mail.assertSent<PostPublishedMail>((message) => message.post.id == post.id);mail.assertNotSent<WeeklyDigestMail>();mail.assertSentCount(1);mail.sent<T>() returns matching mailables. mail.emails contains raw emails
and emails sent directly, including notification-channel messages.
Mail is sent inline and send() completes only after the transport finishes.
Queued mail will be added with the queue package; Amarna does not pretend an
inline send is queued.