Without shader topic

Setup and usage

Wrap your desired widget with AnimatedGlitch:

final _controller = AnimatedGlitchController(
  frequency: const Duration(milliseconds: 200),
  level: 1.2,
  distortionShift: const DistortionShift(count: 3),
);

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: AnimatedGlitch(
      filters: [
        GlitchColorFilter(
          blendMode: BlendMode.color,
          color: Colors.blue.shade900,
        )
      ],
      controller: _controller,
      child: Image.asset(
        'assets/cyberpunk.jpg',
        fit: BoxFit.cover,
      ),
    ),
  );
}

Output:

You can wrap any kind of widget, and the glitch effect will be applied as well.:

final _controller = AnimatedGlitchController(
  frequency: const Duration(milliseconds: 200),
  level: 1.2,
  distortionShift: const DistortionShift(count: 3),
);

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: AnimatedGlitch(
      controller: _controller,
      child: Stack(
        alignment: Alignment.center,
        children: [
          Column(
            children: [
              Expanded(
                child: Container(
                  color: Colors.red,
                ),
              ),
              Expanded(
                child: Container(
                  color: Colors.orange,
                ),
              ),
              Expanded(
                child: Container(
                  color: Colors.purple,
                ),
              ),
              Expanded(
                child: Container(
                  color: Colors.yellow,
                ),
              ),
            ],
          ),
          const Icon(
            Icons.person,
            size: 400.0,
          )
        ],
      ),
    ),
  );
}

Output:

Controller

To control glitches, you will need to create an AnimatedGlitchController:

final controller = AnimatedGlitchController();

Controller has several key methods such as start, stop and reset.

final controller = AnimatedGlitchController();

controller.start();
controller.stop();
controller.reset();

You can specify (and mutate) optional values such as:

  • frequency: Determines the interval at which glitches occur (mutable).
  • chance: Determines the probability of a glitch appearing (mutable).
  • level: Determines the extent to which Distortions and ColorChannels are shifted, based on the provided glitching level (mutable).
  • distortionShift: Determines the number of generated Distortions and the delays for appearing and disappearing (mutable).
  • colorChannelShift: ColorChannels are generated based on this. The number of "channel shifts" is equal to the length of the provided colors list. The value of delay specifies the duration after which the sequence of the ColorChannels will appear one by one (mutable).
  • autoStart: The controller will automatically start generating glitches upon creation.
final controller = AnimatedGlitchController(
  frequency: const Duration(milliseconds: 500),
  level: 2.2,
  distortionShift: const DistortionShift(count: 20),
  colorChannelShift: const ColorChannelShift(
    colors: [
      Colors.purple,
      Colors.indigoAccent,
      Colors.orange,
    ],
  ),
);

Output:

Color filters

You can provide specific color filters that will be applied before the ColorChannels are shifted:

return Scaffold(
  body: AnimatedGlitch(
    filters: const [
      GlitchColorFilter(
        blendMode: BlendMode.color,
        color: Colors.grey,
      ),
      GlitchColorFilter(
        blendMode: BlendMode.colorBurn,
        color: Colors.orange,
      )
    ],
    controller: _controller,
    child: Image.asset(
      'assets/cyberpunk.jpg',
      fit: BoxFit.cover,
    ),
  ),
);

Output:

## Known issues
  • Due to frequent rebuilds, some junks may occur when scrolling. To minimize this behavior, you can try stopping the AnimatedGlitchController when scrolling begins and starting it again when scrolling ends. This approach may help:
final _controllers = List.generate(
  20,
  (_) => AnimatedGlitchController(
    frequency: const Duration(milliseconds: 200),
    level: 1.2,
    distortionShift: const DistortionShift(count: 3),
  ),
);
final images = List.generate(
  20,
  (_) => Image.asset(
    'assets/cyberpunk.jpg',
    fit: BoxFit.cover,
  ),
);

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: NotificationListener(
      onNotification: _onNotification,
      child: ListView.builder(
        itemCount: images.length,
        itemBuilder: (_, index) => SizedBox(
          height: 200,
          child: AnimatedGlitch(
            controller: _controllers[index],
            child: images[index],
          ),
        ),
      ),
    ),
  );
}

bool _onNotification(Notification notification) {
  if (notification is ScrollStartNotification) {
    for (final controller in _controllers) {
      controller.stop();
    }

    return true;
  }
  if (notification is ScrollEndNotification) {
    for (final controller in _controllers) {
      controller.start();
    }

    return true;
  }

  return false;
}