Floss
Floss is a declarative vector graphics abstraction library for Flutter,
inspired by the Gloss library in Haskell.
It provides a composable, tree-structured DSL for describing 2D graphics using
CustomPainter
, built around immutable data structures and functional-style
state management.
Features
- Declarative graphics tree using
Drawing
andCanvasOp
nodes - Data-driven input handling with an extensible
InputEvent
model - Composable canvas operations: transform, clip, paint, shape primitives, images, paragraphs, shadows, etc.
- Reactive input integrated with Flutter’s gesture and pointer systems
- Optional non-clearing canvas mode to enable "ghosting"/feedback effects
- Frame-by-frame update loop driven by a
Ticker
- Minimal interface (
Iud
) for state, updates, and rendering logic
Architecture
┌──────────────┐
│ FlossWidget │ <─── Flutter widget tree
└─────┬────────┘
│
┌────────────┴────────────┐
│ Config<M> │
└────────┬──────┬─────────┘
│ │
▼ |
ModelCtor<M> │
│
┌──────┴─────┐
│ Iud<M> │ <── App logic: init, update, draw
└────┬───────┘
▼
┌──────────────────┐
│ Drawing Tree │ <─── CanvasOp nodes (e.g., Rect, Translate, Paint)
└──────────────────┘
Core Concepts
Drawing
Tree
- A
Drawing
is a recursive tree of canvas operations (CanvasOp
) - Composite operations like
Translate
,Rotate
,ClipRect
, etc. apply transformations to nested children - Leaf operations like
Rect
,Line
,Image
, etc. perform direct drawing on the canvas
Iud<M>
Your application logic lives in an implementation of the Iud<M>
interface:
init
— creates your modelupdate
— updates your model with time and inputdraw
— builds aDrawing
tree from your modeldispose
/onExitRequested
— lifecycle hooks
M
is parametrically polymorphic, the only requirement is that it can be
constructed using the ModelCtor<M>
function:
import 'package:floss/floss.dart' as floss;
typedef ModelCtor<M> =
M Function({
required ui.Size size,
required floss.InputEventList inputEvents
});
Example
import 'dart:ui' as ui;
import 'package:flutter/material.dart' as m;
import 'package:floss/floss.dart' as f;
// Model with arbitrary state
class MyModel {
final ui.Offset position;
final ui.Size size;
final f.InputEventList inputEvents;
// Only requirement is that there is a `Function` that takes a `ui.Size` and
// a `f.InputEventList` and returns a model object.
MyModel({required this.size, required this.inputEvents})
: position = ui.Offset(size.width / 2, size.height / 2);
// Named constructor to allow for immutable state updates
MyModel.update({
required this.size,
required this.inputEvents,
required this.position,
});
}
// IUD (Input Update Draw) class
class MyIud extends f.IudBase<MyModel> {
// `init()` is inherited from `IudBase` and is called when the model is created.
// This is where the model is updated based on input events
@override
MyModel update({
required MyModel model,
required Duration elapsed,
required ui.Size size,
required f.InputEventList inputEvents,
}) {
for (final event in inputEvents) {
switch (event) {
// update `position` based on mouse events
case f.PointerMove(:final event):
return MyModel.update(
size: size,
inputEvents: inputEvents,
position: event.localPosition,
);
default:
break;
}
}
// If no mouse events, return the model unchanged
return MyModel.update(
size: size,
inputEvents: inputEvents,
position: model.position,
);
}
// This is where the drawing tree is created based on the model
@override
f.Drawing draw({required MyModel model, required bool lightThemeActive}) {
// `f.Drawing` is just a container for other `CanvasOp`s
return f.Drawing(
ops: [
// `Circle` leaf node that draws a circle at the model's position
f.Circle(
center: model.position,
radius: 20,
paint: ui.Paint()..color = m.Colors.white,
),
],
);
}
}
void main() {
m.runApp(
m.MaterialApp(
color: m.Colors.white,
title: 'Floss Example',
builder: (context, child) {
return f.FlossWidget(
focusNode: m.FocusNode(),
config: f.Config(
modelCtor: MyModel.new,
iud: MyIud(),
clearCanvas: const f.ClearCanvas(),
),
);
},
),
);
}
Included Modules
Module | Description |
---|---|
canvas_ops.dart |
Core canvas primitives and Drawing structure |
floss_widget.dart |
Flutter widget integrating the rendering loop |
input_event.dart |
Input abstraction layer over Flutter gestures |
config.dart |
Configuration container for model/Iud/canvas behavior |
miud.dart |
Iud interface and base classes for model logic |
utils.dart |
Vector math utilities (Offset.norm() , .clampLen() ) |
logger.dart |
Optional logger abstraction based on package:logger |
Status
Floss is a work-in-progress library for experimental canvas-heavy or simulation-based UI in Flutter. It is designed to enable rich, purely declarative drawing via trees of composable operations and model-driven input-handling.
License
MIT © 2025 — Contributions welcome!
Tested On
- macOS 15.4.1
- Flutter 3.29.3 • channel stable • https://github.com/flutter/flutter.git
- Framework • revision ea121f8859 (8 days ago) • 2025-04-11 19:10:07 +0000
- Engine • revision cf56914b32
- Tools • Dart 3.7.2 • DevTools 2.42.3
Libraries
- floss
- Floss is a vector graphics abstraction library for Flutter.