When it comes to user onboarding in mobile apps, creating a smooth and engaging experience is crucial. One way to do this in Flutter is by building an onboarding screen that guides users through your app’s key features. In this tutorial, we’ll walk through the process of creating an onboarding screen in Flutter.
Getting Started
Step 1: Set Up Assets
Add necessary assets
Step 2: Create an Onboarding page
import 'package:flutter/material.dart';
class OnboardingPageModel {
final String title;
final String description;
final String image;
final Color bgColor;
final Color textColor;
OnboardingPageModel(
{required this.title,
required this.description,
required this.image,
this.bgColor = Colors.blue,
this.textColor = Colors.white});
}
class OnboardingPage extends StatefulWidget {
final List<OnboardingPageModel> pages;
const OnboardingPage({Key? key, required this.pages}) : super(key: key);
@override
_OnboardingPageState createState() => _OnboardingPageState();
}
class _OnboardingPageState extends State<OnboardingPage> {
// Store the currently visible page
int _currentPage = 0;
// Define a controller for the pageview
final PageController _pageController = PageController(initialPage: 0);
@override
Widget build(BuildContext context) {
return Scaffold(
body: AnimatedContainer(
duration: const Duration(milliseconds: 250),
color: widget.pages[_currentPage].bgColor,
child: SafeArea(
child: Column(
children: [
Expanded(
// Pageview to render each page
child: PageView.builder(
controller: _pageController,
itemCount: widget.pages.length,
onPageChanged: (idx) {
// Change current page when pageview changes
setState(() {
_currentPage = idx;
});
},
itemBuilder: (context, idx) {
final _item = widget.pages[idx];
return Column(
children: [
Expanded(
flex: 3,
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Image.asset(
_item.image,
),
),
),
Expanded(
flex: 1,
child: Column(children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(_item.title,
style: Theme.of(context)
.textTheme
.headline6
?.copyWith(
fontWeight: FontWeight.bold,
color: _item.textColor,
)),
),
Container(
constraints: BoxConstraints(maxWidth: 280),
padding: const EdgeInsets.symmetric(
horizontal: 24.0, vertical: 8.0),
child: Text(_item.description,
textAlign: TextAlign.center,
style: Theme.of(context)
.textTheme
.bodyText2
?.copyWith(
color: _item.textColor,
)),
)
]))
],
);
},
),
),
// Current page indicator
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: widget.pages
.map((item) => AnimatedContainer(
duration: const Duration(milliseconds: 250),
width: _currentPage == widget.pages.indexOf(item)
? 20
: 4,
height: 4,
margin: const EdgeInsets.all(2.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10.0)),
))
.toList(),
),
// Bottom buttons
SizedBox(
height: 100,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: () {
// Handle Skipping onboarding page
},
child: Text(
"Skip",
style: TextStyle(color: Colors.white),
)),
TextButton(
onPressed: () {
if (_currentPage == widget.pages.length - 1) {
// This is the last page
} else {
_pageController.animateToPage(_currentPage + 1,
curve: Curves.easeInOutCubic,
duration: const Duration(milliseconds: 250));
}
},
child: Text(
_currentPage == widget.pages.length - 1
? "Finish"
: "Next",
style: TextStyle(color: Colors.white),
),
),
],
),
)
],
),
),
),
);
}
}
PageView.builder is a widget that creates a scrollable view of pages, and it uses a builder function to create each page dynamically based on the index.
itemCount is set to the total number of onboarding pages, which is derived from homeCon.demoData.length.
scrollDirection: Axis.horizontal specifies that the pages should be scrollable horizontally.
onPageChanged is a callback function that is called when the user changes the page. It updates the current page index in the homeCon.currentPage variable, presumably for keeping track of the active page.
itemBuilder is a function that generates each onboarding page based on the current index. Inside the builder function, each page is represented as a Column containing various widgets.
SizedBox with a height of 50 pixels creates space at the top of each page.
SvgPicture.asset displays an SVG image specified by homeCon.demoData[index].image.
Spacer() is used to push the content to the bottom of each page, so that other elements, like the dot indicators and buttons, can be placed below the onboarding content.
Overall, this code is responsible for creating a dynamic onboarding screen with pages that include SVG images, titles, and descriptions. The onPageChanged callback updates the current page index as the user swipes through the pages, allowing for seamless navigation.
import 'package:flutter/material.dart';
enum WeatherTypeEnum {
Thunderstorm,
Drizzle,
Rain,
Snow,
Clear,
Clouds,
other,
}
extension WeatherTypeEnumExtension on String? {
WeatherTypeEnum? toWeatherType() {
WeatherTypeEnum? value;
switch (this) {
case 'Thunderstorm':
value = WeatherTypeEnum.Thunderstorm;
break;
case 'Drizzle':
value = WeatherTypeEnum.Drizzle;
break;
case 'Snow':
value = WeatherTypeEnum.Snow;
break;
case 'Rain':
value = WeatherTypeEnum.Rain;
break;
case 'Clear':
value = WeatherTypeEnum.Clear;
break;
case 'Clouds':
value = WeatherTypeEnum.Clouds;
break;
default:
value = WeatherTypeEnum.other;
break;
}
return value;
}
}
extension WeatherTypeEnumThemeExtension on WeatherTypeEnum? {
WeatherThemeEntity? toWeatherTheme() {
WeatherThemeEntity? value;
switch (this) {
case WeatherTypeEnum.Thunderstorm:
case WeatherTypeEnum.Clouds:
case WeatherTypeEnum.Rain:
value = WeatherThemeEntity(
firstColor: blue3E97C8,
secondColor: blue3E97C8.withOpacity(0.8),
);
break;
case WeatherTypeEnum.Drizzle:
case WeatherTypeEnum.Snow:
value = WeatherThemeEntity(
firstColor: blue3E97C8,
secondColor: blueD8E7F2,
);
break;
case WeatherTypeEnum.Clear:
value = WeatherThemeEntity(
firstColor: Colors.orange,
secondColor: Colors.yellow,
);
break;
case WeatherTypeEnum.other:
value = WeatherThemeEntity(
firstColor: greyDDDDDD,
secondColor: black404040,
);
break;
default:
value = WeatherThemeEntity(
firstColor: blue3E97C8,
secondColor: blueD8E7F2,
);
break;
}
return value;
}
}