feat: render HTML email bodies via flutter_html

Email detail screen now renders the message htmlBody with the flutter_html
widget when present, falling back to SelectableText for plain-text-only mail.
Remote http(s) images are blocked by default to defeat tracking pixels; an
opt-in "Load remote images" button reveals them per-screen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Thomas Güttler
2026-04-28 20:10:18 +02:00
co-authored by Claude Opus 4.7
parent ab6dc89665
commit 44d02afc46
4 changed files with 72 additions and 50 deletions
-46
View File
@@ -17,49 +17,3 @@ Git repo should not contain unknown files.
Then commit.
## Tasks
### Render HTML email bodies
#### Current state
HTML mail *reading* (transport + storage) is already implemented: the IMAP
fetcher stores the HTML body on `EmailBody.htmlBody`
(`lib/core/models/email.dart:89`). What is missing is HTML *rendering*:
`lib/ui/screens/email_detail_screen.dart:158` currently strips tags via
`htmlToPlain(body.htmlBody ?? '')` and shows the result as plain text.
#### Plan
1. Add `flutter_html: ^3.0.0` to `pubspec.yaml`.
- Mature, pure-Dart (no JS / no platform views), works on desktop +
mobile + web.
2. Update `lib/ui/screens/email_detail_screen.dart` `_buildBody`:
- Prefer `body.htmlBody` over `body.textBody`.
- If `htmlBody` is present and non-empty, render with the `Html` widget.
- Else fall back to the current `SelectableText(textBody)` path.
3. Block remote image loading by default (privacy — defeats tracking
pixels). At the top of an HTML message, show a small
"Load remote images" button that flips a per-screen `bool` and
re-renders. Implement by passing a custom image-loading delegate to
`flutter_html` that returns an empty widget for `http(s)` images
until the flag is set. Inline `cid:` images are out of scope for
this task.
4. Leave `htmlToPlain` in place — it is still used for reply / forward
quoting (`_quotedBody`) where plain text is correct.
5. No DB schema changes, no generated-code changes, no migration.
6. No new tests required (pure UI rendering change). Existing widget /
integration tests must keep passing.
#### Out of scope (follow-ups if needed)
- Clickable / launchable links inside HTML.
- Dark-mode HTML colour normalisation.
- Sandboxed rendering via `WebView` / `flutter_inappwebview`.
- Inline `cid:` image resolution.
#### Verification
- `task deploy-android` succeeds.
- Manual: open an HTML newsletter — formatting (lists, bold, links) is
visible instead of raw tags or stripped text.
- Manual: a plain-text-only email still renders unchanged.