From 62fcb33e2184be0dcad273241836bb5ee98a1453 Mon Sep 17 00:00:00 2001 From: AmirrezaChegini Date: Wed, 1 Oct 2025 12:30:40 +0330 Subject: [PATCH] add: intro ui --- lib/common_ui/resources/my_text_style.dart | 8 +- .../intro/presentation/ui/intro_page.dart | 40 +++++- .../ui/widgets/intro_loading_widget.dart | 129 ++++++++++++++++++ .../presentation/ui/widgets/level_widget.dart | 2 +- 4 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 lib/features/intro/presentation/ui/widgets/intro_loading_widget.dart diff --git a/lib/common_ui/resources/my_text_style.dart b/lib/common_ui/resources/my_text_style.dart index 34c9b7a..5acd64d 100644 --- a/lib/common_ui/resources/my_text_style.dart +++ b/lib/common_ui/resources/my_text_style.dart @@ -7,7 +7,7 @@ class MyTextStyle { static const String fontFamily = 'dinokids'; - static const TextStyle normal = TextStyle( + static const TextStyle normal26 = TextStyle( fontFamily: fontFamily, fontSize: 26, fontWeight: FontWeight.w400, @@ -19,4 +19,10 @@ class MyTextStyle { ), ] ); + + static const TextStyle normal17 = TextStyle( + fontFamily: fontFamily, + fontSize: 17, + fontWeight: FontWeight.w400, + ); } diff --git a/lib/features/intro/presentation/ui/intro_page.dart b/lib/features/intro/presentation/ui/intro_page.dart index 9de0ae2..8f36bd3 100644 --- a/lib/features/intro/presentation/ui/intro_page.dart +++ b/lib/features/intro/presentation/ui/intro_page.dart @@ -1,10 +1,48 @@ import 'package:flutter/material.dart'; +import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; +import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; +import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; +import 'package:hadi_hoda_flutter/features/intro/presentation/ui/widgets/intro_loading_widget.dart'; class IntroPage extends StatelessWidget { const IntroPage({super.key}); @override Widget build(BuildContext context) { - return const 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)], + ), + 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() => MyImage(image: MyAssets.hadiHoda, size: 200); + + Positioned _loading(BuildContext context) { + return Positioned( + bottom: MediaQuery.viewPaddingOf(context).bottom, + child: IntroLoadingWidget(percent: 80), + ); } } diff --git a/lib/features/intro/presentation/ui/widgets/intro_loading_widget.dart b/lib/features/intro/presentation/ui/widgets/intro_loading_widget.dart new file mode 100644 index 0000000..56816f3 --- /dev/null +++ b/lib/features/intro/presentation/ui/widgets/intro_loading_widget.dart @@ -0,0 +1,129 @@ +import 'package:flutter/material.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 IntroLoadingWidget extends StatelessWidget { + const IntroLoadingWidget({ + super.key, + this.percent, + }); + + final double? percent; + + @override + Widget build(BuildContext context) { + return ClipPath( + clipper: BubbleClip(), + child: Container( + width: 300, + height: 60, + padding: EdgeInsets.symmetric( + vertical: MySpaces.s4, + horizontal: MySpaces.s2, + ), + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment(0, 1), // bottom + end: Alignment(0, -1), // top + colors: [ + Color(0xFFCADCFF), // #CADCFF + Colors.white, // #FFFFFF + ], + ), + ), + child: Row( + children: [ + Expanded( + flex: 85, + child: ClipPath( + clipper: BubbleClip(), + child: AnimatedContainer( + duration: const Duration(milliseconds: 300), + padding: EdgeInsetsDirectional.only( + end: 260 - ((percent ?? 0) * 260 / 100), + ), + decoration: BoxDecoration( + color: Color(0xFF1F59BD).withValues(alpha: 0.25), + ), + child: ClipPath( + clipper: BubbleClip(), + child: Container( + decoration: BoxDecoration( + gradient: RadialGradient( + radius: 2, + colors: [ + Color(0xFFFFBD00), // #CADCFF + Color(0xFFFF772C), // #CADCFF + ], + ), + ), + ), + ), + ), + ), + ), + Expanded( + flex: 15, + child: Center( + child: Text( + '${percent?.toInt() ?? 0}%', + style: MyTextStyle.normal17.copyWith( + color: Color(0XFF6E83A8), + ), + ), + ), + ), + ], + ), + ), + ); + } +} + +class BubbleClip extends CustomClipper { + @override + Path getClip(Size size) { + // Original SVG viewBox: 334 x 60 + const double w0 = 334.0; + const double h0 = 60.0; + final sx = size.width / w0; + final sy = size.height / h0; + + // SVG path: + // M9.82057 10.3597 + // C -1.70838 17.1589 -3.47995 44.4301 6.60447 53.1719 + // C 16.0075 61.291 305.076 61.9385 323.201 53.4956 + // C 341.326 45.0527 332.116 8.04571 324.829 5.7273 + // C 307.985 -2.06805 28.6539 -0.77294 9.82057 10.3597 + // Z + final p = Path() + ..moveTo(9.82057 * sx, 10.3597 * sy) + ..cubicTo( + -1.70838 * sx, 17.1589 * sy, + -3.47995 * sx, 44.4301 * sy, + 6.60447 * sx, 53.1719 * sy, + ) + ..cubicTo( + 16.0075 * sx, 61.291 * sy, + 305.076 * sx, 61.9385 * sy, + 323.201 * sx, 53.4956 * sy, + ) + ..cubicTo( + 341.326 * sx, 45.0527 * sy, + 332.116 * sx, 8.04571 * sy, + 324.829 * sx, 5.7273 * sy, + ) + ..cubicTo( + 307.985 * sx, -2.06805 * sy, + 28.6539 * sx, -0.77294 * sy, + 9.82057 * sx, 10.3597 * sy, + ) + ..close(); + + return p; + } + + @override + bool shouldReclip(covariant CustomClipper oldClipper) => false; +} diff --git a/lib/features/level/presentation/ui/widgets/level_widget.dart b/lib/features/level/presentation/ui/widgets/level_widget.dart index 821bd75..ee6229a 100644 --- a/lib/features/level/presentation/ui/widgets/level_widget.dart +++ b/lib/features/level/presentation/ui/widgets/level_widget.dart @@ -31,7 +31,7 @@ class LevelWidget extends StatelessWidget { MyImage(image: LevelType.image[type] ?? MyAssets.level, size: 46), Text( '$index', - style: MyTextStyle.normal.copyWith(color: context.primaryColor), + style: MyTextStyle.normal26.copyWith(color: context.primaryColor), ), if(type == LevelType.current) Positioned(