Flutter CustomPainter Examples

CustomPainter is an interface used by CustomPaint and RenderCustomPaint. This interface is the solution when we need to create a highly customized user interface.

Draw a shape

We use CustomPaint to draw on.

CustomPaint(
        painter: CenterCircle(),
        child: Center(
          child: Text('Loading...'),
          ),
        )
class CenterCircle extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    var paint = Paint()
      ..color = Colors.red
      ..strokeWidth = 3
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;

    Offset center = Offset(size.width / 2, size.height / 2);

    canvas.drawCircle(center, 100, paint);
  }

  @override
  bool shouldRepaint(CenterCircle oldDelegate) => false;

}

Profile Card

Here is a Profile card with 3 layers: blue background, darker blue wave, hole for profile image.

Container(
        width: MediaQuery.of(context).size.width,
        height: 200,
        child: CustomPaint(
          painter: ProfileCard(
            circleWidth: 64.0,
          ),
        ),
      )
class ProfileCard extends CustomPainter {
  final circleWidth;

  ProfileCard({this.circleWidth});

  @override
  void paint(Canvas canvas, Size size) {
    var fillPaint = Paint()
      ..color = Colors.blue
      ..strokeWidth = 1
      ..style = PaintingStyle.fill
      ..strokeCap = StrokeCap.round;

    var wavePaint = Paint()
      ..color = Colors.blue[900].withOpacity(0.1)
      ..strokeWidth = 1
      ..style = PaintingStyle.fill
      ..strokeCap = StrokeCap.round;
    Path path = Path();
    path.moveTo(0, size.height);
    path.cubicTo(size.width * 1/4, size.height * 1/4, size.width / 2, size.height / 2, size.width, 0);
    path.lineTo(size.width, size.height);


    var holePaint = Paint()
      ..color = Colors.lightBlue
      ..strokeWidth = 1
      ..style = PaintingStyle.fill
      ..strokeCap = StrokeCap.round;
    Offset holeOffset = Offset(size.width / 2, size.height - circleWidth / 6);

    canvas.drawRect(Rect.fromLTRB(0, 0, size.width, size.height), fillPaint);
    canvas.drawPath(path, wavePaint );
    canvas.drawCircle(holeOffset, circleWidth, holePaint);
  }

  @override
  bool shouldRepaint(ProfileCard oldDelegate) => false;

  @override
  bool shouldRebuildSemantics(ProfileCard oldDelegate) => false;
}

Wave Animation

Combining with AnimatedBuilder widget, we can animate canvas’ drawing.

WaveContainer(
            width: double.infinity,
            height: 100,
            waveColor: Colors.green)
class WaveContainer extends StatefulWidget {
  final Duration duration;
  final double height;
  final double width;
  final Color waveColor;

  const WaveContainer({
    Key key,
    this.duration,
    @required this.height,
    @required this.width,
    this.waveColor,
  }) : super(key: key);

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

class _WaveContainerState extends State<WaveContainer>
    with TickerProviderStateMixin {
  AnimationController _animationController;
  Duration _duration;
  Color _waveColor;

  @override
  void initState() {
    super.initState();

    _duration = widget.duration ?? const Duration(milliseconds: 1000);
    _animationController = AnimationController(vsync: this, duration: _duration);
    _waveColor = widget.waveColor ?? Colors.lightBlueAccent;

    _animationController.repeat();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      width: widget.width,
      height: widget.height,
      child: AnimatedBuilder(
        animation: _animationController,
        builder: (BuildContext context, Widget child) {
          return CustomPaint(
            painter: WavePainter(
                waveAnimation: _animationController, waveColor: _waveColor),
          );
        },
      ),
    );
  }

  @override
  void dispose() {
    _animationController.stop();
    _animationController.dispose();
    super.dispose();
  }
}

class WavePainter extends CustomPainter {
  Animation<double> waveAnimation;
  Color waveColor;

  WavePainter({this.waveAnimation, this.waveColor});

  @override
  void paint(Canvas canvas, Size size) {
    Path path = Path();
    for (double i = 0.0; i < size.width; i++) {
      path.lineTo(i,
          sin((i / size.width * 2 * pi) + (waveAnimation.value * 2 * pi)) * 4);
    }
    path.lineTo(size.width, size.height);
    path.lineTo(0.0, size.height);
    path.close();

    Paint wavePaint = Paint()..color = waveColor;
    canvas.drawPath(path, wavePaint);
  }

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

Leave a Comment

Your email address will not be published. Required fields are marked *