feat: 增加侧滑事件

This commit is contained in:
2025-12-02 06:09:25 -08:00
parent 8e27ddeded
commit e82480b937
13 changed files with 669 additions and 250 deletions
+156
View File
@@ -0,0 +1,156 @@
import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'swipe/swipe_controller.dart';
import 'swipe/swipe_config.dart';
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
class HIEdgeSwipeDetector extends StatefulWidget {
final Widget child;
final double reservedEdgeWidth;
final double detectionEdgeWidth;
final double minSwipeDistance;
final double minSwipeVelocity;
final SwipeConfig? config;
const HIEdgeSwipeDetector(
{super.key,
required this.child,
this.reservedEdgeWidth = 0,
this.detectionEdgeWidth = 20,
this.minSwipeDistance = 20,
this.minSwipeVelocity = 300,
this.config});
@override
State<HIEdgeSwipeDetector> createState() => _HIEdgeSwipeDetectorState();
}
class _HIEdgeSwipeDetectorState extends State<HIEdgeSwipeDetector> {
Offset? _start;
Offset? _last;
DateTime? _startTime;
final SwipeController _controller =
Get.put(SwipeController(), permanent: true);
bool _fromLeft = false;
bool _fromRight = false;
@override
Widget build(BuildContext context) {
KRLogUtil.kr_d('HIEdgeSwipeDetector build');
return Stack(
fit: StackFit.expand,
children: [
widget.child,
Positioned(
left: 0,
top: 0,
bottom: 0,
width: widget.detectionEdgeWidth,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onHorizontalDragStart: (details) {
_fromLeft = true;
_fromRight = false;
_start = details.globalPosition;
_startTime = DateTime.now();
KRLogUtil.kr_d('dragStart LEFT x=${details.globalPosition.dx}',
tag: 'HIEdgeSwipeDetector');
},
onHorizontalDragUpdate: (details) {
_last = details.globalPosition;
KRLogUtil.kr_d('dragUpdate LEFT dx=${details.delta.dx}',
tag: 'HIEdgeSwipeDetector');
},
onHorizontalDragEnd: (details) {
if (_start == null || _startTime == null) return;
final durationMs =
DateTime.now().difference(_startTime!).inMilliseconds;
final velocity = details.velocity.pixelsPerSecond.dx.abs();
final distance = _last != null ? (_last!.dx - _start!.dx) : 0.0;
final okByVelocity = velocity >= widget.minSwipeVelocity;
final okByDistance = distance.abs() >= widget.minSwipeDistance;
KRLogUtil.kr_d(
'dragEnd LEFT vel=${details.velocity.pixelsPerSecond.dx} dur=$durationMs dist=$distance okV=$okByVelocity okD=$okByDistance',
tag: 'HIEdgeSwipeDetector');
if (!okByVelocity && !okByDistance) {
_reset();
return;
}
if (distance > 0) {
final cfg = widget.config;
if (cfg == null || !cfg.enableLeft) {
_reset();
return;
}
if (cfg.onLeft != null) {
cfg.onLeft!();
} else {
Get.back();
}
}
_reset();
},
),
),
Positioned(
right: 0,
top: 0,
bottom: 0,
width: widget.detectionEdgeWidth,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onHorizontalDragStart: (details) {
_fromLeft = false;
_fromRight = true;
_start = details.globalPosition;
_startTime = DateTime.now();
KRLogUtil.kr_d('dragStart RIGHT x=${details.globalPosition.dx}',
tag: 'HIEdgeSwipeDetector');
},
onHorizontalDragUpdate: (details) {
_last = details.globalPosition;
KRLogUtil.kr_d('dragUpdate RIGHT dx=${details.delta.dx}',
tag: 'HIEdgeSwipeDetector');
},
onHorizontalDragEnd: (details) {
if (_start == null || _startTime == null) return;
final durationMs =
DateTime.now().difference(_startTime!).inMilliseconds;
final velocity = details.velocity.pixelsPerSecond.dx.abs();
final distance = _last != null ? (_last!.dx - _start!.dx) : 0.0;
final okByVelocity = velocity >= widget.minSwipeVelocity;
final okByDistance = distance.abs() >= widget.minSwipeDistance;
KRLogUtil.kr_d(
'dragEnd RIGHT vel=${details.velocity.pixelsPerSecond.dx} dur=$durationMs dist=$distance okV=$okByVelocity okD=$okByDistance',
tag: 'HIEdgeSwipeDetector');
if (!okByVelocity && !okByDistance) {
_reset();
return;
}
if (distance < 0) {
final cfg = widget.config;
if (cfg == null || !cfg.enableRight) {
_reset();
return;
}
if (cfg.onRight != null) {
cfg.onRight!();
} else {
_controller.openRightMenu();
}
}
_reset();
},
),
),
],
);
}
void _reset() {
_start = null;
_last = null;
_startTime = null;
_fromLeft = false;
_fromRight = false;
}
}
@@ -0,0 +1,6 @@
import 'swipe_config.dart';
abstract class HasSwipeConfig {
SwipeConfig get swipeConfig;
}
+10
View File
@@ -0,0 +1,10 @@
import 'package:flutter/widgets.dart';
class SwipeConfig {
final bool enableLeft;
final bool enableRight;
final VoidCallback? onLeft;
final VoidCallback? onRight;
const SwipeConfig({this.enableLeft = false, this.enableRight = false, this.onLeft, this.onRight});
}
@@ -0,0 +1,61 @@
import 'package:get/get.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get_navigation/src/extension_navigation.dart';
import 'package:kaer_with_panels/app/routes/app_pages.dart';
import 'swipe_config.dart';
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
class SwipeController extends GetxController {
final Rxn<SwipeConfig> currentConfig = Rxn<SwipeConfig>();
final RxInt currentPageIndex = 0.obs;
final RxBool leftMenuOpen = false.obs;
final RxBool rightMenuOpen = false.obs;
void setConfig(SwipeConfig? config) {
currentConfig.value = config;
}
void setPageIndex(int index) {
currentPageIndex.value = index;
}
void closeMenus() {
leftMenuOpen.value = false;
rightMenuOpen.value = false;
}
void openLeftMenu() {
KRLogUtil.kr_d('openLeftMenu', tag: 'SwipeController');
leftMenuOpen.value = true;
Get.toNamed(Routes.HI_MENU);
}
void openRightMenu() {
KRLogUtil.kr_d('openRightMenu', tag: 'SwipeController');
rightMenuOpen.value = true;
Get.toNamed(Routes.KR_SETTING);
}
void triggerLeft() {
final cfg = currentConfig.value;
if (cfg == null || !cfg.enableLeft) return;
KRLogUtil.kr_d('call11 onLeft$cfg', tag: 'SwipeController');
if (cfg.onLeft != null) {
KRLogUtil.kr_d('call onLeft', tag: 'SwipeController');
cfg.onLeft!.call();
return;
}
Get.back();
}
void triggerRight() {
final cfg = currentConfig.value;
if (cfg == null || !cfg.enableRight) return;
if (cfg.onRight != null) {
KRLogUtil.kr_d('call onRight', tag: 'SwipeController');
cfg.onRight!.call();
return;
}
openRightMenu();
}
}
+45
View File
@@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../hi_edge_swipe_detector.dart';
import 'swipe_config.dart';
import 'has_swipe_config.dart';
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
class SwipeWrapper extends StatelessWidget {
final Widget child;
final SwipeConfig config;
const SwipeWrapper({super.key, required this.child, required this.config});
static Widget detect(Widget Function() builder) {
final w = builder();
SwipeConfig effective = const SwipeConfig();
if (w is HasSwipeConfig) {
effective = (w as HasSwipeConfig).swipeConfig;
} else if (GetPlatform.isIOS) {
effective = SwipeConfig(enableLeft: true, onLeft: () => Get.back());
}
final enabled = effective.enableLeft ||
effective.enableRight ||
effective.onLeft != null ||
effective.onRight != null;
KRLogUtil.kr_d(
'SwipeWrapper.detect enabled=$enabled left=${effective.enableLeft} right=${effective.enableRight}',
tag: 'SwipeWrapper');
if (!enabled) return w;
return HIEdgeSwipeDetector(child: w, config: effective);
}
@override
Widget build(BuildContext context) {
final effective = config;
final enabled = effective.enableLeft ||
effective.enableRight ||
effective.onLeft != null ||
effective.onRight != null;
KRLogUtil.kr_d(
'SwipeWrapper.build enabled=$enabled left=${effective.enableLeft} right=${effective.enableRight}',
tag: 'SwipeWrapper');
if (!enabled) return child;
return HIEdgeSwipeDetector(child: child, config: effective);
}
}