flutter_gesture_hit_intercept 0.0.2 copy "flutter_gesture_hit_intercept: ^0.0.2" to clipboard
flutter_gesture_hit_intercept: ^0.0.2 copied to clipboard

Flutter 实现命中拦截, 用于解决手势冲突, 阻止其它小部件获取手势事件

example/lib/main.dart

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_gesture_hit_intercept/flutter_gesture_hit_intercept.dart';

void main() {
  runApp(const MainApp());
}

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  Widget buildItem(BuildContext context) {
    return GestureDetector(
      onTap: () {
        debugPrint('点击了');
      },
      child: Container(
        height: 100,
        color: Colors.transparent,
        child: Stack(
          children: [
            const Text('在此区域的手势不会阻止`ListView`的滚动'),
            Align(
                alignment: Alignment.center,
                child: SizedBox(
                  width: 100,
                  height: 40,
                  child: FilledButton(
                    onPressed: () {
                      debugPrint('点击了');
                    },
                    child: const Text("按钮"),
                  ),
                )),
          ],
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: GestureHitInterceptScope(
          child: ListView(
            physics: const AlwaysScrollableScrollPhysics(
              parent: BouncingScrollPhysics(),
            ),
            children: [
              const GestureTestWidget(),
              buildItem(context),
              for (var i = 0; i < 100; i++)
                if (i % 3 == 0)
                  const GestureTestWidget()
                else
                  buildItem(context),
            ],
          ),
        ),
      ),
    );
  }
}

class GestureTestWidget extends LeafRenderObjectWidget {
  const GestureTestWidget({super.key});

  @override
  RenderObject createRenderObject(BuildContext context) {
    return GestureTestBox(context);
  }
}

Color randomColor({int min = 120, int max = 200}) => Color.fromARGB(
      255,
      nextInt(max, min: min),
      nextInt(max, min: min),
      nextInt(max, min: min),
    );

int nextInt(int max, {int min = 0}) => min + Random().nextInt(max);

class GestureTestBox extends RenderBox {
  /// 用来保存所有的手指事件
  final pointerMap = <int, PointerEvent>{};

  /// 用来保存所有的手指颜色
  final pointerColorMap = <int, Color>{};

  BuildContext context;

  GestureTestBox(this.context);

  Color getPointerColor(int pointer) {
    return pointerColorMap.putIfAbsent(pointer, () => randomColor());
  }

  @override
  void performLayout() {
    size = Size(constraints.maxWidth, constraints.maxWidth / 2);
  }

  /// 如果为true, 会影响[PointerEvent.localPosition]位置信息
  @override
  bool get isRepaintBoundary => false;

  @override
  void paint(PaintingContext context, Offset offset) {
    //context.canvas.drawColor(Colors.redAccent, BlendMode.src);
    final canvas = context.canvas;

    canvas.drawRect(
      Rect.fromLTWH(
        paintBounds.left + offset.dx,
        paintBounds.top + offset.dy,
        paintBounds.width,
        paintBounds.height,
      ),
      Paint()..color = Colors.grey,
    );

    TextPainter(
      text: TextSpan(
          text: "在此区域的手势会阻止`ListView`的滚动\n${pointerMap.length}",
          style: const TextStyle(
            color: Colors.white,
            fontWeight: FontWeight.bold,
            fontSize: 12,
          )),
      textDirection: TextDirection.ltr,
    )
      ..layout(maxWidth: paintBounds.width)
      ..paint(canvas, offset);

    //绘制map
    const radius = 30.0;
    final paint = Paint()
      ..strokeWidth = 1
      ..style = PaintingStyle.stroke;

    //debugger();
    pointerMap.forEach((key, pointer) {
      paint.color = getPointerColor(key);
      canvas.drawLine(Offset(offset.dx, pointer.localPosition.dy),
          Offset(offset.dx + size.width, pointer.localPosition.dy), paint);
      canvas.drawLine(Offset(pointer.localPosition.dx, offset.dy),
          Offset(pointer.localPosition.dx, offset.dy + size.height), paint);
      canvas.drawCircle(pointer.localPosition, radius, paint);
    });
  }

  @override
  bool hitTestSelf(Offset position) {
    return true;
  }

  /// 只有命中通过之后, 才会回调事件
  /// [GestureBinding.handlePointerEvent] -> [GestureBinding._handlePointerEventImmediately] -> [HitTestResult.addWithPaintTransform]
  @override
  void handleEvent(PointerEvent event, covariant BoxHitTestEntry entry) {
    //debugger();

    final hitInterceptBox = GestureHitInterceptScope.of(context);
    hitInterceptBox?.interceptHitBox = this;
    if (!event.synthesized) {
      pointerMap[event.pointer] = event;
    }

    if (event is PointerUpEvent || event is PointerCancelEvent) {
      pointerMap.remove(event.pointer);
    }
    markNeedsPaint();
    super.handleEvent(event, entry);
  }
}
2
likes
150
points
23
downloads
screenshot

Publisher

verified publisherangcyo.com

Weekly Downloads

Flutter 实现命中拦截, 用于解决手势冲突, 阻止其它小部件获取手势事件

Repository (GitHub)
View/report issues

Topics

#flutter #gesture #hit #intercept

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on flutter_gesture_hit_intercept