GraphX는 CustomPainter 기반의 렌더링 API를 제공하는 Flutter 패키지로, Flutter 애플리케이션에서 고급이고 고성능 그래픽을 구현할 수 있게 해줍니다. 개발자가 동적 시각 효과, 애니메이션 및 상호작용 그래픽을 쉽게 만들 수 있도록 지원합니다. 복잡한 페인팅 로직을 추상화함으로써 GraphX는 사용자 정의 시각 요소의 구현을 단순화하면서도 여러 플랫폼에서 최적의 성능을 유지합니다. 풍부한 시각 경험을 필요로 하는 앱에 적합하며, Android, iOS, Web, macOS, Windows, Linux를 모두 지원합니다.
사용 사례
복잡한 애니메이션 만들기
상호작용 가능한 시각화 대시보드 만들기
사용자 정의 UI 요소 구현
데이터 시각화 도구 개발
게임 그래픽 향상
주요 기능
CustomPainter를 통한 고성능 렌더링
크로스 플랫폼 호환성
내장 애니메이션 유틸리티
유연한 그림 API
실시간 업데이트를 지원합니다
적합한 대상
고급 그래픽을 찾는 Flutter 개발자
시각 효과를 구현하는 UI/UX 디자이너
게임 및 대시보드 앱 제작자
데이터 시각화 프로젝트
고려 사항
CustomPainter에 대한 이해가 필요합니다
간단한 UI에 대해 복잡성을 증가시킬 수 있음
성능은 구현에 따라 달라집니다
시각적으로 집중적인 앱에 가장 적합
원본 README
연결된 소스 저장소의 README 원문 스냅샷입니다. 최신 내용은 저장소에서 확인하세요.
| rendering | prototype | design |
Making drawings and animations in Flutter, super simple and FUN.
v1.0.14 has some breaking changes, the flutter_svg dependency was removed, you can copy paste the
old SvgUtils from the repo and use your own
flutter_svg constraints. Use ResourceLoader.setSvgDataParser() to keep the svg loading support.
To get some extended, boring explanations, and eventually some sample codes, check
the GraphX™ Wiki
prototyping.
GraphX is all about visuals, here you have some screen captures of random prototypes I've been
doing, while developing and testing graphx.
For your GraphX scene to support Hot Reload, you should initialize your variables and
DisplayObjects inside addedToStage, and optionally clean them in dispose.
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.
The repo is in early stages. You can check the changelog to get the latest updates.
ResourceLoader also stores in cache based on the assetId or url provided. You can pass cacheId
in most methods
to override that, once the resources loaded, you can access them with:
GraphX™ also provides "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(
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", 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.
Also takes a SceneConfig(), so you can configure what you need from the Widget's side.
You can make use of some predefined Scene configurators:
SceneConfig.static: If you plan to only use this scene to draw some graphics, like a background.
SceneConfig.games: Activates all GraphX features, auto render and update, pointers and keyboard
support.
SceneConfig.tools: Shortcut of games, helpful if you wanna use it in some custom drawing
editor, or similar with keyboard shortcuts.
SceneConfig.interactive (default): Probably the most common setup for mobile, enables all
features except keyboard support.
SceneConfig.autoRender: Allows you to have a ticker running, and auto update the scene, with NO
inputs (mouse/touch/keyboard), if you wanna have an animated Widget, or maybe if you wanna control
it externally.
Each "Scene" has to extend Sprite, this root class 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 make custom UI widgets, games, or make use of GraphX to create a static drawing, like curved
backgrounds, or complex shapes.
Is a good practice to override addedToStage() as your entry point, here the Scene is ready,
the root class has been added to the glorified stage, so you can access the Canvas size
through stage.stageWidth and stage.stageHeight, the keyboard manager (if available), and lots of
other properties, up to the SceneController that owns the scene (stage.scene.core, although,
that's irrelevant for now):
classGameSceneextendsSprite{
@overridevoidaddedToStage() {
/// Here you can access the `stage`, get the size of the/// current Scene, keyboard events, or any stage property/// You can initialize your DisplayObjects here to play/// "safe" if you need to access any stage property.
}
For now, GraphX™ has a few classes for rendering in the "display list":
Like Shape (for "pen" drawings commands through it's graphics property), Sprite (create
hierarchies of rendering objects), StaticText (for Texts), GxIcon (for Flutter icons)
, Bitmap (for GTexture, which is a wrapper around dart:ui.Image), MovieClip(for Spritesheet
and Gif support), SvgShape (dependency for svg, package not included), SimpleParticleSystem (
to create optimized particles for games), and Flare/Rive render objects which will live in another
package/utility eventually to avoid dependencies.
By the way, in the previous example, GameScene is the root node in the display tree, the entry
point where DisplayObjects renders, and where you need to add your own objects.
For instance, to create a simple purple circle:
@overridevoidaddedToStage() {
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.
}
Sprite internally extends from the abstract class DisplayObjectContainer, and as the name
implies, is a container that can contain more DisplayObjects. Yet, Shape is a DisplayObject (
another abstract class, and also, the root class of all rendering objects in GraphX), so it
can't contain children. That makes it a bit more performant on each painter step.
So, when you need to group objects, you should create Sprites and add children into it, even
other Sprites, that's the idea of GraphX after all, group rendering objects so you can
transform them independently or transform a parent Sprite (or subclass of it), and apply it to the
tree inside of it, transformations are accumulative from parent to child ...
What is a transformation?
The ability to translate, scale, rotate, skew a DisplayObject through his properties: x, y, width,
height, scaleX, scaleY, rotation, skewX, skewY, etc.
Pointer signals has been "simplified" as Mouse events now... as it's super easy to work with single
touch / mouse interactions in DisplayObjects.
There are a bunch of signals to listen on each object... 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.