refactor: simplify layout calculations in flex grid view

This commit is contained in:
Aman Raj Singh Mourya
2025-05-20 14:25:16 +05:30
parent 40ff361af1
commit 539145d38d
2 changed files with 171 additions and 187 deletions

View File

@@ -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) {

View File

@@ -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(