Back to Posts

Flutter Painting Widgets: Custom Drawing

7 min read

Painting widgets are essential for creating custom drawings and visual effects in Flutter applications. Let's explore the various painting widgets and how to use them effectively.

1. Basic Painting Widgets

CustomPaint

class CustomPaintExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      size: Size(200, 200),
      painter: MyPainter(),
    );
  }
}

class MyPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.blue
      ..style = PaintingStyle.fill;

    final path = Path()
      ..moveTo(0, size.height)
      ..lineTo(size.width / 2, 0)
      ..lineTo(size.width, size.height)
      ..close();

    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

RepaintBoundary

class RepaintBoundaryExample extends StatefulWidget {
  @override
  _RepaintBoundaryExampleState createState() => _RepaintBoundaryExampleState();
}

class _RepaintBoundaryExampleState extends State<RepaintBoundaryExample> {
  int _counter = 0;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        RepaintBoundary(
          child: CustomPaint(
            size: Size(200, 200),
            painter: MyPainter(),
          ),
        ),
        SizedBox(height: 20),
        ElevatedButton(
          onPressed: () {
            setState(() {
              _counter++;
            });
          },
          child: Text('Counter: $_counter'),
        ),
      ],
    );
  }
}

2. Advanced Painting Widgets

ShaderMask

class ShaderMaskExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ShaderMask(
      shaderCallback: (bounds) => LinearGradient(
        colors: [Colors.red, Colors.blue],
        begin: Alignment.topLeft,
        end: Alignment.bottomRight,
      ).createShader(bounds),
      child: Container(
        width: 200,
        height: 200,
        color: Colors.white,
        child: Center(
          child: Text(
            'Gradient Text',
            style: TextStyle(
              fontSize: 24,
              color: Colors.white,
            ),
          ),
        ),
      ),
    );
  }
}

BackdropFilter

class BackdropFilterExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Container(
          width: 200,
          height: 200,
          color: Colors.blue,
        ),
        BackdropFilter(
          filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
          child: Container(
            width: 200,
            height: 200,
            color: Colors.white.withOpacity(0.1),
            child: Center(
              child: Text(
                'Blur Effect',
                style: TextStyle(
                  fontSize: 24,
                  color: Colors.white,
                ),
              ),
            ),
          ),
        ),
      ],
    );
  }
}

3. Custom Painting Widgets

Custom Gradient Painter

class CustomGradientPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final rect = Rect.fromLTWH(0, 0, size.width, size.height);
    final gradient = LinearGradient(
      colors: [Colors.red, Colors.orange, Colors.yellow],
      begin: Alignment.topLeft,
      end: Alignment.bottomRight,
    );

    final paint = Paint()
      ..shader = gradient.createShader(rect)
      ..style = PaintingStyle.fill;

    canvas.drawRect(rect, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

Custom Pattern Painter

class CustomPatternPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.blue
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2;

    for (var i = 0; i < size.width; i += 20) {
      for (var j = 0; j < size.height; j += 20) {
        canvas.drawCircle(Offset(i.toDouble(), j.toDouble()), 5, paint);
      }
    }
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

4. Best Practices

  1. Implement proper painting

    • Choose appropriate painting widgets
    • Handle painting states
    • Consider performance impact
  2. Enhance painting experience

    • Add smooth transitions
    • Implement proper effects
    • Consider visual feedback
  3. Optimize painting performance

    • Use const constructors
    • Minimize repaints
    • Handle memory efficiently

By mastering these painting widgets and following best practices, you can create Flutter applications that are:

  • More visually appealing
  • More dynamic
  • More efficient
  • More maintainable