spoiler_widget 1.0.14

SDKflutter
Platformandroidioswindowslinuxmacosweb

A Flutter package to create spoiler animations similar to the one used in Telegram, allowing you to hide sensitive or spoiler-filled content until it's tapped or clicked.

Spoiler Animation for Flutter

Pub Likes Pub Points Pub Version License: MIT Flutter GitHub stars

A Flutter package to create spoiler animations similar to the one used in Telegram, allowing you to hide sensitive or spoiler-filled content until it's tapped or clicked.

Demo

Mask OperationDemo
PathOperation.intersect
PathOperation.difference
PathOperation.union
PathOperation.xor

Features

  • Spoiler Animation: Blur effect to hide content until tapped or hidden

  • Wave Effects: Optional wave/ripple expansions with SpoilerSpotsController

  • Particle System: Configure particle density, size, speed, color, etc.

  • Fade Animation: Smooth circular reveal/cover transitions

  • Gesture Control: Enable or disable gestures to users can tap toggle the spoiler.

  • Masking Support: Use custom Path + PathOperation via maskConfig.

  • Platform Agnostic: Works on iOS, Android, Web and more


Installation

In your pubspec.yaml:

dependencies:
  spoiler_widget: latest

Then run:

flutter pub get

Usage

1. Basic Spoiler Usage

Import the package:

import 'package:spoiler_widget/spoiler_widget.dart';

Wrap text or widgets you want to hide in a spoiler:

SpoilerOverlay(
  config: WidgetSpoilerConfig(
    isEnabled: true,
    fadeRadius: 3,
    enableFadeAnimation: true,
    enableGestureReveal: true,
    imageFilter: ImageFilter.blur(sigmaX:30, sigmaY:30),
    onSpoilerVisibilityChanged: (isVisible) {
      debugPrint('Spoiler is now: ${isVisible ? 'Visible' : 'Hidden'}');
    },
  ),
  child: Text('Hidden Content'),
);

Or use the text-specific widget:

SpoilerText(
  text: 'Tap me to reveal secret text!',
  config: TextSpoilerConfig(
    isEnabled: true,
    enableFadeAnimation: true,
    enableGestureReveal: true,
    textStyle: TextStyle(fontSize: 16, color: Colors.black),
    onSpoilerVisibilityChanged: (isVisible) {
      debugPrint('Spoiler is now: ${isVisible ? 'Visible' : 'Hidden'}');
    },
  ),
  
);

2. Wave Animations (SpoilerSpotsController)

For dynamic "wave" effects:

class WaveDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SpoilerOverlay(
      config: WidgetSpoilerConfig( 
        isEnabled: true,
        maxActiveWaves: 3,
        enableFadeAnimation: true,
        enableGestureReveal: true,
        imageFilter: ImageFilter.blur(sigmaX:30, sigmaY:20),
        onSpoilerVisibilityChanged: (isVisible) {
          debugPrint('Spoiler is now: ${isVisible ? 'Visible' : 'Hidden'}');
        },
      ),
      child: Image.network('https://your-image-url'),
    );
  }
}

3. Custom Path Masking

You can apply a custom-shaped mask using maskConfig in both TextSpoilerConfig and WidgetSpoilerConfig. This allows the spoiler effect to only appear inside specific areas defined by a Path.

SpoilerText(
  text: 'Masked spoiler!',
  config: TextSpoilerConfig(
    isEnabled: true,
    enableGestureReveal: true,
    particleDensity: 0.1,
    textStyle: TextStyle(fontSize: 24, color: Colors.white),
    maskConfig: SpoilerMask(
      maskPath: myCustomPath,
      maskOperation: PathOperation.intersect,
      offset: Offset(50, 30),
    ),
    onSpoilerVisibilityChanged: (isVisible) {
      debugPrint('Spoiler is now: ${isVisible ? 'Visible' : 'Hidden'}');
    },
  ),
);

3. Example

Below is a minimalist code sample:

import 'package:flutter/material.dart';
import 'package:spoiler_widget/spoiler_widget.dart';
import 'package:cached_network_image/cached_network_image.dart';


void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  final String text = 'Tap to reveal a surprise spoiler!';
  final String imageUrl =
      'https://img.freepik.com/premium-photo/drawing-female-superhero-female-character_1308175-151081.jpg?w=1800';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold
      appBar: AppBar(
        title: Text('Spoiler Widget Demo',
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [

            // Text-based spoiler
            SpoilerText(
              config: TextSpoilerConfig(
                isEnabled: true,
                maxParticleSize: 1,
                particleDensity: .2,
                particleSpeed: 0.2,
                fadeRadius: 3,
                enableFadeAnimation: true,
                enableGestureReveal: true,
                textSelection: const TextSelection(baseOffset: 0, extentOffset: 30),
                textStyle: const TextStyle(fontSize: 28, color: Colors.black),
                onSpoilerVisibilityChanged: (isVisible) {
                  debugPrint('Spoiler is now: ${isVisible ? 'Visible' : 'Hidden'}');
                },
              ),
            text: text,
           
          ),

          // Widget-based spoiler
            ClipRect(
              child: SpoilerOverlay(
                config: WidgetSpoilerConfig(
                  isEnabled: true,
                  maxParticleSize: 1,
                  particleDensity: .2,
                  particleSpeed: 0.2,
                  fadeRadius: 3,
                  enableFadeAnimation: true,
                  enableGestureReveal: true,
                  imageFilter: ImageFilter.blur(sigmaX:30, sigmaY:30),
                  onSpoilerVisibilityChanged: (isVisible) {
                    debugPrint('Spoiler is now: ${isVisible ? 'Visible' : 'Hidden'}');
                  },
                ),
              child: CachedNetworkImage(imageUrl: imageUrl),
           
            ),
          ),
        ],
      ),
     ),
    ),
    );
  }
}

Configuration

Common Fields

Table showing common config parameters for both TextSpoilerConfiguration and WidgetSpoilerConfiguration.

FieldTypeDescription
isEnabledboolWhether the spoiler starts covered true.
enableFadeAnimationboolEnables smooth fade-in/out.
fadeRadiusdoubleThe circle radius for radial fade.
particleDensitydoubleThe density of particles in the spoiler.
maxParticleSizedoubleThe maximum size of particles.
particleSpeeddoubleSpeed factor for particle movement.
enableGestureRevealboolWhether tapped toggle should be out of the box.
maskConfigSpoilerMask?Optional mask to apply using a Path.
onSpoilerVisibilityChangedValueChanged?Optional callback fired when spoiler becomes visible/hidden.

TextSpoilerConfiguration

FieldTypeDescription
textStyleTextStyle?The text style applied to the spoiler text.
textSelectionTextSelection?Range of text to apply the spoiler.
textAlignTextAlign?Text alignment inside the widget.
maxLinesint?An optional maximum number of lines for the text to span, wrapping if necessary.
isEllipsisbool?Determines whether overflowing text should display an ellipsis ("…") at the end.

WidgetSpoilerConfiguration

FieldTypeDescription
imageFilterImageFilter?Blur filter used to hide the child.
maxActiveWavesintMax concurrent waves for wave-based effects.

FAQ

  1. How can I animate the blur or wave concurrency? Adjust the properties in your configuration object at runtime. For instance, set a new imageFilter or call methods on the wave controller to dynamically tune the effect.

  2. Can I skip the wave logic? Yes—by default, you get a basic spoiler with fade. Use SpoilerSpotsController only if you want wave animations.

  3. Does this work on the web? Yes! It’s entirely in Flutter/Dart. Just ensure you handle any platform quirks with gesture input.

Contributing

Contributions are welcome! Whether it’s bug fixes, new features, or documentation improvements, open a Pull Request or Issue.


License

Licensed under the MIT License. Enjoy building your spoiler effects!