Skip to content
Blog

Flutter GPU: The Experimental Graphics API Explained

What Flutter GPU is, how it lets you write custom renderers in Dart and GLSL with no native code, what the 3.47.1 fixes change, and when to actually use it.

Published on • October 8, 2026

AI Assistant

Flutter has always had two escape hatches for graphics: custom fragment shaders through FragmentProgram, and platform-specific code when you need a real GPU pipeline. Flutter GPU is the third option — a low-level graphics API that ships inside the Flutter SDK, letting you write custom renderers in Dart and GLSL with no native platform code at all.

The Flutter API reference describes it in one line: “Flutter GPU is a low level API for building rendering packages from scratch.”

Where it sits in the stack

To understand Flutter GPU, it helps to see the layers:

Your app widgets
  └─ CustomPainter / Canvas        ← everyday Flutter drawing
       └─ FragmentProgram (GLSL)   ← custom fragment shaders
            └─ Impeller            ← Flutter's rendering engine
                 └─ Flutter GPU    ← raw render passes, buffers, textures
                      └─ Vulkan / Metal / OpenGL ES

Flutter GPU sits below the widget layer and beside Impeller’s own pipeline. You get explicit CommandBuffers, RenderPasses, Textures, DeviceBuffers, vertex layouts, and blend/depth/stencil state — the vocabulary of a modern graphics API, but written in Dart.

The class list from the API docs reads like a mini Vulkan: GpuContext, CommandBuffer, RenderTarget, ColorAttachment, DepthStencilAttachment, DeviceBuffer, VertexBuffer, VertexLayout, VertexAttribute, Texture, Sampler, ShaderLibrary.

The rules of the preview

The engine’s own documentation is blunt about the constraints:

  • Flutter GPU is in an early preview state and does not guarantee API stability.
  • It requires Impeller to be enabled.
  • Automated shader building relies on the experimental Dart “Native Assets” feature.
  • Because it is experimental, switching to the master channel is strongly recommended.

That last point matters in practice: Flutter Scene, the 3D rendering package powered by Flutter GPU, “generally keeps up to date with breaking changes to Flutter GPU’s API, so it’s strongly recommended to use the main channel when experimenting.”

Setup

Flutter GPU is distributed as an SDK package, fetched alongside dart:ui/sky_engine when the Flutter tool downloads artifacts. You add it as an SDK dependency:

dependencies:
  flutter:
    sdk: flutter
  flutter_gpu:
    sdk: flutter
import 'package:flutter_gpu/gpu.dart' as gpu;

Authoring shaders

Flutter GPU shaders are GLSL, and their semantics differ from Flutter’s fragment-shader feature — particularly around uniform bindings. You write both a vertex and a fragment shader, and their entry-point names are how the runtime finds them:

// simple.vert
#version 320 es
precision highp float;

layout(location = 0) in vec2 position;
layout(location = 1) in vec3 color;

layout(location = 0) out vec3 fragColor;

void main() {
  gl_Position = vec4(position, 0.0, 1.0);
  fragColor = color;
}
// simple.frag
#version 320 es
precision highp float;

layout(location = 0) in vec3 fragColor;
layout(location = 0) out vec4 outColor;

void main() {
  outColor = vec4(fragColor, 1.0);
}

The flutter_gpu_shaders package compiles these into a shader bundle. With the experimental native-assets hook enabled, the bundle is built automatically:

dependencies:
  flutter_gpu_shaders:
    # ...

At runtime the library loads once:

gpu.ShaderLibrary? _shaderLibrary;

gpu.ShaderLibrary get shaderLibrary {
  return _shaderLibrary ??= gpu.ShaderLibrary.fromAsset(_kShaderBundlePath);
}

Rendering a frame

The rendering loop is explicit. Allocate a texture, attach it to a render target, encode a render pass, then present that texture into a widget:

final texture = gpu.Texture(
  width: size.width.toInt(),
  height: size.height.toInt(),
  format: gpu.PixelFormat.rgba8888,
);

final renderTarget = gpu.RenderTarget.singleColor(
  gpu.ColorAttachment(texture: texture, clearValue: const gpu.Color(0, 0, 0, 1)),
);

