mirror of
https://github.com/immich-app/immich.git
synced 2026-02-28 09:38:43 +03:00
feat(mobile): inline asset details (#25952)
The existing implementation for showing asset details uses a bottom sheet, and is not in sync with the preview or scroll intent. Other apps use inline details, which is much cleaner and feels better to use.
This commit is contained in:
@@ -32,3 +32,125 @@ class FastClampingScrollPhysics extends ClampingScrollPhysics {
|
||||
damping: 80,
|
||||
);
|
||||
}
|
||||
|
||||
class SnapScrollPhysics extends ScrollPhysics {
|
||||
static const _minFlingVelocity = 700.0;
|
||||
static const minSnapDistance = 30.0;
|
||||
|
||||
static final _spring = SpringDescription.withDampingRatio(mass: .5, stiffness: 300);
|
||||
|
||||
const SnapScrollPhysics({super.parent});
|
||||
|
||||
@override
|
||||
SnapScrollPhysics applyTo(ScrollPhysics? ancestor) {
|
||||
return SnapScrollPhysics(parent: buildParent(ancestor));
|
||||
}
|
||||
|
||||
@override
|
||||
Simulation? createBallisticSimulation(ScrollMetrics position, double velocity) {
|
||||
assert(
|
||||
position is SnapScrollPosition,
|
||||
'SnapScrollPhysics can only be used with Scrollables that use a '
|
||||
'controller whose createScrollPosition returns a SnapScrollPosition',
|
||||
);
|
||||
|
||||
final snapOffset = (position as SnapScrollPosition).snapOffset;
|
||||
if (snapOffset <= 0) {
|
||||
return super.createBallisticSimulation(position, velocity);
|
||||
}
|
||||
|
||||
if (position.pixels >= snapOffset) {
|
||||
final simulation = super.createBallisticSimulation(position, velocity);
|
||||
if (simulation == null || simulation.x(double.infinity) >= snapOffset) {
|
||||
return simulation;
|
||||
}
|
||||
}
|
||||
|
||||
return ScrollSpringSimulation(
|
||||
_spring,
|
||||
position.pixels,
|
||||
target(position, velocity, snapOffset),
|
||||
velocity,
|
||||
tolerance: toleranceFor(position),
|
||||
);
|
||||
}
|
||||
|
||||
static double target(ScrollMetrics position, double velocity, double snapOffset) {
|
||||
if (velocity > _minFlingVelocity) return snapOffset;
|
||||
if (velocity < -_minFlingVelocity) return position.pixels < snapOffset ? 0.0 : snapOffset;
|
||||
return position.pixels < minSnapDistance ? 0.0 : snapOffset;
|
||||
}
|
||||
}
|
||||
|
||||
class SnapScrollPosition extends ScrollPositionWithSingleContext {
|
||||
double snapOffset;
|
||||
|
||||
SnapScrollPosition({this.snapOffset = 0.0, required super.physics, required super.context, super.oldPosition});
|
||||
}
|
||||
|
||||
class ProxyScrollController extends ScrollController {
|
||||
final ScrollController scrollController;
|
||||
|
||||
ProxyScrollController({required this.scrollController});
|
||||
|
||||
SnapScrollPosition get snapPosition => position as SnapScrollPosition;
|
||||
|
||||
@override
|
||||
ScrollPosition createScrollPosition(ScrollPhysics physics, ScrollContext context, ScrollPosition? oldPosition) {
|
||||
return ProxyScrollPosition(
|
||||
scrollController: scrollController,
|
||||
physics: physics,
|
||||
context: context,
|
||||
oldPosition: oldPosition,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class ProxyScrollPosition extends SnapScrollPosition {
|
||||
final ScrollController scrollController;
|
||||
|
||||
ProxyScrollPosition({
|
||||
required this.scrollController,
|
||||
required super.physics,
|
||||
required super.context,
|
||||
super.oldPosition,
|
||||
});
|
||||
|
||||
@override
|
||||
double setPixels(double newPixels) {
|
||||
final overscroll = super.setPixels(newPixels);
|
||||
if (scrollController.hasClients && scrollController.position.pixels != pixels) {
|
||||
scrollController.position.forcePixels(pixels);
|
||||
}
|
||||
return overscroll;
|
||||
}
|
||||
|
||||
@override
|
||||
void forcePixels(double value) {
|
||||
super.forcePixels(value);
|
||||
if (scrollController.hasClients && scrollController.position.pixels != pixels) {
|
||||
scrollController.position.forcePixels(pixels);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
double get maxScrollExtent => scrollController.hasClients && scrollController.position.hasContentDimensions
|
||||
? scrollController.position.maxScrollExtent
|
||||
: super.maxScrollExtent;
|
||||
|
||||
@override
|
||||
double get minScrollExtent => scrollController.hasClients && scrollController.position.hasContentDimensions
|
||||
? scrollController.position.minScrollExtent
|
||||
: super.minScrollExtent;
|
||||
|
||||
@override
|
||||
double get viewportDimension => scrollController.hasClients && scrollController.position.hasViewportDimension
|
||||
? scrollController.position.viewportDimension
|
||||
: super.viewportDimension;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user