Skip to content

Instantly share code, notes, and snippets.

@jonahwilliams
Created June 10, 2025 17:15
Show Gist options
  • Save jonahwilliams/9c81fd00f7d97b007016b4b5bbf59e20 to your computer and use it in GitHub Desktop.
Save jonahwilliams/9c81fd00f7d97b007016b4b5bbf59e20 to your computer and use it in GitHub Desktop.
Flutter liquid Glass
#include <flutter/runtime_effect.glsl>
uniform vec2 u_size;
uniform float u_time;
uniform sampler2D u_texture_input;
#define I FlutterFragCoord()
#define R u_size.xy
#define PI 3.14159265
#define S smoothstep
#define PX(a) a/R.y
#define iMouse vec4(450, 1200, 100, 100)
out vec4 frag_color;
mat2 Rot (float a) {
return mat2(cos(a), sin(-a), sin(a), cos(a));
}
float Box (vec2 p, vec2 b) {
vec2 d = abs(p) - b;
return length(max(d,0.)) + min(max(d.x,d.y),0.);
}
vec4 LiquidGlass (sampler2D tex, vec2 uv, float direction, float quality, float size) {
vec2 radius = size/R;
vec4 color = texture(tex, uv);
for (float d = 0.; d < PI; d += PI/direction) {
for (float i = 1./quality; i <= 1.; i += 1./quality) {
color += texture(tex, uv + vec2(cos(d),sin(d)) * radius * i);
}
}
color /= quality * direction;
return color;
}
vec4 Icon (vec2 uv) {
float box = Box(uv, vec2(PX(50.))),
boxShape = S(PX(1.5), 0., box - PX(50.)),
boxDisp = S(PX(35.), 0., box - PX(25.)),
boxLight = boxShape * S(0., PX(30.), box - PX(40.)),
icon = 0;
return vec4(boxShape, boxDisp, boxLight, icon);
}
void main() {
vec2 uv = I/R,
st = (I-.5*R)/R.y,
M = iMouse.z > 0. ? (iMouse.xy-.5*R)/R.y : vec2(0.);
vec4 icon = Icon(st-M);
vec2 uv2 = uv - iMouse.xy/R;
uv2 *= S(-.2, 1., icon.y);
uv2 += iMouse.xy/R;
vec3 col = mix(texture(u_texture_input, uv).rgb * .8, .1 + LiquidGlass(u_texture_input, uv2, 10., 10., 20.).rgb * .7, icon.x);
col += icon.z * .9 + icon.w;
frag_color = vec4(col, 1.0);
}
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
late final ui.FragmentProgram program;
void main() async {
program = await ui.FragmentProgram.fromAsset('shaders/glass.frag');
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: Stack(
children: [
const MyHomePage(title: 'Flutter Demo Home Page'),
ClipRect(child: Center(child: BackdropFilter(filter: ui.ImageFilter.shader(program.fragmentShader()), child: SizedBox(width: 80, height: 80)))),
]
),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('You have pushed the button this many times:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment