【问题标题】:Flutter - Creating a custom control with FlutterFlutter - 使用 Flutter 创建自定义控件
【发布时间】:2017-11-14 21:13:44
【问题描述】:

我需要创建一个允许用户在有界矩形内拖动指针的自定义控件。非常喜欢这里的操纵杆控制:https://github.com/zerokol/JoystickView

我已经设法使用 CustomPainter 来绘制控制点和 GestureDetector 来跟踪用户在视图上拖动指针的位置来拼凑一些东西。但是,我无法用它来捕获平移输入。我根本无法让它捕获任何输入。我不知道我正在做的是否是最好的方法。我可能走错了路。这是代码。

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

void main() {
  runApp(new TouchTest());
}

class TouchTest extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Touch Test',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new Scaffold(
        appBar: new AppBar(
          title: const Text('Test'),
        ),
        body: new Container(

             decoration: new BoxDecoration(
               color: Colors.white,
               border: new Border.all(
                 color: Colors.black,
                 width: 2.0,
               ),
             ),
             child: new Center(
                child: new TouchControl()
             ),
           ),
      )
    );
  }
}

class TouchControl extends StatefulWidget {
  final double xPos;
  final double yPos;
  final ValueChanged<Offset> onChanged;

  const TouchControl({Key key,
          this.onChanged,
          this.xPos:0.0,
          this.yPos:0.0}) : super(key: key);

  @override
  TouchControlState createState() => new TouchControlState();
}

/**
 * Draws a circle at supplied position.
 *
 */
class TouchControlState extends State<TouchControl> {
  double xPos = 0.0;
  double yPos = 0.0;

  GestureDetector _gestureDetector;

  TouchControl() {
    _gestureDetector = new GestureDetector(
        onPanStart:_handlePanStart,
        onPanEnd: _handlePanEnd,
        onPanUpdate: _handlePanUpdate);
  }

  void onChanged(Offset offset) {
    setState(() {
      widget.onChanged(offset);

      xPos = offset.dx;
      yPos = offset.dy;
    });
  }

  @override
  bool hitTestSelf(Offset position) => true;

  @override
  void handleEvent(PointerEvent event, BoxHitTestEntry entry) {
    if (event is PointerDownEvent ) {
     // ??
    }
  }

  void _handlePanStart(DragStartDetails details) {
     onChanged(details.globalPosition);
  }

  void _handlePanEnd(DragEndDetails details) {
    // TODO
  }

  void _handlePanUpdate(DragUpdateDetails details) {
    onChanged(details.globalPosition);
  }

  @override
  Widget build(BuildContext context) {
    return new Center(
      child: new CustomPaint(
        size: new Size(xPos, yPos),
        painter: new TouchControlPainter(xPos, yPos),
      ),
    );
  }
}

class TouchControlPainter extends CustomPainter {
  static const markerRadius = 10.0;
  final double xPos;
  final double yPos;

  TouchControlPainter(this.xPos, this.yPos);

  @override
  void paint(Canvas canvas, Size size) {
    final paint = new Paint()
      ..color = Colors.blue[400]
      ..style = PaintingStyle.fill;

    canvas.drawCircle(new Offset(xPos, yPos), markerRadius, paint);
  }


  @override
  bool shouldRepaint(TouchControlPainter old) => xPos != old.xPos && yPos !=old.yPos;
}

