Flutter 颤振:CustomPainter绘制方法被调用多次,而不是只调用一次

Flutter 颤振:CustomPainter绘制方法被调用多次,而不是只调用一次,flutter,dart,canvas,graphics,flutter-layout,Flutter,Dart,Canvas,Graphics,Flutter Layout,我有一个简单的应用程序,它通过CustomPainter在画布上画一个红色或绿色的圆圈,具体取决于在AppBar中按下的按钮: 类colorcle扩展CustomPainter并负责绘制彩色圆: class ColorCircle扩展了CustomPainter{ 材料色霉色; ColorCircle({@required this.myColor}); @凌驾 空心油漆(帆布,尺寸){ debugPrint('colorcycle.paint,${DateTime.now()}'); 最终

我有一个简单的应用程序,它通过
CustomPainter
在画布上画一个红色或绿色的圆圈,具体取决于在
AppBar
中按下的按钮:


colorcle
扩展
CustomPainter
并负责绘制彩色圆:

class ColorCircle扩展了CustomPainter{
材料色霉色;
ColorCircle({@required this.myColor});
@凌驾
空心油漆(帆布,尺寸){
debugPrint('colorcycle.paint,${DateTime.now()}');
最终油漆=油漆()…颜色=颜色;
画布.画圈(偏移量(尺寸.宽度/2,尺寸.高度/2),100,油漆);
}
@凌驾
bool shouldRepaint(CustomPainter oldDelegate)=>false;
}
不同颜色的绘图效果很好,但当我单击(仅一次!)或将鼠标悬停在其中一个按钮上时,
paint
方法会被多次调用:


进一步实施细节: 我使用一个
StatefulWidget
来存储
actualColor
。在构建方法中,将
actualColor
传递给
colorcycle
构造函数:

class\u MyHomePageState扩展状态{
MaterialColor实际颜色=Colors.red;
@凌驾
小部件构建(构建上下文){
返回脚手架(
appBar:appBar(
行动:[
大纲按钮(
按下时:()=>setState(()=>actualColor=Colors.red),
子项:文本('RedCircle'),
),
大纲按钮(
按下时:()=>setState(()=>actualColor=Colors.green),
子项:文本(“绿色圆圈”),
),
],
),
正文:中(
孩子:定制油漆(
尺寸:尺寸(300300),
画家:彩色圈(myColor:actualColor),
),
),
);
}
}  
可在此处找到完整的源代码和运行示例:



那么为什么
paint
调用多次而不是一次呢?(您如何实现它,以便只调用一次
paint

一个糟糕的解决方案可能是在悬停窗口小部件周围添加一个
repainboundary

class _MyHomePageState extends State<MyHomePage> {
  MaterialColor actualColor = Colors.red;

  @override
  Widget build(BuildContext context) {
    print('Rebuilding with $actualColor');
    return Scaffold(
      appBar: AppBar(
        title: Text('CustomPainter Demo'),
        actions: <Widget>[
          RepaintBoundary(
            child: OutlinedButton(
                style: ButtonStyle(
                    foregroundColor: MaterialStateProperty.all(Colors.black)),
                onPressed: () {
                  setState(() => actualColor = Colors.red);
                },
                child: Text('RedCircle')),
          ),
          RepaintBoundary(
            child: OutlinedButton(
                style: ButtonStyle(
                    foregroundColor: MaterialStateProperty.all(Colors.black)),
                onPressed: () {
                  setState(() => actualColor = Colors.green);
                },
                child: Text('GreenCircle')),
          ),
        ],
      ),
      body: Center(
        child: CustomPaint(
          size: Size(300, 300),
          painter: ColorCircle(myColor: actualColor),
        ),
      ),
    );
  }
}
这似乎是一个非常糟糕的解决方案。我很想知道一个更好、更可持续的答案

带有
repainboundary
变通方法的完整源代码
导入“包装:颤振/材料.省道”;
void main(){
runApp(MyApp());
}
类MyApp扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
返回材料PP(
debugShowCheckedModeBanner:false,
标题:“CustomPainter演示”,
主页:MyHomePage(),
);
}
}
类ColorCirle扩展了CustomPainter{
材料色霉色;
ColorCirle({@required this.myColor});
@凌驾
空心油漆(帆布,尺寸){
debugPrint('colorcycle.paint,${DateTime.now()}');
最终油漆=油漆()…颜色=颜色;
画布.画圈(偏移量(尺寸.宽度/2,尺寸.高度/2),100,油漆);
}
@凌驾
bool应重新绘制(自定义代理){
return(oldDelegate作为ColorCirle).myColor!=myColor;
}
}
类MyHomePage扩展StatefulWidget{
constmyhomepage({Key}):超级(Key:Key);
@凌驾
_MyHomePageState createState()=>\u MyHomePageState();
}
类_MyHomePageState扩展状态{
MaterialColor实际颜色=Colors.red;
@凌驾
小部件构建(构建上下文){
返回脚手架(
appBar:appBar(
标题:文本(“CustomPainter演示”),
行动:[
重新绘制边界(
子:大纲显示按钮(
样式:钮扣样式(
foregroundColor:MaterialStateProperty.all(Colors.black)),
已按下:(){
设置状态(()=>actualColor=Colors.red);
},
子项:文本('RedCircle'),
),
重新绘制边界(
子:大纲显示按钮(
样式:钮扣样式(
foregroundColor:MaterialStateProperty.all(Colors.black)),
已按下:(){
设置状态(()=>actualColor=Colors.green);
},
子项:文本(“绿色圆圈”),
),
],
),
正文:中(
孩子:定制油漆(
尺寸:尺寸(300300),
画家:彩色卷轴(我的颜色:实际颜色),
),
),
);
}
}

您需要做两件事:

  • 使用
    重新绘制边界

    Center(
     child: RepaintBoundary(
       child: CustomPaint(
         size: Size(300, 300),
         painter: ColorCircle(myColor: actualColor),
       ),
     ),
    
  • 对于
    shouldRepaint
    方法,返回true

    bool shouldRepaint(CustomPainter oldDelegate) {
      return true;
    }
    

  • 重新绘制边界
    似乎不起作用。单击按钮时颜色不变。正确。很抱歉我会检查一下。有趣的是,重新粉刷边界是可行的,但反过来。如果您在“我的更新答案”中的操作按钮周围添加重新绘制边界。但这确实不实际。我将为你的问题添加一个悬赏,因为我也对答案感兴趣。很抱歉,现在没有其他解决方案,因为这个问题在框架级别仍然悬而未决:实际上,每当你将鼠标悬停在任何按钮上方,或者至少当你从一个按钮悬停到另一个按钮时,都会发生重新绘制。
    Center(
     child: RepaintBoundary(
       child: CustomPaint(
         size: Size(300, 300),
         painter: ColorCircle(myColor: actualColor),
       ),
     ),
    
    bool shouldRepaint(CustomPainter oldDelegate) {
      return true;
    }