Flutter 颤振上带有CustomPaint小部件的自定义形状可敲击区域

Flutter 颤振上带有CustomPaint小部件的自定义形状可敲击区域,flutter,Flutter,我看到过一些类似于这个问题的帖子,但它们不是我想要的。我想在颤振中创建一个具有自定义形状的按钮。为此,我在GestureDetector小部件中使用CustomPaint小部件。问题是我不希望不可见的区域是可点击的。这正是手势检测器所发生的。换句话说,我只希望我创建的形状是可点击的。但现在,似乎有一个无形的广场,我的自定义形状是,这也是可点击。我不想那样。我在这篇文章中发现了最相似的问题: 然而,在我的例子中,我处理的是自定义形状,而不是正方形或圆形 让我与您分享代码和一个可能按钮的示例图像。

我看到过一些类似于这个问题的帖子,但它们不是我想要的。我想在颤振中创建一个具有自定义形状的按钮。为此,我在GestureDetector小部件中使用CustomPaint小部件。问题是我不希望不可见的区域是可点击的。这正是手势检测器所发生的。换句话说,我只希望我创建的形状是可点击的。但现在,似乎有一个无形的广场,我的自定义形状是,这也是可点击。我不想那样。我在这篇文章中发现了最相似的问题:

然而,在我的例子中,我处理的是自定义形状,而不是正方形或圆形

让我与您分享代码和一个可能按钮的示例图像。你可以直接复制并粘贴到主屏幕上。复制我的问题应该很容易

import 'package:flutter/material.dart';
import 'dart:ui' as ui;

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Custom Shapes',
      theme: ThemeData.dark(),
      home: MyHomePage(title: 'Custom Shapes App'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      backgroundColor: Colors.white24,
      body: Center(
        child: GestureDetector(
          child: CustomPaint(
            size: Size(300,300), //You can Replace this with your desired WIDTH and HEIGHT
            painter: RPSCustomPainter(),
          ),
          onTap: (){
            print("Working?");
          },
        ),
      ),
    );
  }
}
class RPSCustomPainter extends CustomPainter{