final commandBuffer = gpu.gpuContext.createCommandBuffer();
final renderPass = commandBuffer.createRenderPass(renderTarget);
renderPass.bindPipeline(pipeline);
renderPass.bindVertexBuffer(vertexBuffer);
renderPass.draw();
commandBuffer.submit();

Then draw the result with plain Flutter:

class GpuPreview extends StatelessWidget {
  const GpuPreview({super.key, required this.image});
  final ui.Image image;

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: ImagePainter(image),
      size: const Size.square(300),
    );
  }
}

If Flutter GPU isn’t supported — Impeller disabled, or a platform without an Impeller backend — accessing gpu.gpuContext throws: Exception: Flutter GPU requires the Impeller rendering backend to be enabled. Detect this before building your UI.

What changed in 3.47.1

The Flutter 3.47.1 patch release changelog includes a specific Flutter GPU fix:

Flutter GPU could not be enabled in release builds on Linux and Windows, since those embedders had no project-level opt-in and release builds ignore engine switches from the environment. (flutter/190871)

This is a meaningful bug: on desktop, release builds silently kept Flutter GPU off because the opt-in came from an environment variable the release pipeline doesn’t read. With the fix, project-level opt-in now works on Linux and Windows release builds — so a Flutter GPU feature you tested in debug no longer disappears when you ship.

The same release also fixed an impellerc crash on Windows when paths contain Unicode characters (with better shader compiler error diagnostics), and a resource leak causing crashes with external textures on some GPUs such as Arm Mali — both relevant if you’re shipping GPU-heavy Flutter apps.

What Flutter GPU is not

The Flutter team’s guidance is explicit: “It’s overwhelmingly likely that the vast majority of Flutter devs who will benefit from Flutter GPU’s existence will do so by consuming higher level rendering libraries published on pub.dev, such as the Flutter Scene rendering package.”

Reach for Flutter GPU directly only if you are:

  • Building a rendering library others will consume
  • Doing something the fragment-shader API genuinely can’t express — multi-pass rendering, custom vertex processing, compute-style work
  • Working on 3D, particle systems, or data visualization at a scale that fights the Canvas API

For everything else: CustomPainter plus a fragment shader, or a package built on Flutter GPU.

Flutter GPU vs. Impeller vs. fragment shaders

Fragment shadersFlutter GPUImpeller
LevelPer-pixel effect on a RectFull render pipelineFlutter’s own engine
CodeGLSL onlyDart + GLSLC++ (you don’t write it)
ControlNo vertex control, single passBuffers, passes, blend/depth stateAutomatic
StabilityStableEarly previewStable
Use whenStyling a widget regionBuilding a rendererAlways

Impeller is not being replaced by Flutter GPU — Impeller is what renders your Flutter app, and Flutter GPU is a tool built on the same foundations for people building renderers of their own.

Practical advice for 2026

Pin your channel. If you depend on Flutter GPU, you are tracking an experimental API. Budget for breaking changes, and test on the channel you ship from.

Guard the capability check. Feature-detect gpu.gpuContext at startup and degrade to CustomPainter when unsupported — Impeller isn’t enabled everywhere, and legacy Android devices fall back.

Prototype in debug, verify in release. The Linux/Windows release-build opt-in bug is exactly the class of problem that only shows up in a release pipeline. Test the real artifact.

Prefer a library. Before writing a pipeline by hand, check pub.dev — Flutter Scene and other packages already wrap Flutter GPU for the common cases.

Keep shaders in the bundle. Shader compilation at runtime defeats the purpose; Flutter’s whole performance story (and Impeller’s) is precompiled shaders.

Wrapping up

Flutter GPU opens the bottom of Flutter’s stack to Dart: explicit render passes, buffers, and GLSL, with no Objective-C, Kotlin, or C++ in sight. It’s still an early-preview API with real constraints — Impeller required, native assets experimental, API surface in flux — but the 3.47.1 release-build fix shows it’s maturing. For most Flutter developers the win is indirect: the rendering packages built on Flutter GPU that you’ll eventually import.

References