shader
Adds remote compilation to your Flutter application to test fragment shaders.
Usage
Use the GlslFragmentProgramBuilder
widget to get your shader compiled.
Quick example
class ScreenUsingShader extends StatelessWidget {
const ScreenUsingShader({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: GlslFragmentProgramBuilder(
// GLSL shader code
code: '''
#version 320 es
precision highp float;
layout(location = 0) out vec4 fragColor;
void main() {
fragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
''',
builder: (context, shaderProgram) {
if (shaderProgram == null) {
// shader is compiling
return const CircularProgressIndicator();
}
// shader is reader to use
return DoSomethingWithShader(shaderProgram);
},
),
),
);
}
}
Full application example
import 'dart:typed_data';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:shader/shader.dart';
void main() {
runApp(const MaterialApp(home: ScreenUsingShader()));
}
class ScreenUsingShader extends StatelessWidget {
const ScreenUsingShader({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: GlslFragmentProgramBuilder(
code: '''
#version 320 es
precision highp float;
layout(location = 0) out vec4 fragColor;
layout(location = 0) uniform float timeElapsed;
layout(location = 1) uniform vec2 size;
void main() {
float time = fract(timeElapsed);
vec3 color = vec3(time, gl_FragCoord.xy / size.xy);
fragColor = vec4(color, 1.0);
}
''',
builder: (context, shaderProgram) {
if (shaderProgram == null) {
return const Center(child: CircularProgressIndicator());
}
return RebuildEachFrame(builder: (context) {
return SizedBox.expand(
child: CustomPaint(painter: ShaderPainter(shaderProgram)),
);
});
},
),
);
}
}
/// Will paint an area with our beautiful fragment shader
class ShaderPainter extends CustomPainter {
final FragmentProgram shaderProgram;
ShaderPainter(this.shaderProgram);
@override
void paint(Canvas canvas, Size size) {
var time = (DateTime.now().millisecondsSinceEpoch % 3000) / 3000.0;
final shader = shaderProgram.shader(
floatUniforms: Float32List.fromList([time, size.width, size.height]),
);
canvas.drawRect(
Rect.fromLTWH(0, 0, size.width, size.height),
Paint()..shader = shader,
);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
Glowing ring
Based on https://www.shadertoy.com/view/XdlSDs:
import 'dart:math';
import 'dart:typed_data';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:shader/shader.dart';
void main() {
runApp(const MaterialApp(home: ScreenUsingShader()));
}
class ScreenUsingShader extends StatelessWidget {
const ScreenUsingShader({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: GlslFragmentProgramBuilder(
code: '''
#version 320 es
// based on: https://www.shadertoy.com/view/XdlSDs
precision highp float;
layout(location = 0) out vec4 fragColor;
layout(location = 0) uniform float timeElapsed;
layout(location = 1) uniform vec2 size;
void main() {
vec2 p = (2.0*gl_FragCoord.xy-size.xy)/size.y;
float tau = 3.1415926535*2.0;
float a = atan(p.x,p.y);
float r = length(p)*0.75;
vec2 uv = vec2(a/tau,r);
float xCol = (uv.x - (timeElapsed / 3.0)) * 3.0;
xCol = mod(xCol, 3.0);
vec3 horColour = vec3(0.25, 0.25, 0.25);
if (xCol < 1.0) {
horColour.r += 1.0 - xCol;
horColour.g += xCol;
} else if (xCol < 2.0) {
xCol -= 1.0;
horColour.g += 1.0 - xCol;
horColour.b += xCol;
} else {
xCol -= 2.0;
horColour.b += 1.0 - xCol;
horColour.r += xCol;
}
uv = (2.0 * uv) - 1.0;
float beamWidth = (0.7 + 0.5 * cos(uv.x * 10.0 * tau * 0.15 * clamp(floor(5.0 + 10.0 * cos(timeElapsed)), 0.0, 10.0))) * abs(1.0 / (30.0 * uv.y));
vec3 horBeam = vec3(beamWidth);
fragColor = vec4(((horBeam) * horColour), 1.0);
}
''',
builder: (context, shaderProgram) {
if (shaderProgram == null) {
return const Center(child: CircularProgressIndicator());
}
return RebuildEachFrame(builder: (context) {
return LayoutBuilder(builder: (context, constraints) {
return SizedBox.expand(
child: CustomPaint(
painter: ShaderPainter(shaderProgram),
child: Center(
child: SizedBox(
width: constraints.biggest.shortestSide * 0.4,
height: constraints.biggest.shortestSide * 0.4,
child: const FlutterLogo()),
),
),
);
});
});
},
),
);
}
}
/// Will paint an area with our beautiful fragment shader
class ShaderPainter extends CustomPainter {
final FragmentProgram shaderProgram;
ShaderPainter(this.shaderProgram);
@override
void paint(Canvas canvas, Size size) {
var time = (DateTime.now().millisecondsSinceEpoch % 3000) / 3000.0;
final shader = shaderProgram.shader(
floatUniforms: Float32List.fromList([
(0.7 * pi) + (sin(pi * time) * (0.3 * pi)),
size.width,
size.height
]),
);
canvas.drawRect(
Rect.fromLTWH(0, 0, size.width, size.height),
Paint()..shader = shader,
);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
Other resources
- https://github.com/flutter/engine/tree/master/lib/spirv
- https://wolfenrain.medium.com/flutter-shaders-an-initial-look-d9eb98d3fd7a