[mob][photos] feat: enhance CollectionsFlexiGridViewWidget with selection mode and album creation options
This commit is contained in:
@@ -1,11 +1,22 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import "package:flutter/services.dart";
|
||||
import "package:logging/logging.dart";
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import 'package:photos/models/collection/collection.dart';
|
||||
import "package:photos/models/collection/collection_items.dart";
|
||||
import "package:photos/services/collections_service.dart";
|
||||
import "package:photos/ui/collections/album/list_item.dart";
|
||||
import "package:photos/ui/collections/album/new_list_item.dart";
|
||||
import "package:photos/ui/collections/album/new_row_item.dart";
|
||||
import "package:photos/ui/collections/album/row_item.dart";
|
||||
import "package:photos/ui/collections/new_album_icon.dart";
|
||||
import "package:photos/ui/viewer/gallery/collection_page.dart";
|
||||
import "package:photos/utils/dialog_util.dart";
|
||||
import "package:photos/utils/local_settings.dart";
|
||||
import "package:photos/utils/navigation_util.dart";
|
||||
|
||||
class CollectionsFlexiGridViewWidget extends StatelessWidget {
|
||||
class CollectionsFlexiGridViewWidget extends StatefulWidget {
|
||||
/*
|
||||
Aspect ratio 1:1 Max width 224 Fixed gap 8
|
||||
Width changes dynamically with screen width such that we can fit 2 in one row.
|
||||
@@ -24,59 +35,125 @@ class CollectionsFlexiGridViewWidget extends StatelessWidget {
|
||||
final bool shrinkWrap;
|
||||
final String tag;
|
||||
|
||||
final AlbumViewType albumViewType;
|
||||
final bool enableSelectionMode;
|
||||
final bool shouldShowCreateAlbum;
|
||||
|
||||
const CollectionsFlexiGridViewWidget(
|
||||
this.collections, {
|
||||
this.displayLimitCount = 9,
|
||||
this.shrinkWrap = false,
|
||||
this.tag = "",
|
||||
this.enableSelectionMode = false,
|
||||
super.key,
|
||||
this.albumViewType = AlbumViewType.grid,
|
||||
this.shouldShowCreateAlbum = false,
|
||||
});
|
||||
|
||||
@override
|
||||
State<CollectionsFlexiGridViewWidget> createState() =>
|
||||
_CollectionsFlexiGridViewWidgetState();
|
||||
}
|
||||
|
||||
class _CollectionsFlexiGridViewWidgetState
|
||||
extends State<CollectionsFlexiGridViewWidget> {
|
||||
List<Collection> selectedAlbums = [];
|
||||
bool isAnyAlbumSelected = false;
|
||||
|
||||
Future<void> _toggleAlbumSelection(Collection c) async {
|
||||
if (selectedAlbums.contains(c)) {
|
||||
selectedAlbums.remove(c);
|
||||
} else {
|
||||
selectedAlbums.isEmpty ? await HapticFeedback.vibrate() : null;
|
||||
selectedAlbums.add(c);
|
||||
}
|
||||
setState(() {
|
||||
isAnyAlbumSelected = selectedAlbums.isNotEmpty;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _navigateToCollectionPage(Collection c) async {
|
||||
final thumbnail = await CollectionsService.instance.getCover(c);
|
||||
// ignore: unawaited_futures
|
||||
routeToPage(
|
||||
context,
|
||||
CollectionPage(
|
||||
CollectionWithThumbnail(c, thumbnail),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget.albumViewType == AlbumViewType.grid
|
||||
? _buildGridView(context, const ValueKey("grid_view"))
|
||||
: _buildListView(context, const ValueKey("list_view"));
|
||||
}
|
||||
|
||||
Widget _buildGridView(BuildContext context, Key key) {
|
||||
final double screenWidth = MediaQuery.of(context).size.width;
|
||||
final int albumsCountInOneRow = max(screenWidth ~/ maxThumbnailWidth, 3);
|
||||
final double gapBetweenAlbums =
|
||||
(albumsCountInOneRow - 1) * fixedGapBetweenAlbum;
|
||||
final int albumsCountInOneRow =
|
||||
max(screenWidth ~/ CollectionsFlexiGridViewWidget.maxThumbnailWidth, 3);
|
||||
final double gapBetweenAlbums = (albumsCountInOneRow - 1) *
|
||||
CollectionsFlexiGridViewWidget.fixedGapBetweenAlbum;
|
||||
// gapOnSizeOfAlbums will be
|
||||
final double gapOnSizeOfAlbums = minGapForHorizontalPadding +
|
||||
(screenWidth - gapBetweenAlbums - (2 * minGapForHorizontalPadding)) %
|
||||
albumsCountInOneRow;
|
||||
final double gapOnSizeOfAlbums =
|
||||
CollectionsFlexiGridViewWidget.minGapForHorizontalPadding +
|
||||
(screenWidth -
|
||||
gapBetweenAlbums -
|
||||
(2 *
|
||||
CollectionsFlexiGridViewWidget
|
||||
.minGapForHorizontalPadding)) %
|
||||
albumsCountInOneRow;
|
||||
|
||||
final double sideOfThumbnail =
|
||||
(screenWidth - gapOnSizeOfAlbums - gapBetweenAlbums) /
|
||||
albumsCountInOneRow;
|
||||
final List<Widget> displayItems = [];
|
||||
|
||||
if (tag.isEmpty) {
|
||||
displayItems.add(
|
||||
NewAlbumIcon(
|
||||
final List<Widget> gridItems = [];
|
||||
if (widget.shouldShowCreateAlbum) {
|
||||
gridItems.add(
|
||||
NewAlbumRowItemWidget(
|
||||
height: sideOfThumbnail,
|
||||
width: sideOfThumbnail,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (collections != null && collections!.isNotEmpty) {
|
||||
for (int i = 0; i < collections!.length; i++) {
|
||||
displayItems.add(
|
||||
if (widget.collections != null && widget.collections!.isNotEmpty) {
|
||||
for (int i = 0; i < widget.collections!.length; i++) {
|
||||
gridItems.add(
|
||||
AlbumRowItemWidget(
|
||||
collections![i],
|
||||
widget.collections![i],
|
||||
sideOfThumbnail,
|
||||
tag: tag,
|
||||
tag: widget.tag,
|
||||
selectedAlbums: selectedAlbums,
|
||||
onTapCallback: (c) {
|
||||
isAnyAlbumSelected
|
||||
? _toggleAlbumSelection(c)
|
||||
: _navigateToCollectionPage(c);
|
||||
},
|
||||
onLongPressCallback: widget.enableSelectionMode
|
||||
? (c) {
|
||||
isAnyAlbumSelected
|
||||
? _navigateToCollectionPage(c)
|
||||
: _toggleAlbumSelection(c);
|
||||
}
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return SliverPadding(
|
||||
key: key,
|
||||
padding: const EdgeInsets.all(8),
|
||||
sliver: SliverGrid(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return displayItems[index];
|
||||
return gridItems[index];
|
||||
},
|
||||
childCount: min(displayItems.length, displayLimitCount),
|
||||
childCount: min(gridItems.length, widget.displayLimitCount),
|
||||
),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: albumsCountInOneRow,
|
||||
@@ -87,4 +164,91 @@ class CollectionsFlexiGridViewWidget extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildListView(BuildContext context, Key key) {
|
||||
final List<Widget> listItems = [];
|
||||
|
||||
if (widget.shouldShowCreateAlbum) {
|
||||
listItems.add(
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
final result = await showTextInputDialog(
|
||||
context,
|
||||
title: S.of(context).newAlbum,
|
||||
submitButtonLabel: S.of(context).create,
|
||||
hintText: S.of(context).enterAlbumName,
|
||||
alwaysShowSuccessState: false,
|
||||
initialValue: "",
|
||||
textCapitalization: TextCapitalization.words,
|
||||
popnavAfterSubmission: false,
|
||||
onSubmit: (String text) async {
|
||||
if (text.trim() == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final Collection c =
|
||||
await CollectionsService.instance.createAlbum(text);
|
||||
// ignore: unawaited_futures
|
||||
await routeToPage(
|
||||
context,
|
||||
CollectionPage(CollectionWithThumbnail(c, null)),
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
} catch (e, s) {
|
||||
Logger("CreateNewAlbumIcon")
|
||||
.severe("Failed to rename album", e, s);
|
||||
rethrow;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (result is Exception) {
|
||||
await showGenericErrorDialog(context: context, error: result);
|
||||
}
|
||||
},
|
||||
child: const NewAlbumListItemWidget(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (widget.collections != null && widget.collections!.isNotEmpty) {
|
||||
for (var collection in widget.collections!) {
|
||||
listItems.add(
|
||||
AlbumListItemWidget(
|
||||
collection,
|
||||
selectedAlbums: selectedAlbums,
|
||||
onTapCallback: (c) {
|
||||
isAnyAlbumSelected
|
||||
? _toggleAlbumSelection(c)
|
||||
: _navigateToCollectionPage(c);
|
||||
},
|
||||
onLongPressCallback: widget.enableSelectionMode
|
||||
? (c) {
|
||||
isAnyAlbumSelected
|
||||
? _navigateToCollectionPage(c)
|
||||
: _toggleAlbumSelection(c);
|
||||
}
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return SliverPadding(
|
||||
key: key,
|
||||
padding: const EdgeInsets.all(8),
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: listItems[index],
|
||||
);
|
||||
},
|
||||
childCount: min(listItems.length, widget.displayLimitCount),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,6 +184,8 @@ class _UserCollectionsTabState extends State<UserCollectionsTab>
|
||||
collections,
|
||||
displayLimitCount: _kOnEnteItemLimitCount,
|
||||
shrinkWrap: true,
|
||||
shouldShowCreateAlbum: true,
|
||||
enableSelectionMode: false,
|
||||
)
|
||||
: const SliverToBoxAdapter(child: EmptyState()),
|
||||
SliverToBoxAdapter(
|
||||
|
||||
Reference in New Issue
Block a user