refactor: simplify layout calculations in flex grid view
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import "package:figma_squircle/figma_squircle.dart";
|
||||
import 'package:flutter/material.dart';
|
||||
import "package:intl/intl.dart";
|
||||
import "package:photos/core/configuration.dart";
|
||||
@@ -54,192 +53,187 @@ class AlbumRowItemWidget extends StatelessWidget {
|
||||
: null;
|
||||
|
||||
return GestureDetector(
|
||||
child: ClipSmoothRect(
|
||||
radius: SmoothBorderRadius(cornerRadius: 2.35, cornerSmoothing: 1),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Stack(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(1),
|
||||
child: SizedBox(
|
||||
height: sideOfThumbnail,
|
||||
width: sideOfThumbnail,
|
||||
child: Stack(
|
||||
children: [
|
||||
FutureBuilder<EnteFile?>(
|
||||
future: CollectionsService.instance.getCover(c),
|
||||
builder: (context, snapshot) {
|
||||
EnteFile? thumbnail;
|
||||
if (snapshot.hasData) {
|
||||
thumbnail = snapshot.data!;
|
||||
} else {
|
||||
//Need to use cached thumbnail so that the hero
|
||||
//animation works as expected.
|
||||
thumbnail =
|
||||
CollectionsService.instance.getCoverCache(c);
|
||||
}
|
||||
if (thumbnail != null) {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Stack(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(1),
|
||||
child: SizedBox(
|
||||
height: sideOfThumbnail,
|
||||
width: sideOfThumbnail,
|
||||
child: Stack(
|
||||
children: [
|
||||
FutureBuilder<EnteFile?>(
|
||||
future: CollectionsService.instance.getCover(c),
|
||||
builder: (context, snapshot) {
|
||||
EnteFile? thumbnail;
|
||||
if (snapshot.hasData) {
|
||||
thumbnail = snapshot.data!;
|
||||
} else {
|
||||
//Need to use cached thumbnail so that the hero
|
||||
//animation works as expected.
|
||||
thumbnail =
|
||||
CollectionsService.instance.getCoverCache(c);
|
||||
}
|
||||
if (thumbnail != null) {
|
||||
final bool isSelected =
|
||||
selectedAlbums?.isAlbumSelected(c) ?? false;
|
||||
final String heroTag = tagPrefix + thumbnail.tag;
|
||||
final thumbnailWidget = ThumbnailWidget(
|
||||
thumbnail,
|
||||
shouldShowArchiveStatus: isOwner
|
||||
? c.isArchived()
|
||||
: c.hasShareeArchived(),
|
||||
showFavForAlbumOnly: true,
|
||||
shouldShowSyncStatus: false,
|
||||
shouldShowPinIcon: isOwner && c.isPinned,
|
||||
key: Key(heroTag),
|
||||
);
|
||||
return Hero(
|
||||
tag: heroTag,
|
||||
transitionOnUserGestures: true,
|
||||
child: isSelected
|
||||
? ColorFiltered(
|
||||
colorFilter: ColorFilter.mode(
|
||||
Colors.black.withOpacity(
|
||||
0.4,
|
||||
),
|
||||
BlendMode.darken,
|
||||
),
|
||||
child: thumbnailWidget,
|
||||
)
|
||||
: thumbnailWidget,
|
||||
);
|
||||
} else {
|
||||
return const NoThumbnailWidget();
|
||||
}
|
||||
},
|
||||
),
|
||||
if (isOwner && (c.hasSharees || c.hasLink))
|
||||
Hero(
|
||||
tag: tagPrefix + "_sharees",
|
||||
transitionOnUserGestures: true,
|
||||
child: Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: AlbumSharesIcons(
|
||||
padding: const EdgeInsets.only(left: 4, top: 4),
|
||||
sharees: c.getSharees(),
|
||||
type: AvatarType.mini,
|
||||
trailingWidget: linkIcon,
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 5,
|
||||
right: 5,
|
||||
child: Hero(
|
||||
tag: tagPrefix + "_album_selection",
|
||||
transitionOnUserGestures: true,
|
||||
child: ListenableBuilder(
|
||||
listenable: selectedAlbums ?? ValueNotifier(false),
|
||||
builder: (context, _) {
|
||||
final bool isSelected =
|
||||
selectedAlbums?.isAlbumSelected(c) ?? false;
|
||||
final String heroTag = tagPrefix + thumbnail.tag;
|
||||
final thumbnailWidget = ThumbnailWidget(
|
||||
thumbnail,
|
||||
shouldShowArchiveStatus: isOwner
|
||||
? c.isArchived()
|
||||
: c.hasShareeArchived(),
|
||||
showFavForAlbumOnly: true,
|
||||
shouldShowSyncStatus: false,
|
||||
shouldShowPinIcon: isOwner && c.isPinned,
|
||||
key: Key(heroTag),
|
||||
);
|
||||
return Hero(
|
||||
tag: heroTag,
|
||||
transitionOnUserGestures: true,
|
||||
return AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
switchInCurve: Curves.easeOut,
|
||||
switchOutCurve: Curves.easeIn,
|
||||
child: isSelected
|
||||
? ColorFiltered(
|
||||
colorFilter: ColorFilter.mode(
|
||||
Colors.black.withOpacity(
|
||||
0.4,
|
||||
),
|
||||
BlendMode.darken,
|
||||
),
|
||||
child: thumbnailWidget,
|
||||
? const Icon(
|
||||
Icons.check_circle_rounded,
|
||||
color: Colors.white,
|
||||
size: 22,
|
||||
)
|
||||
: thumbnailWidget,
|
||||
: null,
|
||||
);
|
||||
} else {
|
||||
return const NoThumbnailWidget();
|
||||
}
|
||||
},
|
||||
),
|
||||
if (isOwner && (c.hasSharees || c.hasLink))
|
||||
Hero(
|
||||
tag: tagPrefix + "_sharees",
|
||||
transitionOnUserGestures: true,
|
||||
child: Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: AlbumSharesIcons(
|
||||
padding: const EdgeInsets.only(left: 4, top: 4),
|
||||
sharees: c.getSharees(),
|
||||
type: AvatarType.mini,
|
||||
trailingWidget: linkIcon,
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
Positioned(
|
||||
top: 5,
|
||||
right: 5,
|
||||
),
|
||||
),
|
||||
if (!isOwner)
|
||||
Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: Hero(
|
||||
tag: tagPrefix + "_album_selection",
|
||||
tag: tagPrefix + "_owner_other",
|
||||
transitionOnUserGestures: true,
|
||||
child: ListenableBuilder(
|
||||
listenable:
|
||||
selectedAlbums ?? ValueNotifier(false),
|
||||
builder: (context, _) {
|
||||
final bool isSelected =
|
||||
selectedAlbums?.isAlbumSelected(c) ?? false;
|
||||
return AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
switchInCurve: Curves.easeOut,
|
||||
switchOutCurve: Curves.easeIn,
|
||||
child: isSelected
|
||||
? const Icon(
|
||||
Icons.check_circle_rounded,
|
||||
color: Colors.white,
|
||||
size: 22,
|
||||
)
|
||||
: null,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!isOwner)
|
||||
Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: Hero(
|
||||
tag: tagPrefix + "_owner_other",
|
||||
transitionOnUserGestures: true,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(right: 4, bottom: 4),
|
||||
child: UserAvatarWidget(
|
||||
c.owner,
|
||||
thumbnailView: true,
|
||||
),
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(right: 4, bottom: 4),
|
||||
child: UserAvatarWidget(
|
||||
c.owner,
|
||||
thumbnailView: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Hero(
|
||||
tag: tagPrefix + "_title",
|
||||
transitionOnUserGestures: true,
|
||||
child: SizedBox(
|
||||
width: sideOfThumbnail,
|
||||
child: FutureBuilder<int>(
|
||||
future: showFileCount
|
||||
? CollectionsService.instance.getFileCount(c)
|
||||
: Future.value(0),
|
||||
builder: (context, snapshot) {
|
||||
int? cachedCount;
|
||||
if (showFileCount) {
|
||||
if (snapshot.hasData) {
|
||||
cachedCount = snapshot.data;
|
||||
} else {
|
||||
//Need to use cached count so that the hero
|
||||
//animation works as expected without flickering.
|
||||
cachedCount =
|
||||
CollectionsService.instance.getCachedFileCount(c);
|
||||
}
|
||||
}
|
||||
if (cachedCount != null && cachedCount > 0) {
|
||||
final String textCount =
|
||||
NumberFormat().format(cachedCount);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: sideOfThumbnail,
|
||||
),
|
||||
child: Text(
|
||||
c.displayName,
|
||||
style: enteTextTheme.small,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
style: enteTextTheme.tinyMuted,
|
||||
children: [
|
||||
TextSpan(text: textCount),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Hero(
|
||||
tag: tagPrefix + "_title",
|
||||
transitionOnUserGestures: true,
|
||||
child: SizedBox(
|
||||
width: sideOfThumbnail,
|
||||
child: FutureBuilder<int>(
|
||||
future: showFileCount
|
||||
? CollectionsService.instance.getFileCount(c)
|
||||
: Future.value(0),
|
||||
builder: (context, snapshot) {
|
||||
int? cachedCount;
|
||||
if (showFileCount) {
|
||||
if (snapshot.hasData) {
|
||||
cachedCount = snapshot.data;
|
||||
} else {
|
||||
return Text(
|
||||
c.displayName,
|
||||
style: enteTextTheme.small,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
);
|
||||
//Need to use cached count so that the hero
|
||||
//animation works as expected without flickering.
|
||||
cachedCount =
|
||||
CollectionsService.instance.getCachedFileCount(c);
|
||||
}
|
||||
},
|
||||
),
|
||||
}
|
||||
if (cachedCount != null && cachedCount > 0) {
|
||||
final String textCount = NumberFormat().format(cachedCount);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: sideOfThumbnail,
|
||||
),
|
||||
child: Text(
|
||||
c.displayName,
|
||||
style: enteTextTheme.small,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
style: enteTextTheme.tinyMuted,
|
||||
children: [
|
||||
TextSpan(text: textCount),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return Text(
|
||||
c.displayName,
|
||||
style: enteTextTheme.small,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: () async {
|
||||
if (onTapCallback != null) {
|
||||
|
||||
@@ -28,7 +28,6 @@ class CollectionsFlexiGridViewWidget extends StatefulWidget {
|
||||
*/
|
||||
static const maxThumbnailWidth = 224.0;
|
||||
static const fixedGapBetweenAlbum = 4.0;
|
||||
static const minGapForHorizontalPadding = 8.0;
|
||||
static const collectionItemsToPreload = 20;
|
||||
static const silverHorizontalPadding = 16.0;
|
||||
final List<Collection>? collections;
|
||||
@@ -120,18 +119,8 @@ class _CollectionsFlexiGridViewWidgetState
|
||||
max(screenWidth ~/ CollectionsFlexiGridViewWidget.maxThumbnailWidth, 3);
|
||||
final double gapBetweenAlbums = (albumsCountInOneRow - 1) *
|
||||
CollectionsFlexiGridViewWidget.fixedGapBetweenAlbum;
|
||||
// gapOnSizeOfAlbums will be
|
||||
final double gapOnSizeOfAlbums =
|
||||
CollectionsFlexiGridViewWidget.minGapForHorizontalPadding +
|
||||
(screenWidth -
|
||||
gapBetweenAlbums -
|
||||
(2 *
|
||||
CollectionsFlexiGridViewWidget
|
||||
.minGapForHorizontalPadding)) %
|
||||
albumsCountInOneRow;
|
||||
|
||||
final double sideOfThumbnail = (screenWidth -
|
||||
gapOnSizeOfAlbums -
|
||||
gapBetweenAlbums -
|
||||
CollectionsFlexiGridViewWidget.silverHorizontalPadding) /
|
||||
albumsCountInOneRow;
|
||||
@@ -145,8 +134,8 @@ class _CollectionsFlexiGridViewWidgetState
|
||||
key: key,
|
||||
padding: EdgeInsets.only(
|
||||
top: 8,
|
||||
left: 8,
|
||||
right: 8,
|
||||
left: CollectionsFlexiGridViewWidget.silverHorizontalPadding / 2,
|
||||
right: CollectionsFlexiGridViewWidget.silverHorizontalPadding / 2,
|
||||
bottom: widget.scrollBottomSafeArea,
|
||||
),
|
||||
sliver: SliverGrid(
|
||||
@@ -184,8 +173,9 @@ class _CollectionsFlexiGridViewWidgetState
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: albumsCountInOneRow,
|
||||
mainAxisSpacing: 2,
|
||||
crossAxisSpacing: gapBetweenAlbums,
|
||||
childAspectRatio: sideOfThumbnail / (sideOfThumbnail + 56),
|
||||
crossAxisSpacing:
|
||||
CollectionsFlexiGridViewWidget.fixedGapBetweenAlbum * 2,
|
||||
childAspectRatio: sideOfThumbnail / (sideOfThumbnail + 46),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -202,8 +192,8 @@ class _CollectionsFlexiGridViewWidgetState
|
||||
key: key,
|
||||
padding: EdgeInsets.only(
|
||||
top: 8,
|
||||
left: 8,
|
||||
right: 8,
|
||||
left: CollectionsFlexiGridViewWidget.silverHorizontalPadding / 2,
|
||||
right: CollectionsFlexiGridViewWidget.silverHorizontalPadding / 2,
|
||||
bottom: widget.scrollBottomSafeArea,
|
||||
),
|
||||
sliver: SliverList(
|
||||
|
||||
Reference in New Issue
Block a user