2017-06-13 40 views
2

我需要創建一個自定義控件,它允許用戶在有界的矩形內拖動指針。非常喜歡這個遊戲杆控制:https://github.com/zerokol/JoystickViewFlutter - 使用Flutter創建自定義控件

我設法使用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; 
} 

回答

3

您的代碼沒有在任何地方使用GestureDetector

你應該用它來包裝CustomPaintbuild()函數TouchControlState

screenshot

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; 
} 
+0

啊,現在我看到它感謝你,是有意義的。但是,嘗試過這種方法後,我仍然無法捕捉到任何來自GestureDetector的輸入。它是否需要特殊的父母或孩子? – alrutherford

+0

由於'xPos'和'yPos'默認爲'0.0',所以你給自定義的畫家一個'(0.0,0.0)'的Size。您還需要從全局轉換爲相對座標。我已經用完整的工作示例更新了我的答案。 –

+0

我一直在研究Flutter小部件的源代碼,試圖弄清楚事情是如何掛在一起的(沒有太多的成功),所以謝謝你,這是非常有用的。 – alrutherford