Pocket Starship



What is Flutter?

Flutter is Google’s UI toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase. It uses the Dart programming language.

For cross platform app development Microsoft has Xamarin, Facebook has React Native, and now Google's answer to the problem is Flutter. Some of the benefits are it's fast. It uses it's own renderer and themed widgets, so you can make an app look like it's built with native tools or opt for something more distinct. During development it uses just in time compilation allowing you to hot reload the project to see changes on the fly.

I was looking to expand my programming knowledge outside of C# and I have since completed the excellent Flutter Bootcamp course by Angela Yu, lead instructor of the London App Brewery.


Birthday Reminder App (WIP)

In order to help solidify my knowlege of Flutter my current work in progress is a little birthday reminder app. It uses a number of prebuilt widgets as well as some custom ones. I'm terrible at remembering birthdays and I found the in build calendars never alerted me in time or I'd miss the notification. So for my app you can specify what you need to send, and it will alert you a few days before to give you time to purchase things. E.g. 7 days for a card, 14 days for a gift.

birthday countdown birthday countdown

I'm using a number of pre-built packages, including the provider package to help manage state, and the shared_preferences for saving and loading data. However there was a small graphical I wanted to achieve that wasn't available out of the box and that led me to build a custom painter.


Custom Painter

birthday clock birthday clock

Flutter allows you to extend the CustomPainter class and draw your own widget ready to compose. In my case I wanted a little countdown with a more obvious dot visual. This then forms part of a visual stack and I can add a normal text or icon widget for a day countdown...and cake on the day.

Full code sample is as follows.

 
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'dart:math' as Math;

class ProgressPainter extends CustomPainter {
  final Color progressColor;
  final double progress;
  final double _progressRadius = 24;
  final double _ringStrokeWidth = 2;
  final double _dotRadius = 4;
  final bool countdown;

  ProgressPainter({this.progress, this.progressColor, this.countdown});

  double getProgress() {
    if (countdown) {
      return progress;
    }
    return 1 - progress;
  }

  Offset getArcOffset(double radius, double progress) {
    double angle = progress * Math.pi * 2;
    double x = radius * Math.sin(angle);
    double y = radius * -Math.cos(angle);
    print(x + radius);
    return Offset(x, y);
  }

  @override
  void paint(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);
    final paint = Paint()..color = progressColor;

    // main style for our countdown
    final arcPaint = Paint()
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round
      ..strokeWidth = _ringStrokeWidth
      ..color = progressColor;
    // background which will be the full ring
    final arcPaintBackground = Paint()
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.butt
      ..strokeWidth = _ringStrokeWidth
      ..color = progressColor.withOpacity(0.25);

    // draw feint circle
    canvas.drawArc(Rect.fromCircle(center: center, radius: _progressRadius), 0,
        2 * Math.pi, false, arcPaintBackground);
    // draw highlighted countdown section - need to minus Pi/2 to it starts at 12 not 3 on the clock
    // and we use a -ve value to get a countdown look
    canvas.drawArc(
        Rect.fromCircle(center: center, radius: _progressRadius),
        0 - Math.pi / 2,
        (countdown ? -1 : 1) * getProgress() * Math.pi * 2,
        false,
        arcPaint);
    // draw circle to indicate current countdown progress
    canvas.drawCircle(
        getArcOffset(_progressRadius, (countdown ? -1 : 1) * getProgress()) +
            center,
        _dotRadius,
        paint);
  }

  @override
  bool shouldRepaint(ProgressPainter oldDelegate) {
    // check for any change and return accordingly
    return oldDelegate.progress != this.progress ||
        oldDelegate.progressColor != this.progressColor ||
        oldDelegate.countdown != this.countdown;
  }
}