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 *

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close