Browse Source

add: question page ui

pull/7/head
AmirrezaChegini 1 week ago
parent
commit
827e8358e7
  1. BIN
      assets/images/diamond.png
  2. 4
      assets/images/done.svg
  3. 2
      lib/common_ui/resources/my_assets.dart
  4. 300
      lib/features/question/presentation/ui/question_page.dart
  5. 41
      lib/features/question/presentation/ui/widgets/glassy_button.dart
  6. 27
      lib/features/question/presentation/ui/widgets/left_blob.dart
  7. 102
      lib/features/question/presentation/ui/widgets/question_stepper.dart
  8. 32
      lib/features/question/presentation/ui/widgets/refresh_button.dart
  9. 27
      lib/features/question/presentation/ui/widgets/right_blob.dart
  10. 34
      pubspec.lock
  11. 1
      pubspec.yaml

BIN
assets/images/diamond.png

After

Width: 46  |  Height: 38  |  Size: 2.6 KiB

4
assets/images/done.svg

@ -0,0 +1,4 @@
<svg width="13" height="11" viewBox="0 0 13 11" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.558 0.348633L12.7359 1.73042C12.7359 1.73042 10.3562 7.14247 5.62103 10.07L4.30176 8.70894C6.51804 5.29707 8.49556 3.42465 11.558 0.348633Z" fill="white"/>
<path d="M0.594727 5.23829L2.01443 3.89746C2.01443 3.89746 4.17176 5.14167 6.90451 9.1387L5.59316 10.1301C2.25105 7.7622 2.64541 7.99882 0.594727 5.23829Z" fill="white"/>
</svg>

2
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';
}

300
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: () {},
),
),
],
),
);
}

41
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),
),
),
),
);
}
}

27
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),
),
),
],
);
}
}

102
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<Path> {
@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<Path> oldClipper) => false;
}

32
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)),
),
),
);
}
}

27
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),
),
),
],
);
}
}

34
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"

1
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

Loading…
Cancel
Save