Flutter 颤振上带有CustomPaint小部件的自定义形状可敲击区域
我看到过一些类似于这个问题的帖子,但它们不是我想要的。我想在颤振中创建一个具有自定义形状的按钮。为此,我在GestureDetector小部件中使用CustomPaint小部件。问题是我不希望不可见的区域是可点击的。这正是手势检测器所发生的。换句话说,我只希望我创建的形状是可点击的。但现在,似乎有一个无形的广场,我的自定义形状是,这也是可点击。我不想那样。我在这篇文章中发现了最相似的问题: 然而,在我的例子中,我处理的是自定义形状,而不是正方形或圆形 让我与您分享代码和一个可能按钮的示例图像。你可以直接复制并粘贴到主屏幕上。复制我的问题应该很容易Flutter 颤振上带有CustomPaint小部件的自定义形状可敲击区域,flutter,Flutter,我看到过一些类似于这个问题的帖子,但它们不是我想要的。我想在颤振中创建一个具有自定义形状的按钮。为此,我在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方法(在后者中,可以使用objectsize
属性)
例如:
类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();
}
}