Search code examples
flutterdartflutter-custompainter

Battery level indicator drawing upside down in Flutter CustomPainter Class


I have this code to draw the battery level but it is drawing the inside colored indicator from the top down. It should paint it from the bottom to the top like you see on all Android phones. Any thoughts on what I am doing wrong?

class BatteryLevelPainter extends CustomPainter {
  final int _batteryLevel;
  final BatteryState _batteryState;

  BatteryLevelPainter(this._batteryLevel, this._batteryState);

  @override
  void paint(Canvas canvas, Size size) {
    Paint getPaint({Color color = Colors.black, PaintingStyle style = PaintingStyle.stroke}) {
      return Paint()
        ..color = color
        ..strokeWidth = 1.0
        ..style = style;
    }

    final RRect batteryOutline = RRect.fromLTRBR(0.0, 0.0, size.width, size.height, const Radius.circular(2.0));

    // Battery body
    canvas.drawRRect(
      batteryOutline,
      getPaint(),
    );

    // Battery nub
    canvas.drawRect(
      const Rect.fromLTWH(4.0, -3.0, 4.0, 3.0),
      getPaint(style: PaintingStyle.fill),
    );

    // Fill rect
    canvas.clipRect(Rect.fromLTWH(0.0, 0.0, size.width, size.height * (_batteryLevel / 100)));

    Color indicatorColor;

    if (_batteryLevel < 15) {
      indicatorColor = Colors.red;
    } else if (_batteryLevel < 30) {
      indicatorColor = Colors.orange;
    } else {
      indicatorColor = Colors.green;
    }

    canvas.drawRRect(
      RRect.fromLTRBR(0.5, 0.5, size.width - 0.5, size.height - 0.5, const Radius.circular(2.0)),
      getPaint(style: PaintingStyle.fill, color: indicatorColor),
    );
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    final BatteryLevelPainter old = oldDelegate as BatteryLevelPainter;

    return old._batteryLevel != _batteryLevel || old._batteryState != _batteryState;
  }
}

Battery Level Indicator


Solution

  • Use Rect.fromLTRB() instead of Rect.fromLTWH(). Or you can use Rect.fromCircle() or Rect.fromCenter() or Rect.fromPoints(). Rect class

    Add this line:

    canvas.translate(0.0, (size.height - size.height * (_batteryLevel / 100)));
    

    The complete code:

    class BatteryLevelPainter extends CustomPainter {
      final int _batteryLevel;
      final BatteryState _batteryState;
    
      BatteryLevelPainter(this._batteryLevel, this._batteryState);
    
      @override
      void paint(Canvas canvas, Size size) {
        Paint getPaint({Color color = Colors.black, PaintingStyle style = PaintingStyle.stroke}) {
          return Paint()
            ..color = color
            ..strokeWidth = 1.0
            ..style = style;
        }
    
        final RRect batteryOutline = RRect.fromLTRBR(0.0, 0.0, size.width, size.height, const Radius.circular(2.0));
    
        // Battery body
        canvas.drawRRect(
          batteryOutline,
          getPaint(),
        );
    
         canvas.translate(0.0, (size.height - size.height * (_batteryLevel / 100))); // add this line
    
        // Battery nub
        canvas.drawRect(
          const Rect.fromLTWH(4.0, -3.0, 4.0, 3.0),
          getPaint(style: PaintingStyle.fill),
        );
    
        // Fill rect
        canvas.clipRect(Rect.fromLTWH(0.0, 0.0, size.width, size.height * (_batteryLevel / 100)));
    
        Color indicatorColor;
    
        if (_batteryLevel < 15) {
          indicatorColor = Colors.red;
        } else if (_batteryLevel < 30) {
          indicatorColor = Colors.orange;
        } else {
          indicatorColor = Colors.green;
        }
    
        canvas.drawRRect(
          RRect.fromLTRBR(0.5, 0.5, size.width - 0.5, size.height - 0.5, const Radius.circular(2.0)),
          getPaint(style: PaintingStyle.fill, color: indicatorColor),
        );
      }
    
      @override
      bool shouldRepaint(CustomPainter oldDelegate) {
        final BatteryLevelPainter old = oldDelegate as BatteryLevelPainter;
    
        return old._batteryLevel != _batteryLevel || old._batteryState != _batteryState;
      }
    }