Careem HomePage like Customized Carousel Slider in Flutter with controls

Kashif Chandio
5 min readMay 20, 2022

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)],),),),),))]),);}}

--

--

Kashif Chandio

Flutter Developer and Passionate to Learn new Technologies and Tools