  @override
  void paint(Canvas canvas, Size size) {



    Paint paint_0 = new Paint()
      ..color = Color.fromARGB(255, 33, 150, 243)
      ..style = PaintingStyle.fill
      ..strokeWidth = 1;
    paint_0.shader = ui.Gradient.linear(Offset(0,size.height*0.50),Offset(size.width,size.height*0.50),[Color(0xffffed08),Color(0xffffd800),Color(0xffff0000)],[0.00,0.34,1.00]);

    Path path_0 = Path();
    path_0.moveTo(0,size.height*0.50);
    path_0.lineTo(size.width*0.33,size.height*0.33);
    path_0.lineTo(size.width*0.50,0);
    path_0.lineTo(size.width*0.67,size.height*0.33);
    path_0.lineTo(size.width,size.height*0.50);
    path_0.lineTo(size.width*0.67,size.height*0.67);
    path_0.lineTo(size.width*0.50,size.height);
    path_0.lineTo(size.width*0.33,size.height*0.67);
    path_0.lineTo(0,size.height*0.50);
    path_0.close();

    canvas.drawPath(path_0, paint_0);


  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }

}
导入“包装:颤振/材料.省道”;
将“dart:ui”导入为ui;
void main(){
runApp(MyApp());
}
类MyApp扩展了无状态小部件{
//此小部件是应用程序的根。
@凌驾
小部件构建(构建上下文){
返回材料PP(
标题:“自定义形状”,
主题:ThemeData.dark(),
主页:我的主页(标题:“自定义形状应用程序”),
);
}
}
类MyHomePage扩展StatefulWidget{
MyHomePage({Key,this.title}):超级(Key:Key);
最后的字符串标题;
@凌驾
_MyHomePageState createState()=>\u MyHomePageState();
}
类_MyHomePageState扩展状态{
@凌驾
小部件构建(构建上下文){
返回脚手架(
appBar:appBar(
标题:文本(widget.title),
),
背景颜色:Colors.white24,
正文:中(
儿童:手势检测器(
孩子:定制油漆(
大小:大小(300300),//您可以将其替换为所需的宽度和高度
油漆工:RPSCustomPainter(),
),
onTap:(){
打印(“工作?”);
},
),
),
);
}
}
类RPSCustomPainter扩展了CustomPainter{
@凌驾
空心油漆(帆布,尺寸){
油漆油漆0=新油漆()
…color=color.fromARGB(255、33、150、243)
…style=PaintingStyle.fill
..冲程宽度=1;
paint_0.shader=ui.Gradient.linear(偏移量(0,大小.高度*0.50),偏移量(大小.宽度,大小.高度*0.50),[颜色(0xFFFFD08),颜色(0xffffd800),颜色(0xffff0000)],[0.00,0.34,1.00];
路径路径_0=路径();
路径0.moveTo(0,大小高度*0.50);
路径0.lineTo(尺寸.宽度*0.33,尺寸.高度*0.33);
路径0.lineTo(尺寸、宽度*0.50,0);
路径0.lineTo(尺寸、宽度*0.67,尺寸、高度*0.33);
路径0.lineTo(大小、宽度、大小、高度*0.50);
路径0.lineTo(大小.宽度*0.67,大小.高度*0.67);
路径0.lineTo(大小。宽度*0.50,大小。高度);
路径0.lineTo(尺寸.宽度*0.33,尺寸.高度*0.67);
路径0.lineTo(0,大小、高度*0.50);
路径0.close();
画布.drawPath(路径0,绘制0);
}
@凌驾
布尔应重新绘制(协变自定义代理){
返回true;
}
}
我试着让星星成为唯一可以点击的东西,而不是屏幕上其他看不见的地方


提前谢谢

A
gesturedector
在它的孩子说它被击中时被击中(并开始检测)(除非你偶然发现它的
行为
属性)。 要指定何时点击
CustomPaint
CustomPainter
有一个可以覆盖的
hitTest(Offset)
方法。它应该返回是否应该在您的形状内考虑<代码>偏移量/代码>。不幸的是,该方法没有大小参数。(这是一个bug,解决方案遇到了一些惯性,请参阅) 唯一好的解决方案是创建一个自定义渲染对象,在该对象中覆盖paint和hitTestSelf方法(在后者中,可以使用object
size
属性)

例如:

类MyCirclePaint扩展了LeafRenderObjectWidget{
const myCirclePaint({@required this.radius,Key}):super(Key:Key);
//相对于小部件大小的半径
最终双半径;
@凌驾
RenderObject createRenderObject(BuildContext上下文)=>RenderMyCirclePaint()…半径=半径;
@凌驾
void updatenderObject(BuildContext上下文,renderMyCirclePaint renderObject)=>renderObject.radius=radius;
}
类RenderMyCirclePaint扩展了RenderBox{
双半径;
@凌驾
无效性能布局(){
大小=限制。最大;
}
@凌驾
void performResize(){
大小=限制。最大;
}
@凌驾
无效绘制(绘制上下文上下文,偏移){
最终中心=尺寸。中心(偏移);
最终r=1.0*半径*尺寸.宽度;
最终背景油漆=油漆()
…颜色=常量颜色(0x88202020)
..风格=绘画风格。填充;
背景.画布.画圈(中间,r,背景画);
}
@凌驾
布尔hitTestSelf(偏移位置){
最终中心=尺寸中心(偏移量为零);
返回(位置-中心)。距离
请注意,小部件的左上角位于
paint
方法中的
offset
参数处,而不是
offset.zero
位于
CustomPainter


您可能希望构造一次路径,并在
hitTestSelf

中使用
path\u 0.contains(position)
,当其子对象说它被击中时,
手势检测器被击中(并开始检测)(除非您偶然发现它的
行为
属性)。 要指定何时点击
CustomPaint
CustomPainter
有一个可以覆盖的
hitTest(Offset)
方法。它应该返回是否应该在您的形状内考虑<代码>偏移量/代码>。不幸的是,该方法没有大小参数。(这是一个bug,解决方案遇到了一些惯性,请参阅) 唯一好的解决方案是创建自定义渲染对象,在其中覆盖绘制并单击
import 'package:flutter/material.dart';

void main() {
  runApp(App());
}

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(body: Center(child: BuyTicketButton(100.0, ()=>{})))
    );
  }
}

class BuyTicketButton extends StatelessWidget {
  final double cost;
  final Function onTap;

  const BuyTicketButton(this.cost, this.onTap, {Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        height: 400,
        child: RaisedButton(
          shape: CustomBorder(),
          onPressed: (){
            print("this works");
          },
        ),
      ),
    );
  }
}


