feat:onboarding page ui done

This commit is contained in:
jahongireshonqulov
2025-10-31 16:32:28 +05:00
parent 0388d22cc1
commit d8bd9c4925
23 changed files with 853 additions and 11 deletions

View File

@@ -42,8 +42,8 @@ class AppButton extends StatelessWidget {
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: backgroundColor ?? AppColors.c000000,
borderRadius: BorderRadius.circular(borderRadius ?? 0),
color: backgroundColor ?? AppColors.cFF6F00,
borderRadius: BorderRadius.circular(borderRadius ?? 12),
),
child: isLoading
? Center(

View File

@@ -0,0 +1,58 @@
import 'package:flutter/material.dart';
import 'package:food_delivery_client/core/core.dart';
import '../../../../core/theme/app_colors.dart';
/// A simple animated dot indicator that uses AnimatedContainer.
/// Update [activeIndex] to animate which dot is active.
class AnimatedDotsIndicator extends StatelessWidget {
final int count;
final int activeIndex;
final double dotSize;
final double activeDotWidth;
final double spacing;
final Duration duration;
final Curve curve;
final Color activeColor;
final Color inactiveColor;
final BorderRadius borderRadius;
const AnimatedDotsIndicator({
Key? key,
required this.count,
required this.activeIndex,
this.dotSize = 4,
this.activeDotWidth = 32.0,
this.spacing =4,
this.duration = const Duration(milliseconds: 300),
this.curve = Curves.easeInOut,
this.activeColor = AppColors.cFF6F00,
this.inactiveColor = AppColors.cFFEDCC,
this.borderRadius = const BorderRadius.all(Radius.circular(12)),
}) : assert(count >= 0),
assert(activeIndex >= 0),
super(key: key);
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: List.generate(count, (index) {
final bool isActive = index == activeIndex;
return Padding(
padding: EdgeInsets.only(right: index == count - 1 ? 0 : spacing),
child: AnimatedContainer(
duration: duration,
curve: curve,
width: activeDotWidth,
height: dotSize,
decoration: BoxDecoration(
color: isActive ? activeColor : inactiveColor,
borderRadius: borderRadius,
),
),
);
}),
);
}
}

View File

@@ -1,6 +1,4 @@
import 'package:bloc/bloc.dart';
import 'package:food_delivery_client/food_delivery_client.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'splash_event.dart';

View File

