| name | flutter-shader |
| description | Develop Flutter fragment shaders (GLSL) end-to-end. Use when creating, debugging, or integrating custom shaders in Flutter apps. Triggers on shader, GLSL, fragment shader, CustomPainter with shader, ShaderMask, visual effects, procedural graphics, or GPU rendering in Flutter. |
Flutter Fragment Shader Development
Comprehensive guide for building, integrating, debugging, and shipping Flutter fragment shaders using Flutter's FragmentProgram / FragmentShader APIs.
Scope and Capabilities
What Flutter Supports
- Fragment shaders (pixel shaders) only - vertex shaders are NOT supported
- Core runtime objects:
FragmentProgram: compiled shader asset that creates shader instancesFragmentShader: instance with uniforms bound toPaint.shader
Backend Awareness
Flutter runs with Skia or Impeller backends:
- Both support custom shaders
ImageFilter.shaderis Impeller-only- Performance characteristics differ between backends
Project Setup Checklist
1. File Structure
/shaders/
my_effect.frag
/lib/
shaders/
widgets/
2. Declare in pubspec.yaml
flutter:
shaders:
- shaders/my_effect.frag
3. Hot Reload
In debug mode, shader edits trigger recompilation on hot reload/restart.
GLSL Authoring Rules
Required Header
#version 460 core
#include <flutter/runtime_effect.glsl>
out vec4 fragColor;
Coordinate Access
Use FlutterFragCoord() instead of gl_FragCoord:
vec2 p = FlutterFragCoord().xy / u_size;
Key Limitations
- Only
sampler2D(no samplerCube, etc.) - Only
texture(sampler, uv)two-argument form - No extra varying inputs
- No UBO/SSBO
- No unsigned ints or booleans
OpenGLES Y-Flip Fix
When sampling engine textures on OpenGLES:
vec2 uv = FlutterFragCoord().xy / u_size;
#ifdef IMPELLER_TARGET_OPENGLES
uv.y = 1.0 - uv.y;
#endif
Uniform Indexing Rules
Rule 1: Float uniforms use declaration order
float, vec2, vec3, vec4 set via setFloat(index, value) in declaration order.
Rule 2: Samplers have separate index space
sampler2D set via setImageSampler(samplerIndex, image) - does NOT consume float indices.
Example
uniform float uScale; // setFloat(0, ...)
uniform sampler2D uTexture; // setImageSampler(0, ...) - separate!
uniform vec2 uMagnitude; // setFloat(1, x), setFloat(2, y)
uniform vec4 uColor; // setFloat(3..6, r,g,b,a)
shader.setFloat(0, 23); // uScale
shader.setFloat(1, 114); // uMagnitude.x
shader.setFloat(2, 83); // uMagnitude.y
shader.setFloat(3, r); // uColor.r
shader.setFloat(4, g); // uColor.g
shader.setFloat(5, b); // uColor.b
shader.setFloat(6, a); // uColor.a
shader.setImageSampler(0, image); // uTexture
Loading and Using Shaders
Load Program
final program = await FragmentProgram.fromAsset('shaders/my_effect.frag');
Create and Bind Uniforms
final shader = program.fragmentShader();
shader.setFloat(0, timeSeconds);
shader.setFloat(1, width);
shader.setFloat(2, height);
Draw with Shader
canvas.drawRect(rect, Paint()..shader = shader);
ImageFilter (Impeller-only)
ImageFilter.shader(shader) // Only works on Impeller!
Development Workflow
Step 1: Write Shader Spec
Define before coding:
- Inputs (uniforms):
u_time,u_size, effect parameters - Texture sampling requirements
- Application context: full-screen, mask, backdrop
Step 2: Establish Uniform Contract
Consistent ordering convention:
uniform vec2 u_size;uniform float u_time;- Effect parameters...
Mirror in Dart with constants for indices.
Step 3: Create Integration Wrapper
Choose pattern:
CustomPainter- drawing into CanvasShaderMask- masking child contentBackdropFilter/ImageFiltered- post-processing (Impeller-only)
Step 4: Performance Optimization
- Reuse
FragmentShaderinstances - don't recreate each frame - Precache
FragmentProgrambefore animations start - SkSL warm-up for Skia backend jank mitigation
- Use Impeller where available (precompiles at build-time)
Step 5: Cross-Platform Testing
Test on:
- Android (OpenGLES/Vulkan)
- iOS (Metal)
- Web (CanvasKit/skwasm)
Debugging Guide
Black Screen Diagnosis
- Check
fragColoris written - Verify coordinate normalization (divide by size)
- Audit uniform index mapping
Uniform Layout Inspection
Use impellerc + flatc for reflection data on uniform offsets.
Backend Errors
ImageFilter.shader throws on non-Impeller backends.
Additional Resources
- TEMPLATES.md - Copy-paste shader starter kits
- REFERENCE.md - Complete API and troubleshooting guide
Quick Reference
| Task | Tool/Pattern |
|---|---|
| Load shader | FragmentProgram.fromAsset() |
| Create instance | program.fragmentShader() |
| Set float uniform | shader.setFloat(index, value) |
| Set texture | shader.setImageSampler(index, image) |
| Draw to canvas | Paint()..shader = shader |
| Post-process | ImageFilter.shader() (Impeller) |