class CustomBorder extends OutlinedBorder {

  const CustomBorder({
    BorderSide side = BorderSide.none
  }) : assert(side != null), super(side: side);

  Path customBorderPath(Rect rect) {
    Path path = Path();
    path.moveTo(0, 0);
    path.lineTo(rect.width, 0);
    path.lineTo(rect.width, rect.height);
    path.lineTo(0, rect.height);

    double diameter = rect.height / 3;
    double radius = diameter / 2;

    path.lineTo(0, diameter * 2);
    path.arcToPoint(
      Offset(0, diameter),
      radius: Radius.circular(radius),
      clockwise: false,
    );
    path.lineTo(0, 0);
    return path;
  }


  @override
  OutlinedBorder copyWith({BorderSide side}) {
    return CustomBorder(side: side ?? this.side);
  }

  @override
  EdgeInsetsGeometry get dimensions => EdgeInsets.all(side.width);

  @override
  Path getInnerPath(Rect rect, {TextDirection textDirection}) {
    return customBorderPath(rect);
  }

  @override
  Path getOuterPath(Rect rect, {TextDirection textDirection}) {
    return customBorderPath(rect);
  }

  @override
  void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {
    switch (side.style) {
      case BorderStyle.none:
        break;
      case BorderStyle.solid:
        canvas.drawPath(customBorderPath(rect), Paint()
          ..style = PaintingStyle.stroke
          ..color = Colors.black
          ..strokeWidth = 1.0
        );
    }
  }

  @override
  ShapeBorder scale(double t) => CustomBorder(side: side.scale(t));

}
import 'package:flutter/material.dart';
import 'dart:ui' as ui;

void main() {
  runApp(App());
}

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.black54,
        body: Center(
          child: TappableStarButton(),
        ),
      ),
    );
  }
}

class TappableStarButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: GestureDetector(
        child: CustomPaint(
          size: Size(300, 300),
          painter: RPSCustomPainter(),
        ),
        onTap: () {
          print("This works");
        },
      ),
    );
  }
}

class RPSCustomPainter extends CustomPainter {
  Path path_0 = Path();

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint_0 = new Paint()
      ..color = Color.fromARGB(255, 33, 150, 243)
      ..style = PaintingStyle.fill
      ..strokeWidth = 1;
    paint_0.shader = ui.Gradient.linear(
        Offset(10, 150),
        Offset(290, 150),
        [Color(0xffff1313), Color(0xffffbc00), Color(0xffffca00)],
        [0.00, 0.69, 1.00]);

    path_0.moveTo(150, 10);
    path_0.lineTo(100, 100);
    path_0.lineTo(10, 150);
    path_0.lineTo(100, 200);
    path_0.lineTo(150, 290);
    path_0.lineTo(200, 200);
    path_0.lineTo(290, 150);
    path_0.lineTo(200, 100);
    canvas.drawPath(path_0, paint_0);
  }

  @override
  bool hitTest(Offset position) {
    bool hit = path_0.contains(position);
    return hit;
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
} 
import 'package:flutter/material.dart';
import 'dart:ui' as ui;

void main() {
  runApp(App());
}

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.black38,
        body: StarButton(),
      ),
    );
  }
}

class StarButton extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        height: 300,
        width: 300,
        child: Material(
          shape: StarShape(),
          color: Colors.orange,
          child: InkWell(
            splashColor: Colors.yellow,
            onTap: () => print('it works'),
          ),
        ),
      ),
    );
  }
}

class StarShape extends ShapeBorder {
  @override
  EdgeInsetsGeometry get dimensions => null;

  @override
  Path getInnerPath(Rect rect, {ui.TextDirection textDirection}) => null;

  @override
  void paint(Canvas canvas, Rect rect, {ui.TextDirection textDirection}) =>
      null;

  @override
  ShapeBorder scale(double t) => null;

  @override
  Path getOuterPath(Rect rect, {ui.TextDirection textDirection}) {
    return Path()
      ..moveTo(150, 10)
      ..lineTo(100, 100)
      ..lineTo(10, 150)
      ..lineTo(100, 200)
      ..lineTo(150, 290)
      ..lineTo(200, 200)
      ..lineTo(290, 150)
      ..lineTo(200, 100)
      ..close();
  }
}