flutter_circular_list 0.0.2 copy "flutter_circular_list: ^0.0.2" to clipboard
flutter_circular_list: ^0.0.2 copied to clipboard

A highly customizable, reusable circular/arc list widget for Flutter with infinite looping, smooth snapping, drag physics and a dedicated controller.

dashstack_poster

flutter_circular_list #

A reusable, highly customizable circular/arc list widget for Flutter — items lay out along an arc and support infinite looping, tap/drag/swipe and programmatic selection, all with deep visual customization and no third-party dependencies.

Requires Flutter >= 3.10, Dart >= 3.0.

Basic circular list Custom cards Avatar list
Infinite scrolling Controller-based navigation Custom animation & styling

All captured from example/ — run it yourself to try every feature below live.

Install #

dependencies:
  flutter_circular_list: <latest_version>
flutter pub get

Quick start #

import 'package:flutter_circular_list/flutter_circular_list.dart';

SizedBox(
  height: 220,
  child: CircularList<String>(
    items: const ['A', 'B', 'C', 'D', 'E'],
    radius: 120,
    itemExtent: 80,
    itemBuilder: (context, item, index, isSelected) {
      return CircleAvatar(
        radius: isSelected ? 32 : 24,
        child: Text(item),
      );
    },
    onItemSelected: (index, item) => debugPrint('Selected $item at $index'),
  ),
)

CircularList needs a bounded size on both axes — wrap it in a SizedBox (or place it inside an already-constrained parent) as shown above.

Features #

  • Infinite looping or bounded scrolling (loop: true/false).
  • Horizontal or vertical axis.
  • Tap, drag, swipe and programmatic selection, all backed by the same underlying scroll position.
  • CircularListControllernext(), previous(), animateToIndex(), jumpToIndex().
  • Deep visual customization via CircularListStyle — scale, opacity, rotation, arc direction, elevation/shadow.
  • GenericCircularList<T> works with any data type via an itemBuilder.
  • CallbacksonItemSelected, onItemChanged, onScroll.
  • Works with light and dark themes — no imposed colors/styling.
  • Zero third-party dependencies — only flutter.
  • Basic accessibility (Semantics) and keyboard (arrow key) support.
  • Built to avoid unnecessary rebuilds — only on-screen items are built, and itemBuilder only re-runs when an item's selection state actually changes, not on every animation frame.

Controller usage #

Use CircularListController for programmatic control — next/previous, jump straight to an index, or animate to one (taking the shortest path around the loop):

final controller = CircularListController(initialIndex: 2);

CircularList<String>(
  controller: controller,
  items: items,
  itemBuilder: (context, item, index, isSelected) => MyItem(item: item),
);

controller.next();
controller.previous();
controller.animateToIndex(5);
controller.jumpToIndex(0); // no animation

// The controller is also a Listenable, so you can react to selection
// changes that originate from drags/taps, not just your own calls:
controller.addListener(() => print(controller.selectedIndex));

// Dispose it like any other controller.
controller.dispose();

Customization #

Fine-tune the look with CircularListStyle:

CircularList<String>(
  items: items,
  itemBuilder: (context, item, index, isSelected) => MyItem(item: item),
  axis: Axis.vertical,
  loop: false,
  radius: 100,
  itemExtent: 70,
  spacing: 16,
  animationDuration: const Duration(milliseconds: 400),
  animationCurve: Curves.easeOutBack,
  style: const CircularListStyle(
    selectedScale: 1.2,
    minScale: 0.7,
    selectedOpacity: 1.0,
    minOpacity: 0.4,
    rotationFactor: 0.4,              // tilt items as they move away
    curveDirection: CircularListCurveDirection.outward,
    elevation: 16,                    // soft drop shadow
  ),
)
Option Notes
radius: 0 Collapses the arc into a straight line (a classic coverflow).
curveDirection Bows items towards or away from the main axis, or stays flat with .none.
dragSensitivity Scales how far the list moves per pixel dragged.
clipBehavior Defaults to Clip.none so a scaled-up selected item is never cut off.
maxSweepRadians Caps how far (in radians) an item may sweep around the arc — defaults to just short of a half turn, which keeps a handful of wide items (cards, avatars) fanning outward instead of folding back past 90° and overlapping their neighbor. Raise it towards 2 * pi for a dense wheel/dial of many small items where you want a fully closed circle; pair it with a larger extraRenderSlots so enough off-screen slots exist to reach the far side of the loop.
extraRenderSlots How many extra items render beyond the visible viewport on each side (default 2).

API #

CircularList<T>(
  items: items,
  controller: controller,
  itemBuilder: (context, item, index, isSelected) => MyItem(item: item),
  initialIndex: 0,
  axis: Axis.horizontal,
  radius: 120,
  itemExtent: 80,
  spacing: 12,
  loop: true,
  animationDuration: const Duration(milliseconds: 350),
  animationCurve: Curves.easeOutCubic,
  style: const CircularListStyle(),
  onItemSelected: (index, item) {},   // fires when the selection settles
  onItemChanged: (index, item) {},    // fires live, as the nearest item changes
  onScroll: (position) {},            // fires continuously with the raw position
  emptyBuilder: (context) => const Text('No items'),
)
Member Description
CircularList<T> The widget.
CircularListController next(), previous(), animateToIndex(int), jumpToIndex(int), selectedIndex.
CircularListStyle Scale/opacity/rotation/arc/elevation tuning.
CircularListCurveDirection inward / outward / none.

Example project #

Example app home screen

Full working app: example/lib/main.dart, covering:

  1. A basic circular list.
  2. Custom Material cards.
  3. An avatar/profile switcher.
  4. Infinite scrolling over hundreds of items.
  5. Controller-driven navigation (buttons, no gestures).
  6. A vertical-axis list with live-tunable radius/rotation/curve.
  7. Light/dark theme toggling.

Run it with:

cd example
flutter run

Performance notes #

  • Only the items within (and just beyond) the visible viewport are ever built — dragging through a list of thousands of items does not build thousands of widgets.
  • itemBuilder is isolated from the per-frame animation: it's only re-invoked when an item's isSelected flag actually flips, not on every tick of a drag or animation.
  • Each item is wrapped in a RepaintBoundary so animating the list doesn't force sibling/ancestor repaints.
  • No setState is triggered on every drag pixel — only when the rendered "window" of items needs to grow, which happens roughly once per item crossed, not once per frame.

Limitations #

  • Requires bounded constraints on both axes (an explicit width/height, or a bounded parent) — same as most non-sliver scrollables in Flutter.
  • Not a Sliver; cannot currently be embedded directly inside a CustomScrollView.
  • Very large itemExtent/radius combinations with rotationFactor can cause visual overlap at the edges of the viewport; tune visibleItemFraction on CircularListStyle if you see this.
  • Semantics/keyboard support covers the common cases (selection announced, arrow-key navigation) but hasn't been exhaustively tested with every screen reader.

Bugs & Credits #

Report bugs and ask questions on GitHub Issues. Maintained by Dashstack Infotech, Surat.

License #

MIT — see LICENSE.

1
likes
160
points
140
downloads

Documentation

API reference

Publisher

verified publisherdashstack.tech

Weekly Downloads

A highly customizable, reusable circular/arc list widget for Flutter with infinite looping, smooth snapping, drag physics and a dedicated controller.

Repository (GitHub)
View/report issues

Topics

#carousel #circular-list #ui #widget #animation

License

MIT (license)

Dependencies

flutter

More

Packages that depend on flutter_circular_list