@@ -1,10 +1,91 @@
import 'package:food_delivery_client/feature/common/presentation/widgets/w_animated_dots_indicator.dart';
import 'package:food_delivery_client/feature/onboarding/presentation/pages/onboarding_page/widgets/w_onbaording_background.dart';
import 'package:food_delivery_client/feature/onboarding/presentation/pages/onboarding_page/widgets/w_onboarding_body.dart';
import '../../../../../food_delivery_client.dart';
class OnboardingPage extends StatelessWidget {
class OnboardingPage extends StatefulWidget {
const OnboardingPage({super.key});
@override
State<OnboardingPage> createState() => _OnboardingPageState();
}
class _OnboardingPageState extends State<OnboardingPage> {
late PageController _pageController;
int currentIndex = 0;
@override
void initState() {
_pageController = PageController(initialPage: 0);
super.initState();
}
@override
void dispose() {
_pageController.dispose();
super.dispose();
}
void onPressed() {
setState(() {
currentIndex++;
});
_pageController.animateToPage(
currentIndex,
duration: TimeDelayConst.durationMill300,
curve: Curves.easeIn,
);
}
void onPageChanged(int int) {
setState(() {
currentIndex = int;
});
}
@override
Widget build(BuildContext context) {
return WLayout(child: Scaffold());
return WLayout(
bottom: false,
child: Scaffold(
body: Stack(
children: [
WOnBoardingBackground(),
WOnBoardingBody(
pageController: _pageController,
onPageChanged: onPageChanged,
),
Positioned(
bottom: 18,
left: 24,
right: 24,
child: SafeArea(
child: Column(
children: [
AnimatedDotsIndicator(count: 2, activeIndex: currentIndex),
40.verticalSpace,
AppButton(
name: currentIndex == 0
? context.loc.onboarding_button_next
: context.loc.onboarding_button_get_started,
onPressed: () {
if (currentIndex < 1) {
onPressed();
}
if (currentIndex == 1) {
log("Navigate to login");
}
},
),
],
),
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,54 @@
import '../../../../../../food_delivery_client.dart';
class Onboarding2 extends StatelessWidget {
const Onboarding2({super.key});
@override
Widget build(BuildContext context) {
return SizedBox(
height: context.h,
width: context.w,
child: Column(
children: [
Expanded(
child: Stack(
alignment: Alignment.center,
children: [
SvgPicture.asset(AppIcons.icOnBoarding2),
SvgPicture.asset(AppIcons.icOnBoarding3),
],
),
),
Expanded(
child: DecoratedBox(
decoration: BoxDecoration(
color: context.theme.scaffoldBackgroundColor,
boxShadow: [
BoxShadow(
color: AppColors.cD6D4D4.newWithOpacity(.25),
offset: Offset(0, 0),
blurRadius: 10,
),
],
),
child: Column(
spacing: 20,
children: [
25.verticalSpace,
Text(
"Bringing the best flavors food in the world",
textAlign: TextAlign.center,
),
Text(
"Discover your favorite meals and get them delivered fast wherever you are.",
textAlign: TextAlign.center,
),
],
).paddingSymmetric(horizontal: 22),
),
),
],
),
);
}
}

View File

@@ -0,0 +1,31 @@
import '../../../../../../food_delivery_client.dart';
class WOnBoardingBackground extends StatelessWidget {
const WOnBoardingBackground({super.key});
@override
Widget build(BuildContext context) {
return Positioned.fill(
child: Column(
children: [
Expanded(child: SizedBox.expand()),
Expanded(
child: DecoratedBox(
decoration: BoxDecoration(
color: context.appThemeColors.onBoardingColor,
boxShadow: [
BoxShadow(
color: context.appThemeColors.boxShadow.newWithOpacity(.25),
offset: Offset(0, 0),
blurRadius: 10,
),
],
),
child: SizedBox.expand(),
),
),
],
),
);
}
}

View File

@@ -0,0 +1,73 @@
import '../../../../../../food_delivery_client.dart';
class WOnBoardingBody extends StatelessWidget {
const WOnBoardingBody({
super.key,
required this.pageController,
required this.onPageChanged,
});
final PageController pageController;
final Function(int value) onPageChanged;
@override
Widget build(BuildContext context) {
final List<String> titles = [
context.loc.onboarding_title_1,
context.loc.onboarding_title_1,
];
final List<String> descriptions = [
context.loc.onboarding_subtitle_1,
context.loc.onboarding_subtitle_2,
];
final List<Widget> children = [
SvgPicture.asset(AppIcons.icOnBoarding1),
Stack(
alignment: Alignment.center,
children: [
Positioned(
top: 80,
left: 40,
child: SvgPicture.asset(AppIcons.icOnBoarding2),
),
SvgPicture.asset(AppIcons.icOnBoarding3),
],
),
];
return Positioned.fill(
child: PageView.builder(
itemCount: titles.length,
controller: pageController,
scrollDirection: Axis.horizontal,
onPageChanged: onPageChanged,
itemBuilder: (context, index) => Column(
children: [
Expanded(child: children[index]),
Expanded(
child: Column(
spacing: 20,
children: [
25.verticalSpace,
Text(
titles[index],
textAlign: TextAlign.center,
style: context.appThemeTextStyles.size24SemiBold,
),
Text(
descriptions[index],
textAlign: TextAlign.center,
style: AppTextStyles.size14Regular.copyWith(
color: AppColors.cA9A9A9,
),
),
],
).paddingSymmetric(horizontal: 22),
),
],
),
),
);
}
}