feat(profile): Material default avatar + square photo crop

Drop the DiceBear generated avatar (and dicebear_core/dicebear_styles/
flutter_svg): the default is again a Material coloured-initial disc from
the pubkey. Picking a photo now goes through a square crop step
(crop_your_image — pure Flutter, all platforms incl. desktop, Apache-2.0,
only pulls the image package we already had) before the 24 KB thumbnail.
This commit is contained in:
vjrj 2026-07-12 23:39:56 +02:00
parent 17612b8147
commit f6967e8cbb
8 changed files with 200 additions and 175 deletions

View file

@ -2,14 +2,16 @@ import 'package:flutter/material.dart';
import '../i18n/strings.g.dart';
import '../services/offer_thumbnail.dart' show offerThumbnailDataUri;
import 'photo_crop.dart';
import 'photo_pick.dart';
import 'theme.dart';
/// Lets the user choose a profile avatar: take/pick a photo (shrunk to a tiny
/// inline thumbnail) or remove it. With no photo, a generated avatar is used.
/// Lets the user choose a profile avatar: take/pick a photo, square-crop it, and
/// keep it as a tiny inline thumbnail or remove it. With no photo, the
/// coloured-initial disc is used.
///
/// Returns the new avatar value (a `data:` photo thumbnail), an empty string to
/// clear it (falls back to the generated avatar), or null when cancelled.
/// clear it (falls back to the coloured-initial disc), or null when cancelled.
Future<String?> showAvatarPicker(
BuildContext context, {
required String current,
@ -37,11 +39,11 @@ Future<String?> showAvatarPicker(
title: Text(t.avatar.fromPhoto),
onTap: () async {
final bytes = await pickPhoto(sheetContext);
if (bytes == null) return;
final uri = offerThumbnailDataUri(bytes, maxBytes: 24000);
if (sheetContext.mounted) {
Navigator.of(sheetContext).pop(uri ?? '');
}
if (bytes == null || !sheetContext.mounted) return;
final cropped = await cropToSquare(sheetContext, bytes);
if (cropped == null || !sheetContext.mounted) return;
final uri = offerThumbnailDataUri(cropped, maxBytes: 24000);
Navigator.of(sheetContext).pop(uri ?? '');
},
),
if (current.isNotEmpty) ...[