Browse Source
Merge pull request 'feature/intro' (#27) from feature/intro into develop
Merge pull request 'feature/intro' (#27) from feature/intro into develop
Reviewed-on: https://git.nwhco.ir/amirreza.chegini/hade_hoda_flutter/pulls/27pull/28/head
45 changed files with 876 additions and 157 deletions
-
BINassets/fonts/Baloo2-Bold.ttf
-
BINassets/fonts/Baloo2-ExtraBold.ttf
-
BINassets/fonts/Baloo2-Medium.ttf
-
BINassets/fonts/Baloo2-SemiBold.ttf
-
0assets/fonts/DinoKids-Regular.ttf
-
BINassets/fonts/Marhey-Light.ttf
-
BINassets/fonts/Marhey-Medium.ttf
-
BINassets/fonts/Marhey-Regular.ttf
-
BINassets/images/intro_1.jpg
-
BINassets/images/intro_2.jpg
-
BINassets/images/intro_3.jpg
-
BINassets/images/intro_4.jpg
-
BINassets/images/intro_5.jpg
-
10lib/common_ui/resources/my_assets.dart
-
137lib/common_ui/resources/my_text_style.dart
-
18lib/common_ui/theme/my_theme.dart
-
2lib/core/middlewares/my_middlewares.dart
-
23lib/core/routers/my_routes.dart
-
14lib/core/utils/my_device.dart
-
7lib/core/utils/screen_size.dart
-
18lib/core/utils/set_platform_size.dart
-
16lib/core/widgets/about_us_dialog/about_us_dialog.dart
-
6lib/core/widgets/about_us_dialog/styles/background.dart
-
67lib/core/widgets/animations/slide_up_fade.dart
-
8lib/core/widgets/hadith_dialog/hadith_dialog.dart
-
48lib/features/intro/presentation/bloc/intro_bloc.dart
-
2lib/features/intro/presentation/bloc/intro_event.dart
-
14lib/features/intro/presentation/bloc/intro_state.dart
-
97lib/features/intro/presentation/ui/intro_page.dart
-
54lib/features/intro/presentation/ui/screens/intro_1_screen.dart
-
37lib/features/intro/presentation/ui/screens/intro_2_screen.dart
-
37lib/features/intro/presentation/ui/screens/intro_3_screen.dart
-
36lib/features/intro/presentation/ui/screens/intro_4_screen.dart
-
34lib/features/intro/presentation/ui/screens/intro_5_screen.dart
-
91lib/features/intro/presentation/ui/widgets/bubble_chat_widget.dart
-
44lib/features/splash/presentation/bloc/splash_bloc.dart
-
3lib/features/splash/presentation/bloc/splash_event.dart
-
15lib/features/splash/presentation/bloc/splash_state.dart
-
82lib/features/splash/presentation/ui/splash_page.dart
-
6lib/init_bindings.dart
-
9lib/l10n/app_en.arb
-
42lib/l10n/app_localizations.dart
-
24lib/l10n/app_localizations_en.dart
-
1lib/main.dart
-
31pubspec.yaml
|
After Width: 819 | Height: 1229 | Size: 147 KiB |
|
After Width: 819 | Height: 1229 | Size: 139 KiB |
|
After Width: 819 | Height: 1229 | Size: 134 KiB |
|
After Width: 819 | Height: 1229 | Size: 141 KiB |
|
After Width: 819 | Height: 1229 | Size: 179 KiB |
@ -1,24 +1,10 @@ |
|||||
import 'package:flutter/material.dart'; |
import 'package:flutter/material.dart'; |
||||
import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; |
|
||||
|
|
||||
enum ColorsName { primaryColor, backgroundDialog } |
|
||||
|
|
||||
class MyTheme { |
class MyTheme { |
||||
static const MyTheme _i = MyTheme._internal(); |
static const MyTheme _i = MyTheme._internal(); |
||||
const MyTheme._internal(); |
const MyTheme._internal(); |
||||
factory MyTheme() => _i; |
factory MyTheme() => _i; |
||||
|
|
||||
static final ThemeData light = ThemeData(brightness: Brightness.dark); |
|
||||
|
|
||||
static final ThemeData dark = ThemeData(brightness: Brightness.light); |
|
||||
|
|
||||
static Map<ColorsName, Color> get lightColors => { |
|
||||
ColorsName.primaryColor: MyColors.white, |
|
||||
ColorsName.backgroundDialog: MyColors.purple, |
|
||||
}; |
|
||||
|
|
||||
static Map<ColorsName, Color> get darkColors => { |
|
||||
ColorsName.primaryColor: MyColors.black, |
|
||||
ColorsName.backgroundDialog: MyColors.purple, |
|
||||
}; |
|
||||
|
static final ThemeData light = ThemeData(brightness: Brightness.light); |
||||
|
static final ThemeData dark = ThemeData(brightness: Brightness.dark); |
||||
} |
} |
||||
@ -1,6 +1,13 @@ |
|||||
import 'package:flutter/material.dart'; |
import 'package:flutter/material.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/context_provider.dart'; |
||||
|
|
||||
extension ScreenSize on BuildContext { |
extension ScreenSize on BuildContext { |
||||
double get widthScreen => MediaQuery.sizeOf(this).width; |
double get widthScreen => MediaQuery.sizeOf(this).width; |
||||
double get heightScreen => MediaQuery.sizeOf(this).height; |
double get heightScreen => MediaQuery.sizeOf(this).height; |
||||
} |
} |
||||
|
|
||||
|
|
||||
|
extension AdaptiveSize on double { |
||||
|
double get w => ContextProvider.context.widthScreen * this; |
||||
|
double get h => ContextProvider.context.heightScreen * this; |
||||
|
} |
||||
@ -0,0 +1,67 @@ |
|||||
|
import 'package:flutter/material.dart'; |
||||
|
|
||||
|
class SlideUpFade extends StatefulWidget { |
||||
|
const SlideUpFade({ |
||||
|
super.key, |
||||
|
required this.child, |
||||
|
this.delay = Duration.zero, |
||||
|
}); |
||||
|
|
||||
|
final Widget child; |
||||
|
final Duration delay; |
||||
|
|
||||
|
@override |
||||
|
State<SlideUpFade> createState() => _SlideUpFadeState(); |
||||
|
} |
||||
|
|
||||
|
class _SlideUpFadeState extends State<SlideUpFade> |
||||
|
with SingleTickerProviderStateMixin { |
||||
|
late AnimationController _controller; |
||||
|
late Animation<double> _fadeAnim; |
||||
|
late Animation<Offset> _slideAnim; |
||||
|
|
||||
|
@override |
||||
|
void initState() { |
||||
|
super.initState(); |
||||
|
_controller = AnimationController( |
||||
|
vsync: this, |
||||
|
duration: Duration(milliseconds: 500), |
||||
|
reverseDuration: Duration(milliseconds: 500), |
||||
|
); |
||||
|
|
||||
|
_fadeAnim = Tween<double>( |
||||
|
begin: 0, |
||||
|
end: 1, |
||||
|
).animate(CurvedAnimation(parent: _controller, curve: Curves.easeIn)); |
||||
|
|
||||
|
_slideAnim = Tween<Offset>( |
||||
|
begin: Offset(0, 0.1), |
||||
|
end: Offset.zero, |
||||
|
).animate(CurvedAnimation(parent: _controller, curve: Curves.easeIn)); |
||||
|
startAnim(); |
||||
|
} |
||||
|
|
||||
|
Future<void> startAnim() async { |
||||
|
await Future.delayed(widget.delay, () { |
||||
|
_controller.forward(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
void dispose() { |
||||
|
_controller.dispose(); |
||||
|
super.dispose(); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
Widget build(BuildContext context) { |
||||
|
return AnimatedBuilder( |
||||
|
animation: _controller, |
||||
|
child: widget.child, |
||||
|
builder: (context, child) => FadeTransition( |
||||
|
opacity: _fadeAnim, |
||||
|
child: SlideTransition(position: _slideAnim, child: child), |
||||
|
), |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -1,44 +1,48 @@ |
|||||
|
import 'dart:async'; |
||||
|
|
||||
import 'package:bloc/bloc.dart'; |
import 'package:bloc/bloc.dart'; |
||||
import 'package:flutter/cupertino.dart'; |
import 'package:flutter/cupertino.dart'; |
||||
import 'package:go_router/go_router.dart'; |
import 'package:go_router/go_router.dart'; |
||||
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; |
|
||||
import 'package:hadi_hoda_flutter/core/routers/my_routes.dart'; |
import 'package:hadi_hoda_flutter/core/routers/my_routes.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/context_provider.dart'; |
||||
import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_event.dart'; |
import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_event.dart'; |
||||
import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_state.dart'; |
import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_state.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/intro/presentation/ui/screens/intro_1_screen.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/intro/presentation/ui/screens/intro_2_screen.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/intro/presentation/ui/screens/intro_3_screen.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/intro/presentation/ui/screens/intro_4_screen.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/intro/presentation/ui/screens/intro_5_screen.dart'; |
||||
|
|
||||
class IntroBloc extends Bloc<IntroEvent, IntroState> { |
class IntroBloc extends Bloc<IntroEvent, IntroState> { |
||||
/// ------------constructor------------ |
/// ------------constructor------------ |
||||
IntroBloc() : super(const IntroState()); |
|
||||
|
IntroBloc() : super(const IntroState()){ |
||||
|
on<ChangeIntroEvent>(_changeIntroEvent); |
||||
|
} |
||||
|
|
||||
/// ------------UseCases------------ |
/// ------------UseCases------------ |
||||
|
|
||||
/// ------------Variables------------ |
/// ------------Variables------------ |
||||
|
final List<Widget> intros = [ |
||||
|
Intro1Screen(key: Key('0')), |
||||
|
Intro2Screen(key: Key('1')), |
||||
|
Intro3Screen(key: Key('2')), |
||||
|
Intro4Screen(key: Key('3')), |
||||
|
Intro5Screen(key: Key('4')), |
||||
|
]; |
||||
|
|
||||
/// ------------Controllers------------ |
/// ------------Controllers------------ |
||||
|
|
||||
/// ------------Functions------------ |
/// ------------Functions------------ |
||||
Future<void> _precacheAllImages(BuildContext context) async { |
|
||||
await Future.wait( |
|
||||
MyAssets.images.map( |
|
||||
(assetPath) => precacheImage(AssetImage(assetPath), context), |
|
||||
), |
|
||||
); |
|
||||
|
void goToLevelPage(){ |
||||
|
ContextProvider.context.go(Routes.levelPage); |
||||
} |
} |
||||
|
|
||||
Future<void> goToHomePage(BuildContext context) async { |
|
||||
if (context.mounted) { |
|
||||
await _precacheAllImages(context); |
|
||||
|
/// ------------Api Calls------------ |
||||
|
FutureOr<void> _changeIntroEvent(ChangeIntroEvent event, Emitter emit) async { |
||||
|
if (state.currentIntro < intros.length - 1) { |
||||
|
emit(state.copyWith(currentIntro: state.currentIntro + 1)); |
||||
|
} else { |
||||
|
goToLevelPage(); |
||||
} |
} |
||||
|
|
||||
await Future.delayed( |
|
||||
Duration(seconds: 2), |
|
||||
() { |
|
||||
if (context.mounted) { |
|
||||
context.goNamed(Routes.homePage); |
|
||||
} |
|
||||
}, |
|
||||
); |
|
||||
} |
} |
||||
|
|
||||
/// ------------Api Calls------------ |
|
||||
} |
} |
||||
@ -1,3 +1,5 @@ |
|||||
sealed class IntroEvent { |
sealed class IntroEvent { |
||||
const IntroEvent(); |
const IntroEvent(); |
||||
} |
} |
||||
|
|
||||
|
class ChangeIntroEvent extends IntroEvent {} |
||||
@ -1,15 +1,21 @@ |
|||||
import 'package:hadi_hoda_flutter/core/status/base_status.dart'; |
import 'package:hadi_hoda_flutter/core/status/base_status.dart'; |
||||
|
|
||||
class IntroState { |
class IntroState { |
||||
final BaseStatus getFilesStatus; |
|
||||
|
final BaseStatus getIntroStatus; |
||||
|
final int currentIntro; |
||||
|
|
||||
const IntroState({this.getFilesStatus = const BaseInit()}); |
|
||||
|
const IntroState({ |
||||
|
this.getIntroStatus = const BaseInit(), |
||||
|
this.currentIntro = 0, |
||||
|
}); |
||||
|
|
||||
IntroState copyWith({ |
IntroState copyWith({ |
||||
BaseStatus? getFilesStatus, |
|
||||
|
BaseStatus? getIntroStatus, |
||||
|
int? currentIntro, |
||||
}) { |
}) { |
||||
return IntroState( |
return IntroState( |
||||
getFilesStatus: getFilesStatus ?? this.getFilesStatus, |
|
||||
|
getIntroStatus: getIntroStatus ?? this.getIntroStatus, |
||||
|
currentIntro: currentIntro ?? this.currentIntro, |
||||
); |
); |
||||
} |
} |
||||
} |
} |
||||
@ -1,82 +1,63 @@ |
|||||
import 'package:flutter/material.dart'; |
import 'package:flutter/material.dart'; |
||||
import 'package:flutter_bloc/flutter_bloc.dart'; |
import 'package:flutter_bloc/flutter_bloc.dart'; |
||||
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; |
|
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; |
||||
import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; |
import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; |
||||
import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; |
|
||||
import 'package:hadi_hoda_flutter/core/widgets/animations/rotation_anim.dart'; |
|
||||
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; |
|
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; |
||||
import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_bloc.dart'; |
import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_bloc.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_event.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_state.dart'; |
||||
|
|
||||
class IntroPage extends StatefulWidget { |
|
||||
|
class IntroPage extends StatelessWidget { |
||||
const IntroPage({super.key}); |
const IntroPage({super.key}); |
||||
|
|
||||
@override |
|
||||
State<IntroPage> createState() => _IntroPageState(); |
|
||||
} |
|
||||
|
|
||||
class _IntroPageState extends State<IntroPage> { |
|
||||
@override |
|
||||
void initState() { |
|
||||
super.initState(); |
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) { |
|
||||
context.read<IntroBloc>().goToHomePage(context); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@override |
@override |
||||
Widget build(BuildContext context) { |
Widget build(BuildContext context) { |
||||
return Scaffold( |
return Scaffold( |
||||
body: Container( |
|
||||
height: context.heightScreen, |
|
||||
width: context.widthScreen, |
|
||||
decoration: BoxDecoration( |
|
||||
gradient: LinearGradient( |
|
||||
begin: Alignment.topCenter, |
|
||||
end: Alignment.bottomCenter, |
|
||||
colors: [ |
|
||||
Color(0XFF00154C), |
|
||||
Color(0XFF150532), |
|
||||
|
body: SizedBox.expand( |
||||
|
child: GestureDetector( |
||||
|
onTap: () => context.read<IntroBloc>().add(ChangeIntroEvent()), |
||||
|
child: Stack( |
||||
|
children: [ |
||||
|
_mainScreen(), |
||||
|
_skipButton(context), |
||||
], |
], |
||||
), |
), |
||||
image: DecorationImage( |
|
||||
image: AssetImage(MyAssets.pattern), |
|
||||
scale: 3, |
|
||||
repeat: ImageRepeat.repeat, |
|
||||
colorFilter: ColorFilter.mode( |
|
||||
Colors.white.withValues(alpha: 0.2), |
|
||||
BlendMode.srcIn, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
child: Stack( |
|
||||
alignment: Alignment.center, |
|
||||
children: [ |
|
||||
_image(), |
|
||||
_loading(context), |
|
||||
], |
|
||||
), |
), |
||||
), |
), |
||||
); |
); |
||||
} |
} |
||||
|
|
||||
MyImage _image() { |
|
||||
return MyImage( |
|
||||
image: MyAssets.hadiHoda, |
|
||||
size: 200, |
|
||||
|
BlocBuilder<IntroBloc, IntroState> _mainScreen() { |
||||
|
return BlocBuilder<IntroBloc, IntroState>( |
||||
|
builder: (context, state) => AnimatedSwitcher( |
||||
|
duration: Duration(milliseconds: 200), |
||||
|
reverseDuration: Duration(milliseconds: 200), |
||||
|
switchInCurve: Curves.linear, |
||||
|
switchOutCurve: Curves.linear, |
||||
|
child: context.read<IntroBloc>().intros[state.currentIntro], |
||||
|
transitionBuilder: (child, animation) => |
||||
|
FadeTransition(opacity: animation, child: child), |
||||
|
), |
||||
); |
); |
||||
} |
} |
||||
|
|
||||
|
|
||||
Positioned _loading(BuildContext context) { |
|
||||
return Positioned( |
|
||||
bottom: MediaQuery.viewPaddingOf(context).bottom + MySpaces.s16, |
|
||||
child: RotationAnim( |
|
||||
child: MyImage( |
|
||||
image: MyAssets.loading, |
|
||||
size: 70, |
|
||||
|
PositionedDirectional _skipButton(BuildContext context) { |
||||
|
return PositionedDirectional( |
||||
|
start: MySpaces.s30, |
||||
|
bottom: MySpaces.s16, |
||||
|
child: TextButton( |
||||
|
onPressed: () => context.read<IntroBloc>().goToLevelPage(), |
||||
|
style: TextButton.styleFrom( |
||||
|
foregroundColor: MyColors.white.withValues(alpha: 0.7), |
||||
), |
), |
||||
) |
|
||||
|
child: Text( |
||||
|
context.translate.skip, |
||||
|
style: MYTextStyle.button2.copyWith( |
||||
|
color: MyColors.white.withValues(alpha: 0.7), |
||||
|
), |
||||
|
), |
||||
|
), |
||||
); |
); |
||||
} |
} |
||||
} |
} |
||||
@ -0,0 +1,54 @@ |
|||||
|
import 'package:flutter/material.dart'; |
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; |
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/set_platform_size.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/widgets/animations/slide_up_fade.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/intro/presentation/ui/widgets/bubble_chat_widget.dart'; |
||||
|
|
||||
|
class Intro1Screen extends StatelessWidget { |
||||
|
const Intro1Screen({super.key}); |
||||
|
|
||||
|
@override |
||||
|
Widget build(BuildContext context) { |
||||
|
return Stack( |
||||
|
alignment: Alignment.center, |
||||
|
children: [ |
||||
|
Positioned.fill( |
||||
|
child: MyImage(image: MyAssets.intro_1, fit: BoxFit.cover), |
||||
|
), |
||||
|
PositionedDirectional( |
||||
|
end: setSize<double>( |
||||
|
context: context, |
||||
|
mobile: MySpaces.s10, |
||||
|
tablet: 0.15.w, |
||||
|
), |
||||
|
top: setSize<double>( |
||||
|
context: context, |
||||
|
mobile: 0.15.h, |
||||
|
tablet: 0.25.h, |
||||
|
), |
||||
|
width: 290, |
||||
|
child: SlideUpFade( |
||||
|
delay: Duration(milliseconds: 300), |
||||
|
child: BubbleChatWidget( |
||||
|
text: context.translate.intro_1_1, |
||||
|
flip: true, |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
PositionedDirectional( |
||||
|
start: setSize(context: context, mobile: MySpaces.s30, tablet: 0.2.w), |
||||
|
top: setSize(context: context, mobile: 0.3.h, tablet: 0.35.h), |
||||
|
width: 250, |
||||
|
child: SlideUpFade( |
||||
|
delay: Duration(milliseconds: 800), |
||||
|
child: BubbleChatWidget(text: context.translate.intro_1_2), |
||||
|
), |
||||
|
), |
||||
|
], |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
import 'package:flutter/material.dart'; |
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; |
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/set_platform_size.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/widgets/animations/slide_up_fade.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/intro/presentation/ui/widgets/bubble_chat_widget.dart'; |
||||
|
|
||||
|
class Intro2Screen extends StatelessWidget { |
||||
|
const Intro2Screen({super.key}); |
||||
|
|
||||
|
@override |
||||
|
Widget build(BuildContext context) { |
||||
|
return Stack( |
||||
|
alignment: Alignment.center, |
||||
|
children: [ |
||||
|
Positioned.fill( |
||||
|
child: Transform.flip( |
||||
|
flipX: true, |
||||
|
child: MyImage(image: MyAssets.intro_2, fit: BoxFit.cover), |
||||
|
), |
||||
|
), |
||||
|
PositionedDirectional( |
||||
|
end: setSize(context: context, mobile: MySpaces.s40, tablet: 0.3.w), |
||||
|
top: setSize(context: context, mobile: 0.17.h, tablet: 0.23.h), |
||||
|
width: 250, |
||||
|
child: SlideUpFade( |
||||
|
delay: Duration(milliseconds: 300), |
||||
|
child: BubbleChatWidget(text: context.translate.intro_2), |
||||
|
), |
||||
|
), |
||||
|
], |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
import 'package:flutter/material.dart'; |
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; |
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/set_platform_size.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/widgets/animations/slide_up_fade.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/intro/presentation/ui/widgets/bubble_chat_widget.dart'; |
||||
|
|
||||
|
class Intro3Screen extends StatelessWidget { |
||||
|
const Intro3Screen({super.key}); |
||||
|
|
||||
|
@override |
||||
|
Widget build(BuildContext context) { |
||||
|
return Stack( |
||||
|
alignment: Alignment.center, |
||||
|
children: [ |
||||
|
Positioned.fill( |
||||
|
child: MyImage(image: MyAssets.intro_3, fit: BoxFit.cover), |
||||
|
), |
||||
|
PositionedDirectional( |
||||
|
start: setSize(context: context, mobile: MySpaces.s30), |
||||
|
top: setSize(context: context, mobile: 180, tablet: 0.25.h), |
||||
|
width: 270, |
||||
|
child: SlideUpFade( |
||||
|
delay: Duration(milliseconds: 300), |
||||
|
child: BubbleChatWidget( |
||||
|
text: context.translate.intro_3, |
||||
|
flip: true, |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
], |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
import 'package:flutter/material.dart'; |
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; |
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/set_platform_size.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/widgets/animations/slide_up_fade.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/intro/presentation/ui/widgets/bubble_chat_widget.dart'; |
||||
|
|
||||
|
class Intro4Screen extends StatelessWidget { |
||||
|
const Intro4Screen({super.key}); |
||||
|
|
||||
|
@override |
||||
|
Widget build(BuildContext context) { |
||||
|
return Stack( |
||||
|
alignment: Alignment.center, |
||||
|
children: [ |
||||
|
Positioned.fill( |
||||
|
child: MyImage(image: MyAssets.intro_4, fit: BoxFit.cover), |
||||
|
), |
||||
|
PositionedDirectional( |
||||
|
start: setSize(context: context, mobile: MySpaces.s10, tablet: 0.2.w), |
||||
|
bottom: setSize(context: context, mobile: 0.4.h, tablet: 0.4.h), |
||||
|
width: 237, |
||||
|
child: SlideUpFade( |
||||
|
delay: Duration(milliseconds: 300), |
||||
|
child: BubbleChatWidget( |
||||
|
text: context.translate.intro_4, |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
], |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
import 'package:flutter/material.dart'; |
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/set_platform_size.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/widgets/animations/slide_up_fade.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/intro/presentation/ui/widgets/bubble_chat_widget.dart'; |
||||
|
|
||||
|
class Intro5Screen extends StatelessWidget { |
||||
|
const Intro5Screen({super.key}); |
||||
|
|
||||
|
@override |
||||
|
Widget build(BuildContext context) { |
||||
|
return Stack( |
||||
|
alignment: Alignment.center, |
||||
|
children: [ |
||||
|
Positioned.fill( |
||||
|
child: MyImage(image: MyAssets.intro_5, fit: BoxFit.cover), |
||||
|
), |
||||
|
PositionedDirectional( |
||||
|
top: setSize(context: context, mobile: 0.37.h, tablet: 0.42.h), |
||||
|
width: 200, |
||||
|
child: SlideUpFade( |
||||
|
delay: Duration(milliseconds: 300), |
||||
|
child: BubbleChatWidget( |
||||
|
text: context.translate.intro_5, |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
], |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,91 @@ |
|||||
|
import 'package:flutter/material.dart'; |
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; |
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; |
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart'; |
||||
|
|
||||
|
class BubbleChatWidget extends StatelessWidget { |
||||
|
const BubbleChatWidget({ |
||||
|
super.key, |
||||
|
this.text, |
||||
|
this.width, |
||||
|
this.flip = false, |
||||
|
}); |
||||
|
|
||||
|
final String? text; |
||||
|
final double? width; |
||||
|
final bool flip; |
||||
|
|
||||
|
@override |
||||
|
Widget build(BuildContext context) { |
||||
|
return ClipPath( |
||||
|
clipper: _ShapeClipper(flip: flip), |
||||
|
child: Container( |
||||
|
width: width, |
||||
|
padding: EdgeInsets.only( |
||||
|
left: MySpaces.s18, |
||||
|
right: MySpaces.s18, |
||||
|
top: MySpaces.s16, |
||||
|
bottom: MySpaces.s28, |
||||
|
), |
||||
|
decoration: BoxDecoration( |
||||
|
color: Color(0XFFF5E8D7).withValues(alpha: 0.5), |
||||
|
borderRadius: BorderRadius.all(Radius.circular(20)), |
||||
|
border: Border.all( |
||||
|
width: 1, |
||||
|
color: MyColors.white.withValues(alpha: 0.5), |
||||
|
), |
||||
|
), |
||||
|
child: Text( |
||||
|
text ?? '', |
||||
|
style: MYTextStyle.titr3.copyWith( |
||||
|
color: MyColors.black, |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
class _ShapeClipper extends CustomClipper<Path> { |
||||
|
final bool flip; |
||||
|
|
||||
|
_ShapeClipper({this.flip = false}); // Add constructor |
||||
|
|
||||
|
@override |
||||
|
Path getClip(Size size) { |
||||
|
var path = Path(); |
||||
|
double w = size.width; |
||||
|
double h = size.height; |
||||
|
|
||||
|
// The drawing logic remains exactly the same |
||||
|
path.moveTo(w * 0.92, 0); |
||||
|
path.cubicTo(w * 0.96, 0, w, h * 0.05, w, h * 0.12); |
||||
|
path.lineTo(w, h * 0.70); |
||||
|
path.cubicTo(w, h * 0.78, w * 0.96, h * 0.83, w * 0.92, h * 0.83); |
||||
|
path.lineTo(w * 0.35, h * 0.83); |
||||
|
path.lineTo(w * 0.285, h); |
||||
|
path.lineTo(w * 0.20, h * 0.83); |
||||
|
path.lineTo(w * 0.08, h * 0.83); |
||||
|
path.cubicTo(w * 0.03, h * 0.83, 0, h * 0.78, 0, h * 0.70); |
||||
|
path.lineTo(0, h * 0.12); |
||||
|
path.cubicTo(0, h * 0.05, w * 0.03, 0, w * 0.08, 0); |
||||
|
path.close(); |
||||
|
|
||||
|
// If flip is true, transform the path horizontally |
||||
|
if (flip) { |
||||
|
final flipMatrix = Matrix4.identity() |
||||
|
..translateByDouble(w, 0.0, 0.0, 1.0) |
||||
|
..scaleByDouble(-1.0, 1.0, 1.0, 1.0); |
||||
|
return path.transform(flipMatrix.storage); |
||||
|
} |
||||
|
|
||||
|
return path; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
bool shouldReclip(covariant _ShapeClipper oldClipper) { |
||||
|
return oldClipper.flip != flip; // Reclip if the flip state changes |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,44 @@ |
|||||
|
import 'package:bloc/bloc.dart'; |
||||
|
import 'package:flutter/cupertino.dart'; |
||||
|
import 'package:go_router/go_router.dart'; |
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/routers/my_routes.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/splash/presentation/bloc/splash_event.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/splash/presentation/bloc/splash_state.dart'; |
||||
|
|
||||
|
class SplashBloc extends Bloc<SplashEvent, SplashState> { |
||||
|
/// ------------constructor------------ |
||||
|
SplashBloc() : super(const SplashState()); |
||||
|
|
||||
|
/// ------------UseCases------------ |
||||
|
|
||||
|
/// ------------Variables------------ |
||||
|
|
||||
|
/// ------------Controllers------------ |
||||
|
|
||||
|
/// ------------Functions------------ |
||||
|
Future<void> _precacheAllImages(BuildContext context) async { |
||||
|
await Future.wait( |
||||
|
MyAssets.images.map( |
||||
|
(assetPath) => precacheImage(AssetImage(assetPath), context), |
||||
|
), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
Future<void> goToHomePage(BuildContext context) async { |
||||
|
if (context.mounted) { |
||||
|
await _precacheAllImages(context); |
||||
|
} |
||||
|
|
||||
|
await Future.delayed( |
||||
|
Duration(seconds: 2), |
||||
|
() { |
||||
|
if (context.mounted) { |
||||
|
context.goNamed(Routes.homePage); |
||||
|
} |
||||
|
}, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/// ------------Api Calls------------ |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
sealed class SplashEvent { |
||||
|
const SplashEvent(); |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
import 'package:hadi_hoda_flutter/core/status/base_status.dart'; |
||||
|
|
||||
|
class SplashState { |
||||
|
final BaseStatus getFilesStatus; |
||||
|
|
||||
|
const SplashState({this.getFilesStatus = const BaseInit()}); |
||||
|
|
||||
|
SplashState copyWith({ |
||||
|
BaseStatus? getFilesStatus, |
||||
|
}) { |
||||
|
return SplashState( |
||||
|
getFilesStatus: getFilesStatus ?? this.getFilesStatus, |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,82 @@ |
|||||
|
import 'package:flutter/material.dart'; |
||||
|
import 'package:flutter_bloc/flutter_bloc.dart'; |
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; |
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/widgets/animations/rotation_anim.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/splash/presentation/bloc/splash_bloc.dart'; |
||||
|
|
||||
|
class SplashPage extends StatefulWidget { |
||||
|
const SplashPage({super.key}); |
||||
|
|
||||
|
@override |
||||
|
State<SplashPage> createState() => _SplashPageState(); |
||||
|
} |
||||
|
|
||||
|
class _SplashPageState extends State<SplashPage> { |
||||
|
@override |
||||
|
void initState() { |
||||
|
super.initState(); |
||||
|
WidgetsBinding.instance.addPostFrameCallback((_) { |
||||
|
context.read<SplashBloc>().goToHomePage(context); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@override |
||||
|
Widget build(BuildContext context) { |
||||
|
return Scaffold( |
||||
|
body: Container( |
||||
|
height: context.heightScreen, |
||||
|
width: context.widthScreen, |
||||
|
decoration: BoxDecoration( |
||||
|
gradient: LinearGradient( |
||||
|
begin: Alignment.topCenter, |
||||
|
end: Alignment.bottomCenter, |
||||
|
colors: [ |
||||
|
Color(0XFF00154C), |
||||
|
Color(0XFF150532), |
||||
|
], |
||||
|
), |
||||
|
image: DecorationImage( |
||||
|
image: AssetImage(MyAssets.pattern), |
||||
|
scale: 3, |
||||
|
repeat: ImageRepeat.repeat, |
||||
|
colorFilter: ColorFilter.mode( |
||||
|
Colors.white.withValues(alpha: 0.2), |
||||
|
BlendMode.srcIn, |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
child: Stack( |
||||
|
alignment: Alignment.center, |
||||
|
children: [ |
||||
|
_image(), |
||||
|
_loading(context), |
||||
|
], |
||||
|
), |
||||
|
), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
MyImage _image() { |
||||
|
return MyImage( |
||||
|
image: MyAssets.hadiHoda, |
||||
|
size: 200, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
Positioned _loading(BuildContext context) { |
||||
|
return Positioned( |
||||
|
bottom: MediaQuery.viewPaddingOf(context).bottom + MySpaces.s16, |
||||
|
child: RotationAnim( |
||||
|
child: MyImage( |
||||
|
image: MyAssets.loading, |
||||
|
size: 70, |
||||
|
), |
||||
|
) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue