manager-app/lib/Screens/Resources/new_resource_popup.dart

142 lines
4.7 KiB
Dart

import 'package:flutter/material.dart';
import 'package:manager_app/Components/resource_picker.dart';
import 'package:manager_app/Screens/Resources/resource_formatting.dart';
import 'package:manager_app/app_context.dart';
import 'package:manager_app/constants.dart';
import 'package:manager_app/l10n/app_localizations.dart';
/// Dialogue d'ajout d'une ressource.
///
/// Un seul corps, pas deux onglets : « Local » / « En ligne » imposait une question
/// technique — où vivent les octets — avant que l'utilisateur ait rien choisi. Une
/// zone de dépôt, et dessous une ligne « ou coller une URL » qui se déplie.
///
/// Rend la liste des entrées choisies, ou `null` si le dialogue est annulé.
Future<List<PickedResource>?> showNewResource(
AppContext appContext, BuildContext context) async {
final picked = <PickedResource>[];
return showDialog<List<PickedResource>>(
context: context,
builder: (BuildContext context) => _NewResourceDialog(picked: picked),
);
}
class _NewResourceDialog extends StatefulWidget {
final List<PickedResource> picked;
const _NewResourceDialog({required this.picked});
@override
State<_NewResourceDialog> createState() => _NewResourceDialogState();
}
class _NewResourceDialogState extends State<_NewResourceDialog> {
int get _totalBytes =>
widget.picked.fold<int>(0, (sum, entry) => sum + entry.sizeBytes);
@override
Widget build(BuildContext context) {
final l = AppLocalizations.of(context)!;
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(kRadiusShell),
side: const BorderSide(color: kLine),
),
clipBehavior: Clip.antiAlias,
child: SizedBox(
width: 560,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_header(l),
Flexible(
child: SingleChildScrollView(
padding: const EdgeInsets.fromLTRB(kSpace6, 0, kSpace6, kSpace4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: [
ResourcePicker(
picked: widget.picked,
onChanged: () => setState(() {}),
),
if (widget.picked.isNotEmpty) _totals(l),
],
),
),
),
_footer(l),
],
),
),
);
}
Widget _header(AppLocalizations l) {
return Padding(
padding: const EdgeInsets.fromLTRB(kSpace6, kSpace5, kSpace4, kSpace4),
child: Row(
children: [
Expanded(child: Text(l.mediaAddTitle, style: kTitleCard)),
IconButton(
icon: const Icon(Icons.close, size: 18, color: kInk3),
splashRadius: 16,
tooltip: l.close,
onPressed: () => Navigator.of(context).pop(),
),
],
),
);
}
Widget _totals(AppLocalizations l) {
return Padding(
padding: const EdgeInsets.only(top: kSpace4),
child: Text(
l.mediaSelectedTotal(widget.picked.length, formatBytes(context, _totalBytes)),
style: kTextHint,
),
);
}
Widget _footer(AppLocalizations l) {
// Désactivé plutôt qu'expliqué : l'ancien dialogue acceptait le clic à vide,
// puis affichait une notification orange « aucun fichier chargé ».
final canAdd = widget.picked.isNotEmpty;
return Container(
padding: const EdgeInsets.all(kSpace4),
decoration: const BoxDecoration(
color: kSurface2,
border: Border(top: BorderSide(color: kLineSoft)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(l.cancel,
style: const TextStyle(fontSize: 13, color: kInk2)),
),
const SizedBox(width: kSpace2),
FilledButton(
onPressed:
canAdd ? () => Navigator.pop(context, widget.picked) : null,
style: FilledButton.styleFrom(
backgroundColor: kPrimaryColor,
padding: const EdgeInsets.symmetric(
horizontal: kSpace5, vertical: kSpace3),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(kRadiusPill)),
),
child: Text(l.mediaAddCount(widget.picked.length),
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600)),
),
],
),
);
}
}