Skip to content

Commit f2d78fe

Browse files
committed
feat: refactor shader architecture by replacing config classes with builder pattern and enhancing UI components
1 parent 8e82284 commit f2d78fe

19 files changed

+929
-210
lines changed

lib/crt_shader_builder.dart

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_shaders/flutter_shaders.dart';
3+
import 'dart:ui';
4+
import 'shader_builder.dart';
5+
6+
class CrtShaderBuilder extends CustomShaderBuilder {
7+
const CrtShaderBuilder();
8+
9+
@override
10+
void setUniforms(FragmentShader shader, Size size, double time) {
11+
shader
12+
..setFloat(0, size.width)
13+
..setFloat(1, size.height)
14+
..setFloat(2, time);
15+
}
16+
17+
@override
18+
Widget buildShader(
19+
ShaderMetadata metadata,
20+
FragmentShader shader,
21+
Size size,
22+
double time,
23+
Widget? child,
24+
) {
25+
return AnimatedSampler((image, size, canvas) {
26+
if (requiresImageSampler) {
27+
shader.setImageSampler(0, image);
28+
}
29+
canvas.drawRect(
30+
Rect.fromLTWH(0, 0, size.width, size.height),
31+
Paint()..shader = shader,
32+
);
33+
}, child: child ?? const SizedBox());
34+
}
35+
36+
@override
37+
Widget buildControls(BuildContext context) {
38+
return Card(
39+
child: Padding(
40+
padding: const EdgeInsets.all(16.0),
41+
child: Column(
42+
crossAxisAlignment: CrossAxisAlignment.start,
43+
children: [
44+
Text(
45+
'CRT Shader Controls',
46+
style: Theme.of(context).textTheme.titleMedium,
47+
),
48+
const SizedBox(height: 8),
49+
const Text('This shader creates a glitching CRT screen effect with RGB displacement.'),
50+
const SizedBox(height: 16),
51+
Row(
52+
children: [
53+
Icon(Icons.info_outline, size: 16, color: Colors.grey[400]),
54+
const SizedBox(width: 8),
55+
const Text('Animation: Bounded (loops with reverse)'),
56+
],
57+
),
58+
],
59+
),
60+
),
61+
);
62+
}
63+
}

lib/crt_shader_config.dart

Lines changed: 0 additions & 14 deletions
This file was deleted.

lib/main.dart

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import 'package:flutter/material.dart';
22
import 'package:url_launcher/url_launcher.dart';
33

4-
import 'crt_shader_config.dart';
5-
import 'noise_overlay_shader_config.dart';
6-
import 'noise_shader_config.dart';
7-
import 'ntsc_shader_config.dart';
8-
import 'shader_configs.dart';
4+
import 'shader_builder.dart';
5+
import 'crt_shader_builder.dart';
6+
import 'noise_overlay_shader_builder.dart';
7+
import 'noise_shader_builder.dart';
8+
import 'ntsc_shader_builder.dart';
99
import 'shader_screen.dart';
1010
import 'tv_test_screen.dart';
1111

@@ -18,45 +18,65 @@ class ShaderInfo {
1818
final String assetKey;
1919
final String description;
2020
final String sourceUrl;
21-
final ShaderConfig config;
21+
final String author;
22+
final DateTime dateAdded;
23+
final CustomShaderBuilder builder;
2224

2325
const ShaderInfo({
2426
required this.name,
2527
required this.assetKey,
2628
required this.description,
2729
required this.sourceUrl,
28-
required this.config,
30+
required this.author,
31+
required this.dateAdded,
32+
required this.builder,
2933
});
34+
35+
ShaderMetadata get metadata => ShaderMetadata(
36+
assetKey: assetKey,
37+
name: name,
38+
url: sourceUrl,
39+
author: author,
40+
dateAdded: dateAdded,
41+
);
3042
}
3143

