RomanticShaderWidget

Flutter package for rendering GLSL fragment shaders and a built-in Dart CLI for converting Shadertoy-style GLSL into Flutter-compatible fragment shaders.

More packages by Romantic Developer

Highlights

  • Render runtime shaders with RomanticShaderWidget.
  • Parse compiled shader uniform metadata into ShaderUniformBindings.
  • Build and update float and sampler uniforms with ShaderUniformsBuilder.
  • Convert source GLSL files into Flutter runtime_effect shader files.
  • Generate optional JSON uniform property files for editor-style controls.

Showcase Shader Set

The example app intentionally ships with a compact 3-shader showcase set:

  • 2d_cloud
  • fractal_thing
  • liquid_glass

This keeps the package lightweight for publishing while still demonstrating procedural and texture-based shader workflows.

API Map

  • Widget rendering: RomanticShaderWidget, RomanticShaderPainter
  • Uniform domain: ShaderUniforms, ShaderUniformBindings, UniformValue
  • Uniform builder: ShaderUniformsBuilder and extension image helpers
  • Utility APIs: parseShaderUniforms, readShaderUniformsFromAsset, ShaderConverter
  • CLI APIs: runShaderConverterCli and shader_converter executable

Package Setup

Add the package to your app and include shader assets in pubspec.

Example pubspec asset section:

flutter:
  shaders:
    - assets/generated_shaders/2d_cloud.frag

RomanticShaderWidget Quick Start

import 'package:flutter/material.dart';
import 'package:romantic_shader_widget/romantic_shader_widget.dart';

class ShaderScreen extends StatefulWidget {
  const ShaderScreen({super.key});

  @override
  State<ShaderScreen> createState() => _ShaderScreenState();
}

class _ShaderScreenState extends State<ShaderScreen> {
  ShaderUniformBindings _bindings = const ShaderUniformBindings();

  @override
  void initState() {
    super.initState();
    _loadBindings();
  }

  Future<void> _loadBindings() async {
    final bindings =
        await readShaderUniformsFromAsset('assets/generated_shaders/2d_cloud.json');
    if (!mounted) return;
    setState(() {
      _bindings = bindings;
    });
  }

  @override
  Widget build(BuildContext context) {
    final uniforms = ShaderUniformsBuilder()
        .setTime(1.2)
        .setResolution(1080, 1920)
        .setMouse(0, 0)
        .setViewportScale(1.0)
        .addFloat('intensity', 0.8)
        .build();

    return RomanticShaderWidget(
      shaderAssetPath: 'assets/generated_shaders/2d_cloud.frag',
      uniformBindings: _bindings,
      uniforms: uniforms,
      fallbackColor: Colors.black,
    );
  }
}

GLSL Converter CLI

Use the package executable named shader_converter.

Local package run

Run from this repository root:

dart run bin/shader_converter.dart \
  --input <file-or-dir> \
  --output-dir <dir> \
  [--properties-dir <dir>] \
  [--map <sourceA:targetA,sourceB:targetB>] \
  [--overwrite] \
  [--dry-run] \
  [--no-mobile-compat]

Global activation

dart pub global activate --source path .
shader_converter --help

If your shell PATH does not include the Pub cache bin directory, use:

dart pub global run romantic_shader_widget:shader_converter --help

Converter example

dart run bin/shader_converter.dart \
  --input original_shaders \
  --output-dir build/generated_shaders \
  --properties-dir build/generated_properties \
  --dry-run

Flutter compatibility guarantees

The converter enforces these runtime shader requirements:

  • Injects #include <flutter/runtime_effect.glsl>
  • Injects precision highp float;
  • Ensures out vec4 fragColor;
  • Converts gl_FragCoord to FlutterFragCoord-based coordinates
  • Converts gl_FragColor to fragColor
  • Adds a main wrapper for mainImage shaders
  • Adds standard float uniforms when missing: iTime, iResolutionX, iResolutionY, iMouseX, iMouseY, viewportScale
  • Returns non-zero exit code on conversion failures

Exit codes

  • 0: success
  • 1: one or more files failed conversion
  • 2: input path missing or no GLSL files discovered
  • 64: argument or usage error

Supported Features

Runtime widget and API support

  • Flutter runtime fragment shader rendering via RomanticShaderWidget and RomanticShaderPainter.
  • Shader loading from assets compiled under flutter.shaders.
  • Uniform binding parsing from:
    • Flutter compiled shader metadata (sksl.uniforms).
    • Plain GLSL uniform declarations.
  • Uniform types currently supported by package runtime bindings:
    • float
    • sampler2D
    • Flutter metadata alias shader (treated as sampler)
  • Fluent uniform updates with ShaderUniformsBuilder:
    • Built-ins: iTime, iResolutionX, iResolutionY, iMouseX, iMouseY, viewportScale, cameraOffsetX, cameraOffsetY
    • Custom floats and textures (addFloat, addTexture, addTextures)

Converter input/output support

  • Input sources:
    • Single .glsl file
    • Directory of .glsl files (recursive)
  • Output:
    • Flutter .frag runtime effect shader files
    • Optional JSON properties file per shader
  • CLI options:
    • Required: --input, --output-dir
    • Optional: --properties-dir, --map, --overwrite, --dry-run, --no-mobile-compat, --help

GLSL syntax and automatic rewrites supported by converter

  • Header/runtime normalization:
    • Removes #version directives.
    • Ensures #include <flutter/runtime_effect.glsl>.
    • Ensures precision highp float;.
    • Ensures out vec4 fragColor;.
  • Entry point adaptation:
    • Keeps main() if present.
    • Wraps mainImage(out vec4, in vec2) into generated main() when main() is missing.
  • Built-in symbol rewrite:
    • gl_FragColor -> fragColor
    • gl_FragCoord -> Flutter coordinate helper
    • iResolution -> iResolutionX/iResolutionY fallback vectors
    • iMouse -> iMouseX/iMouseY fallback vectors
    • iDate -> float-only fallback vector
    • iFrame -> int(iTime * 60.0) fallback
    • iTimeDelta, iFrameRate, iSampleRate, iChannelTime[n] -> compile-safe fallbacks
  • Uniform normalization:
    • Converts const float name = ...; into uniform float name; and uses parsed const as default property value where possible.
    • Auto-injects missing standard float uniforms (iTime, iResolutionX, iResolutionY, iMouseX, iMouseY, viewportScale).
    • Expands uniform samplerXX iChannel0..3; into explicit sampler declarations.
  • Compatibility rewrites for common runtime/parser issues:
    • Rewrites tanh(...), some derivative usage (dFdx, dFdy) to compatibility fallbacks.
    • Rewrites some float-indexed loop forms to int-indexed loops.
    • Rewrites/normalizes some compact matrix constructor forms.
    • Normalizes texture sampling variants toward texture(sampler, coord).

Unsupported or Limited Features

Shader model and pipeline limits

  • Fragment shader workflows only. Vertex/geometry/compute shader workflows are out of scope.
  • Multi-pass Shadertoy pipeline conversion is not implemented.
  • varying-based legacy pipeline patterns are treated as incompatible.

GLSL syntax/function limits

  • Unsupported by Flutter runtime effects and treated as conversion errors:
    • textureLod(...)
    • texelFetch(...)
    • Non-simple texture(...) usage with extra unsupported arguments
  • Function parameters of type sampler2D are not generally portable; converter attempts rewrite only for simple, safe cases.
  • Vector uniform arrays are not preserved as true runtime arrays; converter rewrites references to fallback vectors.
  • Int/advanced uniform models are not first-class in package runtime binding APIs (current runtime data model is float + sampler2D).

Platform and backend caveats

  • Shaders must be declared under flutter.shaders in pubspec.yaml; loading raw source text directly is not the intended runtime path.
  • On Flutter Web, sampler behavior can vary by shader/backend; some sampler bindings may fail at runtime for specific shaders.
  • Precision and parser behavior can differ by platform/backend; test converted shaders on all target platforms.

JSON Property File Format

Generated JSON files contain:

  • Float uniforms with { defaultValue, min, max }
  • Samplers with { type: "sampler2D" }

Example:

{
  "exposure": {
    "defaultValue": 1.0,
    "min": 0.0,
    "max": 4.0
  },
  "iChannel0": {
    "type": "sampler2D"
  }
}

Operational Notes for Developers

  • --dry-run validates and reports conversion issues without writing files.
  • --overwrite is required to replace existing output files.
  • --map source:target remaps output basenames (repeatable or comma-separated).
  • --no-mobile-compat disables mobile precision fallback wrapping for highp.
  • Recommended CI gate after converter/runtime API changes:
    • flutter analyze
    • flutter test

Libraries

cli/shader_converter_cli
domain/index
Domain models for shader uniforms, bindings, and editable values.
domain/shader_uniforms
domain/uniform_value
romantic_shader_widget
Flutter package for rendering custom fragment shaders and managing uniforms.
utils/image_utils
utils/index
Utility APIs for shader parsing, conversion, and asset/network loading.
utils/json_utils
utils/shader_converter
utils/shader_utils
widgets/index
Widget layer for rendering shaders and building runtime uniform payloads.
widgets/romantic_shader_painter
widgets/romantic_shader_widget
widgets/shader_uniforms_builder
widgets/shader_uniforms_builder_extension