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

Libraries

before_after_slider
A Flutter package for creating interactive before/after image comparison widgets.