import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'api.dart'; import 'theme.dart'; import 'widgets.dart'; /// Cover photo for an album or song. Uploads and removes straight away (not on Save), /// so the photo is live in the app as soon as it shows here. class CoverEditor extends StatefulWidget { const CoverEditor({super.key, required this.table, required this.id, required this.coverUrl, this.hint}); /// 'albums' or 'tracks'. final String table; final String id; final String? coverUrl; final String? hint; @override State createState() => _CoverEditorState(); } class _CoverEditorState extends State { late String? _url = widget.coverUrl; bool _busy = false; Future _pick() async { // Resized in the browser: covers show at most ~400 px wide in the app. final file = await ImagePicker().pickImage(source: ImageSource.gallery, maxWidth: 1200, maxHeight: 1200, imageQuality: 85); if (file == null) return; await _run(() async { final bytes = await file.readAsBytes(); if (bytes.length > 5 << 20) throw 'That photo is too large. Please choose one under 5 MB.'; final url = await AdminApi.setCover(widget.table, widget.id, bytes, file.mimeType ?? 'image/jpeg'); setState(() => _url = url); }, 'Cover photo saved'); } Future _remove() => _run(() async { await AdminApi.removeCover(widget.table, widget.id); setState(() => _url = null); }, 'Cover photo removed'); Future _run(Future Function() job, String done) async { setState(() => _busy = true); try { await job(); if (mounted) showMessage(context, done); } catch (e) { if (mounted) showError(context, e); } finally { if (mounted) setState(() => _busy = false); } } @override Widget build(BuildContext context) => Row( children: [ ClipRRect( borderRadius: BorderRadius.circular(10), child: SizedBox.square( dimension: 88, child: _busy ? const Center(child: CircularProgressIndicator()) : _url == null ? const ColoredBox(color: NkColors.surface, child: Icon(Icons.image_outlined, color: NkColors.muted, size: 36)) : Image.network(_url!, fit: BoxFit.cover), ), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Cover photo', style: Theme.of(context).textTheme.titleMedium), Text(widget.hint ?? 'Square photos look best. JPG, PNG or WebP.', style: const TextStyle(color: NkColors.muted)), const SizedBox(height: 8), Wrap( spacing: 8, children: [ OutlinedButton.icon( onPressed: _busy ? null : _pick, icon: const Icon(Icons.upload_outlined), label: Text(_url == null ? 'Upload photo' : 'Change photo'), ), if (_url != null) TextButton(onPressed: _busy ? null : _remove, child: const Text('Remove')), ], ), ], ), ), ], ); }