diff --git a/assets/images/diamond.png b/assets/images/diamond.png
new file mode 100644
index 0000000..ba816b1
Binary files /dev/null and b/assets/images/diamond.png differ
diff --git a/assets/images/done.svg b/assets/images/done.svg
new file mode 100644
index 0000000..28d7f58
--- /dev/null
+++ b/assets/images/done.svg
@@ -0,0 +1,4 @@
+
diff --git a/lib/common_ui/resources/my_assets.dart b/lib/common_ui/resources/my_assets.dart
index b1523d5..473d276 100644
--- a/lib/common_ui/resources/my_assets.dart
+++ b/lib/common_ui/resources/my_assets.dart
@@ -23,4 +23,6 @@ class MyAssets {
static const String persons = 'assets/images/persons.png';
static const String bubbleChatLeft = 'assets/images/bubble_chat_left.svg';
static const String bubbleChatRight = 'assets/images/bubble_chat_right.svg';
+ static const String diamond = 'assets/images/diamond.png';
+ static const String done = 'assets/images/done.svg';
}
\ No newline at end of file
diff --git a/lib/features/question/presentation/ui/question_page.dart b/lib/features/question/presentation/ui/question_page.dart
index 798d48a..c3ae025 100644
--- a/lib/features/question/presentation/ui/question_page.dart
+++ b/lib/features/question/presentation/ui/question_page.dart
@@ -1,9 +1,16 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.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/gap.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/core/widgets/answer_box/answer_box.dart';
+import 'package:hadi_hoda_flutter/features/question/presentation/ui/widgets/glassy_button.dart';
+import 'package:hadi_hoda_flutter/features/question/presentation/ui/widgets/left_blob.dart';
+import 'package:hadi_hoda_flutter/features/question/presentation/ui/widgets/question_stepper.dart';
+import 'package:hadi_hoda_flutter/features/question/presentation/ui/widgets/refresh_button.dart';
+import 'package:hadi_hoda_flutter/features/question/presentation/ui/widgets/right_blob.dart';
class QuestionPage extends StatelessWidget {
const QuestionPage({super.key});
@@ -31,185 +38,130 @@ class QuestionPage extends StatelessWidget {
),
),
child: SafeArea(
- child: Column(
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- Container(
- width: 48,
- height: 48,
- padding: EdgeInsets.all(12),
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- gradient: LinearGradient(
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
- colors: [
- Colors.white.withValues(alpha: 0.3),
- Color(0XFF6930DA).withValues(alpha: 0.1),
- ],
- ),
- border: Border.all(
- color: Colors.white.withValues(alpha: 0.3),
- ),
- ),
- child: MyImage(image: MyAssets.home),
- ),
- Text(
- 'Toothbrushing etiquette',
- style: GoogleFonts.marhey(
- fontSize: 14,
- fontWeight: FontWeight.w700,
- color: Colors.white,
- ),
- ),
- Container(
- width: 48,
- height: 48,
- padding: EdgeInsets.all(12),
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- gradient: LinearGradient(
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
- colors: [
- Colors.white.withValues(alpha: 0.3),
- Color(0XFF6930DA).withValues(alpha: 0.1),
- ],
- ),
- border: Border.all(
- color: Colors.white.withValues(alpha: 0.3),
- ),
- ),
- child: MyImage(image: MyAssets.music),
- ),
- ],
- ),
- Column(
- children: [
- Text(
- 'Question 1 / 5',
- style: GoogleFonts.marhey(
- fontSize: 12,
- fontWeight: FontWeight.w500,
- color: Colors.white.withValues(alpha: 0.5),
- shadows: [
- Shadow(
- offset: Offset(0, 1),
- blurRadius: 1,
- color: Color(0xFF000000).withValues(alpha: 0.25),
- ),
- ],
- ),
- ),
- Text(
- 'Heda wants her teeth to be clean. Which of her actions do you think is correct?',
- style: GoogleFonts.marhey(
- fontSize: 22,
- fontWeight: FontWeight.w600,
- color: Colors.white,
- shadows: [
- Shadow(
- offset: Offset(0, 1),
- blurRadius: 1,
- color: Color(0xFF000000).withValues(alpha: 0.25),
- ),
- ],
- ),
- ),
- ],
- ),
- Expanded(
- child: GridView.builder(
- itemCount: 4,
- gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
- crossAxisCount: 2,
- crossAxisSpacing: 20,
- mainAxisSpacing: 30,
- ),
- itemBuilder: (context, index) => AnswerBox(),
- ),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: MySpaces.s16),
+ child: Column(
+ children: [
+ MySpaces.s4.gapHeight,
+ _topButtons(),
+ MySpaces.s10.gapHeight,
+ QuestionStepper(),
+ _titles(),
+ MySpaces.s14.gapHeight,
+ _questions(),
+ _bottomDetail(context),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ Widget _topButtons() {
+ return Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ GlassyButton(image: MyAssets.home, onTap: () {}),
+ Text(
+ 'Toothbrushing etiquette',
+ style: GoogleFonts.marhey(
+ fontSize: 14,
+ fontWeight: FontWeight.w700,
+ color: Colors.white,
+ ),
+ ),
+ GlassyButton(image: MyAssets.music, onTap: () {}),
+ ],
+ );
+ }
+
+ Column _titles() {
+ return Column(
+ spacing: MySpaces.s4,
+ children: [
+ Text(
+ 'Question 1 / 5',
+ style: GoogleFonts.marhey(
+ fontSize: 12,
+ fontWeight: FontWeight.w500,
+ color: Colors.white.withValues(alpha: 0.5),
+ shadows: [
+ Shadow(
+ offset: Offset(0, 1),
+ blurRadius: 1,
+ color: Color(0xFF000000).withValues(alpha: 0.25),
),
- SizedBox(
- width: context.widthScreen,
- child: Stack(
- clipBehavior: Clip.none,
- alignment: Alignment.center,
- children: [
- Positioned.directional(
- textDirection: Directionality.of(context),
- start: 0,
- top: -10,
- child: Stack(
- alignment: Alignment.center,
- children: [
- MyImage(image: MyAssets.bubbleChatLeft),
- Text(
- 'Your answer\nwas not correct.',
- textAlign: TextAlign.center,
- style: GoogleFonts.marhey(
- fontSize: 12,
- fontWeight: FontWeight.w500,
- color: Color(0XFFB5AEEE),
- ),
- ),
- ],
- ),
- ),
- Padding(
- padding: const EdgeInsetsDirectional.only(end: 90),
- child: MyImage(image: MyAssets.persons),
- ),
- Positioned.directional(
- textDirection: Directionality.of(context),
- start: 220,
- top: -20,
- child: Stack(
- alignment: Alignment.center,
- children: [
- MyImage(image: MyAssets.bubbleChatRight),
- Text(
- 'Be more\ncareful.',
- textAlign: TextAlign.center,
- style: GoogleFonts.marhey(
- fontSize: 12,
- fontWeight: FontWeight.w500,
- color: Color(0XFFB5AEEE),
- ),
- ),
- ],
- ),
- ),
- Positioned.directional(
- textDirection: Directionality.of(context),
- end: 0,
- bottom: 10,
- child: Container(
- height: 48,
- width: 48,
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- gradient: LinearGradient(
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
- colors: [Color(0XFFA393FF), Color(0XFFC6BCFB)],
- ),
- ),
- child: Icon(
- Icons.refresh,
- size: 40,
- color: Color(0XFF263AA1),
- ),
- ),
- ),
- ],
- ),
+ ],
+ ),
+ ),
+ Text(
+ 'Heda wants her teeth to be clean. Which of her actions do you think is correct?',
+ textAlign: TextAlign.center,
+ style: GoogleFonts.marhey(
+ fontSize: 22,
+ fontWeight: FontWeight.w600,
+ color: Colors.white,
+ shadows: [
+ Shadow(
+ offset: Offset(0, 1),
+ blurRadius: 1,
+ color: Color(0xFF000000).withValues(alpha: 0.25),
),
],
),
),
+ ],
+ );
+ }
+
+ Expanded _questions() {
+ return Expanded(
+ child: GridView.builder(
+ itemCount: 4,
+ gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
+ crossAxisCount: 2,
+ crossAxisSpacing: MySpaces.s20,
+ mainAxisSpacing: 50,
+ ),
+ itemBuilder: (context, index) => AnswerBox(),
+ ),
+ );
+ }
+
+ Widget _bottomDetail(BuildContext context) {
+ return SizedBox(
+ width: context.widthScreen,
+ child: Stack(
+ clipBehavior: Clip.none,
+ alignment: Alignment.center,
+ children: [
+ Positioned.directional(
+ textDirection: Directionality.of(context),
+ start: 0,
+ top: -10,
+ child: LeftBlob(),
+ ),
+ Padding(
+ padding: const EdgeInsetsDirectional.only(end: 60),
+ child: MyImage(image: MyAssets.persons),
+ ),
+ Positioned.directional(
+ textDirection: Directionality.of(context),
+ start: 210,
+ top: -20,
+ child: RightBlob(),
+ ),
+ Positioned.directional(
+ textDirection: Directionality.of(context),
+ end: 0,
+ bottom: 10,
+ child: RefreshButton(
+ onTap: () {},
+ ),
+ ),
+ ],
),
);
}
diff --git a/lib/features/question/presentation/ui/widgets/glassy_button.dart b/lib/features/question/presentation/ui/widgets/glassy_button.dart
new file mode 100644
index 0000000..e346f4d
--- /dev/null
+++ b/lib/features/question/presentation/ui/widgets/glassy_button.dart
@@ -0,0 +1,41 @@
+import 'package:flutter/material.dart';
+import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart';
+import 'package:hadi_hoda_flutter/core/utils/my_image.dart';
+
+class GlassyButton extends StatelessWidget {
+ const GlassyButton({super.key, required this.image, this.onTap});
+
+ final String image;
+ final VoidCallback? onTap;
+
+ @override
+ Widget build(BuildContext context) {
+ return Material(
+ color: Colors.transparent,
+ child: Ink(
+ width: 48,
+ height: 48,
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ gradient: LinearGradient(
+ begin: Alignment.topCenter,
+ end: Alignment.bottomCenter,
+ colors: [
+ Colors.white.withValues(alpha: 0.3),
+ Color(0XFF6930DA).withValues(alpha: 0.1),
+ ],
+ ),
+ border: Border.all(color: Colors.white.withValues(alpha: 0.3)),
+ ),
+ child: InkWell(
+ onTap: onTap,
+ borderRadius: BorderRadius.all(Radius.circular(100)),
+ child: Padding(
+ padding: const EdgeInsets.all(MySpaces.s12),
+ child: MyImage(image: image),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/features/question/presentation/ui/widgets/left_blob.dart b/lib/features/question/presentation/ui/widgets/left_blob.dart
new file mode 100644
index 0000000..0964766
--- /dev/null
+++ b/lib/features/question/presentation/ui/widgets/left_blob.dart
@@ -0,0 +1,27 @@
+import 'package:flutter/material.dart';
+import 'package:google_fonts/google_fonts.dart';
+import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart';
+import 'package:hadi_hoda_flutter/core/utils/my_image.dart';
+
+class LeftBlob extends StatelessWidget {
+ const LeftBlob({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return Stack(
+ alignment: Alignment.center,
+ children: [
+ MyImage(image: MyAssets.bubbleChatLeft),
+ Text(
+ 'Your answer\nwas not correct.',
+ textAlign: TextAlign.center,
+ style: GoogleFonts.marhey(
+ fontSize: 12,
+ fontWeight: FontWeight.w500,
+ color: Color(0XFFB5AEEE),
+ ),
+ ),
+ ],
+ );
+ }
+}
diff --git a/lib/features/question/presentation/ui/widgets/question_stepper.dart b/lib/features/question/presentation/ui/widgets/question_stepper.dart
new file mode 100644
index 0000000..c0fbf9d
--- /dev/null
+++ b/lib/features/question/presentation/ui/widgets/question_stepper.dart
@@ -0,0 +1,102 @@
+import 'package:easy_stepper/easy_stepper.dart';
+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';
+
+class QuestionStepper extends StatelessWidget {
+ const QuestionStepper({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return SizedBox(
+ height: 80,
+ child: EasyStepper(
+ activeStep: 1,
+ lineStyle: LineStyle(
+ lineLength: 20,
+ lineType: LineType.normal,
+ defaultLineColor: Color(0XFFDFDDF6),
+ lineThickness: 5,
+ finishedLineColor: Color(0XFF21B738),
+ ),
+ activeStepBackgroundColor: Colors.transparent,
+ finishedStepBackgroundColor: Colors.transparent,
+ unreachedStepBackgroundColor: Colors.transparent,
+ internalPadding: 0,
+ showLoadingAnimation: false,
+ stepRadius: 18,
+ showStepBorder: false,
+ padding: EdgeInsets.all(0),
+ enableStepTapping: false,
+ steps: List.generate(
+ 6,
+ (index) => EasyStep(
+ customStep: index == 5
+ ? MyImage(image: MyAssets.diamond, size: 50)
+ : ClipPath(
+ clipper: _StepperClipper(),
+ child: Container(
+ height: 32,
+ width: 32,
+ padding: EdgeInsets.all(4),
+ decoration: BoxDecoration(
+ color: Color(0XFFDFDDF6),
+ shape: BoxShape.circle,
+ ),
+ child: ClipPath(
+ clipper: _StepperClipper(),
+ child: Container(
+ padding: EdgeInsets.all(6),
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ color: index < 1
+ ? Color(0XFF21B738)
+ : index == 1
+ ? Color(0XFF847AC4)
+ : Colors.transparent,
+ ),
+ child: index < 1 ? MyImage(image: MyAssets.done) : null,
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
+
+class _StepperClipper extends CustomClipper {
+ @override
+ Path getClip(Size size) {
+ // Original SVG viewBox: width=34, height=33
+ final sx = size.width / 34.0;
+ final sy = size.height / 33.0;
+
+ final p = Path()
+ ..moveTo(33.3479 * sx, 14.8127 * sy)
+ ..cubicTo(
+ 33.3479 * sx, 23.7042 * sy,
+ 27.2015 * sx, 32.9501 * sy,
+ 17.8599 * sx, 32.9501 * sy,
+ )..cubicTo(
+ 8.51818 * sx, 32.9501 * sy,
+ 0.945251 * sx, 25.7421 * sy,
+ 0.945251 * sx, 16.8507 * sy,
+ )..cubicTo(
+ 0.945251 * sx, 7.95917 * sy,
+ 8.51818 * sx, 0.751205 * sy,
+ 17.8599 * sx, 0.751205 * sy,
+ )..cubicTo(
+ 27.2015 * sx, 0.751205 * sy,
+ 33.3479 * sx, 5.92127 * sy,
+ 33.3479 * sx, 14.8127 * sy,
+ )..close();
+
+ return p;
+ }
+
+ @override
+ bool shouldReclip(covariant CustomClipper oldClipper) => false;
+}
diff --git a/lib/features/question/presentation/ui/widgets/refresh_button.dart b/lib/features/question/presentation/ui/widgets/refresh_button.dart
new file mode 100644
index 0000000..801678e
--- /dev/null
+++ b/lib/features/question/presentation/ui/widgets/refresh_button.dart
@@ -0,0 +1,32 @@
+import 'package:flutter/material.dart';
+import 'package:hadi_hoda_flutter/common_ui/theme/my_theme.dart';
+
+class RefreshButton extends StatelessWidget {
+ const RefreshButton({super.key, this.onTap,});
+
+ final VoidCallback? onTap;
+
+ @override
+ Widget build(BuildContext context) {
+ return Material(
+ color: context.noColor,
+ child: Ink(
+ height: 48,
+ width: 48,
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ gradient: LinearGradient(
+ begin: Alignment.topCenter,
+ end: Alignment.bottomCenter,
+ colors: [Color(0XFFA393FF), Color(0XFFC6BCFB)],
+ ),
+ ),
+ child: InkWell(
+ onTap: onTap,
+ borderRadius: BorderRadius.all(Radius.circular(100)),
+ child: Icon(Icons.refresh, size: 40, color: Color(0XFF263AA1)),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/features/question/presentation/ui/widgets/right_blob.dart b/lib/features/question/presentation/ui/widgets/right_blob.dart
new file mode 100644
index 0000000..b10237a
--- /dev/null
+++ b/lib/features/question/presentation/ui/widgets/right_blob.dart
@@ -0,0 +1,27 @@
+import 'package:flutter/material.dart';
+import 'package:google_fonts/google_fonts.dart';
+import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart';
+import 'package:hadi_hoda_flutter/core/utils/my_image.dart';
+
+class RightBlob extends StatelessWidget {
+ const RightBlob({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return Stack(
+ alignment: Alignment.center,
+ children: [
+ MyImage(image: MyAssets.bubbleChatRight),
+ Text(
+ 'Be more\ncareful.',
+ textAlign: TextAlign.center,
+ style: GoogleFonts.marhey(
+ fontSize: 12,
+ fontWeight: FontWeight.w500,
+ color: Color(0XFFB5AEEE),
+ ),
+ ),
+ ],
+ );
+ }
+}
diff --git a/pubspec.lock b/pubspec.lock
index 51806b4..2c648e0 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -1,6 +1,14 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
+ archive:
+ dependency: transitive
+ description:
+ name: archive
+ sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.0.7"
args:
dependency: transitive
description:
@@ -81,6 +89,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
+ easy_stepper:
+ dependency: "direct main"
+ description:
+ name: easy_stepper
+ sha256: "63f66314a509ec690c8152a41288961fd96ba9e92ef184299f068a5e78bd16ad"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.8.5+1"
equatable:
dependency: "direct main"
description:
@@ -245,6 +261,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
+ lottie:
+ dependency: transitive
+ description:
+ name: lottie
+ sha256: "8ae0be46dbd9e19641791dc12ee480d34e1fd3f84c749adc05f3ad9342b71b95"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.3.2"
matcher:
dependency: transitive
description:
@@ -373,6 +397,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.8"
+ posix:
+ dependency: transitive
+ description:
+ name: posix
+ sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.0.3"
pretty_dio_logger:
dependency: "direct main"
description:
@@ -572,4 +604,4 @@ packages:
version: "6.6.1"
sdks:
dart: ">=3.9.2 <4.0.0"
- flutter: ">=3.29.0"
+ flutter: ">=3.35.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index dd2a1d6..366e5a5 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -9,6 +9,7 @@ environment:
dependencies:
bloc: ^9.0.0
dio: ^5.9.0
+ easy_stepper: ^0.8.5+1
equatable: ^2.0.7
flutter:
sdk: flutter