Browse Source

add: refactor path and mission code

pull/8/head
AmirrezaChegini 1 week ago
parent
commit
fe2ba38743
  1. BIN
      assets/fonts/dinokids.ttf
  2. 17
      lib/common_ui/resources/my_text_style.dart
  3. 13
      lib/core/routers/my_routes.dart
  4. 15
      lib/features/home/domain/entity/mission_location.dart
  5. 26
      lib/features/home/presentation/bloc/home_bloc.dart
  6. 633
      lib/features/home/presentation/ui/home_page.dart
  7. 87
      lib/features/home/presentation/ui/widgets/bottom_path.dart
  8. 38
      lib/features/home/presentation/ui/widgets/mission_widget.dart
  9. 99
      lib/features/home/presentation/ui/widgets/top_path.dart
  10. 7
      pubspec.yaml

BIN
assets/fonts/dinokids.ttf

17
lib/common_ui/resources/my_text_style.dart

@ -5,11 +5,18 @@ class MyTextStyle {
const MyTextStyle._internal(); const MyTextStyle._internal();
factory MyTextStyle() => _i; factory MyTextStyle() => _i;
static const String fontFamily = '';
static const String fontFamily = 'dinokids';
static const TextStyle lightXS = TextStyle(
fontFamily: fontFamily,
fontSize: 10,
fontWeight: FontWeight.w400,
static const TextStyle normal = TextStyle(
fontFamily: fontFamily,
fontSize: 28,
fontWeight: FontWeight.w400,
shadows: [
Shadow(
color: Color(0XFF5B5B5B),
blurRadius: 2.86,
offset: Offset(0, 2),
),
]
); );
} }

13
lib/core/routers/my_routes.dart

