# How to Use Custom Shaders in FlutterFlow for Visual Effects

- Tool: FlutterFlow
- Difficulty: Beginner
- Time required: 40-60 min
- Compatibility: FlutterFlow Pro+ (code export required for GLSL shaders; BackdropFilter works on all plans)
- Last updated: March 2026

## TL;DR

FlutterFlow supports GPU visual effects through three routes: BackdropFilter for blur effects (no code needed), ShaderMask for gradient overlays (moderate complexity), and Flutter's FragmentProgram API for fully custom GLSL shaders (advanced). Start with BackdropFilter for frosted glass effects — it works in Run Mode with no export required.

## GPU effects in Flutter — what FlutterFlow exposes

Shaders are programs that run on the GPU to calculate the color of each pixel on screen. Flutter exposes three levels of shader access. BackdropFilter is the simplest — it applies a pre-built blur or color matrix effect to whatever is behind a widget. ShaderMask applies a gradient shader to a child widget, useful for gradient text and image fades. FragmentProgram is the most powerful — it loads a custom GLSL (OpenGL Shading Language) file that you write, giving you per-pixel control for effects like ripples, heat distortion, and procedural animations. FlutterFlow's visual editor exposes BackdropFilter directly. ShaderMask and FragmentProgram require Custom Widgets, which need the Pro plan.

## Before you start

- FlutterFlow project (BackdropFilter works on Free; Custom Widgets require Pro)
- Basic understanding of widgets and the FlutterFlow widget tree
- For GLSL shaders: familiarity with basic Dart code and Flutter widget lifecycle
- Pro plan for Custom Widget support (Custom Code → Custom Widgets)

## Step-by-step guide

### 1. Add a frosted glass effect with BackdropFilter

BackdropFilter is the easiest GPU effect and requires no code export. In FlutterFlow, select a Container widget that overlays an image or colorful background. Go to its properties → Background → enable Blur. Set the blur sigma value (10-15 gives a strong frosted glass look, 3-5 is subtle). Add a semi-transparent white fill (white, 30-40% opacity) to the container so the blur looks like frosted glass rather than just a smear. This is perfect for modal cards, navigation drawers, or hero banners overlaid on a background photo.

**Expected result:** A frosted glass card visible over a background image in Run Mode, with a smooth blur on all plans including Free.

### 2. Apply a gradient overlay with ShaderMask in a Custom Widget

ShaderMask applies a gradient that blends with a child widget's colors. Common uses: gradient text that fades from one color to another, images that fade to transparent at the bottom, and icon color transitions. Create a Custom Widget (Custom Code → Custom Widgets → Add Widget), name it 'GradientText'. Paste the code below. The widget wraps a Text child with a ShaderMask using a LinearGradient from your brand colors. After saving, drag the custom widget onto any page and set the text and gradient colors via its parameters.

```
// Custom Widget: GradientText
import 'package:flutter/material.dart';

class GradientText extends StatelessWidget {
  const GradientText({
    super.key,
    required this.text,
    required this.style,
    required this.startColor,
    required this.endColor,
  });

  final String text;
  final TextStyle style;
  final Color startColor;
  final Color endColor;

  @override
  Widget build(BuildContext context) {
    return ShaderMask(
      blendMode: BlendMode.srcIn,
      shaderCallback: (Rect bounds) => LinearGradient(
        colors: [startColor, endColor],
        begin: Alignment.topLeft,
        end: Alignment.bottomRight,
      ).createShader(bounds),
      child: Text(text, style: style),
    );
  }
}
```

**Expected result:** Text on your page renders with a smooth gradient from startColor to endColor, instead of a solid color.

### 3. Write a basic GLSL ripple shader and load it with FragmentProgram

FragmentProgram lets you write GLSL shaders that run entirely on the GPU. This is how you get effects like animated ripples, heat haze, or holographic shimmer. You need two files: the GLSL shader file (.frag) and a Custom Widget that loads it. First, export your FlutterFlow project (Pro plan required) to access the pubspec.yaml and assets folder. Add the shader file to an assets/shaders/ folder and register it in pubspec.yaml under flutter: shaders:. The Custom Widget loads it at runtime using FragmentProgram.fromAsset(). Paste the ripple shader code below.

