highlight thumbnail after scaling
This commit is contained in:
parent
e9d12ed3f3
commit
c0e909937d
6 changed files with 105 additions and 27 deletions
|
@ -16,6 +16,7 @@ class CollectionLens with ChangeNotifier, CollectionActivityMixin, CollectionSel
|
|||
GroupFactor groupFactor;
|
||||
SortFactor sortFactor;
|
||||
final AChangeNotifier filterChangeNotifier = AChangeNotifier();
|
||||
final StreamController<ImageEntry> _highlightController = StreamController.broadcast();
|
||||
|
||||
List<ImageEntry> _filteredEntries;
|
||||
List<StreamSubscription> _subscriptions = [];
|
||||
|
@ -74,6 +75,10 @@ class CollectionLens with ChangeNotifier, CollectionActivityMixin, CollectionSel
|
|||
return _sortedEntries;
|
||||
}
|
||||
|
||||
Stream<ImageEntry> get highlightStream => _highlightController.stream;
|
||||
|
||||
void highlight(ImageEntry entry) => _highlightController.add(entry);
|
||||
|
||||
bool get showHeaders {
|
||||
if (sortFactor == SortFactor.size) return false;
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ class GridScaleGestureDetector extends StatefulWidget {
|
|||
final ValueNotifier<double> extentNotifier;
|
||||
final Size mqSize;
|
||||
final double mqHorizontalPadding;
|
||||
final void Function(ImageEntry entry) onScaled;
|
||||
final Widget child;
|
||||
|
||||
const GridScaleGestureDetector({
|
||||
|
@ -25,6 +26,7 @@ class GridScaleGestureDetector extends StatefulWidget {
|
|||
@required this.extentNotifier,
|
||||
@required this.mqSize,
|
||||
@required this.mqHorizontalPadding,
|
||||
this.onScaled,
|
||||
@required this.child,
|
||||
});
|
||||
|
||||
|
@ -112,7 +114,11 @@ class _GridScaleGestureDetectorState extends State<GridScaleGestureDetector> {
|
|||
} else {
|
||||
// scroll to show the focal point thumbnail at its new position
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_scrollToEntry(_metadata.entry);
|
||||
final entry = _metadata.entry;
|
||||
_scrollToEntry(entry);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
widget.onScaled?.call(entry);
|
||||
});
|
||||
_applyingScale = false;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:aves/model/collection_lens.dart';
|
||||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/widgets/album/thumbnail/overlay.dart';
|
||||
import 'package:aves/widgets/album/thumbnail/raster.dart';
|
||||
|
@ -7,39 +8,42 @@ import 'package:flutter/material.dart';
|
|||
class DecoratedThumbnail extends StatelessWidget {
|
||||
final ImageEntry entry;
|
||||
final double extent;
|
||||
final Object heroTag;
|
||||
final CollectionLens collection;
|
||||
final ValueNotifier<bool> isScrollingNotifier;
|
||||
final bool showOverlay;
|
||||
final Object heroTag;
|
||||
|
||||
static final Color borderColor = Colors.grey.shade700;
|
||||
static const double borderWidth = .5;
|
||||
|
||||
const DecoratedThumbnail({
|
||||
DecoratedThumbnail({
|
||||
Key key,
|
||||
@required this.entry,
|
||||
@required this.extent,
|
||||
this.heroTag,
|
||||
this.collection,
|
||||
this.isScrollingNotifier,
|
||||
this.showOverlay = true,
|
||||
}) : super(key: key);
|
||||
}) : heroTag = collection?.heroTag(entry),
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final child = Stack(
|
||||
children: [
|
||||
entry.isSvg
|
||||
? ThumbnailVectorImage(
|
||||
entry: entry,
|
||||
extent: extent,
|
||||
heroTag: heroTag,
|
||||
)
|
||||
: ThumbnailRasterImage(
|
||||
entry: entry,
|
||||
extent: extent,
|
||||
isScrollingNotifier: isScrollingNotifier,
|
||||
heroTag: heroTag,
|
||||
),
|
||||
if (showOverlay)
|
||||
var child = entry.isSvg
|
||||
? ThumbnailVectorImage(
|
||||
entry: entry,
|
||||
extent: extent,
|
||||
heroTag: heroTag,
|
||||
)
|
||||
: ThumbnailRasterImage(
|
||||
entry: entry,
|
||||
extent: extent,
|
||||
isScrollingNotifier: isScrollingNotifier,
|
||||
heroTag: heroTag,
|
||||
);
|
||||
if (showOverlay) {
|
||||
child = Stack(
|
||||
children: [
|
||||
child,
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
|
@ -48,13 +52,17 @@ class DecoratedThumbnail extends StatelessWidget {
|
|||
extent: extent,
|
||||
),
|
||||
),
|
||||
if (showOverlay)
|
||||
ThumbnailSelectionOverlay(
|
||||
entry: entry,
|
||||
extent: extent,
|
||||
),
|
||||
],
|
||||
);
|
||||
ThumbnailHighlightOverlay(
|
||||
highlightedStream: collection.highlightStream.map((highlighted) => highlighted == entry),
|
||||
extent: extent,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:math';
|
|||
|
||||
import 'package:aves/model/collection_lens.dart';
|
||||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/widgets/common/fx/sweeper.dart';
|
||||
import 'package:aves/widgets/common/icons.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
|
@ -103,3 +104,43 @@ class ThumbnailSelectionOverlay extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ThumbnailHighlightOverlay extends StatefulWidget {
|
||||
final double extent;
|
||||
final Stream<bool> highlightedStream;
|
||||
|
||||
const ThumbnailHighlightOverlay({
|
||||
Key key,
|
||||
@required this.extent,
|
||||
@required this.highlightedStream,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_ThumbnailHighlightOverlayState createState() => _ThumbnailHighlightOverlayState();
|
||||
}
|
||||
|
||||
class _ThumbnailHighlightOverlayState extends State<ThumbnailHighlightOverlay> {
|
||||
final ValueNotifier<bool> _highlightedNotifier = ValueNotifier(false);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StreamBuilder<bool>(
|
||||
stream: widget.highlightedStream,
|
||||
builder: (context, snapshot) {
|
||||
_highlightedNotifier.value = snapshot.hasData && snapshot.data;
|
||||
return Sweeper(
|
||||
builder: (context) => Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Theme.of(context).accentColor,
|
||||
width: widget.extent * .1,
|
||||
),
|
||||
),
|
||||
),
|
||||
toggledNotifier: _highlightedNotifier,
|
||||
onSweepEnd: () => _highlightedNotifier.value = false,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ class ThumbnailCollection extends StatelessWidget {
|
|||
extentNotifier: _tileExtentNotifier,
|
||||
mqSize: mqSize,
|
||||
mqHorizontalPadding: mqHorizontalPadding,
|
||||
onScaled: collection.highlight,
|
||||
child: scrollView,
|
||||
);
|
||||
|
||||
|
@ -210,7 +211,6 @@ class _CollectionScrollViewState extends State<CollectionScrollView> {
|
|||
widget.isScrollingNotifier.value = true;
|
||||
_stopScrollMonitoringTimer();
|
||||
_scrollMonitoringTimer = Timer(const Duration(milliseconds: 100), () {
|
||||
debugPrint('$runtimeType _onScrollChange is scrolling false');
|
||||
widget.isScrollingNotifier.value = false;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ class Sweeper extends StatefulWidget {
|
|||
final double sweepAngle;
|
||||
final Curve curve;
|
||||
final ValueNotifier<bool> toggledNotifier;
|
||||
final VoidCallback onSweepEnd;
|
||||
|
||||
const Sweeper({
|
||||
Key key,
|
||||
|
@ -17,6 +18,7 @@ class Sweeper extends StatefulWidget {
|
|||
this.sweepAngle = pi / 4,
|
||||
this.curve = Curves.easeInOutCubic,
|
||||
@required this.toggledNotifier,
|
||||
this.onSweepEnd,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -96,6 +98,9 @@ class _SweeperState extends State<Sweeper> with SingleTickerProviderStateMixin {
|
|||
|
||||
void _onAnimationStatusChange(AnimationStatus status) {
|
||||
setState(() {});
|
||||
if (status == AnimationStatus.completed) {
|
||||
widget.onSweepEnd?.call();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onToggle() async {
|
||||
|
@ -121,10 +126,23 @@ class _SweepClipPath extends CustomClipper<Path> {
|
|||
|
||||
@override
|
||||
Path getClip(Size size) {
|
||||
final width = size.width;
|
||||
final height = size.height;
|
||||
final centerX = width / 2;
|
||||
final centerY = height / 2;
|
||||
final diagonal = sqrt(width * width + height * height);
|
||||
return Path()
|
||||
..moveTo(size.width / 2, size.height / 2)
|
||||
..addArc(Rect.fromLTWH(0, 0, size.width, size.height), startAngle, sweepAngle)
|
||||
..lineTo(size.width / 2, size.height / 2);
|
||||
..moveTo(centerX, centerY)
|
||||
..addArc(
|
||||
Rect.fromCenter(
|
||||
center: Offset(centerX, centerY),
|
||||
width: diagonal,
|
||||
height: diagonal,
|
||||
),
|
||||
startAngle,
|
||||
sweepAngle,
|
||||
)
|
||||
..lineTo(centerX, centerY);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
Loading…
Reference in a new issue