forked from
grain.social/native
this repo has no description
1import 'package:flutter/cupertino.dart';
2import 'package:flutter/material.dart';
3import 'package:flutter/services.dart';
4import 'package:grain/models/gallery_photo.dart';
5import 'package:grain/widgets/app_image.dart';
6
7class EditAltTextSheet extends StatefulWidget {
8 final List<GalleryPhoto> photos;
9 final void Function(Map<String, String?>) onSave;
10
11 const EditAltTextSheet({super.key, required this.photos, required this.onSave});
12
13 @override
14 State<EditAltTextSheet> createState() => _EditAltTextSheetState();
15}
16
17class _EditAltTextSheetState extends State<EditAltTextSheet> {
18 late Map<String, TextEditingController> _controllers;
19
20 @override
21 void initState() {
22 super.initState();
23 _controllers = {
24 for (final photo in widget.photos) photo.uri: TextEditingController(text: photo.alt ?? ''),
25 };
26 }
27
28 @override
29 void dispose() {
30 for (final c in _controllers.values) {
31 c.dispose();
32 }
33 super.dispose();
34 }
35
36 void _onSave() {
37 final altTexts = {for (final photo in widget.photos) photo.uri: _controllers[photo.uri]?.text};
38 widget.onSave(altTexts);
39 Navigator.of(context).pop();
40 }
41
42 @override
43 Widget build(BuildContext context) {
44 final theme = Theme.of(context);
45 return CupertinoPageScaffold(
46 backgroundColor: theme.colorScheme.surface,
47 navigationBar: CupertinoNavigationBar(
48 backgroundColor: theme.colorScheme.surface,
49 middle: Text(
50 'Edit alt text',
51 style: theme.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600),
52 ),
53 leading: CupertinoButton(
54 padding: EdgeInsets.zero,
55 child: const Text('Cancel'),
56 onPressed: () => Navigator.of(context).pop(),
57 ),
58 trailing: CupertinoButton(
59 padding: EdgeInsets.zero,
60 onPressed: _onSave,
61 child: const Text('Save'),
62 ),
63 ),
64 child: SafeArea(
65 child: ListView.separated(
66 padding: const EdgeInsets.all(16),
67 itemCount: widget.photos.length,
68 separatorBuilder: (_, __) => const SizedBox(height: 12),
69 itemBuilder: (context, index) {
70 final theme = Theme.of(context);
71 final photo = widget.photos[index];
72 final width = photo.aspectRatio?.width;
73 final height = photo.aspectRatio?.height;
74 return Row(
75 crossAxisAlignment: CrossAxisAlignment.center,
76 children: [
77 SizedBox(
78 width: 64,
79 child: AspectRatio(
80 aspectRatio: (width != null && height != null && width > 0 && height > 0)
81 ? width / height
82 : 1.0,
83 child: AppImage(
84 url: photo.thumb ?? photo.fullsize,
85 borderRadius: BorderRadius.circular(8),
86 ),
87 ),
88 ),
89 const SizedBox(width: 16),
90 Expanded(
91 child: TextField(
92 controller: _controllers[photo.uri],
93 decoration: InputDecoration(
94 hintText: 'Enter alt text',
95 border: InputBorder.none,
96 filled: false,
97 contentPadding: const EdgeInsets.symmetric(vertical: 16, horizontal: 12),
98 hintStyle: theme.textTheme.bodyMedium?.copyWith(color: theme.hintColor),
99 ),
100 style: theme.textTheme.bodyMedium,
101 cursorColor: theme.colorScheme.primary,
102 minLines: 1,
103 maxLines: 6,
104 textAlignVertical: TextAlignVertical.top,
105 scrollPhysics: const AlwaysScrollableScrollPhysics(),
106 keyboardType: TextInputType.multiline,
107 ),
108 ),
109 ],
110 );
111 },
112 ),
113 ),
114 );
115 }
116}
117
118Future<void> showEditAltTextSheet(
119 BuildContext context, {
120 required List<GalleryPhoto> photos,
121 required void Function(Map<String, String?>) onSave,
122}) async {
123 final theme = Theme.of(context);
124 await showCupertinoSheet(
125 context: context,
126 useNestedNavigation: false,
127 pageBuilder: (context) => Material(
128 type: MaterialType.transparency,
129 child: EditAltTextSheet(photos: photos, onSave: onSave),
130 ),
131 );
132 // Restore status bar style or any other cleanup
133 SystemChrome.setSystemUIOverlayStyle(
134 theme.brightness == Brightness.dark ? SystemUiOverlayStyle.light : SystemUiOverlayStyle.dark,
135 );
136}