before_after_slider 3.1.0 copy "before_after_slider: ^3.1.0" to clipboard
before_after_slider: ^3.1.0 copied to clipboard

Compare before/after images or widgets with a smooth draggable divider, pinch zoom, pan, and customizable overlay UI.

before_after_slider #

pub version likes license

Live Demo Showcase Source Issues

A production-ready Flutter widget for before/after comparison with smooth divider drag, zoom/pan gestures, and customizable labels and overlay.

Features #

  • Single universal widget: BeforeAfter(beforeChild, afterChild)
  • Works with images and arbitrary widgets
  • Controlled and uncontrolled progress modes
  • Pinch zoom + pan on mobile
  • Cmd/Ctrl + wheel zoom on desktop and web
  • Optional double-tap zoom
  • Grouped options API (interaction, zoom, labels, overlay)
  • External ZoomController support
  • Platform-adaptive demo app (web/desktop/mobile)

Get Started #

Installation #

dependencies:
  before_after_slider: ^3.0.0
flutter pub get

Quick Start #

import 'package:before_after_slider/before_after_slider.dart';

BeforeAfter(
  beforeChild: const Image(
    image: AssetImage('assets/before.jpg'),
    fit: BoxFit.cover,
  ),
  afterChild: const Image(
    image: AssetImage('assets/after.jpg'),
    fit: BoxFit.cover,
  ),
)

API Design #

BeforeAfter keeps top-level usage clean and groups behavior into dedicated options:

  • interactionOptions for dragging and hit zones
  • zoomOptions for zoom/pan/pointer settings
  • labelsOptions for label visibility and rendering
  • overlayOptions for style or custom overlay builder

Usage Recipes #

Controlled slider #

class _MyPageState extends State<MyPage> {
  double progress = 0.5;

  @override
  Widget build(BuildContext context) {
    return BeforeAfter(
      beforeChild: const Image(image: AssetImage('assets/before.jpg'), fit: BoxFit.cover),
      afterChild: const Image(image: AssetImage('assets/after.jpg'), fit: BoxFit.cover),
      progress: progress,
      onProgressChanged: (value) => setState(() => progress = value),
    );
  }
}

Full interactive setup #

BeforeAfter(
  beforeChild: const Image(image: AssetImage('assets/before.jpg'), fit: BoxFit.cover),
  afterChild: const Image(image: AssetImage('assets/after.jpg'), fit: BoxFit.cover),

  interactionOptions: const BeforeAfterInteractionOptions(
    sliderOrientation: SliderOrientation.horizontal,
    sliderDragMode: SliderDragMode.fullOverlay,
    sliderHitZone: SliderHitZone(
      minLineHalfWidth: 18,
      minThumbRadius: 30,
    ),
  ),

  zoomOptions: const BeforeAfterZoomOptions(
    enabled: true,
    pointer: PointerZoomOptions(
      requiresModifier: true,
      smoothing: 0.4,
    ),
    enableDoubleTapZoom: true,
    doubleTapZoomScale: 3.0,
  ),

  labelsOptions: BeforeAfterLabelsOptions(
    behavior: LabelBehavior.attachedToContent,
    beforeBuilder: (_) => const Text('Before'),
    afterBuilder: (_) => const Text('After'),
  ),

  overlayOptions: const BeforeAfterOverlayOptions(
    style: OverlayStyle(
      dividerWidth: 2,
      thumbSize: 40,
    ),
  ),
)

Vertical slider orientation #

BeforeAfter(
  beforeChild: ...,
  afterChild: ...,
  interactionOptions: const BeforeAfterInteractionOptions(
    sliderOrientation: SliderOrientation.vertical,
  ),
)

Custom overlay #

BeforeAfter(
  beforeChild: ...,
  afterChild: ...,
  overlayOptions: BeforeAfterOverlayOptions(
    builder: (size, position) {
      return Stack(
        children: [
          Positioned(
            left: position.dx,
            top: 0,
            bottom: 0,
            child: const VerticalDivider(width: 2, color: Colors.white),
          ),
        ],
      );
    },
  ),
)

Auto viewport ratio from image #

BeforeAfter(
  autoViewportAspectRatioFromImage: true,
  beforeChild: const Image(image: AssetImage('assets/before.jpg')),
  afterChild: const Image(image: AssetImage('assets/after.jpg')),
)

Notes:

  • viewportAspectRatio has higher priority than auto mode.
  • Auto mode currently reads ratio from direct Image children.

Programmatic zoom control #

final zoomController = ZoomController();

BeforeAfter(
  beforeChild: ...,
  afterChild: ...,
  zoomController: zoomController,
)

zoomController.reset();

Desktop and Web Controls #

If PointerZoomOptions.requiresModifier = true:

  • macOS: hold Cmd and use wheel/scroll
  • Windows/Linux/Web: hold Ctrl and use wheel/scroll

Migration #

2.x -> 3.x #

3.x finalized grouped options in BeforeAfter.

Before:

BeforeAfter(
  beforeChild: ...,
  afterChild: ...,
  overlayStyle: const OverlayStyle(...),
  enableProgressWithTouch: true,
  enableZoom: true,
)

After:

BeforeAfter(
  beforeChild: ...,
  afterChild: ...,
  interactionOptions: const BeforeAfterInteractionOptions(
    enableProgressWithTouch: true,
  ),
  zoomOptions: const BeforeAfterZoomOptions(
    enabled: true,
  ),
  overlayOptions: const BeforeAfterOverlayOptions(
    style: OverlayStyle(...),
  ),
)

1.x -> 2.x #

  • Removed BeforeAfterImage
  • Removed BeforeAfterLayout
  • Unified API in BeforeAfter
1
likes
160
points
176
downloads

Publisher

verified publisherbytecabins.com

Weekly Downloads

Compare before/after images or widgets with a smooth draggable divider, pinch zoom, pan, and customizable overlay UI.

Repository (GitHub)
View/report issues

Topics

#image-comparison #before-after #slider #zoom #pan

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on before_after_slider