【问题讨论】:

    标签: dart flutter


    【解决方案1】:

    您的代码没有在任何地方使用GestureDetector

    你应该用它来包装CustomPaintbuild()函数TouchControlState

    import 'package:flutter/material.dart';
    import 'package:flutter/gestures.dart';
    import 'package:flutter/rendering.dart';
    
    void main() {
      runApp(new TouchTest());
    }
    
    class TouchTest extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'Touch Test',
          theme: new ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: new Scaffold(
            appBar: new AppBar(
              title: const Text('Test'),
            ),
            body: new Container(
    
              decoration: new BoxDecoration(
                color: Colors.white,
                border: new Border.all(
                  color: Colors.black,
                  width: 2.0,
                ),
              ),
              child: new Center(
                child: new TouchControl()
              ),
            ),
          )
        );
      }
    }
    
    class TouchControl extends StatefulWidget {
      final double xPos;
      final double yPos;
      final ValueChanged<Offset> onChanged;
    
      const TouchControl({Key key,
        this.onChanged,
        this.xPos:0.0,
        this.yPos:0.0}) : super(key: key);
    
      @override
      TouchControlState createState() => new TouchControlState();
    }
    
    /**
     * Draws a circle at supplied position.
     *
     */
    class TouchControlState extends State<TouchControl> {
      double xPos = 0.0;
      double yPos = 0.0;
    
      GlobalKey _painterKey = new GlobalKey();
    
      void onChanged(Offset offset) {
        final RenderBox referenceBox = context.findRenderObject();
        Offset position = referenceBox.globalToLocal(offset);
        if (widget.onChanged != null)
          widget.onChanged(position);
    
        setState(() {
          xPos = position.dx;
          yPos = position.dy;
        });
      }
    
      @override
      bool hitTestSelf(Offset position) => true;
    
      @override
      void handleEvent(PointerEvent event, BoxHitTestEntry entry) {
        if (event is PointerDownEvent ) {
          // ??
        }
      }
    
      void _handlePanStart(DragStartDetails details) {
        onChanged(details.globalPosition);
      }
    
      void _handlePanEnd(DragEndDetails details) {
        print('end');
        // TODO
      }
    
      void _handlePanUpdate(DragUpdateDetails details) {
        onChanged(details.globalPosition);
      }
    
      @override
      Widget build(BuildContext context) {
        return new ConstrainedBox(
          constraints: new BoxConstraints.expand(),
          child: new GestureDetector(
            behavior: HitTestBehavior.opaque,
            onPanStart:_handlePanStart,
            onPanEnd: _handlePanEnd,
            onPanUpdate: _handlePanUpdate,
            child: new CustomPaint(
              size: new Size(xPos, yPos),
              painter: new TouchControlPainter(xPos, yPos),
            ),
          ),
        );
      }
    }
    
    class TouchControlPainter extends CustomPainter {
      static const markerRadius = 10.0;
      final double xPos;
      final double yPos;
    
      TouchControlPainter(this.xPos, this.yPos);
    
      @override
      void paint(Canvas canvas, Size size) {
        final paint = new Paint()
          ..color = Colors.blue[400]
          ..style = PaintingStyle.fill;
    
        canvas.drawCircle(new Offset(xPos, yPos), markerRadius, paint);
      }
    
    
      @override
      bool shouldRepaint(TouchControlPainter old) => xPos != old.xPos && yPos !=old.yPos;
    }
    

    【讨论】:

    • 啊,谢谢你,现在我明白了。但是,尝试过这个后,我仍然无法从 GestureDetector 捕获任何输入。它需要有特定类型的父母或孩子吗?
    • 您为自定义画家提供了 Size(0.0, 0.0),因为 xPosyPos 都默认为 0.0。您还需要从全局坐标转换为相对坐标。我已经用完整的工作示例更新了我的答案。
    • 我一直在研究 Flutter 小部件的源代码,试图弄清楚事情是如何联系在一起的(但没有取得多大成功),所以谢谢你,这很有帮助。
    • 嘿,我看到这个答案有点老了,我刚刚在 iOS 模拟器上试过这个,这超级慢?知道为什么吗? @CollinJackson 谢谢
    猜你喜欢
    • 2021-01-05
    • 1970-01-01
    • 2021-12-24
    • 1970-01-01
    • 2019-04-10
    • 1970-01-01
    • 1970-01-01
    • 2020-03-18
    • 1970-01-01
    相关资源
    最近更新 更多