improve bottom sheet logic
This commit is contained in:
@@ -115,44 +115,44 @@ class BrowserScreen extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
if (next != null) {
|
||||
bool dismissOnThreshold(
|
||||
DraggableScrollableNotification notification,
|
||||
) {
|
||||
if (!context.mounted) return false;
|
||||
final relativeSafeArea = MediaQuery.of(context).relativeSafeArea();
|
||||
|
||||
if (notification.extent <= 0.1) {
|
||||
ref.read(bottomSheetControllerProvider.notifier).dismiss();
|
||||
return true;
|
||||
} else {
|
||||
ref
|
||||
.read(bottomSheetExtendProvider.notifier)
|
||||
.add(notification.extent);
|
||||
sheetController.value = state.showBottomSheet((context) {
|
||||
bool dismissOnThreshold(
|
||||
DraggableScrollableNotification notification,
|
||||
) {
|
||||
if (notification.extent <= 0.1) {
|
||||
ref.read(bottomSheetControllerProvider.notifier).dismiss();
|
||||
return true;
|
||||
} else {
|
||||
ref
|
||||
.read(bottomSheetExtendProvider.notifier)
|
||||
.add(notification.extent);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
final sheet = switch (next) {
|
||||
ViewTabsSheet() =>
|
||||
NotificationListener<DraggableScrollableNotification>(
|
||||
key: ValueKey(next),
|
||||
onNotification: dismissOnThreshold,
|
||||
child: _ViewTabsSheet(
|
||||
maxChildSize: MediaQuery.of(context).relativeSafeArea(),
|
||||
final sheet = switch (next) {
|
||||
ViewTabsSheet() =>
|
||||
NotificationListener<DraggableScrollableNotification>(
|
||||
key: ValueKey(next),
|
||||
onNotification: dismissOnThreshold,
|
||||
child: _ViewTabsSheet(maxChildSize: relativeSafeArea),
|
||||
),
|
||||
),
|
||||
final EditUrlSheet parameter =>
|
||||
NotificationListener<DraggableScrollableNotification>(
|
||||
key: ValueKey(parameter),
|
||||
onNotification: dismissOnThreshold,
|
||||
child: _ViewUrlSheet(
|
||||
initialTabState: parameter.tabState,
|
||||
maxChildSize: MediaQuery.of(context).relativeSafeArea(),
|
||||
final EditUrlSheet parameter =>
|
||||
NotificationListener<DraggableScrollableNotification>(
|
||||
key: ValueKey(parameter),
|
||||
onNotification: dismissOnThreshold,
|
||||
child: _ViewUrlSheet(
|
||||
initialTabState: parameter.tabState,
|
||||
maxChildSize: relativeSafeArea,
|
||||
),
|
||||
),
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
sheetController.value = state.showBottomSheet((context) => sheet);
|
||||
return sheet;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
+112
-5
@@ -21,26 +21,133 @@ import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DraggableScrollableHeader extends StatelessWidget {
|
||||
class DraggableScrollableHeader extends StatefulWidget {
|
||||
final DraggableScrollableController controller;
|
||||
final Widget child;
|
||||
|
||||
// Velocity control parameters
|
||||
final double velocitySensitivity;
|
||||
final double maxVelocity;
|
||||
final double minVelocity;
|
||||
final Duration animationDuration;
|
||||
final Curve animationCurve;
|
||||
final double dragSensitivity;
|
||||
|
||||
const DraggableScrollableHeader({
|
||||
super.key,
|
||||
required this.controller,
|
||||
required this.child,
|
||||
this.velocitySensitivity = 0.35, // Higher = more sensitive to velocity
|
||||
this.maxVelocity = 2500.0, // Maximum velocity to consider
|
||||
this.minVelocity = 150.0, // Minimum velocity to trigger momentum
|
||||
this.animationDuration = const Duration(milliseconds: 300),
|
||||
this.animationCurve = Curves.decelerate,
|
||||
this.dragSensitivity = 1.0, // Controls drag responsiveness
|
||||
});
|
||||
|
||||
@override
|
||||
State<DraggableScrollableHeader> createState() =>
|
||||
_DraggableScrollableHeaderState();
|
||||
}
|
||||
|
||||
class _DraggableScrollableHeaderState extends State<DraggableScrollableHeader>
|
||||
with TickerProviderStateMixin {
|
||||
late AnimationController _animationController;
|
||||
late Animation<double>? _animation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_animationController = AnimationController(
|
||||
vsync: this,
|
||||
duration: widget.animationDuration,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_animationController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _animateToPosition(
|
||||
double targetSize, {
|
||||
Duration? customDuration,
|
||||
}) async {
|
||||
if (customDuration != null) {
|
||||
_animationController.duration = customDuration;
|
||||
} else {
|
||||
_animationController.duration = widget.animationDuration;
|
||||
}
|
||||
|
||||
_animation = Tween<double>(begin: widget.controller.size, end: targetSize)
|
||||
.animate(
|
||||
CurvedAnimation(
|
||||
parent: _animationController,
|
||||
curve: widget.animationCurve,
|
||||
),
|
||||
);
|
||||
|
||||
_animationController.reset();
|
||||
await _animationController.forward();
|
||||
|
||||
_animation!.addListener(() {
|
||||
widget.controller.jumpTo(_animation!.value);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onVerticalDragUpdate: (details) {
|
||||
// Use the DraggableScrollableSheet's controller
|
||||
controller.jumpTo(
|
||||
min(1, controller.pixelsToSize(controller.pixels - details.delta.dy)),
|
||||
_animationController.stop();
|
||||
|
||||
// Apply drag sensitivity
|
||||
final adjustedDelta = details.delta.dy * widget.dragSensitivity;
|
||||
|
||||
widget.controller.jumpTo(
|
||||
min(
|
||||
1,
|
||||
widget.controller.pixelsToSize(
|
||||
widget.controller.pixels - adjustedDelta,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: child,
|
||||
onVerticalDragEnd: (details) async {
|
||||
final velocity = details.primaryVelocity ?? 0;
|
||||
final currentSize = widget.controller.size;
|
||||
|
||||
// Clamp velocity to defined range
|
||||
final clampedVelocity = velocity.clamp(
|
||||
-widget.maxVelocity,
|
||||
widget.maxVelocity,
|
||||
);
|
||||
|
||||
// Only apply momentum if velocity exceeds minimum threshold
|
||||
if (clampedVelocity.abs() < widget.minVelocity) {
|
||||
return; // No momentum, stay at current position
|
||||
}
|
||||
|
||||
// Calculate momentum-based target with sensitivity control
|
||||
final velocityFactor =
|
||||
clampedVelocity / 1000 * widget.velocitySensitivity;
|
||||
double targetSize = currentSize - velocityFactor;
|
||||
|
||||
// Clamp to valid range
|
||||
targetSize = targetSize.clamp(0.0, 1.0);
|
||||
|
||||
// Calculate animation duration based on velocity (faster velocity = longer animation)
|
||||
final velocityRatio = clampedVelocity.abs() / widget.maxVelocity;
|
||||
final dynamicDuration = Duration(
|
||||
milliseconds:
|
||||
(widget.animationDuration.inMilliseconds * (0.5 + velocityRatio))
|
||||
.round(),
|
||||
);
|
||||
|
||||
await _animateToPosition(targetSize, customDuration: dynamicDuration);
|
||||
},
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+15
-13
@@ -96,18 +96,17 @@ class ViewTabSheetWidget extends HookConsumerWidget {
|
||||
final totalHeight =
|
||||
headerBox.size.height + textBox.size.height + kToolbarHeight;
|
||||
|
||||
final relative = totalHeight / MediaQuery.of(context).size.height;
|
||||
final relative = (totalHeight / MediaQuery.of(context).size.height)
|
||||
.clamp(0.0, 1.0);
|
||||
|
||||
if (relative >= 0 && relative <= 1) {
|
||||
if (draggableScrollableController.size < relative &&
|
||||
relative > scrolledTo.value) {
|
||||
await draggableScrollableController.animateTo(
|
||||
relative,
|
||||
duration: const Duration(milliseconds: 150),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
scrolledTo.value = relative;
|
||||
}
|
||||
if (draggableScrollableController.size < relative &&
|
||||
relative > scrolledTo.value) {
|
||||
await draggableScrollableController.animateTo(
|
||||
relative,
|
||||
duration: const Duration(milliseconds: 150),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
scrolledTo.value = relative;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,10 +124,13 @@ class ViewTabSheetWidget extends HookConsumerWidget {
|
||||
MediaQuery.of(context).size.height) -
|
||||
bottomInsets.value;
|
||||
|
||||
draggableScrollableController.jumpTo(
|
||||
draggableScrollableController.size + diff,
|
||||
final jumpValue = (draggableScrollableController.size + diff).clamp(
|
||||
0.0,
|
||||
1.0,
|
||||
);
|
||||
|
||||
draggableScrollableController.jumpTo(jumpValue);
|
||||
|
||||
bottomInsets.value += diff;
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user