const _horizontalPadding = 32.0;
const _carouselItemMargin = 8.0;
const _carouselHeightMin = 200.0 + _carouselItemMargin * 2;
class Carousel extends StatefulWidget {
final List<Widget> carousel;
final AnimationController controller;
const Carousel({Key key, this.carousel, this.controller}) : super(key: key);
@override
CarouselState createState() => CarouselState();
}
class CarouselState extends State<Carousel> {
PageController _controller;
int _currentPage = 0;
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (_controller == null) {
double padding = (_horizontalPadding - _carouselItemMargin) * 2;
double width = MediaQuery.of(context).size.width;
_controller = PageController(
initialPage: 0, viewportFraction: (width - padding) / width);
}
}
@override
void dispose() {
super.dispose();
_controller.dispose();
}
Widget builder(int index) {
final carouselCard = AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget child) {
double value;
bool haveDimension = _controller.position.haveDimensions;
if (haveDimension) {
double page = _controller.page;
value = page - index;
} else {
value = (_currentPage - index).toDouble();
}
value = (1 - value.abs() * .38).clamp(0, 1).toDouble();
value = Curves.easeOut.transform(value);
return Center(
child: Transform(
transform: Matrix4.diagonal3Values(1.0, value, 1.0),
alignment: Alignment.center,
child: child,
),
);
},
child: widget.carousel[index],
);
if (index == 1) {
return AnimationCarouselCard(
childer: carouselCard, controller: widget.controller);
} else {
return carouselCard;
}
}
@override
Widget build(BuildContext context) {
return AnimationCarousel(
animationController: widget.controller,
childer: PageView.builder(
physics: BouncingScrollPhysics(),
allowImplicitScrolling: true,
itemCount: widget.carousel.length,
controller: _controller,
onPageChanged: (value) {
setState(() {
_currentPage = value;
});
},
itemBuilder: (BuildContext context, int index) => builder(index),
),
);
}
}
class AnimationCarousel extends StatelessWidget {
AnimationCarousel(
{Key key, @required this.animationController, @required this.childer})
: startPositionAnimation = Tween(begin: 1.0, end: 0.0).animate(
CurvedAnimation(
parent: animationController,
curve: const Interval(0.200, 0.800, curve: Curves.ease))),
super(key: key);
final AnimationController animationController;
final Widget childer;
final Animation<double> startPositionAnimation;
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Stack(
children: [
SizedBox(height: _carouselHeightMin),
AnimatedBuilder(
animation: animationController,
builder: (BuildContext context, Widget child) {
return PositionedDirectional(
child: child,
start: constraints.maxWidth * startPositionAnimation.value,
);
},
child: Container(
height: _carouselHeightMin,
width: constraints.maxWidth,
child: childer,
),
),
],
);
},
);
}
}
class AnimationCarouselCard extends StatelessWidget {
final Widget childer;
final AnimationController controller;
final Animation<double> startPositionAnimation;
AnimationCarouselCard(
{Key key, @required this.childer, @required this.controller})
: startPositionAnimation = Tween(begin: _horizontalPadding, end: 0.0)
.animate(CurvedAnimation(
parent: controller,
curve: Interval(0.800, 1.000, curve: Curves.ease))),
super(key: key);
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: controller,
builder: (context, child) {
return Padding(
padding: EdgeInsetsDirectional.only(
start: startPositionAnimation.value),
child: childer);
},
child: childer);
}
}