```
// assets/shaders/ripple.frag
#include <flutter/runtime_effect.glsl>

uniform float uTime;
uniform vec2 uCenter;
uniform sampler2D uTexture;

out vec4 fragColor;

void main() {
  vec2 fragCoord = FlutterFragCoord().xy;
  vec2 uv = fragCoord / vec2(400.0, 800.0);

  float dist = distance(fragCoord, uCenter);
  float wave = sin(dist * 0.05 - uTime * 3.0) * 10.0;
  float strength = max(0.0, 1.0 - dist / 200.0);

  vec2 offset = normalize(fragCoord - uCenter) * wave * strength * 0.005;
  fragColor = texture(uTexture, uv + offset);
}
```

**Expected result:** After exporting and building the app natively, the ripple shader creates a water-distortion effect on the widget it wraps.

### 4. Load the FragmentProgram shader in a Custom Widget

Now write the Dart Custom Widget that loads the .frag file at runtime and drives the animation with a Ticker. This widget uses AnimationController to update the uTime uniform every frame — the shader uses this value to animate the ripple wave. Add this as a Custom Widget in FlutterFlow or in your exported project. The widget wraps any child with the animated shader effect.

```
// Custom Widget: RippleShaderWidget
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';

class RippleShaderWidget extends StatefulWidget {
  const RippleShaderWidget({super.key, required this.child});
  final Widget child;

  @override
  State<RippleShaderWidget> createState() => _RippleShaderWidgetState();
}

class _RippleShaderWidgetState extends State<RippleShaderWidget>
    with SingleTickerProviderStateMixin {
  ui.FragmentProgram? _program;
  double _time = 0;
  late final Ticker _ticker;

  @override
  void initState() {
    super.initState();
    _loadShader();
    _ticker = createTicker((elapsed) {
      setState(() => _time = elapsed.inMilliseconds / 1000.0);
    });
    _ticker.start();
  }

  Future<void> _loadShader() async {
    final program = await ui.FragmentProgram.fromAsset('shaders/ripple.frag');
    if (mounted) setState(() => _program = program);
  }

  @override
  void dispose() {
    _ticker.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (_program == null) return widget.child;
    final shader = _program!.fragmentShader()
      ..setFloat(0, _time)
      ..setFloat(1, 200)
      ..setFloat(2, 400);
    return CustomPaint(
      painter: _ShaderPainter(shader),
      child: widget.child,
    );
  }
}

class _ShaderPainter extends CustomPainter {
  _ShaderPainter(this.shader);
  final ui.FragmentShader shader;

  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawRect(Offset.zero & size, Paint()..shader = shader);
  }

  @override
  bool shouldRepaint(_ShaderPainter old) => true;
}
```

**Expected result:** The widget renders with an animated ripple distortion effect running at 60fps on physical devices.

### 5. Test shaders on device — not in web preview

GLSL shaders in Flutter have limited WebGL support. Some effects that work perfectly on iOS and Android will not render at all in the web browser — including the ripple FragmentProgram shader above. Always test shader effects using FlutterFlow's 'Run on Device' option (Scan QR code in Run Mode to open on a physical phone). BackdropFilter blur effects do work in web preview. For production apps, add a capability check and fall back to a simpler effect on web: check if the shader loaded successfully (program != null) and use a plain color if it didn't.

**Expected result:** Shader effects render correctly on iOS and Android physical devices. BackdropFilter effects also work in web preview.

## Complete code example

File: `gradient_image_fade_widget.dart`

```dart
// FlutterFlow Custom Widget: GradientFadeImage
// Applies a bottom-to-transparent gradient fade over an image
// Works on all platforms including web
// Add as Custom Widget in FlutterFlow Custom Code section

import 'package:flutter/material.dart';

class GradientFadeImage extends StatelessWidget {
  const GradientFadeImage({
    super.key,
    required this.imageUrl,
    required this.height,
    required this.width,
    this.fadeColor = Colors.black,
    this.fadeFraction = 0.4,
  });

  final String imageUrl;
  final double height;
  final double width;
  final Color fadeColor;
  final double fadeFraction; // 0.0 to 1.0 — how much of the image fades

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: height,
      width: width,
      child: ShaderMask(
        shaderCallback: (Rect bounds) {
          return LinearGradient(
            begin: Alignment.topCenter,
            end: Alignment.bottomCenter,
            colors: [
              Colors.white,
              Colors.white,
              Colors.transparent,
            ],
            stops: [0.0, 1.0 - fadeFraction, 1.0],
          ).createShader(bounds);
        },
        blendMode: BlendMode.dstIn,
        child: Image.network(
          imageUrl,
          height: height,
          width: width,
          fit: BoxFit.cover,
          errorBuilder: (context, error, stackTrace) => Container(
            color: Colors.grey[300],
            child: const Icon(Icons.broken_image, color: Colors.grey),
          ),
        ),
      ),
    );
  }
}
```

