GraphX™

GraphX lib for Flutter.

WARNING: this lib is on alpha stage, the api can change.

NOTE: GraphX™ uses the $ prefix convention for all internal and private members (properties and methods). DO NOT call them in your code... is meant to be consumed internally by the lib, it will remain as it is, at least initially, while the package takes shape.

As graphx is about visuals, here you have some screen captures of random prototypes I've been doing, while developing and testing graphx.

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f4e4d47386766704a784669753165414c5a6f2f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f5249727668665a6f4474616c3431546234652f67697068792d646f776e73697a65642e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f705164657572554f417157645a757878554b2f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f5659534a463675554f3332334656304e68682f67697068792e676966

Background.

GraphX™ is here to help you build custom drawings in your Flutter apps. Providing a great versatility to power those screen pixels to a different level.

It's inspired by the good-old Flash API, which forged my way into programming back in the days, and inspired many other rendering frameworks, in several languages through the years.

I was thinking how much I missed to "play" with code, to make things more organic, artistic, alive... I totally love Flutter, but I always feel that it requires too much boilerplate to make things move around (compared to what I used to code).

Even if GraphX™ is not an animation library (although has a small tween engine), nor a game engine, It can help you build really awesome user experiences! It just runs on top of CustomPainter... Using what Flutter SDK exposes from the SKIA engine through the Canvas, yet, gives you some "framework" to run isolated from the Widget's world.

Can be used to simple draw a line, a circle, maybe a custom button, some splash effect on your UI, or even a full-blown game in a portion of the screen.

Mix and match with Flutter as you please, as GraphX™ uses CustomPainter, it is part of your Widget's tree.

Concept.

This repo is a very early WIP ... the library still lacks of support for loading remote assets, 2.5 transformation and some other niceties.

Yet, it has a ver basic support for loading rootBundle assets:

AssetLoader.loadBinary(assetId)  
AssetLoader.loadGif(assetId)  
AssetLoader.loadTextureAtlas(imagePath, xmlPath)  
AssetLoader.loadTexture(assetId)  
AssetLoader.loadImage(assetId)  
AssetLoader.loadString(assetId)  
AssetLoader.loadJson(assetId)  

GraphX™ also provides basic "raw" support for Text rendering, using the StaticText class.


How does it work?

GraphX™ drives a CustomPainter inside. The idea is to simplify the usage of Flutter's Canvas, plus adding the display list concept, very similar to the Widget Tree concept; so you can imperatively code, manage and create more complex "Scenes".

The library has its own rendering cycle using Flutter's Ticker (pretty much like AnimationController does), and each SceneWidgetBuilder does its own input capture and processing (mouse, keyboard, touches). Even if it runs on the Widget tree, you can enable the flags to capture mouse/touch input, or keystrokes events (if u wanna do some simple game, or desktop/web tool).

Sample code.

  body: Center(
    child: SceneBuilderWidget( /// wrap any Widget with SceneBuilderWidget
      builder: () => SceneController.withLayers(
        back: GameSceneBack(), /// optional provide the background layer
        front: GameSceneFront(), /// optional provide the foreground layer
      ),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text(
            'You have pushed the button this many times:',
          ),
          Text(
            '$_counter',
            style: Theme.of(context).textTheme.headline4,
          ),
        ],
      ),
    ),
  ),

GraphX™ is based on "Scenes" layers, each SceneBuilderWidget requires a SceneController.
This controller is the "initializer" of the Scenes layers, which can be:

  • back (background painter),
  • front (foreground painter),
  • or both.

Each "Scene Layer" has to extend SceneRoot, which represents the starting point of that particular scene hierarchy. Think of it as MaterialApp widget is to all other children Widgets in the tree.

Here we get into GraphX™ world, no more Widgets Trees or immutable properties.

You can override init() to setup things needed for this current Scene Painter object, like if it needs keyboard/mouse/touch access, or if it requires a Ticker and redraw the CustomPainter, cause it will animate.

Override ready() as your entry point, here the engine is set up, and the glorified Stage is available to use.

class GameScene extends SceneRoot {
  GameScene(){
    config(autoUpdateAndRender: true, usePointer: true);
  }

