簡體   English   中英

在 Flutter 中防止拖動時的小跳躍

[英]Prevent Small Hops in Drag Upon Touch Up/End in Flutter

我已經構建了一個自定義 slider 並且一直在使用GestureDetectoronHorizontalDragUpdate來報告拖動變化,更新 UI 和value

但是,當用戶抬起手指時,有時會出現小的、無意的跳躍/拖動,足以調整 slider 上的值並降低准確性。 我怎樣才能阻止這種情況發生?

我考慮過添加一個小的延遲以防止如果拖動在很短的時間內沒有移動並評估primaryDelta時進行更新,但不確定這是否適合目的或者是否有更常規的通用實踐來防止這種情況。

--

我正在使用的現有拖動邏輯示例。 初始拖動數據來自onHorizontalDragUpdate中的_buildThumb 重建slider時,在LayoutBuilder中計算track size和thumb LayoutBuilder ,然后根據thumb position計算值。

    double valueForPosition({required double min, required double max}) {
    double posIncrements = ((max) / (_divisions));
    double posIncrement = (_thumbPosX / (posIncrements));
    double incrementVal =
        (increment) * (posIncrement + widget.minimumValue).round() +
            (widget.minimumValue - widget.minimumValue.truncate());
    return incrementVal.clamp(widget.minimumValue, widget.maximumValue);
  }

  double thumbPositionForValue({required double min, required double max}) {
    return (max / (widget.maximumValue - widget.minimumValue - 1)) *
        (value - widget.minimumValue - 1);
  }

  double trackWidthForValue({
    required double min,
    required double max,
    required double thumbPosition,
  }) {
    return (thumbPosition + (_thumbTouchZoneWidth / 2))
        .clamp(min, max)
        .toDouble();
  }

  bool isDragging = false;
  bool isSnapping = false;
  Widget _buildSlider() {
    return SizedBox(
      height: _contentHeight,
      child: LayoutBuilder(
        builder: (context, constraints) {
          double minThumbPosX = -(_thumbTouchZoneWidth - _thumbWidth) / 2;
          double maxThumbPosX =
              constraints.maxWidth - (_thumbTouchZoneWidth / 2);
          if (isDragging) {
            _thumbPosX = _thumbPosX.clamp(minThumbPosX, maxThumbPosX);
            value = valueForPosition(min: minThumbPosX, max: maxThumbPosX);
            WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
              widget.onChanged(value);
            });
          } else {
            _thumbPosX = thumbPositionForValue(
              min: minThumbPosX,
              max: maxThumbPosX,
            );
          }

          double minTrackWidth = 0;
          double maxTrackWidth = constraints.maxWidth;
          double trackWidth = 0;
          if (isDragging) {
            trackWidth = (_thumbPosX + (_thumbTouchZoneWidth / 2))
                .clamp(_thumbWidth, constraints.maxWidth);
          } else {
            trackWidth = trackWidthForValue(
              min: minTrackWidth,
              max: maxTrackWidth,
              thumbPosition: _thumbPosX,
            );
          }

          return Stack(
            alignment: Alignment.centerLeft,
            clipBehavior: Clip.none,
            children: [
              _buildLabels(),
              _buildInactiveTrack(),
              Positioned(
                width: trackWidth,
                child: _buildActiveTrack(),
              ),
              Positioned(
                left: _thumbPosX,
                child: _buildThumb(),
              ),
            ],
          );
        },
      ),
    );
  }

    Widget _buildThumb() {
        return GestureDetector(
          behavior: HitTestBehavior.opaque,
          dragStartBehavior: DragStartBehavior.down,
          onHorizontalDragUpdate: (details) {
            setState(() {
              _thumbPosX += details.delta.dx;
              isDragging = true;
            });
          },
          child: // Thumb UI
        );
      }

更新:我通過添加延遲 state 和 lastChangedTime 進行了一些調整。

  • 如果用戶短時間(3 秒)停止拖動,則 slider 將被鎖定,直到更新下一個新值 + 短暫的延遲(1.5 秒)

我按照你的思路,從 Slider 小部件中做了一個簡單的例子。

結果是否符合您的預期? (您可以將持續時間調整為任意數字)

DartPad: https://dartpad.dev/?id=95f2bd6d004604b3c37f27dd2852cb31

class MyStatefulWidget extends StatefulWidget {
  const MyStatefulWidget({super.key});

  @override
  State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  double _currentSliderValue = 20;
  DateTime lastChangedTime = DateTime.now();
  bool isDalying = false;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(_currentSliderValue.toString()),
        const SizedBox(height: 30),
        Slider(
          value: _currentSliderValue,
          max: 100,
          label: _currentSliderValue.round().toString(),
          onChanged: (double value) async {
            if (isDalying) {
              await Future.delayed(
                Duration(milliseconds: 1500), 
                () => isDalying = false,
              );
            } else {
              if (DateTime.now().difference(lastChangedTime) >
                  Duration(seconds: 3)) {
                isDalying = true;
              } else {
                setState(() {
                  _currentSliderValue = value;
                });
              }
            }
            lastChangedTime = DateTime.now();
          },
        ),
      ],
    );
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM