flutter_circular_list 0.0.2
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.
import 'package:flutter/material.dart';
import 'pages/avatar_demo_page.dart';
import 'pages/basic_demo_page.dart';
import 'pages/controller_demo_page.dart';
import 'pages/custom_animation_demo_page.dart';
import 'pages/custom_cards_demo_page.dart';
import 'pages/infinite_scroll_demo_page.dart';
void main() {
runApp(const CircularListExampleApp());
}
/// Root of the example app. Owns the light/dark [ThemeMode] so every demo
/// page can be previewed in both themes via the app bar toggle.
class CircularListExampleApp extends StatefulWidget {
const CircularListExampleApp({super.key});
@override
State<CircularListExampleApp> createState() => _CircularListExampleAppState();
}
class _CircularListExampleAppState extends State<CircularListExampleApp> {
ThemeMode _themeMode = ThemeMode.light;
void _toggleTheme() {
setState(() {
_themeMode =
_themeMode == ThemeMode.light ? ThemeMode.dark : ThemeMode.light;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'flutter_circular_list demo',
themeMode: _themeMode,
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorSchemeSeed: Colors.indigo, brightness: Brightness.light),
darkTheme: ThemeData(
colorSchemeSeed: Colors.indigo, brightness: Brightness.dark),
home: HomePage(themeMode: _themeMode, onToggleTheme: _toggleTheme),
);
}
}
class _DemoEntry {
const _DemoEntry(this.title, this.subtitle, this.icon, this.builder);
final String title;
final String subtitle;
final IconData icon;
final WidgetBuilder builder;
}
class HomePage extends StatelessWidget {
const HomePage(
{super.key, required this.themeMode, required this.onToggleTheme});
final ThemeMode themeMode;
final VoidCallback onToggleTheme;
static final _demos = <_DemoEntry>[
_DemoEntry(
'Basic circular list',
'Minimal setup with a plain itemBuilder',
Icons.circle_outlined,
(context) => const BasicDemoPage(),
),
_DemoEntry(
'Custom cards',
'Rich Material cards with elevation and shadows',
Icons.style_outlined,
(context) => const CustomCardsDemoPage(),
),
_DemoEntry(
'Avatar list',
'Circular avatars, a common profile-switcher pattern',
Icons.person_outline,
(context) => const AvatarDemoPage(),
),
_DemoEntry(
'Infinite scrolling',
'Looping through a large data set seamlessly',
Icons.all_inclusive,
(context) => const InfiniteScrollDemoPage(),
),
_DemoEntry(
'Controller-based navigation',
'Drive the selection with next/previous/jump buttons',
Icons.gamepad_outlined,
(context) => const ControllerDemoPage(),
),
_DemoEntry(
'Custom animation & styling',
'Vertical axis, tunable radius/rotation/curve',
Icons.tune,
(context) => const CustomAnimationDemoPage(),
),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('flutter_circular_list'),
actions: [
IconButton(
tooltip: 'Toggle light/dark theme',
icon: Icon(themeMode == ThemeMode.light
? Icons.dark_mode_outlined
: Icons.light_mode_outlined),
onPressed: onToggleTheme,
),
],
),
body: ListView.separated(
padding: const EdgeInsets.symmetric(vertical: 8),
itemCount: _demos.length,
separatorBuilder: (context, index) => const Divider(height: 1),
itemBuilder: (context, index) {
final demo = _demos[index];
return ListTile(
leading: CircleAvatar(child: Icon(demo.icon)),
title: Text(demo.title),
subtitle: Text(demo.subtitle),
trailing: const Icon(Icons.chevron_right),
onTap: () => Navigator.of(context).push(
MaterialPageRoute(builder: demo.builder),
),
);
},
),
);
}
}