Flutter Walkthrough screens without any package
Have you ever used an app with a smooth introduction that explained its features and excited you? That’s the power of a well-designed Flutter Walkthrough screens. But what if you don’t want to use any external packages? In this blog post, we will detail how to make a walk-through screen for your app using core Flutter widgets and customise it according to your needs. So fasten your seat belts while we showcase the custom-built walkthrough screens.
Why Custom-Built Flutter Walkthrough screens
Many prebuilt packages let you add Flutter Walkthrough screens to your app without hassle, so why should you write your code? Because it is
- Easy to customise, and we have complete control.
- Lightweight and efficient.
- Flexible for future changes.
Step-by-Step Implementation
I assume you have set up the basic Flutter Walkthrough screens project. We will make Car and House Rental app walkthroughs for this tutorial. Below are the example screens that we are going to make.
If you look closely, we have four components on the screen.
- Image
- Buttons
- Text
- Dot Indicator
Custom Button Code Implementation
Let’s write down the code for the button, and we will extract it in the widget so it can be reused inside the app at any point.
import 'package:flutter/material.dart';
class CustomButton extends StatelessWidget {
final Color color;
final Color? borderColor;
final double width, height, radius;
final Widget child;
final void Function()? onPressed;
final double? elevation;
const CustomButton({
super.key,
this.borderColor,
this.onPressed,
required this.color,
required this.width,
this.elevation,
required this.height,
required this.child,
required this.radius,
});
@override
Widget build(BuildContext context) {
return SizedBox(
width: width,
height: height,
child: ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
elevation: elevation,
backgroundColor: color,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radius),
side: BorderSide(
color: borderColor ?? color,
width: 2,
),
),
),
child: child,
),
);
}
}
Custom Button Code Explanation
We have used an elevated button that has a parent Sizedbox which is used to give the button width and height and a widget as a child so we can add an icon, text or icon and text when needed in flutterforums.
Dot Indicators Used
Once that is done application using flutter, let’s move to the custom dot indicator part. For that Flutter Walkthrough Screens, we will besing the dots_indicator package. In your terminal write:
Flutter Pub Add Dots_Indicator
Flutter Walkthrough screens Code Implementation Now, let’s move to the final part, where we write the code for the screen, the switching of screens on next and skip and final navigation after completion of the tutorial.
import 'package:flutter/material.dart';
import 'package:dots_indicator/dots_indicator.dart';
import 'package:get/get.dart';
import '../../core/constants/color_constants.dart';
import '../../core/routes/named_routes.dart';
import '../../widgets/custom_button.dart';
class WalkthroughScreen extends StatefulWidget {
const WalkthroughScreen({super.key});
@override
State<WalkthroughScreen> createState() => _WalkthroughScreenState();
}
class _WalkthroughScreenState extends State<WalkthroughScreen> {
int totalPages = 6;
int currentIndex = 0;
List<String> titles = [
'Welcome to the AK Rentals',
'Explore Cars & Houses',
'Favourite Your Finds',
'Connect directly with Owners',
'Secure & Easy Payments',
'Get Rolling with AK Rentals'
];
List<String> descriptions = [
'Your one-stop shop for finding your dream car and your perfect vacation
home.',
'Browse a wide selection of cars and houses, all in one app. Find the
perfect fit for your needs.',
'Tap the heart icon to save your favorite cars and houses for easy
access later.',
'Ask questions, clarify details, and negotiate directly with car and
house owners through chat.',
'Securely pay for car rentals and house bookings directly within the
app.',
'Create an account to access all the features and start your car and
house booking journey.',
];
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: ColorConstants.whiteColor,
body: GestureDetector(
onHorizontalDragEnd: (details) {
if (details.primaryVelocity! > 0) {
if (currentIndex == 0) {
return;
}
setState(() {
currentIndex--;
});
} else if (details.primaryVelocity! < 0) {
if (currentIndex == 5) {
return;
}
setState(() {
currentIndex++;
});
}
},
child: Stack(
children: [
Image.asset(
'assets/images/${currentIndex + 1}-onboarding.png',
fit: BoxFit.contain,
height: Get.height * 0.65,
width: double.infinity,
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: EdgeInsets.symmetric(
horizontal: Get.width * 0.1,
),
width: double.infinity,
height: Get.height * 0.4,
decoration: const BoxDecoration(
color: ColorConstants.whiteColor,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12),
),
),
child: Column(
children: [
SizedBox(
height: Get.height * 0.04,
),
DotsIndicator(
dotsCount: totalPages,
position: currentIndex,
onTap: (index) {
setState(() {
currentIndex = index;
});
},
decorator: DotsDecorator(
spacing: const EdgeInsets.all(2.0),
activeColor: ColorConstants.redColor,
color: const Color(0xffCEC8C7),
activeSize: const Size(18.0, 9.0),
size: const Size(14.0, 7.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4.0),
),
activeShape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
),
),
),
SizedBox(
height: Get.height * 0.06,
),
Text(
titles[currentIndex],
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
SizedBox(
height: Get.height * 0.02,
),
Text(
descriptions[currentIndex],
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
color: ColorConstants.blackColor.withOpacity(0.6),
fontWeight: FontWeight.w500,
),
),
const Spacer(),
if (currentIndex == 5)
CustomButton(
onPressed: () {
// TODO: Navigate to Authentication Screen
},
color: ColorConstants.redColor,
width: Get.width,
height: Get.height * 0.065,
radius: 36,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Get Started',
style: TextStyle(
color: ColorConstants.whiteColor,
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
SizedBox(
width: Get.width * 0.02,
),
const Icon(
Icons.arrow_forward,
color: ColorConstants.whiteColor,
size: 20,
),
],
),
),
if (currentIndex != 5)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: () {
Get.offAllNamed(NamedRoutes.login);
},
child: const Text(
'Skip',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
CustomButton(
onPressed: () {
if (currentIndex == 6) {
// TODO: Navigate to Authentication Screen
}
setState(() {
currentIndex++;
});
},
color: ColorConstants.redColor,
width: Get.width * 0.32,
height: Get.height * 0.065,
radius: 36,
child: const Row(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [
Text(
'Next',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
Icon(
Icons.arrow_forward_ios,
color: ColorConstants.whiteColor,
size: 20,
),
],
),
),
],
),
SizedBox(
height: Get.height * 0.04,
),
],
),
),
),
],
),
),
);
}
}
Walkthrough Code Explanation
We have used getx as state management along with dots_indicator for dots. Getx is an easy-to-use, light weight and combines dependency injection, route management and state management easily and practically in Flutter Walkthrough screens.
- The Flutter Walkthrough screens class extends Stateful Widget, which means this widget can change over time (i.e., it has mutable state). The state for this widget is defined in_WalkthroughScreenState.
- In _WalkthroughScreenState, several variables are defined to manage the walkthrough pages. totalPages is the total number of pages in the walkthrough, currentIndex is the index of the currently displayed page, titles is a list of titles for each page, and descriptions is a list of descriptions for each page.
- The Scaffold contains a Gesture Detector that listens for horizontal swipes. If a swipe is detected, it checks the direction of the swipe and updates currentIndex accordingly, moving to the next or previous page.
- The GestureDetector contains a Stack widget, which allows for stacking multiple widgets on top of each other. The first child in the Stack is an Image.asset, which displays an image corresponding to the current page.
- The child is an Align widget, which contains a Container that holds the content for the current page. The Container includes a DotsIndicator widget that shows the current page’s position, a Text widget for the title, another Text widget for the description, and a CustomButton widget for navigation.
The CustomButton changes based on the current page. If the user is on the last page, the button will navigate to the authentication screen. If Flutter Walkthrough screens the user is not on the last page, the button will navigate to
the next page.
Conclusion
We have learned how to make a custom button that can then be used inside your entire app, we have also learned how to implement extraction logic to avoid code reuse, and finally, we have developed a custom Flutter Walkthrough screens solution that can be integrated inside any of your app and change according to your needs. If you have any suggestions, you can reach us out and we will improve it for you.