@ -1,6 +1,8 @@
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hadi_hoda_flutter/core/utils/context_provider.dart'; import 'package:hadi_hoda_flutter/core/utils/context_provider.dart';
import 'package:hadi_hoda_flutter/features/home/presentation/bloc/home_bloc.dart';
import 'package:hadi_hoda_flutter/features/home/presentation/ui/home_page.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/ui/intro_page.dart'; import 'package:hadi_hoda_flutter/features/intro/presentation/ui/intro_page.dart';
import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_bloc.dart'; import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_bloc.dart';
@ -14,10 +16,11 @@ class Routes {
static const String introPage = '/intro_page'; static const String introPage = '/intro_page';
static const String questionPage = '/question_page'; static const String questionPage = '/question_page';
static const String homePage = '/home_page';
} }
GoRouter get appPages => GoRouter( GoRouter get appPages => GoRouter(
initialLocation: Routes.introPage,
initialLocation: Routes.homePage,
navigatorKey: ContextProvider.navigatorKey, navigatorKey: ContextProvider.navigatorKey,
routes: [ routes: [
GoRoute( GoRoute(
@ -28,6 +31,14 @@ GoRouter get appPages => GoRouter(
child: const IntroPage(), child: const IntroPage(),
), ),
), ),
GoRoute(
name: Routes.homePage,
path: Routes.homePage,
builder: (context, state) => BlocProvider(
create: (context) => HomeBloc(locator()),
child: const HomePage(),
),
),
GoRoute( GoRoute(
name: Routes.questionPage, name: Routes.questionPage,
path: Routes.questionPage, path: Routes.questionPage,

15
lib/features/home/domain/entity/mission_location.dart

@ -0,0 +1,15 @@
class MissionLocation {
final int? index;
final double? top;
final double? bottom;
final double? right;
final double? left;
const MissionLocation({
this.index,
this.top,
this.bottom,
this.right,
this.left,
});
}

26
lib/features/home/presentation/bloc/home_bloc.dart

@ -2,6 +2,7 @@ import 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:hadi_hoda_flutter/core/status/base_status.dart'; import 'package:hadi_hoda_flutter/core/status/base_status.dart';
import 'package:hadi_hoda_flutter/features/home/domain/entity/home_entity.dart'; import 'package:hadi_hoda_flutter/features/home/domain/entity/home_entity.dart';
import 'package:hadi_hoda_flutter/features/home/domain/entity/mission_location.dart';
import 'package:hadi_hoda_flutter/features/home/domain/usecases/get_home_usecase.dart'; import 'package:hadi_hoda_flutter/features/home/domain/usecases/get_home_usecase.dart';
import 'package:hadi_hoda_flutter/features/home/presentation/bloc/home_event.dart'; import 'package:hadi_hoda_flutter/features/home/presentation/bloc/home_event.dart';
import 'package:hadi_hoda_flutter/features/home/presentation/bloc/home_state.dart'; import 'package:hadi_hoda_flutter/features/home/presentation/bloc/home_state.dart';
@ -18,6 +19,31 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
final GetHomeUseCase _getHomeUseCase; final GetHomeUseCase _getHomeUseCase;
/// ------------Variables------------ /// ------------Variables------------
final List<MissionLocation> bottomLocationList = [
MissionLocation(bottom: -30, left: 30, index: 1),
MissionLocation(bottom: 50, left: 100, index: 2),
MissionLocation(bottom: 150, left: 50, index: 3),
MissionLocation(bottom: 250, left: 130, index: 4),
MissionLocation(bottom: 300, right: 50, index: 5),
MissionLocation(top: 170, right: 40, index: 6),
MissionLocation(top: 70, right: 70, index: 7),
MissionLocation(top: -20, right: 60, index: 8),
];
final List<MissionLocation> topLocationList = [
MissionLocation(bottom: 30, right: 80, index: 9),
MissionLocation(bottom: 70, left: 20, index: 10),
MissionLocation(bottom: 150, left: 50, index: 11),
MissionLocation(bottom: 180, left: 140, index: 12),
MissionLocation(bottom: 260, right: 20, index: 13),
MissionLocation(bottom: 370, right: 30, index: 14),
MissionLocation(bottom: 420, left: 40, index: 15),
MissionLocation(top: 410, left: 0, index: 16),
MissionLocation(top: 320, left: 60, index: 17),
MissionLocation(top: 220, left: 80, index: 18),
MissionLocation(top: 130, left: 20, index: 19),
MissionLocation(top: 50, left: 70, index: 20),
];
/// ------------Controllers------------ /// ------------Controllers------------

633
lib/features/home/presentation/ui/home_page.dart

@ -1,601 +1,88 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.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_assets.dart';
import 'package:hadi_hoda_flutter/core/utils/my_image.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/utils/screen_size.dart';
import 'package:path_drawing/path_drawing.dart';
import 'package:hadi_hoda_flutter/features/home/presentation/bloc/home_bloc.dart';
import 'package:hadi_hoda_flutter/features/home/presentation/ui/widgets/bottom_path.dart';
import 'package:hadi_hoda_flutter/features/home/presentation/ui/widgets/mission_widget.dart';
import 'package:hadi_hoda_flutter/features/home/presentation/ui/widgets/top_path.dart';
class HomePage extends StatefulWidget {
class HomePage extends StatelessWidget {
const HomePage({super.key}); const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final ScrollController scrollController = ScrollController();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
body: SingleChildScrollView( body: SingleChildScrollView(
controller: scrollController,
child: Stack( child: Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
MyImage(image: MyAssets.mapBackground, fit: BoxFit.cover),
Positioned(
top: context.heightScreen * 0.16,
left: context.widthScreen * 0.15,
child: Stack(
children: [
CurvyDashedPathPD(
height: 950,
width: context.widthScreen * 0.6,
),
Positioned(
bottom: 30,
right: 80,
child: Stack(
alignment: Alignment.center,
children: [
MyImage(image: MyAssets.mission, size: 43),
Text(
'9',
style: GoogleFonts.marhey(
fontSize: 28,
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
],
),
),
Positioned(
bottom: 70,
left: 20,
child: Stack(
alignment: Alignment.center,
children: [
MyImage(image: MyAssets.mission, size: 43),
Text(
'10',
style: GoogleFonts.marhey(
fontSize: 28,
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
],
),
),
Positioned(
bottom: 150,
left: 50,
child: Stack(
alignment: Alignment.center,
children: [
MyImage(image: MyAssets.mission, size: 43),
Text(
'11',
style: GoogleFonts.marhey(
fontSize: 28,
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
],
),
),
Positioned(
bottom: 180,
left: 140,
child: Stack(
alignment: Alignment.center,
children: [
MyImage(image: MyAssets.mission, size: 43),
Text(
'12',
style: GoogleFonts.marhey(
fontSize: 28,
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
],
),
),
Positioned(
bottom: 260,
right: 20,
child: Stack(
alignment: Alignment.center,
children: [
MyImage(image: MyAssets.mission, size: 43),
Text(
'13',
style: GoogleFonts.marhey(
fontSize: 28,
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
],
),
),
Positioned(
bottom: 370,
right: 30,
child: Stack(
alignment: Alignment.center,
children: [
MyImage(image: MyAssets.mission, size: 43),
Text(
'14',
style: GoogleFonts.marhey(
fontSize: 28,
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
],
),
),
Positioned(
bottom: 420,
left: 40,
child: Stack(
alignment: Alignment.center,
children: [
MyImage(image: MyAssets.mission, size: 43),
Text(
'15',
style: GoogleFonts.marhey(
fontSize: 28,
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
],
),
),
Positioned(
top: 410,
left: 0,
child: Stack(
alignment: Alignment.center,
children: [
MyImage(image: MyAssets.mission, size: 43),
Text(
'16',
style: GoogleFonts.marhey(
fontSize: 28,
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
],
),
),
Positioned(
top: 320,
left: 60,
child: Stack(
alignment: Alignment.center,
children: [
MyImage(image: MyAssets.mission, size: 43),
Text(
'17',
style: GoogleFonts.marhey(
fontSize: 28,
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
],
),
),
Positioned(
top: 220,
left: 80,
child: Stack(
alignment: Alignment.center,
children: [
MyImage(image: MyAssets.mission, size: 43),
Text(
'18',
style: GoogleFonts.marhey(
fontSize: 28,
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
],
),
),
Positioned(
top: 130,
left: 20,
child: Stack(
alignment: Alignment.center,
children: [
MyImage(image: MyAssets.mission, size: 43),
Text(
'19',
style: GoogleFonts.marhey(
fontSize: 28,
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
],
),
),
Positioned(
top: 50,
left: 70,
child: Stack(
alignment: Alignment.center,
children: [
MyImage(image: MyAssets.mission, size: 43),
Text(
'20',
style: GoogleFonts.marhey(
fontSize: 28,
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
],
),
),
],
),
),
Positioned(
bottom: context.heightScreen * 0.12,
left: context.widthScreen * 0.2,
child: Stack(
clipBehavior: Clip.none,
children: [
DottedRoute(
width: context.widthScreen * 0.75,
height: context.heightScreen * 0.65,
),
Positioned(
bottom: -30,
left: 30,
child: Stack(
alignment: Alignment.center,
children: [
MyImage(image: MyAssets.mission, size: 43),
Text(
'1',
style: GoogleFonts.marhey(
fontSize: 28,
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
],
),
),
Positioned(
bottom: 50,
left: 100,
child: Stack(
alignment: Alignment.center,
children: [
MyImage(image: MyAssets.mission, size: 43),
Text(
'2',
style: GoogleFonts.marhey(
fontSize: 28,
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
],
),
),
Positioned(
bottom: 150,
left: 50,
child: Stack(
alignment: Alignment.center,
children: [
MyImage(image: MyAssets.mission, size: 43),
Text(
'3',
style: GoogleFonts.marhey(
fontSize: 28,
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
],
),
),
Positioned(
bottom: 250,
left: 130,
child: Stack(
alignment: Alignment.center,
children: [
MyImage(image: MyAssets.mission, size: 43),
Text(
'4',
style: GoogleFonts.marhey(
fontSize: 28,
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
],
),
),
Positioned(
bottom: 300,
right: 50,
child: Stack(
alignment: Alignment.center,
children: [
MyImage(image: MyAssets.mission, size: 43),
Text(
'5',
style: GoogleFonts.marhey(
fontSize: 28,
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
],
),
),
Positioned(
top: 170,
right: 40,
child: Stack(
alignment: Alignment.center,
children: [
MyImage(image: MyAssets.mission, size: 43),
Text(
'6',
style: GoogleFonts.marhey(
fontSize: 28,
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
],
),
),
Positioned(
top: 70,
right: 70,
child: Stack(
alignment: Alignment.center,
children: [
MyImage(image: MyAssets.mission, size: 43),
Text(
'7',
style: GoogleFonts.marhey(
fontSize: 28,
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
],
),
),
Positioned(
top: -20,
right: 60,
child: Stack(
alignment: Alignment.center,
children: [
MyImage(image: MyAssets.mission, size: 43),
Text(
'8',
style: GoogleFonts.marhey(
fontSize: 28,
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
],
),
),
],
),
),
_background(),
_topPath(context),
_bottomPath(context),
], ],
), ),
), ),
); );
} }
}
class DottedRoute extends StatelessWidget {
const DottedRoute({
super.key,
this.width = 500,
this.height = 1523,
this.color = Colors.white,
});
final double width;
final double height;
final Color color;
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: _DottedRoutePainter(color),
size: Size(
width,
height,
), // or Size.infinite inside a parent with constraints
);
MyImage _background() {
return MyImage(image: MyAssets.mapBackground, fit: BoxFit.cover);
} }
}
class _DottedRoutePainter extends CustomPainter {
_DottedRoutePainter(this.color);
final Color color;
// SVG viewBox
static const double _vbW = 500;
static const double _vbH = 1523;
// Your SVG path data (unchanged)
static const String _svgPath =
'M1.95892 1520.75C1.95892 1520.75 199.206 1423.63 169.156 1328.9C143.058 1246.63 30.8103 1281.9 15.4421 1225.27C-11.9488 1124.33 48.5736 1164.01 42.795 1033.8C38.5466 938.07 154.913 925.725 219.623 855.048C286.233 782.296 385.022 821.209 446.532 744.097C516.262 656.681 493.917 461.712 493.917 461.712C493.917 461.712 440.122 333.473 428.04 246.36C414.846 151.222 436.901 0.572754 436.901 0.572754';
@override
void paint(Canvas canvas, Size size) {
// Scale SVG viewBox -> current canvas while preserving aspect
final scale = _scaleToFit(
srcW: _vbW,
srcH: _vbH,
dstW: size.width,
dstH: size.height,
);
canvas.translate(
(size.width - _vbW * scale) / 2,
(size.height - _vbH * scale) / 2,
);
canvas.scale(scale);
// Parse and dash the path
final path = parseSvgPathData(_svgPath);
final dashed = dashPath(
path,
dashArray: CircularIntervalList<double>(const [3.08, 13.1]),
);
// Stroke paint
final paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 4.62295
..color = color
..strokeCap = StrokeCap.butt
..strokeJoin = StrokeJoin.miter;
canvas.drawPath(dashed, paint);
}
double _scaleToFit({
required double srcW,
required double srcH,
required double dstW,
required double dstH,
}) {
final sx = dstW / srcW;
final sy = dstH / srcH;
return sx < sy ? sx : sy; // contain
}
@override
bool shouldRepaint(covariant _DottedRoutePainter oldDelegate) =>
oldDelegate.color != color;
}
class CurvyDashedPathPD extends StatelessWidget {
const CurvyDashedPathPD({
super.key,
this.width = 604,
this.height = 2651,
this.color = Colors.white,
});
final double width;
final double height;
final Color color;
@override
Widget build(BuildContext context) {
return CustomPaint(
size: Size(width, height),
painter: _CurvyDashedPathPDPainter(color: color),
isComplex: true,
willChange: false,
Positioned _topPath(BuildContext context) {
return Positioned(
top: context.heightScreen * 0.16,
left: context.widthScreen * 0.15,
child: Stack(
children: [
TopPath(
height: 950,
width: context.widthScreen * 0.6,
),
...List.generate(
context.read<HomeBloc>().topLocationList.length,
(index) => Positioned(
top: context.read<HomeBloc>().topLocationList[index].top,
bottom: context.read<HomeBloc>().topLocationList[index].bottom,
right: context.read<HomeBloc>().topLocationList[index].right,
left: context.read<HomeBloc>().topLocationList[index].left,
child: MissionWidget(
index: context.read<HomeBloc>().topLocationList[index].index ?? 0,
),
),
),
],
),
); );
} }
}
class _CurvyDashedPathPDPainter extends CustomPainter {
_CurvyDashedPathPDPainter({required this.color});
final Color color;
// SVG viewBox
static const double _vbW = 604.0;
static const double _vbH = 2651.0;
// SVG stroke styling
static const double _strokeWidth = 4.62295;
static const List<double> _dashPattern = [3.08, 13.1];
// The original SVG "d" attribute:
static const String _d = '''
M323.844 1.5163
C323.844 1.5163 254.064 132.635 209.041 216.483
C167.229 294.353 105.588 327.363 101.557 415.655
C96.6703 522.7 235.238 655.278 235.238 655.278
C235.238 655.278 284.393 748.372 277.229 810.918
C270.331 871.151 205.959 948.836 205.959 948.836
C205.959 948.836 31.3055 963.687 11.4099 1046.3
C-1.94798 1101.77 54.9427 1185.76 54.9427 1185.76
C54.9427 1185.76 -32.6228 1326.5 19.8853 1386.09
C63.1414 1435.18 185.541 1411.13 185.541 1411.13
C185.541 1411.13 427.654 1354.52 472.164 1458.9
C492.877 1507.48 472.164 1594.12 472.164 1594.12
C472.164 1594.12 454.029 1680.13 464.844 1733.58
C476.306 1790.23 531.492 1865.72 531.492 1865.72
C531.492 1865.72 515.799 1970.22 472.164 2015.58
C437.994 2051.1 361.598 2076.45 361.598 2076.45
C361.598 2076.45 304.217 2117.23 262.59 2133.08
C221.475 2148.74 152.41 2156.58 152.41 2156.58
C152.41 2156.58 60.2151 2199.32 47.623 2253.66
C34.196 2311.61 108.877 2393.12 108.877 2393.12
C108.877 2393.12 201.293 2437.1 262.59 2460.15
C315.761 2480.15 400.893 2505.23 400.893 2505.23
C400.893 2505.23 509.508 2505.09 553.451 2548.76
C584.574 2579.7 601.221 2650.47 601.221 2650.47
''';
@override
void paint(Canvas canvas, Size size) {
// Scale to widget size
final sx = size.width / _vbW;
final sy = size.height / _vbH;
canvas.save();
canvas.scale(sx, sy);
// Parse SVG path data to a Path
final Path original = parseSvgPathData(_d);
// Apply dash pattern
final Path dashed = dashPath(
original,
dashArray: CircularIntervalList<double>(_dashPattern),
Positioned _bottomPath(BuildContext context) {
return Positioned(
bottom: context.heightScreen * 0.12,
left: context.widthScreen * 0.2,
child: Stack(
clipBehavior: Clip.none,
children: [
BottomPath(
width: context.widthScreen * 0.75,
height: context.heightScreen * 0.65,
),
...List.generate(
context.read<HomeBloc>().bottomLocationList.length,
(index) => Positioned(
top: context.read<HomeBloc>().bottomLocationList[index].top,
bottom: context.read<HomeBloc>().bottomLocationList[index].bottom,
right: context.read<HomeBloc>().bottomLocationList[index].right,
left: context.read<HomeBloc>().bottomLocationList[index].left,
child: MissionWidget(
index: context.read<HomeBloc>().bottomLocationList[index].index ?? 0,
),
),
),
],
),
); );
// Stroke paint
final paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = _strokeWidth
..strokeCap = StrokeCap.butt
..strokeJoin = StrokeJoin.miter
..color = color;
canvas.drawPath(dashed, paint);
canvas.restore();
} }
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
} }

87
lib/features/home/presentation/ui/widgets/bottom_path.dart

@ -0,0 +1,87 @@
import 'package:flutter/material.dart';
import 'package:path_drawing/path_drawing.dart';
class BottomPath extends StatelessWidget {
const BottomPath({
super.key,
this.width = 500,
this.height = 1523,
this.color = Colors.white,
});
final double width;
final double height;
final Color color;
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: _Path(color),
size: Size(
width,
height,
), // or Size.infinite inside a parent with constraints
);
}
}
class _Path extends CustomPainter {
_Path(this.color);
final Color color;
// SVG viewBox
static const double _vbW = 500;
static const double _vbH = 1523;
// Your SVG path data (unchanged)
static const String _svgPath =
'M1.95892 1520.75C1.95892 1520.75 199.206 1423.63 169.156 1328.9C143.058 1246.63 30.8103 1281.9 15.4421 1225.27C-11.9488 1124.33 48.5736 1164.01 42.795 1033.8C38.5466 938.07 154.913 925.725 219.623 855.048C286.233 782.296 385.022 821.209 446.532 744.097C516.262 656.681 493.917 461.712 493.917 461.712C493.917 461.712 440.122 333.473 428.04 246.36C414.846 151.222 436.901 0.572754 436.901 0.572754';
@override
void paint(Canvas canvas, Size size) {
// Scale SVG viewBox -> current canvas while preserving aspect
final scale = _scaleToFit(
srcW: _vbW,
srcH: _vbH,
dstW: size.width,
dstH: size.height,
);
canvas.translate(
(size.width - _vbW * scale) / 2,
(size.height - _vbH * scale) / 2,
);
canvas.scale(scale);
// Parse and dash the path
final path = parseSvgPathData(_svgPath);
final dashed = dashPath(
path,
dashArray: CircularIntervalList<double>(const [3.08, 13.1]),
);
// Stroke paint
final paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 4.62295
..color = color
..strokeCap = StrokeCap.butt
..strokeJoin = StrokeJoin.miter;
canvas.drawPath(dashed, paint);
}
double _scaleToFit({
required double srcW,
required double srcH,
required double dstW,
required double dstH,
}) {
final sx = dstW / srcW;
final sy = dstH / srcH;
return sx < sy ? sx : sy; // contain
}
@override
bool shouldRepaint(covariant _Path oldDelegate) => oldDelegate.color != color;
}

38
lib/features/home/presentation/ui/widgets/mission_widget.dart

@ -0,0 +1,38 @@
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_text_style.dart';
import 'package:hadi_hoda_flutter/common_ui/theme/my_theme.dart';
import 'package:hadi_hoda_flutter/core/utils/my_image.dart';
enum MissionType {
unFinished,
finished,
current;
static Map<MissionType, String> get image => {
MissionType.unFinished: MyAssets.mission,
MissionType.finished: MyAssets.finishedMission,
MissionType.current: MyAssets.currentMission,
};
}
class MissionWidget extends StatelessWidget {
const MissionWidget({super.key, required this.index, this.type});
final int index;
final MissionType? type;
@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.topCenter,
children: [
MyImage(image: MissionType.image[type] ?? MyAssets.mission, size: 48),
Text(
'$index',
style: MyTextStyle.normal.copyWith(color: context.primaryColor),
),
],
);
}
}

99
lib/features/home/presentation/ui/widgets/top_path.dart

@ -0,0 +1,99 @@
import 'package:flutter/material.dart';
import 'package:path_drawing/path_drawing.dart';
class TopPath extends StatelessWidget {
const TopPath({
super.key,
this.width = 604,
this.height = 2651,
this.color = Colors.white,
});
final double width;
final double height;
final Color color;
@override
Widget build(BuildContext context) {
return CustomPaint(
size: Size(width, height),
painter: _Path(color: color),
isComplex: true,
willChange: false,
);
}
}
class _Path extends CustomPainter {
_Path({required this.color});
final Color color;
// SVG viewBox
static const double _vbW = 604.0;
static const double _vbH = 2651.0;
// SVG stroke styling
static const double _strokeWidth = 4.62295;
static const List<double> _dashPattern = [3.08, 13.1];
// The original SVG "d" attribute:
static const String _d = '''
M323.844 1.5163
C323.844 1.5163 254.064 132.635 209.041 216.483
C167.229 294.353 105.588 327.363 101.557 415.655
C96.6703 522.7 235.238 655.278 235.238 655.278
C235.238 655.278 284.393 748.372 277.229 810.918
C270.331 871.151 205.959 948.836 205.959 948.836
C205.959 948.836 31.3055 963.687 11.4099 1046.3
C-1.94798 1101.77 54.9427 1185.76 54.9427 1185.76
C54.9427 1185.76 -32.6228 1326.5 19.8853 1386.09
C63.1414 1435.18 185.541 1411.13 185.541 1411.13
C185.541 1411.13 427.654 1354.52 472.164 1458.9
C492.877 1507.48 472.164 1594.12 472.164 1594.12
C472.164 1594.12 454.029 1680.13 464.844 1733.58
C476.306 1790.23 531.492 1865.72 531.492 1865.72
C531.492 1865.72 515.799 1970.22 472.164 2015.58
C437.994 2051.1 361.598 2076.45 361.598 2076.45
C361.598 2076.45 304.217 2117.23 262.59 2133.08
C221.475 2148.74 152.41 2156.58 152.41 2156.58
C152.41 2156.58 60.2151 2199.32 47.623 2253.66
C34.196 2311.61 108.877 2393.12 108.877 2393.12
C108.877 2393.12 201.293 2437.1 262.59 2460.15
C315.761 2480.15 400.893 2505.23 400.893 2505.23
C400.893 2505.23 509.508 2505.09 553.451 2548.76
C584.574 2579.7 601.221 2650.47 601.221 2650.47
''';
@override
void paint(Canvas canvas, Size size) {
// Scale to widgets size
final sx = size.width / _vbW;
final sy = size.height / _vbH;
canvas.save();
canvas.scale(sx, sy);
// Parse SVG path data to a Path
final Path original = parseSvgPathData(_d);
// Apply dash pattern
final Path dashed = dashPath(
original,
dashArray: CircularIntervalList<double>(_dashPattern),
);
// Stroke paint
final paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = _strokeWidth
..strokeCap = StrokeCap.butt
..strokeJoin = StrokeJoin.miter
..color = color;
canvas.drawPath(dashed, paint);
canvas.restore();
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

7
pubspec.yaml

@ -37,3 +37,10 @@ flutter:
assets: assets:
- assets/images/ - assets/images/
- assets/fonts/
fonts:
- family: dinokids
fonts:
- asset: assets/fonts/dinokids.ttf
Loading…
Cancel
Save