3244
final shaders = [
33-
const ShaderInfo(
45+
ShaderInfo(
3446
name: 'NTSC',
3547
assetKey: 'shaders/ntsc_shader.frag',
3648
description: 'An effect that emulates an old NTSC television signal.',
3749
sourceUrl: 'https://www.shadertoy.com/view/3tVBWR',
38-
config: NtscShaderConfig(),
50+
author: 'Shadertoy Community',
51+
dateAdded: DateTime(2024, 1, 15),
52+
builder: const NtscShaderBuilder(),
3953
),
40-
const ShaderInfo(
54+
ShaderInfo(
4155
name: 'CRT',
4256
assetKey: 'shaders/crt_shader.frag',
4357
description: 'A glitching screen effect',
4458
sourceUrl: 'https://www.shadertoy.com/view/lt3yz7',
45-
config: CrtShaderConfig(),
59+
author: '@tommclaughlan',
60+
dateAdded: DateTime(2024, 1, 20),
61+
builder: const CrtShaderBuilder(),
4662
),
47-
const ShaderInfo(
63+
ShaderInfo(
4864
name: 'Noise',
4965
assetKey: 'shaders/noise_shader.frag',
5066
description: 'Animated gradient noise with film grain effect',
5167
sourceUrl: 'https://www.shadertoy.com/view/DdcfzH',
52-
config: NoiseShaderConfig(),
68+
author: 'Shadertoy Community',
69+
dateAdded: DateTime(2024, 2, 1),
70+
builder: const NoiseShaderBuilder(),
5371
),
54-
const ShaderInfo(
72+
ShaderInfo(
5573
name: 'Noise Overlay',
5674
assetKey: 'shaders/noise_overlay_shader.frag',
5775
description: 'Applies animated noise effect as an overlay on content',
5876
sourceUrl: 'https://www.shadertoy.com/view/DdcfzH',
59-
config: NoiseOverlayShaderConfig(),
77+
author: 'Shadertoy Community',
78+
dateAdded: DateTime(2024, 2, 5),
79+
builder: const NoiseOverlayShaderBuilder(),
6080
),
6181
];
6282

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_shaders/flutter_shaders.dart';
3+
import 'dart:ui';
4+
import 'shader_builder.dart';
5+
6+
class NoiseOverlayShaderBuilder extends CustomShaderBuilder {
7+
final double filmGrainIntensity;
8+
final double noiseOpacity;
9+
10+
const NoiseOverlayShaderBuilder({
11+
this.filmGrainIntensity = 0.1,
12+
this.noiseOpacity = 0.3,
13+
});
14+
15+
@override
16+
bool get requiresImageSampler => true;
17+
18+
@override
19+
Duration? get animationDuration => null; // Unbounded animation
20+
21+
@override
22+
void setUniforms(FragmentShader shader, Size size, double time) {
23+
shader
24+
..setFloat(0, size.width)
25+
..setFloat(1, size.height)
26+
..setFloat(2, time)
27+
..setFloat(3, filmGrainIntensity)
28+
..setFloat(4, noiseOpacity);
29+
}
30+
31+
@override
32+
Widget buildShader(
33+
ShaderMetadata metadata,
34+
FragmentShader shader,
35+
Size size,
36+
double time,
37+
Widget? child,
38+
) {
39+
return AnimatedSampler((image, size, canvas) {
40+
shader.setImageSampler(0, image);
41+
canvas.drawRect(
42+
Rect.fromLTWH(0, 0, size.width, size.height),
43+
Paint()..shader = shader,
44+
);
45+
}, child: child ?? const SizedBox());
46+
}
47+
48+
@override
49+
Widget buildControls(BuildContext context) {
50+
return Card(
51+
child: Padding(
52+
padding: const EdgeInsets.all(16.0),
53+
child: Column(
54+
crossAxisAlignment: CrossAxisAlignment.start,
55+
children: [
56+
Text(
57+
'Noise Overlay Controls',
58+
style: Theme.of(context).textTheme.titleMedium,
59+
),
60+
const SizedBox(height: 8),
61+
const Text('Applies animated noise effect as an overlay on content'),
62+
const SizedBox(height: 16),
63+
Row(
64+
children: [
65+
Icon(Icons.grain, size: 16, color: Colors.grey[400]),
66+
const SizedBox(width: 8),
67+
Text('Film Grain: ${(filmGrainIntensity * 100).toInt()}%'),
68+
],
69+
),
70+
const SizedBox(height: 8),
71+
Row(
72+
children: [
73+
Icon(Icons.opacity, size: 16, color: Colors.grey[400]),
74+
const SizedBox(width: 8),
75+
Text('Opacity: ${(noiseOpacity * 100).toInt()}%'),
76+
],
77+
),
78+
const SizedBox(height: 8),
79+
Row(
80+
children: [
81+
Icon(Icons.all_inclusive, size: 16, color: Colors.grey[400]),
82+
const SizedBox(width: 8),
83+
const Text('Animation: Continuous'),
84+
],
85+
),
86+
],
87+
),
88+
),
89+
);
90+
}
91+
}

lib/noise_overlay_shader_config.dart

Lines changed: 0 additions & 28 deletions
This file was deleted.

lib/noise_shader_builder.dart

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_shaders/flutter_shaders.dart';
3+
import 'dart:ui';
4+
import 'shader_builder.dart';
5+
6+
class NoiseShaderBuilder extends CustomShaderBuilder {
7+
final double filmGrainIntensity;
8+
9+
const NoiseShaderBuilder({this.filmGrainIntensity = 0.1});
10+
11+
@override
12+
bool get requiresImageSampler => false;
13+
14+
@override
15+
Duration? get animationDuration => null; // Unbounded animation
16+
17+
@override
18+
void setUniforms(FragmentShader shader, Size size, double time) {
19+
shader
20+
..setFloat(0, size.width)
21+
..setFloat(1, size.height)
22+
..setFloat(2, time)
23+
..setFloat(3, filmGrainIntensity);
24+
}
25+
26+
@override
27+
Widget buildShader(
28+
ShaderMetadata metadata,
29+
FragmentShader shader,
30+
Size size,
31+
double time,
32+
Widget? child,
33+
) {
34+
return CustomPaint(
35+
size: Size.infinite,
36+
painter: _NoiseShaderPainter(shader),
37+
);
38+
}
39+
40+
@override
41+
Widget buildControls(BuildContext context) {
42+
return Card(
43+
child: Padding(
44+
padding: const EdgeInsets.all(16.0),
45+
child: Column(
46+
crossAxisAlignment: CrossAxisAlignment.start,
47+
children: [
48+
Text(
49+
'Noise Shader Controls',
50+
style: Theme.of(context).textTheme.titleMedium,
51+
),
52+
const SizedBox(height: 8),
53+
const Text('Animated gradient noise with film grain effect'),
54+
const SizedBox(height: 16),
55+
Row(
56+
children: [
57+
Icon(Icons.grain, size: 16, color: Colors.grey[400]),
58+
const SizedBox(width: 8),
59+
Text('Film Grain: ${(filmGrainIntensity * 100).toInt()}%'),
60+
],
61+
),
62+
const SizedBox(height: 8),
63+
Row(
64+
children: [
65+
Icon(Icons.all_inclusive, size: 16, color: Colors.grey[400]),
66+
const SizedBox(width: 8),
67+
const Text('Animation: Continuous'),
68+
],
69+
),
70+
],
71+
),
72+
),
73+
);
74+
}
75+
}
76+
77+
class _NoiseShaderPainter extends CustomPainter {
78+
final FragmentShader shader;
79+
80+
_NoiseShaderPainter(this.shader);
81+
82+
@override
83+
void paint(Canvas canvas, Size size) {
84+
canvas.drawRect(
85+
Rect.fromLTWH(0, 0, size.width, size.height),
86+
Paint()..shader = shader,
87+
);
88+
}
89+
90+
@override
91+
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
92+
}

0 commit comments

Comments
 (0)