I'am using PageView now. In the PageView, there are four pages.
class OnboardingScreen extends StatefulWidget {
const OnboardingScreen({super.key});
@override
OnboardingScreenState createState() => OnboardingScreenState();
}
class OnboardingScreenState extends State<OnboardingScreen> {
late final List<Widget> _pages;
double _progressRatio = 0.0;
final PageController _pageController = PageController(
initialPage: 0,
);
final OnboardingModel _onboardingData = OnboardingModel();
void _syncProgressRatio() {
if (!_pageController.hasClients) {
_progressRatio = 0.0;
return;
}
double currentPage = _pageController.page!;
int totalPageCount = _pages.length - 1;
_progressRatio = currentPage / totalPageCount;
}
void _goNextPage() async {
await _pageController.nextPage(
duration: const Duration(milliseconds: 1),
curve: Curves.easeInOut,
);
setState(() {
_syncProgressRatio();
});
}
void _goPreviousPage() async {
await _pageController.previousPage(
duration: const Duration(milliseconds: 1),
curve: Curves.easeInOut,
);
setState(() {
_syncProgressRatio();
});
}
void _onboardingFinish() {
context.read<OnboardingBloc>().add(
OnboardingFinished(
nickName: _onboardingData.nickName!,
genderType: _onboardingData.genderType!,
birthday: _onboardingData.birthDay!,
profileImage: _onboardingData.profileImage,
),
);
}
@override
void initState() {
super.initState();
DateTime today = DateTime.now();
_onboardingData.birthDay =
BirthdayModel(today.year, today.month, today.day);
_pages = [
OnboardingNickName(
onboardingData: _onboardingData,
goNextPage: _goNextPage,
),
OnboardingGender(
onboardingData: _onboardingData,
goNextPage: _goNextPage,
goPreviousPage: _goPreviousPage,
),
OnboardingBirthday(
onboardingData: _onboardingData,
goNextPage: _goNextPage,
goPreviousPage: _goPreviousPage,
),
OnboardingProfileImage(
onboardingData: _onboardingData,
onboardingFinish: _onboardingFinish,
goPreviousPage: _goPreviousPage,
),
];
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
return false;
},
child: Scaffold(
body: SafeArea(
child: BlocListener<OnboardingBloc, OnboardingState>(
listener: (context, state) {
if (state is OnboardingSucceed) {
print('OnboardingSucceed');
context.goNamed(RouterLocation.onboardingComplete);
return;
}
if (state is OnboardingFailed) {
print('OnboardingFailed');
BasicDialog.show(
context: context,
title: TextManager.onboardingFailedExceptionTitle.tr(),
contents: TextManager.onboardingFailedExceptionContents.tr(),
confirmAction: () {
context.read<OnboardingBloc>().add(
OnboardingFinished(
nickName: _onboardingData.nickName!,
genderType: _onboardingData.genderType!,
birthday: _onboardingData.birthDay!,
profileImage: _onboardingData.profileImage,
),
);
Navigator.of(context).pop();
},
);
}
},
child: ProgressWrapper(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildProgressBar(_progressRatio),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: ValueManager.s15),
child: PageView(
controller: _pageController,
physics: const NeverScrollableScrollPhysics(),
children: _pages,
),
),
),
],
),
),
),
),
),
);
}
Widget _buildProgressBar(double progressRatio) {
return ProgressBar(
progressRatio: progressRatio,
height: ValueManager.s10,
);
}
@override
void dispose() {
_pageController.dispose();
super.dispose();
}
}
in the last page, which is OnboardingProfileImage call method _onboardingFinish. and OnboardingBloc handle this.
class OnboardingBloc extends Bloc<OnboardingEvent, OnboardingState> {
final LoadingSpinnerBloc loadingSpinnerBloc;
final UserRepository userRepository;
OnboardingBloc({
required this.loadingSpinnerBloc,
required this.userRepository,
}) : super(OnboardingScreenLaunched()) {
on<OnboardingFinished>((event, emit) async {
loadingSpinnerBloc.processing();
OnboardingCompleteCommand command = OnboardingCompleteCommand(
nickName: event.nickName,
genderType: event.genderType,
birthDay: event.birthday,
profileImage: event.profileImage,
);
try {
final Either<AppException, void> response =
await userRepository.onboardingComplete(command);
response.fold((exception) {
emit(OnboardingFailed(exception));
}, (success) {
emit(OnboardingSucceed());
});
loadingSpinnerBloc.finished();
} catch (error) {
emit(OnboardingFailed(AppException(error.toString())));
loadingSpinnerBloc.failed();
}
});
}
}
the problem is here, await userRepository.onboardingComplete(command);
Future<Either<AppException, void>> onboardingComplete(
OnboardingCompleteCommand command) async {
bool isConnected = await connectionManager.isConnected;
if (!isConnected) {
return Left(AppException(TextManager.internetNotConnected.tr()));
}
final Map<String, dynamic> jsonData = command.toJson();
final FormData formData;
if (command.profileImage != null) {
formData = FormData.fromMap({
'profileImage':
await MultipartFile.fromFile(command.profileImage!.path),
});
jsonData.forEach((key, value) {
formData.fields.add(MapEntry(key, value.toString()));
});
} else {
formData = FormData.fromMap(jsonData);
}
try {
// page index not changed
// throw DioException(requestOptions: RequestOptions());
// exception happen and page index changed to 0
await apiClient.post(
'/user/profile',
data: formData,
options: Options(
contentType: 'multipart/form-data',
),
);
return const Right(null);
} on DioException catch (error) {
return Left(
AppException(TextManager.onboardingFailedExceptionContents.tr()));
}
}
when DioException occured, pageView index is changed... catch statement is working. but pageView index is changed.
so I tested that throw DioException on purpose, then pageView index not changed.
what is wrong with dio? how can I maintain pageView index with this code?
maintain pageView index