mosaic fixes

This commit is contained in:
Thibault Deckers 2022-10-01 20:14:40 +02:00
parent ebd6f1c84c
commit 5019cc2065
10 changed files with 66 additions and 36 deletions

View file

@ -141,19 +141,22 @@ class _CollectionGridContent extends StatelessWidget {
spacing: tileSpacing,
horizontalPadding: horizontalPadding,
tileExtent: thumbnailExtent,
tileBuilder: (entry) => AnimatedBuilder(
animation: favourites,
builder: (context, child) {
return InteractiveTile(
key: ValueKey(entry.id),
collection: collection,
entry: entry,
thumbnailExtent: thumbnailExtent,
tileLayout: tileLayout,
isScrollingNotifier: _isScrollingNotifier,
);
},
),
tileBuilder: (entry, tileSize) {
final extent = tileSize.shortestSide;
return AnimatedBuilder(
animation: favourites,
builder: (context, child) {
return InteractiveTile(
key: ValueKey(entry.id),
collection: collection,
entry: entry,
thumbnailExtent: extent,
tileLayout: tileLayout,
isScrollingNotifier: _isScrollingNotifier,
);
},
);
},
tileAnimationDelay: tileAnimationDelay,
child: child!,
);

View file

@ -11,8 +11,9 @@ import 'package:flutter/material.dart';
class TransitionImage extends StatefulWidget {
final ImageProvider image;
final double? width, height;
final ValueListenable<double> animation;
final BoxFit thumbnailFit, viewerFit;
final double? width, height;
final bool gaplessPlayback = false;
final Color? background;
@ -20,6 +21,8 @@ class TransitionImage extends StatefulWidget {
super.key,
required this.image,
required this.animation,
required this.thumbnailFit,
required this.viewerFit,
this.width,
this.height,
this.background,
@ -157,6 +160,8 @@ class _TransitionImageState extends State<TransitionImage> {
image: _imageInfo?.image,
scale: _imageInfo?.scale ?? 1.0,
t: t,
thumbnailFit: widget.thumbnailFit,
viewerFit: widget.viewerFit,
background: widget.background,
),
),
@ -166,15 +171,17 @@ class _TransitionImageState extends State<TransitionImage> {
class _TransitionImagePainter extends CustomPainter {
final ui.Image? image;
final double scale;
final double t;
final double scale, t;
final Color? background;
final BoxFit thumbnailFit, viewerFit;
const _TransitionImagePainter({
required this.image,
required this.scale,
required this.t,
this.background,
required this.thumbnailFit,
required this.viewerFit,
required this.background,
});
@override
@ -190,10 +197,10 @@ class _TransitionImagePainter extends CustomPainter {
final inputSize = Size(image!.width.toDouble(), image!.height.toDouble());
final outputSize = rect.size;
final coverSizes = applyBoxFit(BoxFit.cover, inputSize / scale, size);
final containSizes = applyBoxFit(BoxFit.contain, inputSize / scale, size);
final sourceSize = Size.lerp(coverSizes.source, containSizes.source, t)! * scale;
final destinationSize = Size.lerp(coverSizes.destination, containSizes.destination, t)!;
final thumbnailSizes = applyBoxFit(thumbnailFit, inputSize / scale, size);
final viewerSizes = applyBoxFit(viewerFit, inputSize / scale, size);
final sourceSize = Size.lerp(thumbnailSizes.source, viewerSizes.source, t)! * scale;
final destinationSize = Size.lerp(thumbnailSizes.destination, viewerSizes.destination, t)!;
final halfWidthDelta = (outputSize.width - destinationSize.width) / 2.0;
final halfHeightDelta = (outputSize.height - destinationSize.height) / 2.0;

View file

@ -3,6 +3,7 @@ import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/widgets/common/behaviour/eager_scale_gesture_recognizer.dart';
import 'package:aves/widgets/common/grid/sections/fixed/scale_overlay.dart';
import 'package:aves/widgets/common/grid/sections/mosaic/scale_overlay.dart';
import 'package:aves/widgets/common/grid/sections/section_layout_builder.dart';
import 'package:aves/widgets/common/grid/theme.dart';
import 'package:aves/widgets/common/tile_extent_controller.dart';
import 'package:collection/collection.dart';
@ -23,7 +24,7 @@ class GridScaleGestureDetector<T> extends StatefulWidget {
final TileLayout tileLayout;
final double Function(double width) heightForWidth;
final Widget Function(Offset center, Size tileSize, Widget child) gridBuilder;
final Widget Function(T item, Size tileSize) scaledItemBuilder;
final TileBuilder<T> scaledItemBuilder;
final MosaicItemBuilder mosaicItemBuilder;
final Object Function(T item)? highlightItem;
final Widget child;

View file

@ -11,6 +11,7 @@ import 'package:tuple/tuple.dart';
class FixedExtentSectionLayoutBuilder<T> extends SectionLayoutBuilder<T> {
int _currentIndex = 0;
double _currentOffset = 0;
List<Size> _itemSizes;
FixedExtentSectionLayoutBuilder({
required super.sections,
@ -26,7 +27,7 @@ class FixedExtentSectionLayoutBuilder<T> extends SectionLayoutBuilder<T> {
required super.tileHeight,
required super.tileBuilder,
required super.tileAnimationDelay,
});
}) : _itemSizes = List.generate(columnCount, (index) => Size(tileWidth, tileHeight));
@override
SectionedListLayout<T> updateLayouts(BuildContext context) {
@ -93,6 +94,7 @@ class FixedExtentSectionLayoutBuilder<T> extends SectionLayoutBuilder<T> {
),
sectionKey: sectionKey,
headerExtent: headerExtent,
itemSizes: _itemSizes,
animate: animate,
buildGridRow: (children) => FixedExtentGridRow(
width: tileWidth,

View file

@ -113,6 +113,7 @@ class MosaicSectionLayoutBuilder<T> extends SectionLayoutBuilder<T> {
itemIndexRange: () => isHeader ? const Tuple2(0, 0) : Tuple2(row.firstIndex, row.lastIndex + 1),
sectionKey: sectionKey,
headerExtent: headerExtent,
itemSizes: row.itemWidths.map((v) => Size(v, row.height)).toList(),
animate: animate,
buildGridRow: (children) {
return isHeader

View file

@ -3,6 +3,7 @@ import 'package:aves/model/source/section_keys.dart';
import 'package:aves/widgets/common/grid/sections/fixed/section_layout_builder.dart';
import 'package:aves/widgets/common/grid/sections/list_layout.dart';
import 'package:aves/widgets/common/grid/sections/mosaic/section_layout_builder.dart';
import 'package:aves/widgets/common/grid/sections/section_layout_builder.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:provider/provider.dart';
@ -14,7 +15,7 @@ abstract class SectionedListLayoutProvider<T> extends StatelessWidget {
final TileLayout tileLayout;
final int columnCount;
final double spacing, horizontalPadding, tileWidth, tileHeight;
final Widget Function(T item) tileBuilder;
final TileBuilder<T> tileBuilder;
final Duration tileAnimationDelay;
final CoverRatioResolver<T> coverRatioResolver;
final Widget child;

View file

@ -8,6 +8,8 @@ import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
typedef TileBuilder<T> = Widget Function(T item, Size tileSize);
abstract class SectionLayoutBuilder<T> {
final Map<SectionKey, List<T>> sections;
final bool showHeaders;
@ -17,7 +19,7 @@ abstract class SectionLayoutBuilder<T> {
final TileLayout tileLayout;
final int columnCount;
final double spacing, horizontalPadding, tileWidth, tileHeight, bottom;
final Widget Function(T item) tileBuilder;
final TileBuilder<T> tileBuilder;
final Duration tileAnimationDelay;
final bool animate;
@ -55,6 +57,7 @@ abstract class SectionLayoutBuilder<T> {
required Tuple2<int, int> Function() itemIndexRange,
required SectionKey sectionKey,
required double headerExtent,
required List<Size> itemSizes,
required bool animate,
required Widget Function(List<Widget> children) buildGridRow,
}) {
@ -67,13 +70,17 @@ abstract class SectionLayoutBuilder<T> {
final itemMinMax = itemIndexRange();
final minItemIndex = itemMinMax.item1.clamp(0, sectionItemCount);
final maxItemIndex = itemMinMax.item2.clamp(0, sectionItemCount);
final childrenCount = maxItemIndex - minItemIndex;
final children = <Widget>[];
for (var i = minItemIndex; i < maxItemIndex; i++) {
final itemGridIndex = sectionGridIndex + i - minItemIndex;
for (var i = 0; i < childrenCount; i++) {
final item = RepaintBoundary(
child: tileBuilder(section[i]),
child: tileBuilder(section[minItemIndex + i], itemSizes[i]),
);
children.add(animate ? _buildAnimation(context, itemGridIndex, item) : item);
if (animate) {
children.add(_buildAnimation(context, sectionGridIndex + i, item));
} else {
children.add(item);
}
}
return Padding(
padding: EdgeInsets.symmetric(horizontal: horizontalPadding),

View file

@ -281,10 +281,15 @@ class OverlayIcon extends StatelessWidget {
children: [
iconChild,
const SizedBox(width: 2),
Text(
text!,
// consistent with the color used for the icon next to it
style: TextStyle(color: IconTheme.of(context).color),
Flexible(
child: Text(
text!,
// consistent with the color used for the icon next to it
style: TextStyle(color: IconTheme.of(context).color),
softWrap: false,
overflow: TextOverflow.fade,
maxLines: 1,
),
),
],
),

View file

@ -272,6 +272,8 @@ class _ThumbnailImageState extends State<ThumbnailImage> {
return TransitionImage(
image: entry.bestCachedThumbnail,
animation: animation,
thumbnailFit: isMosaic ? BoxFit.contain : BoxFit.cover,
viewerFit: BoxFit.contain,
background: backgroundColor,
);
},

View file

@ -306,11 +306,12 @@ class _FilterGridContent<T extends CollectionFilter> extends StatelessWidget {
horizontalPadding: horizontalPadding,
tileWidth: thumbnailExtent,
tileHeight: tileHeight,
tileBuilder: (gridItem) {
tileBuilder: (gridItem, tileSize) {
final extent = tileSize.shortestSide;
return InteractiveFilterTile(
gridItem: gridItem,
chipExtent: thumbnailExtent,
thumbnailExtent: thumbnailExtent,
chipExtent: extent,
thumbnailExtent: extent,
tileLayout: tileLayout,
banner: _getFilterBanner(context, gridItem.filter),
heroType: heroType,