Careem HomePage like Customized Carousel Slider in Flutter with controls
Building Carousel Slider in flutter is bit tricky and difficult task for beginners in flutter. Most of the Mobile apps in flutter requires Slider at HomePage/Dashboard and newbies or beginners have to struggle a lot to make slider according to requirements. We have a package named Carousal_slider at pub.dev. We only left with 8 to 9 examples provided by them with less guide and documentation.
I have a task in which I have to make a carousal slider like Careem App HomePage. First I search for the available builtin designs or code to just customize a little bit. Later I found carousal slider package and decided to customize it accordingly.
Careem HomePage carousal slider is:

Here we will use this package name carousel_slider from pub.dev and make it look like careem by modifying one of his example given at package example page.
First of all create a flutter project in your desired IDE, if you have done it move to the next step and add carousel_slider dependency in your projects pubspec.yaml file.
Import carousel slide package into your file where you want to display the carousal.
import 'package:carousel_slider/carousel_slider.dart';
Import one example from their example page named “CarouselWithIndicatorDemo” and paste it in your code. it will look like this:
final List<Widget> imageSliders = imgList
.map((item) => Container(
child: Container(
margin: EdgeInsets.all(5.0),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
child: Stack(
children: <Widget>[
Image.network(item, fit: BoxFit.cover, width: 1000.0),
Positioned(
bottom: 0.0,
left: 0.0,
right: 0.0,
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Color.fromARGB(200, 0, 0, 0),
Color.fromARGB(0, 0, 0, 0)
],
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
),
),
padding: EdgeInsets.symmetric(
vertical: 10.0, horizontal: 20.0),
child: Text(
'No. ${imgList.indexOf(item)} image',
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
),
),
],
)),
),
))
.toList();
final List<String> imgList = [
'https://images.unsplash.com/photo-1520342868574-5fa3804e551c?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=6ff92caffcdd63681a35134a6770ed3b&auto=format&fit=crop&w=1951&q=80',
'https://images.unsplash.com/photo-1522205408450-add114ad53fe?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=368f45b0888aeb0b7b08e3a1084d3ede&auto=format&fit=crop&w=1950&q=80',
'https://images.unsplash.com/photo-1519125323398-675f0ddb6308?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=94a1e718d89ca60a6337a6008341ca50&auto=format&fit=crop&w=1950&q=80',
'https://images.unsplash.com/photo-1523205771623-e0faa4d2813d?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=89719a0d55dd05e2deae4120227e6efc&auto=format&fit=crop&w=1953&q=80',
'https://images.unsplash.com/photo-1508704019882-f9cf40e475b4?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=8c6e5e3aba713b17aa1fe71ab4f0ae5b&auto=format&fit=crop&w=1352&q=80',
'https://images.unsplash.com/photo-1519985176271-adb1088fa94c?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=a0c8d632e977f94e5d312d9893258f59&auto=format&fit=crop&w=1355&q=80'
];class CarouselWithIndicatorDemo extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _CarouselWithIndicatorState();
}
}
class _CarouselWithIndicatorState extends State<CarouselWithIndicatorDemo> {
int _current = 0;
final CarouselController _controller = CarouselController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Carousel with indicator controller demo')),
body: Column(children: [
Expanded(
child: CarouselSlider(
items: imageSliders,
carouselController: _controller,
options: CarouselOptions(
autoPlay: true,
enlargeCenterPage: true,
aspectRatio: 2.0,
onPageChanged: (index, reason) {
setState(() {
_current = index;
});
}),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: imgList.asMap().entries.map((entry) {
return GestureDetector(
onTap: () => _controller.animateToPage(entry.key),
child: Container(
width: 12.0,
height: 12.0,
margin: EdgeInsets.symmetric(vertical: 8.0, horizontal: 4.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: (Theme.of(context).brightness == Brightness.dark
? Colors.white
: Colors.black)
.withOpacity(_current == entry.key ? 0.9 : 0.4)),
),
);
}).toList(),
),
]),
);
}
}
Note: imageList & imageSliders are from top of their example page.
After that I wraped the whole body into a column and place an expanded of flex 3 as its First Child.
return Scaffold(body: Column(children: [Expanded(flex: 3,)]));
For displaying indicator demos at the image, I added a stack widget in Expanded and place CarousalSlider in it as first child and indicatorDemo Row as 2nd child.
Column(children: [Expanded(flex: 3,child: Stack(children: [CarouselSlider(items: imageSliders,carouselController: _controller,options: CarouselOptions(viewportFraction: 1,autoPlay: true,enlargeCenterPage: true,aspectRatio: 2.0,onPageChanged: (index, reason) {setState(() {_current = index;});}),),Positioned(bottom: 5,left: 12,child: Row(mainAxisAlignment: MainAxisAlignment.center,children: imgList.asMap().entries.map((entry) {return GestureDetector(onTap: () => _controller.animateToPage(entry.key),child: Container(width: _current == 12,height: 12,margin: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 4.0),decoration: BoxDecoration(borderRadius: BorderRadius.circular(7),//shape: BoxShape.circle,color: (Theme.of(context).brightness ==Brightness.dark ? Colors.white : Colors.cyan).withOpacity(_current == entry.key ? 1.0 : 0.4)),),);}).toList(),),),]),),
But this is not the actual result you get. Here Images are having padding, seperated apart and whenever item changes it enlarges which isn’t the required feature here.
I modified the code and replace item from Image Sliders to my own custom Image.assets views to display the complete image.
items: imgList.map((item) => Center(child: Image.network(item,fit: BoxFit.cover,width: size.width))).toList(),
Now Images are according to desired but still having enlarge property and aspectRation to be shown as having padding and enlarging when changes. For that I modified CarousalOptions file and commented both the properties to get desired results.
//enlargeCenterPage: true,
//aspectRatio: 2.0,
I also modified the indicator widgets which was circle before to set it as Careem.
Final result will be like this:

The complete code is as:
import 'package:carousel_slider/carousel_slider.dart';import 'package:flutter/material.dart';final List<String> imgList = ['https://images.unsplash.com/photo-1520342868574-5fa3804e551c?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=6ff92caffcdd63681a35134a6770ed3b&auto=format&fit=crop&w=1951&q=80','https://images.unsplash.com/photo-1522205408450-add114ad53fe?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=368f45b0888aeb0b7b08e3a1084d3ede&auto=format&fit=crop&w=1950&q=80','https://images.unsplash.com/photo-1519125323398-675f0ddb6308?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=94a1e718d89ca60a6337a6008341ca50&auto=format&fit=crop&w=1950&q=80','https://images.unsplash.com/photo-1523205771623-e0faa4d2813d?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=89719a0d55dd05e2deae4120227e6efc&auto=format&fit=crop&w=1953&q=80','https://images.unsplash.com/photo-1508704019882-f9cf40e475b4?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=8c6e5e3aba713b17aa1fe71ab4f0ae5b&auto=format&fit=crop&w=1352&q=80','https://images.unsplash.com/photo-1519985176271-adb1088fa94c?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=a0c8d632e977f94e5d312d9893258f59&auto=format&fit=crop&w=1355&q=80'];class CarouselWithIndicatorDemo extends StatefulWidget {const CarouselWithIndicatorDemo({Key? key}) : super(key: key);@overrideState<StatefulWidget> createState() {return _CarouselWithIndicatorState();}}class _CarouselWithIndicatorState extends State<CarouselWithIndicatorDemo> {bool isClicked = false;int _current = 0;final CarouselController _controller = CarouselController();@overrideWidget build(BuildContext context) {var size = MediaQuery.of(context).size;return Scaffold(body: Column(children: [Expanded(flex: 3,child: Stack(children: [CarouselSlider(items: imgList.map((item) => Center(child: Image.network(item,height: size.height * 0.3 + kToolbarHeight,fit: BoxFit.cover,width: size.width))).toList(),carouselController: _controller,options: CarouselOptions(height: size.height * 0.3 + kToolbarHeight,viewportFraction: 1,autoPlay: true,//enlargeCenterPage: true,//aspectRatio: 1.5,onPageChanged: (index, reason) {setState(() {_current = index;});}),),Positioned(bottom: 5,left: 12,child: Row(mainAxisAlignment: MainAxisAlignment.center,children: imgList.asMap().entries.map((entry) {return GestureDetector(onTap: () => _controller.animateToPage(entry.key),child: AnimatedContainer(duration: const Duration(seconds: 1),curve: Curves.easeInCubic,width: _current == entry.key ? 17.0 : 10,height: 7.0,margin: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 4.0),decoration: BoxDecoration(borderRadius: BorderRadius.circular(7),//shape: BoxShape.circle,color: (Theme.of(context).brightness ==Brightness.dark? Colors.white: Colors.cyan).withOpacity(_current == entry.key ? 1.0 : 0.4)),),);}).toList(),),),]),),Expanded(flex: 7,child: GestureDetector(child: Container(height: 100,width: 100,decoration: const BoxDecoration(shape: BoxShape.circle, color: Colors.grey),child: GestureDetector(child: !isClicked? GestureDetector(onTap: () {isClicked = !isClicked;setState(() {});},child: const Icon(Icons.home,size: 35,),): Center(child: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly,children: [IconButton(icon: Icon(Icons.add),onPressed: () {print('Add Icon Pressed');//setState(() {});},),Icon(Icons.manage_accounts)],),),),),))]),);}}