Flutter App Intro/Onboarding Screen with Carousel & Animated Dots — NO Package

The Flutter Way
4 min readJan 28, 2024

You’re going to learn how to create an Onboarding page like shows below in your Flutter app.

Onboard Page Preview

As you can see the indicators are animated — it’s amazing that it only needs 10 lines of code!

Also created a video tutorial 🎬, Build Flutter App Intro/Onboarding Screen

Get started

Let’s start with OnboardingPage, which is a StatefulWidget. Also, define a variable named _selectedIndex and set its default value to 0.

class OnboardingPage extends StatefulWidget {
const OnboardingPage({super.key});

@override
State<OnboardingPage> createState() => _OnboardingPageState();
}

class _OnboardingPageState extends State<OnboardingPage> {
int _selctedIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
// TODO: Onboard Content

// TODO: Animated Dots

// TODO: Button
],
),
),
);
}
}

List<Map<String, dynamic>> demoData = [
{
"illustration": "assets/Illustrations/Illustrations_1.svg",
"title": "All your favorites",
"text":
"Order from the best local restaurants \nwith easy, on-demand delivery.",
},
{
"illustration": "assets/Illustrations/Illustrations_2.svg",
"title": "Free delivery offers",
"text":
"Free delivery for new customers via Apple Pay\nand others payment methods.",
},
{
"illustration": "assets/Illustrations/Illustrations_3.svg",
"title": "Choose your food",
"text":
"Easily find your type of food craving and\nyou’ll get delivery in wide range.",
},
];

Onboard Content

In OnboardContent, we start with an illustration. Following that, there’s a title, which we make larger by setting its style to titleLarge. Finally, there’s a short description. This is why we have three parameters: illustration, title, and text.

class OnboardContent extends StatelessWidget {
const OnboardContent({
super.key,
required this.illustration,
required this.title,
required this.text,
});

final String illustration, title, text;

@override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: AspectRatio(
aspectRatio: 1,
child: SvgPicture.asset(illustration),
),
),
const SizedBox(height: 16),
Text(
title,
style: Theme.of(context)
.textTheme
.titleLarge!
.copyWith(fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Text(
text,
style: Theme.of(context).textTheme.bodyMedium,
textAlign: TextAlign.center,
),
],
);
}
}

Now, let’s put the OnboardContent into action on our onboarding page. If you look at the preview, you’ll notice that users be able to swipe left or right. We achieve this by using a PageView. When a page is changed, we update the _selectedIndex. This will be handy later when we add animated dots to indicate the page position. Replace TODO: Onboard Content with below code

const Spacer(flex: 2),
SizedBox(
height: 500,
child: PageView.builder(
itemCount: demoData.length,
onPageChanged: (value) {
setState(() {
_selctedIndex = value;
});
},
itemBuilder: (context, index) {
return OnboardContent(
illustration: demoData[index]['illustration'],
title: demoData[index]['title'],
text: demoData[index]['text'],
);
},
),
),
const Spacer(),
Preview with OnboardContent

Animated dots

When creating animated dots, we opt for Implicit animations because they are straightforward and simple, eliminating the need for controllers or complex configurations. We just need to define the duration. The AnimatedContainer is one of them. Here, if the dot is active, its width expands to 24; otherwise, it remains at 6. The transition from 24 to 6 doesn’t happen instantly; it gradually takes place over the time period specified in the duration.

lass AnimatedDot extends StatelessWidget {
const AnimatedDot({
super.key,
required this.isActive,
});

final bool isActive;

@override
Widget build(BuildContext context) {
return AnimatedContainer(
duration: const Duration(milliseconds: 300),
height: 6,
width: isActive ? 20 : 6,
decoration: BoxDecoration(
color:
isActive ? primaryColor : const Color(0xFF868686).withOpacity(0.25),
borderRadius: const BorderRadius.all(Radius.circular(12)),
),
);
}
}

Now, to display the dots on our page, simply replace the TODO: Animated Dots with below code

Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(
demoData.length,
(index) => Padding(
padding: const EdgeInsets.only(right: 6),
child: AnimatedDot(isActive: _selctedIndex == index),
),
),
),
const Spacer(flex: 2),
Animated Dots preview

Button

We’re almost there! The only component left to complete is the button. Let’s wrap that up with replace the TODO: Button with below code

ElevatedButton(
onPressed: () {},
child: Text(
"Get Started".toUpperCase(),
),
),
const Spacer(),

There’s more to explore!

This onboarding page serves as the initial screen for our comprehensive Restaurant/Food Delivery App template. To explore the full template, feel free to visit FlutterLibrary.com

Thank you so much for reading. I hope you found this helpful. If you have any suggestions or feedback, please let me know. Your input is invaluable in helping me create better content for you all.

--

--

The Flutter Way

Want to improve your flutter skill? Join our channel, learn how to become an expert flutter developer and land your next dream job!