half_circle_scroll 0.0.2
half_circle_scroll: ^0.0.2 copied to clipboard
A customizable half-circle scroll widget for Flutter.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:half_circle_scroll/half_circle_scroll.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Half Circle Scroll Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: const DemoPage(),
);
}
}
class DemoPage extends StatefulWidget {
const DemoPage({Key? key}) : super(key: key);
@override
State<DemoPage> createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
int selectedIndex = 0;
HalfCircleOrientation currentOrientation = HalfCircleOrientation.vertical;
HalfCircleItemShape currentShape = HalfCircleItemShape.roundedRectangle;
final List<HalfCircleScrollItem> items = [
const HalfCircleScrollItem(text: 'Home'),
const HalfCircleScrollItem(text: 'Profile'),
const HalfCircleScrollItem(text: 'Settings'),
const HalfCircleScrollItem(text: 'Messages'),
const HalfCircleScrollItem(text: 'Notifications'),
const HalfCircleScrollItem(text: 'Search'),
const HalfCircleScrollItem(text: 'Calendar'),
const HalfCircleScrollItem(text: 'Tasks'),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Half Circle Scroll Demo'),
backgroundColor: Colors.blue[600],
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Selected item display
Container(
padding: const EdgeInsets.all(16),
margin: const EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
color: Colors.blue[50],
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.blue[200]!),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.touch_app, color: Colors.blue[600]),
const SizedBox(width: 8),
Text(
'Selected: ${items[selectedIndex].text}',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.blue[800],
),
),
],
),
),
// Half Circle Scroll Widget
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Colors.blue[50]!, Colors.purple[50]!],
),
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 20,
offset: const Offset(0, 10),
),
],
),
padding: const EdgeInsets.all(20),
child: HalfCircleScroll(
items: items,
config: HalfCircleScrollConfig(
radius: 150.0,
orientation: currentOrientation,
itemShape: currentShape,
selectedColor: Colors.orange,
unselectedColor: Colors.blue,
itemWidth: 100.0,
itemHeight: 80.0,
animationDuration: const Duration(milliseconds: 400),
animationCurve: Curves.easeOutCubic,
selectedTextStyle: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12,
),
unselectedTextStyle: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w500,
fontSize: 10,
),
),
onSelectionChanged: (index) {
setState(() {
selectedIndex = index;
});
},
),
),
const SizedBox(height: 30),
// Controls
Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Controls',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 16),
// Orientation Selector
const Text('Orientation:', style: TextStyle(fontWeight: FontWeight.w600)),
const SizedBox(height: 8),
Wrap(
spacing: 8,
children: HalfCircleOrientation.values.map((orientation) {
return ChoiceChip(
label: Text(_getOrientationName(orientation)),
selected: currentOrientation == orientation,
onSelected: (selected) {
if (selected) {
setState(() {
currentOrientation = orientation;
});
}
},
);
}).toList(),
),
const SizedBox(height: 16),
// Shape Selector
const Text('Item Shape:', style: TextStyle(fontWeight: FontWeight.w600)),
const SizedBox(height: 8),
Wrap(
spacing: 8,
children: HalfCircleItemShape.values.map((shape) {
return ChoiceChip(
label: Text(_getShapeName(shape)),
selected: currentShape == shape,
onSelected: (selected) {
if (selected) {
setState(() {
currentShape = shape;
});
}
},
);
}).toList(),
),
],
),
),
),
const SizedBox(height: 30),
// Custom Builder Example
Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Custom Builder Example',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 16),
Container(
height: 300,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.purple[100]!, Colors.pink[100]!],
),
borderRadius: BorderRadius.circular(12),
),
child: HalfCircleScroll(
items: List.generate(6, (index) =>
HalfCircleScrollItem(text: 'Custom ${index + 1}')
),
config: const HalfCircleScrollConfig(
radius: 120.0,
orientation: HalfCircleOrientation.horizontal,
itemWidth: 80.0,
itemHeight: 80.0,
),
itemBuilder: (context, index, item, isSelected, scale, opacity) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: isSelected
? [Colors.orange, Colors.deepOrange]
: [Colors.purple[300]!, Colors.purple[600]!],
),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: (isSelected ? Colors.orange : Colors.purple)
.withOpacity(0.4),
blurRadius: isSelected ? 12 : 6,
offset: const Offset(0, 4),
),
],
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
_getIconForIndex(index),
color: Colors.white,
size: 20 * scale,
),
const SizedBox(height: 4),
Text(
item.text ?? '',
style: TextStyle(
color: Colors.white,
fontSize: 8 * scale,
fontWeight: isSelected
? FontWeight.bold
: FontWeight.normal,
),
),
],
),
),
);
},
),
),
],
),
),
),
],
),
),
);
}
String _getOrientationName(HalfCircleOrientation orientation) {
switch (orientation) {
case HalfCircleOrientation.vertical:
return 'Vertical';
case HalfCircleOrientation.horizontal:
return 'Horizontal';
case HalfCircleOrientation.invertedVertical:
return 'Inverted V';
case HalfCircleOrientation.invertedHorizontal:
return 'Inverted H';
}
}
String _getShapeName(HalfCircleItemShape shape) {
switch (shape) {
case HalfCircleItemShape.rectangle:
return 'Rectangle';
case HalfCircleItemShape.circle:
return 'Circle';
case HalfCircleItemShape.roundedRectangle:
return 'Rounded';
case HalfCircleItemShape.custom:
return 'Custom';
}
}
IconData _getIconForIndex(int index) {
final icons = [
Icons.home,
Icons.star,
Icons.favorite,
Icons.settings,
Icons.person,
Icons.message,
];
return icons[index % icons.length];
}
}