@@ -6,6 +6,7 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart';
66import 'package:flutter_markdown/flutter_markdown.dart' ;
77import 'package:flutter_riverpod/flutter_riverpod.dart' ;
88import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart' ;
9+ import 'package:heartry/screens/poems_screen/providers/multi_select_provider.dart' ;
910import '../../database/database.dart' ;
1011import '../../init_get_it.dart' ;
1112import 'package:url_launcher/url_launcher_string.dart' ;
@@ -15,7 +16,10 @@ import '../../providers/app_version_manager_provider.dart';
1516import '../../providers/changelog_provider.dart' ;
1617import '../../providers/list_grid_provider.dart' ;
1718import '../../providers/stream_poem_provider.dart' ;
19+ import '../../utils/share_helper.dart' ;
20+ import '../../widgets/share_option_list.dart' ;
1821import '../profile_screen/profile_screen.dart' ;
22+ import '../reader_screen/reader_screen.dart' ;
1923import '../settings_screen/settings_screen.dart' ;
2024import '../writing_screen/writing_screen.dart' ;
2125import 'widgets/poem_card.dart' ;
@@ -176,6 +180,24 @@ class _ChangelogDialog extends StatelessWidget {
176180class _CAppBar extends ConsumerWidget {
177181 const _CAppBar ();
178182
183+ @override
184+ Widget build (BuildContext context, WidgetRef ref) {
185+ final showMultiOption = ref.watch (multiSelectEnabledProvider);
186+
187+ return AnimatedCrossFade (
188+ firstChild: _DefaultAppBar (),
189+ secondChild: _Toolbar (),
190+ crossFadeState: showMultiOption
191+ ? CrossFadeState .showSecond
192+ : CrossFadeState .showFirst,
193+ duration: const Duration (milliseconds: 100 ),
194+ );
195+ }
196+ }
197+
198+ class _DefaultAppBar extends ConsumerWidget {
199+ const _DefaultAppBar ();
200+
179201 @override
180202 Widget build (BuildContext context, WidgetRef ref) {
181203 final imagePath =
@@ -209,39 +231,7 @@ class _CAppBar extends ConsumerWidget {
209231 ? const Icon (Icons .list_alt_rounded)
210232 : const Icon (Icons .grid_view),
211233 ),
212- const SizedBox (width: 10 ),
213- SearchAnchor (
214- isFullScreen: true ,
215- builder: (context, controller) {
216- return IconButton (
217- onPressed: () {
218- controller.openView ();
219- },
220- icon: Icon (Icons .search),
221- );
222- },
223- suggestionsBuilder: (context, controller) async {
224- final query = controller.text;
225- if (query.isEmpty) return [];
226-
227- final poems = await locator <Database >().searchPoems (query);
228- return poems.map ((poem) => PoemCard (model: poem));
229- },
230- viewBuilder: (suggestions) => _PoemsLayout (
231- isGrid: isGrid,
232- itemBuilder: (context, index) {
233- final poems = suggestions.toList ();
234-
235- return Padding (
236- padding: ! isGrid
237- ? const EdgeInsets .symmetric (horizontal: 10 , vertical: 5 )
238- : EdgeInsets .zero,
239- child: poems[index],
240- );
241- },
242- itemCount: suggestions.length,
243- ),
244- ),
234+ _SearchIcon (isGrid: isGrid),
245235 const SizedBox (width: 10 ),
246236 GestureDetector (
247237 onTap: () {
@@ -263,13 +253,190 @@ class _CAppBar extends ConsumerWidget {
263253 }
264254}
265255
256+ class _Toolbar extends ConsumerWidget {
257+ const _Toolbar ();
258+
259+ @override
260+ Widget build (BuildContext context, WidgetRef ref) {
261+ final selectedPoems = ref.watch (selectedPoemsProvider);
262+
263+ return Padding (
264+ padding: const EdgeInsets .only (top: 15.0 , left: 5.0 , right: 5.0 ),
265+ child: Row (
266+ children: [
267+ IconButton (
268+ icon: const Icon (Icons .close_rounded),
269+ onPressed: () => ref.read (selectedPoemsProvider.notifier).clear (),
270+ ),
271+ Text (
272+ selectedPoems.length.toString (),
273+ style: Theme .of (context).textTheme.bodyLarge,
274+ ),
275+ const Spacer (),
276+ if (selectedPoems.length == 1 ) ...[
277+ IconButton (
278+ icon: const Icon (Icons .share),
279+ onPressed: () => _shareClicked (context, selectedPoems.first),
280+ ),
281+ IconButton (
282+ icon: const Icon (Icons .remove_red_eye_rounded),
283+ onPressed: () =>
284+ _navigateToReaderScreen (context, selectedPoems.first),
285+ ),
286+ ],
287+ IconButton (
288+ icon: const Icon (Icons .delete),
289+ onPressed: () => _showWarning (context, selectedPoems, ref),
290+ ),
291+ ],
292+ ),
293+ );
294+ }
295+
296+ void _shareClicked (BuildContext context, PoemModel poem) {
297+ showModalBottomSheet <void >(
298+ context: context,
299+ builder: (context) => Consumer (
300+ builder: (context, ref, child) {
301+ final poet =
302+ ref //
303+ .watch (configProvider)
304+ .whenOrNull (data: (data) => data.name);
305+
306+ return ShareOptionList (
307+ onShareAsImage: () => ShareHelper .shareAsImage (
308+ context,
309+ title: poem.title,
310+ poem: poem.poem,
311+ poet: poet ?? "Unknown" ,
312+ ),
313+ onShareAsText: () => ShareHelper .shareAsText (
314+ title: poem.title,
315+ poem: poem.poem,
316+ poet: poet ?? "Unknown" ,
317+ ),
318+ );
319+ },
320+ ),
321+ );
322+ }
323+
324+ void _navigateToReaderScreen (BuildContext context, PoemModel poem) {
325+ Navigator .push <void >(
326+ context,
327+ MaterialPageRoute (builder: (_) => ReaderScreen (model: poem)),
328+ );
329+ }
330+
331+ void _showWarning (
332+ BuildContext context,
333+ List <PoemModel > selectedPoems,
334+ WidgetRef ref,
335+ ) {
336+ showDialog <void >(
337+ context: context,
338+ builder: (_) => AlertDialog (
339+ title: const Text ("Do you really want to delete it?" ),
340+ content: const Text (
341+ "There would be no other way to get back you art."
342+ " Are you really sure?" ,
343+ ),
344+ actions: [
345+ TextButton (
346+ onPressed: () => _delete (context, selectedPoems, ref),
347+ child: const Text ("Yes" ),
348+ ),
349+ FilledButton (
350+ onPressed: () => Navigator .pop (context),
351+ child: const Text ("No" ),
352+ ),
353+ ],
354+ ),
355+ );
356+ }
357+
358+ Future <void > _delete (
359+ BuildContext context,
360+ List <PoemModel > selectedPoems,
361+ WidgetRef ref,
362+ ) async {
363+ final navigator = Navigator .of (context);
364+ final scaffoldMessenger = ScaffoldMessenger .of (context);
365+
366+ final result = await locator <Database >().deletePoems (
367+ selectedPoems.map ((p) => p.id! ),
368+ );
369+
370+ final String msg = result == 0 ? "Failed to delete" : "Deleted" ;
371+
372+ navigator.pop ();
373+ scaffoldMessenger.showSnackBar (SnackBar (content: Text (msg)));
374+ ref.read (selectedPoemsProvider.notifier).clear ();
375+ }
376+ }
377+
378+ class _SearchIcon extends StatelessWidget {
379+ const _SearchIcon ({required this .isGrid});
380+
381+ final bool isGrid;
382+
383+ @override
384+ Widget build (BuildContext context) {
385+ return SearchAnchor (
386+ isFullScreen: true ,
387+ builder: (context, controller) {
388+ return IconButton (
389+ onPressed: () {
390+ controller.openView ();
391+ },
392+ icon: Icon (Icons .search),
393+ );
394+ },
395+ suggestionsBuilder: (context, controller) async {
396+ final query = controller.text;
397+ if (query.isEmpty) return [];
398+
399+ final poems = await locator <Database >().searchPoems (query);
400+
401+ return poems.map (
402+ (poem) => PoemCard (
403+ model: poem,
404+ onPressed: () {
405+ Navigator .push <void >(
406+ context,
407+ MaterialPageRoute (builder: (_) => WritingScreen (model: poem)),
408+ );
409+ },
410+ ),
411+ );
412+ },
413+ viewBuilder: (suggestions) => _PoemsLayout (
414+ isGrid: isGrid,
415+ itemBuilder: (context, index) {
416+ final poems = suggestions.toList ();
417+
418+ return Padding (
419+ padding: ! isGrid
420+ ? const EdgeInsets .symmetric (horizontal: 10 , vertical: 5 )
421+ : EdgeInsets .zero,
422+ child: poems[index],
423+ );
424+ },
425+ itemCount: suggestions.length,
426+ ),
427+ );
428+ }
429+ }
430+
266431class _CBody extends ConsumerWidget {
267432 const _CBody ();
268433
269434 @override
270435 Widget build (BuildContext context, WidgetRef ref) {
271436 final isGrid = ref.watch (listGridProvider);
272437 final poems = ref.watch (streamPoemProvider);
438+ final selectedPoems = ref.watch (selectedPoemsProvider);
439+ final multiSelectedEnabled = ref.watch (multiSelectEnabledProvider);
273440
274441 return poems.when (
275442 data: (poems) {
@@ -298,13 +465,32 @@ class _CBody extends ConsumerWidget {
298465 itemCount: poems.length,
299466 itemBuilder: (context, index) {
300467 final poem = poems[index];
468+ final isSelected =
469+ selectedPoems.indexWhere ((t) => t.id == poem.id) != - 1 ;
470+
301471 return Padding (
302472 padding: ! isGrid
303473 ? const EdgeInsets .symmetric (horizontal: 10 , vertical: 5 )
304474 : EdgeInsets .zero,
305475 child: PoemCard (
306476 model: poem,
477+ isSelected: isSelected,
307478 key: ValueKey ("${poem .lastEdit }-${poem .id }" ),
479+ onPressed: () {
480+ if (multiSelectedEnabled) {
481+ ref.read (selectedPoemsProvider.notifier).toggle (poem);
482+ return ;
483+ }
484+ Navigator .push <void >(
485+ context,
486+ MaterialPageRoute (
487+ builder: (_) => WritingScreen (model: poem),
488+ ),
489+ );
490+ },
491+ onLongPress: () {
492+ ref.read (selectedPoemsProvider.notifier).toggle (poem);
493+ },
308494 ),
309495 );
310496 },
0 commit comments