I have an onboarding screen that I am working on. The onboarding screen navigates to the next page after 300 milliseconds. But the problem I have is that there is a part of the screen that has refused to work properly. The first page appears and display all the images and other contents. But when it tries to navigates to the next screen, it displays a red background color with an error
RangeError (index): invalid value: Only valid value is 0 : 2, see also…
I have tried to resolve this myself but to no avail.
This is my onboarding screen code.
import 'package:flutter/material.dart';
import 'package:greenbii_app/constants/constants.dart';
import 'package:greenbii_app/onboarding/onboarding_data.dart';
import 'package:greenbii_app/onboarding/onboarding_page.dart';
import 'package:greenbii_app/onboarding/onboarding_page_indicator.dart';
import 'dart:async';
class OnboardingScreen extends StatefulWidget {
const OnboardingScreen({Key? key}) : super(key: key);
@override
State<OnboardingScreen> createState() => _OnboardingScreenState();
}
class _OnboardingScreenState extends State<OnboardingScreen> {
int currentPageIndex = 0;
final PageController _pageController = PageController();
Timer? _pageTimer;
@override
void initState() {
super.initState();
_startTimer();
}
@override
void dispose() {
_pageTimer?.cancel();
_pageController.dispose();
super.dispose();
}
void _startTimer() {
_pageTimer = Timer.periodic(const Duration(seconds: 5), (timer) {
if (currentPageIndex < onboardingData.length - 1) {
_pageController.nextPage(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
});
}
void _stopTimer() {
_pageTimer?.cancel();
}
void _navigateToNextPage() {
if (currentPageIndex < onboardingData.length - 1) {
_pageController.nextPage(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
} else {
// Navigate to the login and registration screen
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.onboardingColorBG,
body: SafeArea(
child: Column(children: [
Expanded(
child: PageView.builder(
controller: _pageController,
onPageChanged: (index) {
setState(() {
currentPageIndex = index;
});
},
itemCount: onboardingData.length,
itemBuilder: (context, int index) {
return OnboardingPage(
data: onboardingData[index],
index: index,
currentPageIndex: currentPageIndex,
onStopTimer: _stopTimer,
onNextPage: _navigateToNextPage,
pageController: _pageController,
);
},
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Row(
children: [
OnboardingPageIndicator(
currentPageIndex: currentPageIndex,
totalPages: onboardingData.length,
),
const Spacer(),
TextButton(
onPressed: () {
// Navigate to the login Page
},
child: Row(children: const [
Text(
'Skip',
style: TextStyle(
color: AppColors.primaryColor, fontSize: 16),
),
Icon(
Icons.chevron_right,
color: AppColors.primaryColor,
)
]),
)
],
),
),
const SizedBox(
height: 20,
),
]),
));
}
}
I have used Page.View Widget but the whole screen content disappears when I use it, and without it, it gives the error above.
This is my onboarding page widget that I reused in the onboarding screen
import 'package:flutter/material.dart';
import 'package:greenbii_app/constants/constants.dart';
import 'package:greenbii_app/onboarding/onboarding_data.dart';
// import 'package:google_fonts/google_fonts.dart';
class OnboardingPage extends StatelessWidget {
final OnboardingData data;
final int index;
final int currentPageIndex;
final VoidCallback onStopTimer;
final VoidCallback onNextPage;
final PageController? pageController;
const OnboardingPage({
Key? key,
required this.data,
required this.index,
required this.currentPageIndex,
required this.onStopTimer,
required this.onNextPage,
this.pageController,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
height: 300,
child: Image.asset(
data.imagePath[currentPageIndex],
),
),
//
Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
Text(
data.title,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: AppColors.primaryColor,
),
textAlign: TextAlign.center,
),
const SizedBox(
height: 10,
),
Text(
data.bodyText,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
color: AppColors.primaryColor,
),
textAlign: TextAlign.center,
)
],
),
),
const SizedBox(
height: 30,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 70),
child: Container(
width: double.infinity, // Width takes up 100% of the device
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50.0), // Add border radius
gradient: const LinearGradient(
colors: [
AppColors.primaryColorLow,
AppColors.primaryColor,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: ElevatedButton(
onPressed: () {
onNextPage();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors
.transparent, // Make the button background transparent
shadowColor: Colors.transparent, // Remove the default shadow
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
currentPageIndex == onboardingData.length - 1
? "Get Started"
: "Continue",
style: const TextStyle(color: Colors.white),
),
),
),
),
),
],
);
}
}
Also below is my onboardingdata file, probably something is wrong the way I used it.
class OnboardingData {
final List<String> imagePath;
final String title;
final String bodyText;
OnboardingData(this.imagePath, this.title, this.bodyText);
}
List<OnboardingData> onboardingData = [
OnboardingData(
["assets/images/onboarding_img1.png"],
"Grow Your Business!",
"Access all the digital and financial resources you need, all in one place.",
),
OnboardingData(
["assets/images/onboarding_img2.png"],
"Manage all from one dashboard.",
"Your GreenBii account allows you manage all your softwares and monitor all your devices from one place.",
),
OnboardingData(
["assets/images/onboarding_img3.png"],
"Welcome, Let’s Get Started",
"Get \$10 free GreenBii Credit when you create an account.",
),
];
How to get this solved?
>Solution :
On your onboardingData list, take a look at every imagePath argument:
List<OnboardingData> onboardingData = [
OnboardingData(
["assets/images/onboarding_img1.png"],
"Grow Your Business!",
"Access all the digital and financial resources you need, all in one place.",
),
OnboardingData(
["assets/images/onboarding_img2.png"],
"Manage all from one dashboard.",
"Your GreenBii account allows you manage all your softwares and monitor all your devices from one place.",
),
OnboardingData(
["assets/images/onboarding_img3.png"],
"Welcome, Let’s Get Started",
"Get \$10 free GreenBii Credit when you create an account.",
),
];
They are all a list of strings with a single element. When you do
SizedBox(
height: 300,
child: Image.asset(
data.imagePath[currentPageIndex],
),
),
and currentPageIndex == 2, for example, you are accessing more than a single element on that list.
So you may either
- change it to
data.imagePath[0](the quickest way to solve this) - change
imagePath‘s type fromList<String>to aString, and use it asdata.imagePath