## Common mistakes

- **Writing complex GLSL and expecting it to work in Run Mode web preview** — FlutterFlow's Run Mode runs in a web browser. Flutter's FragmentProgram GLSL shaders rely on native Skia/Impeller rendering, not WebGL. Complex shaders simply won't load or render in the browser. Fix: Always test GLSL shaders on a physical device via the QR code in Run Mode. For effects that need to work on web, use BackdropFilter or ShaderMask with LinearGradient, which are cross-platform.
- **Forgetting the #include <flutter/runtime_effect.glsl> header in shader files** — Flutter requires this header to provide FlutterFragCoord() and other Flutter-specific GLSL functions. Without it, the shader fails to compile at runtime with a cryptic error. Fix: Always start .frag files with #include <flutter/runtime_effect.glsl> and use FlutterFragCoord() instead of gl_FragCoord.
- **Using animated shaders on low-priority background elements** — An animated shader calls repaint every frame (60fps). If you apply it to a full-screen background widget, it forces the entire widget tree to repaint 60 times per second, degrading scroll performance. Fix: Limit animated shaders to small, isolated widgets. Use RepaintBoundary to isolate the shader widget so only it repaints each frame, not the whole page.
- **Not registering the .frag file in pubspec.yaml under flutter: shaders:** — Flutter compiles shaders at build time from the shaders: list in pubspec.yaml. If the file isn't registered, FragmentProgram.fromAsset() throws a runtime exception even though the file exists. Fix: Add the shader path to pubspec.yaml: flutter: shaders: - assets/shaders/ripple.frag. Then run flutter pub get before building.

## Best practices

- Start with BackdropFilter and ShaderMask before writing custom GLSL — they handle 80% of visual effects use cases
- Always test on physical devices for shader effects — web preview is unreliable for GPU features
- Wrap animated shader widgets in RepaintBoundary to prevent full-tree repaints
- Keep GLSL shaders to under 20 lines for mobile performance — complexity has a direct GPU cost
- Provide a non-shader fallback for web builds using platform detection (kIsWeb check)
- Use flutter_shaders package for asset management if you have more than 2-3 shader files
- Profile shader performance with Flutter DevTools' Performance overlay before shipping

## Frequently asked questions

### Do I need the Pro plan to use BackdropFilter blur effects?

No. BackdropFilter blur is available on all FlutterFlow plans through the Container widget's blur property. You only need the Pro plan for Custom Widgets, which are required for ShaderMask and FragmentProgram shaders.

### Will custom GLSL shaders work on both iOS and Android?

Yes, on both platforms. Flutter uses Impeller (default on iOS and newer Android) which fully supports GLSL fragment shaders with the FragmentProgram API. Older Android devices using Skia may have minor rendering differences.

### How do I animate a shader — for example, make a gradient slowly shift colors over time?

Pass a time uniform to the shader (setFloat(0, elapsedSeconds)). Use AnimationController or a Ticker in your Custom Widget to update the time value every frame and call setState. The shader receives the updated time and recalculates pixel colors for each frame.

### Is there a performance difference between BackdropFilter and a custom GLSL shader?

BackdropFilter is highly optimized and uses a single platform-level call. Custom GLSL shaders run on the GPU but add draw call overhead and must compile at runtime. For simple effects, BackdropFilter is faster. For complex per-pixel effects that BackdropFilter can't replicate, GLSL is your only option.

### Can I use a ShaderMask for a radial gradient instead of linear?

Yes. Replace LinearGradient with RadialGradient in the shaderCallback. Set the center and radius properties on RadialGradient to control where the effect originates. This is useful for vignette effects (dark edges, bright center) on images.

### Where can I find pre-written Flutter GLSL shaders to use as a starting point?

Shadertoy.com has thousands of GLSL shaders, but they use WebGL conventions — you need to adapt them for Flutter by replacing gl_FragCoord with FlutterFragCoord() and adjusting coordinate systems. The Flutter gallery on GitHub has working FragmentProgram examples, and the flutter_shaders pub.dev package includes ready-to-use shader utilities.

---

Source: https://www.rapidevelopers.com/flutterflow-tutorials/how-to-use-custom-shaders-in-flutterflow-for-visual-effects
© RapidDev — https://www.rapidevelopers.com/flutterflow-tutorials/how-to-use-custom-shaders-in-flutterflow-for-visual-effects