  @override
  void addedToStage() {
    /// if you have to stop the Ticker. Will stop all
    /// Tweens, Jugglers objects in GraphX.
    stage.scene.core.ticker.pause();
  }

For now, GraphX™ has a couple of classes for rendering the display list:
Shape and Sprite ( which are DisplayObject, DisplayObjectContainer are abstracts),

They both have a graphics property which is of type Graphics and provides a simplistic API to paint on the Flutter's Canvas.

By the way, SceneRoot is a Sprite as well!, and it's your root node in the display tree, kinda where all starts to render, and where you need to add your own objects.

For instance, to create a simple purple circle:

@override
void addedToStage(){
    var circle = Shape();
    circle.graphics.lineStyle(2, Colors.purple.value) /// access hex value of Color
      ..drawCircle(0, 0, 20)
      ..endFill();
    addChild(circle); // add the child to the rootScene.
}

SceneRoot is a Sprite (which is a DisplayObjectContainer) and can have children inside, yet Shape is a DisplayObject, and can't have children. But it makes it a little bit more performant on each painter step.

We could also use our root scene to draw things:

@override
addedToStage(){
  graphics.beginFill(0x0000ff, .6)
  ..drawRoundRect(100, 100, 40, 40, 4)
  ..endFill();
...
}

Scene setup sample in your SceneRoot (in your constructor):

config(
  autoUpdateAndRender: true,
  usePointer: true,
  useTicker: true,
  useKeyboard: false,
  sceneIsComplex: true,
);

Pointer signals has been "simplified" as Mouse events now... as it's super easy to work with single touch / mouse interactions in DisplayObjects.
There're a bunch of signals to listen on each element... taken from AS3, and JS.

  • onMouseDoubleClick
  • onMouseClick
  • onMouseDown
  • onMouseUp
  • onMouseMove
  • onMouseOver
  • onMouseOut
  • onMouseScroll

They all emit a MouseInputData with all the needed info inside, like stage coordinates, or translated local coordinates, which "mouse" button is pressed, etc.


I will keep adding further explanation in the upcoming days.

Demos.

Some demos are only using GraphX™ partially, and might have big CPU impact

Flutter widget mix

Split RGB

Input Text particles

FishEye particles

FishEye particles (basic)

Particles emitter

ShapeMaker clone

Mouse follower

basic hit test

SpriteSheet rendering

DisplayObject Pivot

Simple solo-ping-pong game

First Experimentation with rendering

Screencast Demos

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f5157487566494b3947794541494d3444636e2f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f4f7447706d64316641567a7733704b376b442f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f75564676464f5455494341735971623133722f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f7a316149517a59535347564b65576261624a2f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f4d6a58544b4a70656e3876494e33347266572f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f4559345268566f7148544b56424a554e7a572f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f6134527a64613875764678435076664932322f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f756c6948525657565735496c6c696c6949692f67697068792d646f776e73697a65642e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f6636554a6a3336487146594a75656a7a354d2f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f4c643358495945724b736f794351747a63672f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f4b695372464e5951376b454431487a536c4a2f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f74305a634f555064437467386150744c32422f67697068792d646f776e73697a65642e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f73694d4e7a66525754614b4b395077306e322f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f59667a4e4c6d6645316875745749313736652f67697068792d646f776e73697a65642e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f67433934494f6475367631476f574a5a57592f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f31747362614f32385958587863316c7673642f67697068792d646f776e73697a65642e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f45496e59334d4b5a3278766d594e6c33666d2f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f3453737075773352385264723274734534542f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f7861454e3632766d4551785452317a4670792f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f513263497355333443627a5a48664e41327a2f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f6439635154306d4f776762524a32666279642f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f484c647145517a65334c55446c444354426f2f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f5a394437627057716a58384b4a4d544d4d632f67697068792d646f776e73697a65642e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f7579675a63515049653744703452484872422f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f6c56426b5a366f317142716e656b3932516a2f67697068792d646f776e73697a65642e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f37543368716e48633763527271456a4534612f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f7a39464677743673505153717256754d79462f67697068792d646f776e73697a65642e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f4c646a3769385869505a70595a3932574e4e2f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f4c46416843777737765649746566373876392f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f776b3873376a4a6e776664425166626476622f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f584e4f3551704a43796374644c5a594d43532f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f445762757452303168394c707363685644412f67697068792e676966

68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f31746a586c574731496d5068493365696a342f67697068792e676966

GitHub