flutter_painter 0.1.0 flutter_painter: ^0.1.0 copied to clipboard
A Flutter widget to draw scribbles and text on a background image or color
Flutter Painter 🎨🖌️ #
A pure-Flutter package for painting.
Summary #
flutter_painter
provides you with a widget that can be used to draw on it. Right now, it supports:
- Free-style drawing: Scribble anything you want with any width and color.
- Text: Add text of any
TextStyle
onto the drawing and control its position, size and rotation in a way you're familiar with with other applications.
In flutter_painter
, these are called drawables.
flutter_painter
also allows you to choose a color or an image for the background of your drawing. You can export your painting as an image.
Usage #
First, you'll need a PainterController
object. The PainterController
controls the different drawables, the background you're drawing on and provides the FlutterPainter
widget with the settings it needs. Then, in your UI, use the FlutterPainter
widget with the controller assigned to it.
class ExampleWidget extends StatefulWidget {
const ExampleWidget({Key? key}) : super(key: key);
@override
_ExampleWidgetState createState() => _ExampleWidgetState();
}
class _ExampleWidgetState extends State<ExampleWidget> {
PainterController controller = PainterController();
@override
Widget build(BuildContext context) {
return SizedBox(
width: 300,
height: 300,
child: FlutterPainter(controller: controller,),
);
}
}
Note that FlutterWidget
does not define its own constraints on its size, so it is advised to use a widget that can provide its child with size constraints, such as SizedBox
or AspectRatio
(more on constraints here).
PainterController
#
The PainterController
is the heart of the operation of flutter_painter
. It controls the settings for FlutterPainter
, its background, and all of its drawables.
All setters on PainterController
directly notify your FlutterPainter
to respond and repaint. However, note that if you also respond to changes in the different fields of the controller, you'll need to use setState
to re-build your widget.
NOTE: If you are using multiple painters, make sure that each
FlutterPainter
widget has its ownPainterController
, do not use the same controller for multiple painters.
Settings #
There are currently three types of settings:
freeStyleSettings
: They control the parameters used in drawing scribbles, such as the width and color. It also has a field to enable/disable scribbles, to prevent the user from drawing on theFlutterPainter
.textSettings
: They mainly control theTextStyle
of the text being drawn. It also has a focus node field (more on focus nodes here) to allow you to detect when the user starts and stops editing text.objectSettings
: These settings control objects that can be moved, scaled and rotated. Texts are considered objects (they are currently the only ones, but there are plans to add more in the future, such as images). It mainly controls layout assist, which allows to center objects and rotate them at a right angle, as shown here:
You can provide initial settings for the things you want to draw through the settings parameter in the constructor of the PainterController
.
All of the settings objects are immutable and cannot be modified, so in order to change some settings, you'll have to create a copy of your current settings and apply the changes you need (this is similar to how you would copy ThemeData
). For example, this is how you would change the stroke width for your scribbles:
void setStrokeWidth(double value){
controller.freeStyleSettings = controller.freeStyleSettings.copyWith(
strokeWidth: value
);
}
Background #
You can also provide a background for the FlutterPainter
widget from the controller. You can either use a color or an image as a background.
In order to use a color, you can simply call the backgroundDrawable
getter on any color.
void setBackground(){
// Sets the background to the color black
controller.background = Colors.black.backgroundDrawable;
}
In order to use an image, you will need an Image
object from the dart library dart:ui
. Since Flutter has an Image
type from the Material package, we'll refer to the image type we need as ui.Image
.
import 'dart:ui' as ui;
ui.Image? myImage;
In order to get the ui.Image
object from usual image sources (file, asset, network), you can use an ImageProvider
with the image
getter (Examples of ImageProvider
: FileImage
, MemoryImage
, NetworkImage
). This getter returns a future.
Then, you can use the backgroundDrawable
getter on the ui.Image
.
void setBackground() async {
// Obtains an image from network and creates a [ui.Image] object
final ui.Image myImage = await NetworkImage('https://picsum.photos/960/720').image;
// Sets the background to the image
controller.background = myImage.backgroundDrawable;
}
The background can also be assigned from the constructor of PainterController
directly.
Drawables #
All the drawables drawn on FlutterPainter
are stored and controller by the PainterController
. On most use cases, you won't need to interact with the drawables directly. However, you may add your own drawables however you wish (without the user actually drawing them).
You can assign an initial list of drawables
from the PainterController
constructor to initialize the controller with them. You can also modify them from the controller, but be careful, use the methods from the PainterController
itself and don't modify the drawables
list directly.
DO:
void addMyDrawables(List<Drawable> drawables){
controller.addDrawables(drawables);
}
DON'T:
void addMyDrawables(List<Drawable> drawables){
controller.drawables.addAll(drawables);
}
Rendering Image #
From the PainterController
, you can render the contents of FlutterPainter
as a PNG-encoded ui.Image
object. In order to do that, you need to provide the size of the output image. All the drawings will be scaled according to that size.
From the ui.Image
object, you can convert it into a raw bytes list (Uint8List
) in order to display it with Image.memory
or save it as a file.
Uint8List? renderImage(Size size) async {
final ui.Image renderedImage = await controller.renderImage(size);
final Uint8List? byteData = await renderedImage.pngBytes;
return byteData;
}
Notes #
-
Scaling and rotating objects (such as Text) is not currently possible using a mouse pointer. You can still programmatically set your own scaling and rotation. A suitable implementation for this is planned in the future.
-
Testing is not available right now because I'm not familiar with it. If anybody is willing to help out with it, it would be highly appreciated (contact me through my GitHub).
Example #
You can check out the example tab for an example on how to use the package.
A video recording showing the example running: