Skip to content

Instantly share code, notes, and snippets.

@Nash0x7E2
Last active May 31, 2021 03:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Nash0x7E2/16c9d5eac8c8278e8771fb359fef323c to your computer and use it in GitHub Desktop.
Save Nash0x7E2/16c9d5eac8c8278e8771fb359fef323c to your computer and use it in GitHub Desktop.
Horizontal item selector inspired by this design https://dribbble.com/shots/6591397-Travel-Application/attachments.
import 'package:flutter/material.dart';
class ItemTextStyle {
const ItemTextStyle({
@required this.initialStyle,
@required this.animatedStyle,
});
final TextStyle initialStyle;
final TextStyle animatedStyle;
}
class ItemSelectorBar extends StatelessWidget {
const ItemSelectorBar({
Key key,
@required this.activeIndex,
@required this.onTap,
@required this.items,
this.animatedTextStyle = const ItemTextStyle(
initialStyle: TextStyle(color: Colors.grey, fontSize: 14),
animatedStyle: TextStyle(color: Colors.white, fontSize: 14)),
this.itemPadding = const EdgeInsets.all(8.0),
}) : super(key: key);
final int activeIndex;
final ValueChanged<int> onTap;
final List<String> items;
final EdgeInsetsGeometry itemPadding;
final ItemTextStyle animatedTextStyle;
@override
Widget build(BuildContext context) {
return Container(
color: Theme.of(context).primaryColor,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (int index = 0; index < items.length; index++)
Padding(
padding: itemPadding,
child: ItemSelector(
index: index,
name: items[index],
activeIndex: activeIndex,
style: animatedTextStyle,
onTap: onTap,
),
)
],
),
);
}
}
class ItemSelector extends StatefulWidget {
const ItemSelector({
Key key,
this.name,
this.index,
this.activeIndex,
this.style,
this.onTap,
}) : super(key: key);
final String name;
final int index;
final int activeIndex;
final ItemTextStyle style;
final ValueChanged<int> onTap;
@override
_ItemSelectorState createState() => _ItemSelectorState();
}
class _ItemSelectorState extends State<ItemSelector> with SingleTickerProviderStateMixin {
bool _isSelected;
Animation<TextStyle> _textStyle;
AnimationController _controller;
@override
void initState() {
super.initState();
_isSelected = widget.activeIndex == widget.index;
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 250),
value: _isSelected ? 1.0 : 0.0,
);
_textStyle = TextStyleTween(begin: widget.style.initialStyle, end: widget.style.animatedStyle).animate(_controller);
}
@override
void didUpdateWidget(ItemSelector oldWidget) {
super.didUpdateWidget(oldWidget);
_isSelected = widget.activeIndex == widget.index;
animateSelected();
}
void animateSelected() {
if (_isSelected) {
_controller.forward();
} else {
_controller.reverse();
}
}
@override
void dispose() {
super.dispose();
_controller.dispose();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => widget.onTap(widget.index),
child: AnimatedBuilder(
animation: _controller,
builder: (context, _) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
widget.name,
style: _textStyle.value,
),
CustomPaint(
painter: _CirclePainter(
radius: 4.0,
animation: _controller,
verticalSpacing: 10,
circleColor: Colors.redAccent,
),
),
],
);
},
),
);
}
}
class _CirclePainter extends CustomPainter {
_CirclePainter({
@required this.radius,
@required this.animation,
this.verticalSpacing = 5.0,
this.strokeWidth = 10.0,
this.circleColor = Colors.redAccent,
}) : super(repaint: animation);
final double radius;
final Animation<double> animation;
final double verticalSpacing;
final double strokeWidth;
final Color circleColor;
@override
void paint(Canvas canvas, Size size) {
final Paint paint = Paint()
..color = circleColor
..strokeWidth = strokeWidth;
canvas.drawCircle(
Offset(size.width / 2, size.height / 2 + verticalSpacing),
radius * animation.value,
paint,
);
}
@override
bool shouldRepaint(_CirclePainter oldDelegate) => oldDelegate.animation != animation;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment