diff --git a/assets/images/circle.png b/assets/images/circle.png new file mode 100644 index 0000000..a264d38 Binary files /dev/null and b/assets/images/circle.png differ diff --git a/assets/images/circle2.png b/assets/images/circle2.png new file mode 100644 index 0000000..b37897c Binary files /dev/null and b/assets/images/circle2.png differ diff --git a/lib/application/order_detail_bloc/order_detail_bloc.dart b/lib/application/order_detail_bloc/order_detail_bloc.dart index 6f76d3a..4beb5dc 100644 --- a/lib/application/order_detail_bloc/order_detail_bloc.dart +++ b/lib/application/order_detail_bloc/order_detail_bloc.dart @@ -1,11 +1,17 @@ import 'dart:async'; +import 'package:another_stepper/another_stepper.dart'; +import 'package:cargo/core/constants/colors.dart'; import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:collection/collection.dart'; +import '../../core/constants/assets.dart'; import '../../core/errors/failures.dart'; import '../../domain/entities/route/route.dart'; import '../../domain/usecases/order/get_routes_usecase.dart'; +import '../../presentation/widgets/circle_painter.dart'; part 'order_detail_event.dart'; part 'order_detail_state.dart'; @@ -23,7 +29,87 @@ class OrderDetailBloc extends Bloc { final result = await _getRoutesUseCase(event.cargoId); result.fold( (failure) => emit(RoutesError(failure: failure)), - (response) => emit(RoutesLoaded(routes: response.routes)), + (response) { + // Find the index of the current route + int currentIndex = response.routes.indexWhere((r) => r.isCurrent); + + emit( + RoutesLoaded( + routes: response.routes, + activeIndex: currentIndex, + steppers: response.routes.mapIndexed((index, route) { + // Determine if the icon should be splashed + final bool isSplashedIcon = index == 0 || index == response.routes.length - 1; + + // Determine the color based on the conditions + Color textColor; + Color iconColor; + + if (route.isCurrent) { + textColor = AppColors.greenDark; + iconColor = AppColors.greenDark; + } else if (index < currentIndex) { + textColor = const Color(0xFFD8D8DA); + iconColor = AppColors.greenDark; + } else { + textColor = AppColors.darkGrey; + iconColor = AppColors.darkGrey; + } + + return StepperData( + title: StepperText( + route.name, + textStyle: TextStyle( + color: textColor, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + subtitle: StepperText( + route.dateAt, + textStyle: TextStyle( + color: textColor, + fontWeight: FontWeight.bold, + fontSize: 14, + ), + ), + iconWidget: isSplashedIcon + ? Image.asset( + AppAssets.circle, + color: iconColor, + ) + : CirclePainterWidget( + radius: 8, + color: iconColor, + ), + ); + }).toList(), + ), + /* RoutesLoaded( + routes: response.routes, + steppers: response.routes.mapIndexed((index, route) { + return StepperData( + title: StepperText( + route.name, + textStyle: const TextStyle( + color: Colors.grey, + ), + ), + subtitle: StepperText(route.dateAt), + iconWidget: Container( + padding: const EdgeInsets.all(8), + decoration: const BoxDecoration( + color: Colors.green, + borderRadius: BorderRadius.all( + Radius.circular(18), + ), + ), + ), + ); + }).toList(), + ), */ + ); + }, ); } catch (e) { emit(RoutesError(failure: ExceptionFailure())); diff --git a/lib/application/order_detail_bloc/order_detail_state.dart b/lib/application/order_detail_bloc/order_detail_state.dart index e3eeaf6..1b728f0 100644 --- a/lib/application/order_detail_bloc/order_detail_state.dart +++ b/lib/application/order_detail_bloc/order_detail_state.dart @@ -13,9 +13,15 @@ class RoutesLoading extends OrderDetailState {} class RoutesLoaded extends OrderDetailState { final List routes; - const RoutesLoaded({required this.routes}); + final List steppers; + final int activeIndex; + const RoutesLoaded({ + required this.routes, + required this.steppers, + required this.activeIndex, + }); @override - List get props => [routes]; + List get props => [routes, steppers, activeIndex]; } class RoutesError extends OrderDetailState { diff --git a/lib/core/constants/assets.dart b/lib/core/constants/assets.dart index 906eaea..61ffc1e 100644 --- a/lib/core/constants/assets.dart +++ b/lib/core/constants/assets.dart @@ -10,6 +10,8 @@ sealed class AppAssets { static const String boxesPng = 'assets/images/boxes.png'; static const String trucksPng = 'assets/images/trucks.png'; static const String header = 'assets/images/header.png'; + static const String circle = 'assets/images/circle.png'; + static const String circle2 = 'assets/images/circle2.png'; static const String search = 'assets/images/search.png'; static const String searchGif = 'assets/images/search.gif'; } diff --git a/lib/core/constants/colors.dart b/lib/core/constants/colors.dart index 8255390..6993a51 100644 --- a/lib/core/constants/colors.dart +++ b/lib/core/constants/colors.dart @@ -5,6 +5,7 @@ sealed class AppColors { static const Color yellow = Color(0xFFFFC71E); static const Color surface = Color(0xFFEDEEFC); static const Color green = Color(0xFFA2E052); + static const Color greenDark = Color(0xFF61971B); static const Color grey = Color(0xFF96969C); static const Color darkGrey = Color(0xFF57575C); static const Color lightGrey = Color(0xFFE5E5E6); diff --git a/lib/presentation/screens/order_details.dart b/lib/presentation/screens/order_details.dart index 8aadb4d..7b7733e 100644 --- a/lib/presentation/screens/order_details.dart +++ b/lib/presentation/screens/order_details.dart @@ -99,6 +99,15 @@ class _OrderDetailsScreenState extends State { ), if (!_isFullScreen) ...[ + /// current location info + if (_showLocation) + SliverToBoxAdapter( + child: Padding( + padding: Space.all(1, 1), + child: LocationCard(cargoId: widget.order.cargoId), + ), + ), + /// info text SliverToBoxAdapter( child: Padding( @@ -116,42 +125,19 @@ class _OrderDetailsScreenState extends State { /// info card InfoCard(order: widget.order), + + /// gap + Space.y!, + + ///images widget + ImagesWidget(imageStrings: _images), + + /// gap + Space.yf(2), ], ), ), ), - - /// current location info - if (_showLocation) - SliverToBoxAdapter( - child: Padding( - padding: Space.all(1, 1), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'route'.tr(), - style: AppText.h2b, - ), - - /// gap - Space.y!, - - /// location card - LocationCard(cargoId: widget.order.cargoId), - - /// gap - Space.y!, - - ///images widget - ImagesWidget(imageStrings: _images), - - /// gap - Space.yf(2), - ], - ), - ), - ), ], ], ), diff --git a/lib/presentation/widgets/circle_painter.dart b/lib/presentation/widgets/circle_painter.dart new file mode 100644 index 0000000..77d632c --- /dev/null +++ b/lib/presentation/widgets/circle_painter.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; + +class CirclePainter extends CustomPainter { + final double radius; + final Color color; + + CirclePainter({required this.radius, required this.color}); + + @override + void paint(Canvas canvas, Size size) { + // Create a paint object with the specified color + final paint = Paint() + ..color = color + ..style = PaintingStyle.fill; + + // Draw the circle in the center of the canvas + canvas.drawCircle( + Offset(size.width / 2, size.height / 2), + radius, + paint, + ); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return false; + } +} + +class CirclePainterWidget extends StatelessWidget { + final double radius; + final Color color; + + const CirclePainterWidget({super.key, required this.radius, required this.color}); + + @override + Widget build(BuildContext context) { + return CustomPaint( + painter: CirclePainter(radius: radius, color: color), + // Ensure the widget is large enough to fit the circle + size: Size(radius * 2, radius * 2), + ); + } +} diff --git a/lib/presentation/widgets/location_card.dart b/lib/presentation/widgets/location_card.dart index 5fee6a1..e533c66 100644 --- a/lib/presentation/widgets/location_card.dart +++ b/lib/presentation/widgets/location_card.dart @@ -1,10 +1,11 @@ +import 'package:another_stepper/another_stepper.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../application/application.dart'; import '../../configs/configs.dart'; import '../../core/core.dart'; -import 'dashed_line.dart'; import 'retry_widget.dart'; class LocationCard extends StatelessWidget { @@ -16,69 +17,43 @@ class LocationCard extends StatelessWidget { return BlocBuilder( builder: (context, state) { if (state is RoutesLoading) { - return const Center( - child: CircularProgressIndicator(), - ); + return const SizedBox.shrink(); + // const Center( + // child: CircularProgressIndicator(), + // ); } else if (state is RoutesLoaded) { - final routes = state.routes; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'route'.tr(), + style: AppText.h2b, + ), - return SizedBox( - width: double.infinity, - child: Card( - color: Colors.white, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: ListView.separated( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (context, index) { - final route = routes[index]; - return Row( - children: [ - route.isCurrent - ? const Icon( - Icons.radio_button_checked, - color: AppColors.primary, - ) - : Container( - margin: Space.hf(0.35), - height: AppDimensions.normalize(4.5), - width: AppDimensions.normalize(4.5), - decoration: const BoxDecoration( - color: AppColors.grey, - shape: BoxShape.circle, - ), - ), - Space.x!, - Text( - routes[index].name, - style: AppText.h3b, - ), - const Spacer(), - Text( - routes[index].dateAt, - style: AppText.b1!.copyWith(color: AppColors.grey), - ), - ], - ); - }, - itemCount: routes.length, - separatorBuilder: (BuildContext context, int index) { - return Padding( - padding: Space.vf(0.5), - child: DashedLine( - height: 1, - width: AppDimensions.normalize(8), - color: AppColors.lightGrey, - strokeWidth: 1, - dashWidth: 5, - dashSpace: AppDimensions.normalize(2), - ), - ); - }, + /// gap + Space.y!, + SizedBox( + width: double.infinity, + child: Card( + color: Colors.white, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: AnotherStepper( + stepperList: state.steppers, + stepperDirection: Axis.vertical, + iconWidth: 30, + iconHeight: 30, + activeBarColor: AppColors.greenDark, + inActiveBarColor: AppColors.darkGrey, + inverted: false, + verticalGap: 25, + activeIndex: state.activeIndex, + barThickness: 2.6, + ), + ), ), ), - ), + ], ); } else if (state is RoutesError) { return Center( diff --git a/lib/presentation/widgets/widgets.dart b/lib/presentation/widgets/widgets.dart index 3031313..e2b55ad 100644 --- a/lib/presentation/widgets/widgets.dart +++ b/lib/presentation/widgets/widgets.dart @@ -13,3 +13,4 @@ export 'successful_auth_dialog.dart'; export 'vertical_line.dart'; export 'images.dart'; export 'empty_order.dart'; +export 'circle_painter.dart'; diff --git a/pubspec.lock b/pubspec.lock index 83f399e..10abb6c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + another_stepper: + dependency: "direct main" + description: + name: another_stepper + sha256: "04f5166c57f2412c612b17101e8a3d819f210c53f6bc1b5be541dc4c6987c681" + url: "https://pub.dev" + source: hosted + version: "1.2.2" ansicolor: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 676bc25..a9d12de 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,6 +30,7 @@ dependencies: # cached_network_image: ^3.4.1 flutter_native_splash: ^2.4.1 url_launcher: ^6.3.0 + another_stepper: ^1.2.2 dev_dependencies: flutter_test: