Flutter 上使用 CustomPaint 小部件自定义形状可点击区域

Ivá*_*oed 3 flutter

我\xe2\x80\x99见过一些与此问题类似的帖子,但它们\xe2\x80\x99不是我\xe2\x80\x99m寻找的。我想在 Flutter 中创建一个具有自定义形状的按钮。为此,我在 GestureDetector 小部件内使用 CustomPaint 小部件。问题是我不\xe2\x80\x99不希望不可见区域可点击。这正是 GestureDetector 所发生的情况。换句话说,我只希望我创建的形状是可点击的。但现在看来,我的自定义形状所在的地方有一个看不见的正方形,而且也是可点击的。我不想要这样。我在这篇文章中发现的最相似的问题:

\n

Flutter - 自定义按钮点击区域

\n

然而,就我而言,我\xe2\x80\x99m 处理自定义形状,而不是正方形或圆形。

\n

让我与您分享代码和可能的按钮的示例图像。您可以将其复制并直接粘贴到您的主目录中。复制我的问题应该很容易。

\n
import \'package:flutter/material.dart\';\nimport \'dart:ui\' as ui;\n\nvoid main() {\n  runApp(MyApp());\n}\n\nclass MyApp extends StatelessWidget {\n  // This widget is the root of your application.\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      title: \'Custom Shapes\',\n      theme: ThemeData.dark(),\n      home: MyHomePage(title: \'Custom Shapes App\'),\n    );\n  }\n}\n\nclass MyHomePage extends StatefulWidget {\n  MyHomePage({Key key, this.title}) : super(key: key);\n\n  final String title;\n\n  @override\n  _MyHomePageState createState() => _MyHomePageState();\n}\n\nclass _MyHomePageState extends State<MyHomePage> {\n\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(widget.title),\n      ),\n      backgroundColor: Colors.white24,\n      body: Center(\n        child: GestureDetector(\n          child: CustomPaint(\n            size: Size(300,300), //You can Replace this with your desired WIDTH and HEIGHT\n            painter: RPSCustomPainter(),\n          ),\n          onTap: (){\n            print("Working?");\n          },\n        ),\n      ),\n    );\n  }\n}\nclass RPSCustomPainter extends CustomPainter{\n\n  @override\n  void paint(Canvas canvas, Size size) {\n\n\n\n    Paint paint_0 = new Paint()\n      ..color = Color.fromARGB(255, 33, 150, 243)\n      ..style = PaintingStyle.fill\n      ..strokeWidth = 1;\n    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]);\n\n    Path path_0 = Path();\n    path_0.moveTo(0,size.height*0.50);\n    path_0.lineTo(size.width*0.33,size.height*0.33);\n    path_0.lineTo(size.width*0.50,0);\n    path_0.lineTo(size.width*0.67,size.height*0.33);\n    path_0.lineTo(size.width,size.height*0.50);\n    path_0.lineTo(size.width*0.67,size.height*0.67);\n    path_0.lineTo(size.width*0.50,size.height);\n    path_0.lineTo(size.width*0.33,size.height*0.67);\n    path_0.lineTo(0,size.height*0.50);\n    path_0.close();\n\n    canvas.drawPath(path_0, paint_0);\n\n\n  }\n\n  @override\n  bool shouldRepaint(covariant CustomPainter oldDelegate) {\n    return true;\n  }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我会尝试让星星是唯一可点击的东西,屏幕上没有其他看不见的地方。

\n

星形按钮

\n

提前致谢!

\n

Ivá*_*oed 5

到目前为止,有两种解决方案,第一个是通过重写 CustomPainter 类的 hitTest ,但是该行为并不是最理想的。因为点击时没有任何飞溅颜色或类似颜色。所以这是第一个:

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;
  }
} 
Run Code Online (Sandbox Code Playgroud)

它有效。问题是,当您点击“按钮”时,您看不到任何行为。

另一种更好的解决方案是使用带有 Inkwell 的材质作为按钮。对于您的形状,ShapeBorder 类。

无零安全性

这里是:

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();
  }
}
Run Code Online (Sandbox Code Playgroud)

零安全性:

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

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

class App extends StatelessWidget {
  const App({Key? key}) : super(key: key);

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

class StarButton extends StatelessWidget {
  const StarButton({Key? key}) : super(key: key);

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

class StarShape extends ShapeBorder {
  @override
  void paint(Canvas canvas, Rect rect, {ui.TextDirection? textDirection}) {
    return;
  }

  @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();
  }

  @override
  EdgeInsetsGeometry get dimensions => EdgeInsets.zero;

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

  @override
  ShapeBorder scale(double t) => StarShape();
}
Run Code Online (Sandbox Code Playgroud)

它看起来像这样:

在此输入图像描述