From 34b02a66c8a1fa463b768fdd6a7a12df7ba5d9a6 Mon Sep 17 00:00:00 2001 From: Muthukaliswaran Date: Fri, 6 Dec 2024 14:58:08 +0530 Subject: [PATCH 1/7] Custom stepper implementation done. --- lib/constants/color_constants.dart | 1 + .../view/create_contract_screen.dart | 40 ++++++++ .../create_contract_view_model.dart | 45 +++++++++ .../seller_register_view_model.dart | 8 +- lib/features/shared/custom_stepper.dart | 97 +++++++++++++++++++ lib/features/shared/model/stepper_data.dart | 8 ++ lib/route/multi_provider_list.dart | 2 + lib/route/router_config.dart | 7 ++ lib/route/router_constant.dart | 1 + 9 files changed, 203 insertions(+), 6 deletions(-) create mode 100644 lib/features/seller/authentication/view/create_contract_screen.dart create mode 100644 lib/features/seller/authentication/view_model/create_contract_view_model.dart create mode 100644 lib/features/shared/custom_stepper.dart create mode 100644 lib/features/shared/model/stepper_data.dart diff --git a/lib/constants/color_constants.dart b/lib/constants/color_constants.dart index 0783794..c2f13f1 100644 --- a/lib/constants/color_constants.dart +++ b/lib/constants/color_constants.dart @@ -28,4 +28,5 @@ class AppColors{ static const Color sellerSignUpBg = Color(0xffe6e7ea); static const Color sellerButtonBg = Color(0xff101522); static const Color greyButtonBg = Color(0xff949698); + static const Color stepperInactiveBorder = Color(0xffdcdce5); } \ No newline at end of file diff --git a/lib/features/seller/authentication/view/create_contract_screen.dart b/lib/features/seller/authentication/view/create_contract_screen.dart new file mode 100644 index 0000000..6bf1fb3 --- /dev/null +++ b/lib/features/seller/authentication/view/create_contract_screen.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:frontend_ecommerce/constants/color_constants.dart'; +import 'package:frontend_ecommerce/features/shared/custom_stepper.dart'; +import 'package:provider/provider.dart'; + +import '../view_model/create_contract_view_model.dart'; + +class CreateContractScreen extends StatefulWidget { + const CreateContractScreen({super.key}); + + @override + State createState() => _CreateContractScreenState(); +} + +class _CreateContractScreenState extends State { + + @override + void initState() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + final auth = Provider.of(context, listen: false); + auth.setInitialStepperData(); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Material( + child: LayoutBuilder( + builder: (context, constraints) { + return Consumer(builder: (context, provider, child){ + return CustomStepper(data: provider.stepperData,onChanged: (stepIndex){ + provider.goToSelectedStepperIndex(stepIndex); + },); + }); + } + ), + ); + } +} diff --git a/lib/features/seller/authentication/view_model/create_contract_view_model.dart b/lib/features/seller/authentication/view_model/create_contract_view_model.dart new file mode 100644 index 0000000..2a19856 --- /dev/null +++ b/lib/features/seller/authentication/view_model/create_contract_view_model.dart @@ -0,0 +1,45 @@ + + +import 'package:flutter/material.dart'; + +import '../../../shared/model/stepper_data.dart'; + +class CreateContractViewModel extends ChangeNotifier{ + + final int? steps = 5; + final List stepperData = []; + + setInitialStepperData(){ + stepperData.add(StepperData(headerTitle: 'Personal Information',isCurrentStep: false, stepCompleted: true)); + stepperData.add(StepperData(headerTitle: 'GST Verification',isCurrentStep: true, stepCompleted: false)); + stepperData.add(StepperData(headerTitle: 'Store Details',isCurrentStep: false, stepCompleted: false)); + stepperData.add(StepperData(headerTitle: 'Tax and Account Information',isCurrentStep: false, stepCompleted: false)); + stepperData.add(StepperData(headerTitle: 'Shipping Information',isCurrentStep: false, stepCompleted: false)); + notifyListeners(); + } + + + setStepCompleted(int index, bool isCompleted){ + stepperData[index].stepCompleted = isCompleted; + } + + goToNextStep(int currentIndex){ + for(int i = 0; i< stepperData.length; i++){ + stepperData[i].isCurrentStep = false; + } + if(currentIndex < (stepperData.length - 1)){ + stepperData[currentIndex + 1].isCurrentStep = true; + } + notifyListeners(); + } + + goToSelectedStepperIndex(int index){ + for(int i = 0; i< stepperData.length; i++){ + stepperData[i].isCurrentStep = false; + } + stepperData[index].isCurrentStep = true; + notifyListeners(); + } + + +} diff --git a/lib/features/seller/authentication/view_model/seller_register_view_model.dart b/lib/features/seller/authentication/view_model/seller_register_view_model.dart index ba5a9cc..0f261f6 100644 --- a/lib/features/seller/authentication/view_model/seller_register_view_model.dart +++ b/lib/features/seller/authentication/view_model/seller_register_view_model.dart @@ -267,7 +267,7 @@ class SellerRegisterViewModel extends ChangeNotifier{ } - verifyOtp(context) async{ + verifyOtp(BuildContext context) async{ if(otp.length >= 6){ verifyOtpLoading = true; notifyListeners(); @@ -280,11 +280,7 @@ class SellerRegisterViewModel extends ChangeNotifier{ verifyOtpLoading = false; notifyListeners(); if(onValue.sellerVerifyOTPRegisterModel?.status == 200 ){ - // Move to Home page - CustomSnackbar( - message: 'OTP Verified', - context: context) - .showSnackbar(); + context.go(AppPages.auth+AppPages.sellerCreateContract); }else{ CustomSnackbar( message: onValue.errorResponseModel!.message ?? "", diff --git a/lib/features/shared/custom_stepper.dart b/lib/features/shared/custom_stepper.dart new file mode 100644 index 0000000..0920c07 --- /dev/null +++ b/lib/features/shared/custom_stepper.dart @@ -0,0 +1,97 @@ +import 'package:flutter/material.dart'; + +import '../../constants/color_constants.dart'; +import 'model/stepper_data.dart'; + +class CustomStepper extends StatelessWidget { + const CustomStepper({super.key, this.data,this.onChanged}); + + final List? data; + final Function(int stepIndex)? onChanged; + + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: stepperChildrenHeader(), + ), + ); + } + + List stepperChildrenHeader(){ + List items = []; + + for(int i =0 ; i< (data?.length ?? 0); i++){ + items.add( + IntrinsicWidth( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Stack( + clipBehavior: Clip.none, + children: [ + InkWell( + focusColor: Colors.transparent, + highlightColor: Colors.transparent, + splashColor: Colors.transparent, + hoverColor: Colors.transparent, + onTap: (){ + onChanged?.call(i); + }, + child: Column(children: [ + Padding( + padding: const EdgeInsetsDirectional.only(start: 20,end: 20), + child: Text(data?[i].headerTitle ?? '', style: TextStyle(color: data?[i].isCurrentStep ?? false ? AppColors.primaryColor : AppColors.secondaryTextColor),), + ), + const SizedBox(height: 10,), + Stack( + alignment: Alignment.center, + clipBehavior: Clip.none, + children: [ + Row( + children: [ + Flexible( + child: Container( + height: 1.5, + color: (i <= 0)? Colors.transparent : (data?[i-1].stepCompleted ?? false)? AppColors.primaryColor : AppColors.stepperInactiveBorder, + ), + ), + Flexible( + child: Container( + height: 1.5, + color: (i < ((data?.length ?? 0) -1 )) ? (data?[i].stepCompleted ?? false)? AppColors.primaryColor : AppColors.stepperInactiveBorder : Colors.transparent, + ), + ) + ], + ), + Container( + decoration: BoxDecoration( + color:data?[i].isCurrentStep ?? false? AppColors.white : ( data?[i].stepCompleted ?? false)? AppColors.primaryColor : AppColors.stepperInactiveBorder, + border: Border.all(color: (data?[i].isCurrentStep ?? false)? AppColors.primaryColor : Colors.transparent), + borderRadius: BorderRadius.circular(20) + ), + constraints: const BoxConstraints( + maxWidth: 10, + maxHeight: 10 + ), + padding: const EdgeInsets.all(20), + ), + ], + ), + ],), + ) + ], + ) + ], + ), + ) + ); + } + + + return items; + } + +} diff --git a/lib/features/shared/model/stepper_data.dart b/lib/features/shared/model/stepper_data.dart new file mode 100644 index 0000000..035f2fe --- /dev/null +++ b/lib/features/shared/model/stepper_data.dart @@ -0,0 +1,8 @@ +class StepperData{ + bool? stepCompleted; + String? headerTitle; + bool? isCurrentStep; + + StepperData({this.stepCompleted, this.headerTitle, this.isCurrentStep}); + +} \ No newline at end of file diff --git a/lib/route/multi_provider_list.dart b/lib/route/multi_provider_list.dart index de41b52..9fa93ab 100644 --- a/lib/route/multi_provider_list.dart +++ b/lib/route/multi_provider_list.dart @@ -1,5 +1,6 @@ import 'package:frontend_ecommerce/features/buyer/authentication/view_model/login_viewmodel.dart'; import 'package:frontend_ecommerce/features/buyer/authentication/view_model/register_viewmodel.dart'; +import 'package:frontend_ecommerce/features/seller/authentication/view_model/create_contract_view_model.dart'; import 'package:frontend_ecommerce/features/seller/authentication/view_model/seller_register_view_model.dart'; import 'package:frontend_ecommerce/utils/timer_provider.dart'; import 'package:provider/provider.dart'; @@ -9,4 +10,5 @@ List providersList = [ ChangeNotifierProvider(create: (_) => LoginViewmodel()), ChangeNotifierProvider(create: (_) => SellerRegisterViewModel()), ChangeNotifierProvider(create: (_) => TimerProvider()), + ChangeNotifierProvider(create: (_) => CreateContractViewModel()), ]; \ No newline at end of file diff --git a/lib/route/router_config.dart b/lib/route/router_config.dart index c7ca905..2d80af7 100644 --- a/lib/route/router_config.dart +++ b/lib/route/router_config.dart @@ -3,6 +3,7 @@ import 'package:frontend_ecommerce/features/buyer/authentication/view/buyer_logi import 'package:frontend_ecommerce/features/buyer/authentication/view/buyer_register.dart'; import 'package:frontend_ecommerce/features/buyer/dashboard/view/buyer_dashboard.dart'; import 'package:frontend_ecommerce/features/buyer/landing/view/landing_screen.dart'; +import 'package:frontend_ecommerce/features/seller/authentication/view/create_contract_screen.dart'; import 'package:frontend_ecommerce/features/seller/authentication/view/seller_register.dart'; import 'package:frontend_ecommerce/route/router_constant.dart'; import 'package:go_router/go_router.dart'; @@ -40,6 +41,12 @@ class AppRouter { return SellerRegister(); } ), + GoRoute( + path: AppPages.auth+AppPages.sellerCreateContract, + builder: (BuildContext context, GoRouterState state) { + return CreateContractScreen(); + } + ), ], ); } diff --git a/lib/route/router_constant.dart b/lib/route/router_constant.dart index 01dcfea..1ec9ecc 100644 --- a/lib/route/router_constant.dart +++ b/lib/route/router_constant.dart @@ -8,4 +8,5 @@ class AppPages{ static const String sellerDashBoard = '/seller/dashboard'; static const String buyerLogin = '/buyer/login'; static const String buyerDashboard = '/buyer/dashboard'; + static const String sellerCreateContract = '/seller/creating-contract'; } \ No newline at end of file From ca9a3f3287fa4fef1ffed80ba3c2a2e27351d80d Mon Sep 17 00:00:00 2001 From: Muthukaliswaran Date: Fri, 6 Dec 2024 15:52:41 +0530 Subject: [PATCH 2/7] Custom stepper implementation done. --- lib/constants/color_constants.dart | 2 + .../view/create_contract_screen.dart | 110 ++++++++++++++++-- lib/features/shared/custom_stepper.dart | 11 +- 3 files changed, 113 insertions(+), 10 deletions(-) diff --git a/lib/constants/color_constants.dart b/lib/constants/color_constants.dart index c2f13f1..0bcaac5 100644 --- a/lib/constants/color_constants.dart +++ b/lib/constants/color_constants.dart @@ -29,4 +29,6 @@ class AppColors{ static const Color sellerButtonBg = Color(0xff101522); static const Color greyButtonBg = Color(0xff949698); static const Color stepperInactiveBorder = Color(0xffdcdce5); + static const Color defaultIconColor = Color(0xff788295); + static const Color defaultTextColor = Color(0xff788295); } \ No newline at end of file diff --git a/lib/features/seller/authentication/view/create_contract_screen.dart b/lib/features/seller/authentication/view/create_contract_screen.dart index 6bf1fb3..b99890d 100644 --- a/lib/features/seller/authentication/view/create_contract_screen.dart +++ b/lib/features/seller/authentication/view/create_contract_screen.dart @@ -3,6 +3,7 @@ import 'package:frontend_ecommerce/constants/color_constants.dart'; import 'package:frontend_ecommerce/features/shared/custom_stepper.dart'; import 'package:provider/provider.dart'; +import '../../../../constants/screen_size_constants.dart'; import '../view_model/create_contract_view_model.dart'; class CreateContractScreen extends StatefulWidget { @@ -26,15 +27,108 @@ class _CreateContractScreenState extends State { @override Widget build(BuildContext context) { return Material( - child: LayoutBuilder( - builder: (context, constraints) { - return Consumer(builder: (context, provider, child){ - return CustomStepper(data: provider.stepperData,onChanged: (stepIndex){ - provider.goToSelectedStepperIndex(stepIndex); - },); - }); - } + child: Consumer(builder: (context, provider, child){ + return LayoutBuilder( + builder: (context, constraints) { + if (constraints.maxWidth < ScreenSizeConstants.mobileBreakPoint) { + return mobileLayout(context,provider); + } else if (constraints.maxWidth < ScreenSizeConstants.tabletBreakPoint) { + return tabletLayout(context,provider); + } else if (constraints.maxWidth < ScreenSizeConstants.desktopBreakPoint) { + return desktopOrTvLayout(context,provider); + } else { + return desktopOrTvLayout(context,provider); + } + } + );} + )); + } + + Widget mobileLayout(BuildContext context, CreateContractViewModel provider){ + return Padding( + padding: const EdgeInsetsDirectional.only(start: 20, end: 20, top: 20), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + const Icon( + Icons.keyboard_backspace_outlined, + color: AppColors.defaultIconColor, + ), + const SizedBox(width: 50,), + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + child: CustomStepper(data: provider.stepperData,onChanged: (stepIndex){ + provider.goToSelectedStepperIndex(stepIndex); + },), + ), + ], + ), + ) + ], + ), + ); + } + + Widget tabletLayout(BuildContext context, CreateContractViewModel provider){ + return Padding( + padding: const EdgeInsetsDirectional.only(start: 20, end: 20, top: 20), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + const Icon( + Icons.keyboard_backspace_outlined, + color: AppColors.defaultIconColor, + ), + const SizedBox(width: 50,), + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + child: CustomStepper(data: provider.stepperData,onChanged: (stepIndex){ + provider.goToSelectedStepperIndex(stepIndex); + },), + ), + ], + ), + ) + ], ), ); } + + Widget desktopOrTvLayout(BuildContext context, CreateContractViewModel provider){ + return Padding( + padding: const EdgeInsetsDirectional.only(start: 20, top: 50, end: 20), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + const Icon( + Icons.keyboard_backspace_outlined, + color: AppColors.defaultIconColor, + ), + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + child: CustomStepper(data: provider.stepperData,onChanged: (stepIndex){ + provider.goToSelectedStepperIndex(stepIndex); + },), + ), + ], + ), + ) + ], + ), + ); + } + + } diff --git a/lib/features/shared/custom_stepper.dart b/lib/features/shared/custom_stepper.dart index 0920c07..aed8e1b 100644 --- a/lib/features/shared/custom_stepper.dart +++ b/lib/features/shared/custom_stepper.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:frontend_ecommerce/common/styles/font_style.dart'; import '../../constants/color_constants.dart'; import 'model/stepper_data.dart'; @@ -38,12 +39,18 @@ class CustomStepper extends StatelessWidget { splashColor: Colors.transparent, hoverColor: Colors.transparent, onTap: (){ - onChanged?.call(i); + if(i == 0){ + onChanged?.call(i); + }else{ + if(data?[i -1].stepCompleted ?? false){ + onChanged?.call(i); + } + } }, child: Column(children: [ Padding( padding: const EdgeInsetsDirectional.only(start: 20,end: 20), - child: Text(data?[i].headerTitle ?? '', style: TextStyle(color: data?[i].isCurrentStep ?? false ? AppColors.primaryColor : AppColors.secondaryTextColor),), + child: Text(data?[i].headerTitle ?? '', style: FontStyles.labelMedium.copyWith(fontWeight: FontWeight.w400,color: data?[i].isCurrentStep ?? false ? AppColors.primaryColor : AppColors.defaultTextColor)), ), const SizedBox(height: 10,), Stack( From 0144fd51651f740beb6f086bda9851ebcd0721ca Mon Sep 17 00:00:00 2001 From: Muthukaliswaran Date: Fri, 6 Dec 2024 17:53:38 +0530 Subject: [PATCH 3/7] Custom stepper implementation done. --- .../widget/shared/input_text_field.dart | 22 +- lib/constants/color_constants.dart | 2 + .../view/create_contract_screen.dart | 332 +++++++++++++++--- .../create_contract_view_model.dart | 23 ++ lib/features/shared/custom_stepper.dart | 8 +- lib/localization/intl_de.arb | 7 +- lib/localization/intl_en.arb | 7 +- 7 files changed, 341 insertions(+), 60 deletions(-) diff --git a/lib/common/widget/shared/input_text_field.dart b/lib/common/widget/shared/input_text_field.dart index 131314e..834a204 100644 --- a/lib/common/widget/shared/input_text_field.dart +++ b/lib/common/widget/shared/input_text_field.dart @@ -16,6 +16,10 @@ class InputTextField extends StatefulWidget { final FormFieldValidator? onVaildate; final Function(String)? onTextChange; final List? inputFormatters; + final Color cursorColor; + final Color focusedBorderColor; + final Color textColor; + final Color inactiveBorderColor; const InputTextField( {super.key, @@ -30,7 +34,11 @@ class InputTextField extends StatefulWidget { this.onTextChange, this.errorText, this.onVaildate, - this.inputFormatters}); + this.inputFormatters, + this.cursorColor = AppColors.focusedBorder, + this.focusedBorderColor = AppColors.focusedBorder, + this.inactiveBorderColor = AppColors.inActiveBorder, + this.textColor = AppColors.primaryTextColor}); @override State createState() => _InputTextFieldState(); @@ -65,6 +73,8 @@ class _InputTextFieldState extends State { TextFormField( key: widget.formKey, controller: widget.controller, + cursorColor: widget.cursorColor, + style: FontStyles.labelMedium.copyWith(color: widget.textColor), obscureText: widget.isObscureText ? obscureText : false, decoration: InputDecoration( labelText: widget.labelText, @@ -87,8 +97,8 @@ class _InputTextFieldState extends State { enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: _isHovered - ? AppColors.focusedBorder // Hover border color - : AppColors.inActiveBorder, // Default border color + ? widget.focusedBorderColor // Hover border color + : widget.inactiveBorderColor, // Default border color ), ), focusedBorder: OutlineInputBorder( @@ -96,12 +106,12 @@ class _InputTextFieldState extends State { color: (widget.errorText != null && widget.errorText!.isNotEmpty) ? Colors.red - : AppColors.focusedBorder, // Active border color + : widget.focusedBorderColor, // Active border color ), ), - disabledBorder: const OutlineInputBorder( + disabledBorder: OutlineInputBorder( borderSide: BorderSide( - color: AppColors.inActiveBorder, // Disabled border color + color: widget.inactiveBorderColor, // Disabled border color ), ), focusedErrorBorder: const OutlineInputBorder( diff --git a/lib/constants/color_constants.dart b/lib/constants/color_constants.dart index 0bcaac5..0acbe86 100644 --- a/lib/constants/color_constants.dart +++ b/lib/constants/color_constants.dart @@ -31,4 +31,6 @@ class AppColors{ static const Color stepperInactiveBorder = Color(0xffdcdce5); static const Color defaultIconColor = Color(0xff788295); static const Color defaultTextColor = Color(0xff788295); + static const Color sellerTextFieldBorder = Color(0xffe2e4e9); + static const Color sellerTextFieldTextColor = Color(0xff848e9f); } \ No newline at end of file diff --git a/lib/features/seller/authentication/view/create_contract_screen.dart b/lib/features/seller/authentication/view/create_contract_screen.dart index b99890d..4dbbe7a 100644 --- a/lib/features/seller/authentication/view/create_contract_screen.dart +++ b/lib/features/seller/authentication/view/create_contract_screen.dart @@ -1,10 +1,14 @@ import 'package:flutter/material.dart'; +import 'package:frontend_ecommerce/common/styles/font_style.dart'; import 'package:frontend_ecommerce/constants/color_constants.dart'; import 'package:frontend_ecommerce/features/shared/custom_stepper.dart'; import 'package:provider/provider.dart'; +import '../../../../common/widget/shared/custom_button.dart'; +import '../../../../common/widget/shared/input_text_field.dart'; import '../../../../constants/screen_size_constants.dart'; import '../view_model/create_contract_view_model.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class CreateContractScreen extends StatefulWidget { const CreateContractScreen({super.key}); @@ -27,6 +31,7 @@ class _CreateContractScreenState extends State { @override Widget build(BuildContext context) { return Material( + color: AppColors.white, child: Consumer(builder: (context, provider, child){ return LayoutBuilder( builder: (context, constraints) { @@ -45,90 +50,317 @@ class _CreateContractScreenState extends State { } Widget mobileLayout(BuildContext context, CreateContractViewModel provider){ + int currentStepIndex = provider.stepperData.indexWhere((element) => element.isCurrentStep == true); return Padding( padding: const EdgeInsetsDirectional.only(start: 20, end: 20, top: 20), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.end, + child: Column( children: [ - const Icon( - Icons.keyboard_backspace_outlined, - color: AppColors.defaultIconColor, + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + const Icon( + Icons.keyboard_backspace_outlined, + color: AppColors.defaultIconColor, + ), + const SizedBox(width: 50,), + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + child: CustomStepper(data: provider.stepperData,onChanged: (stepIndex){ + provider.goToSelectedStepperIndex(stepIndex); + },), + ), + ], + ), + ) + ], ), - const SizedBox(width: 50,), + const SizedBox(height: 30,), Expanded( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Flexible( - child: CustomStepper(data: provider.stepperData,onChanged: (stepIndex){ - provider.goToSelectedStepperIndex(stepIndex); - },), - ), - ], + child: SingleChildScrollView( + child: Align( + alignment: Alignment.center, + child: Container( + width: 400, + constraints: const BoxConstraints( + maxWidth: 400 + ), + child: Column( + children: [ + buildInfoUI(provider,currentStepIndex), + const SizedBox(height: 20,), + bottomButton(false, provider, currentStepIndex), + const SizedBox(height: 50,), + ], + )), + ), ), - ) + ), ], ), ); } Widget tabletLayout(BuildContext context, CreateContractViewModel provider){ + int currentStepIndex = provider.stepperData.indexWhere((element) => element.isCurrentStep == true); return Padding( padding: const EdgeInsetsDirectional.only(start: 20, end: 20, top: 20), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.end, + child: Column( children: [ - const Icon( - Icons.keyboard_backspace_outlined, - color: AppColors.defaultIconColor, + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + const Icon( + Icons.keyboard_backspace_outlined, + color: AppColors.defaultIconColor, + ), + const SizedBox(width: 50,), + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + child: CustomStepper(data: provider.stepperData,onChanged: (stepIndex){ + provider.goToSelectedStepperIndex(stepIndex); + },), + ), + ], + ), + ) + ], ), - const SizedBox(width: 50,), + const SizedBox(height: 30,), Expanded( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Flexible( - child: CustomStepper(data: provider.stepperData,onChanged: (stepIndex){ - provider.goToSelectedStepperIndex(stepIndex); - },), - ), - ], + child: SingleChildScrollView( + child: Align( + alignment: Alignment.center, + child: Container( + width: 400, + constraints: const BoxConstraints( + maxWidth: 400 + ), + child: Column( + children: [ + buildInfoUI(provider,currentStepIndex), + const SizedBox(height: 20,), + bottomButton(false, provider, currentStepIndex), + const SizedBox(height: 50,), + ], + )), + ), ), - ) + ), ], ), ); } Widget desktopOrTvLayout(BuildContext context, CreateContractViewModel provider){ + int currentStepIndex = provider.stepperData.indexWhere((element) => element.isCurrentStep == true); return Padding( padding: const EdgeInsetsDirectional.only(start: 20, top: 50, end: 20), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.end, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - const Icon( - Icons.keyboard_backspace_outlined, - color: AppColors.defaultIconColor, + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + const Icon( + Icons.keyboard_backspace_outlined, + color: AppColors.defaultIconColor, + ), + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + child: CustomStepper(data: provider.stepperData,onChanged: (stepIndex){ + provider.goToSelectedStepperIndex(stepIndex); + },), + ), + ], + ), + ) + ], ), + const SizedBox(height: 30,), Expanded( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Flexible( - child: CustomStepper(data: provider.stepperData,onChanged: (stepIndex){ - provider.goToSelectedStepperIndex(stepIndex); - },), - ), - ], + child: SingleChildScrollView( + child: Align( + alignment: Alignment.center, + child: Container( + width: 400, + constraints: const BoxConstraints( + maxWidth: 400 + ), + child: Column( + children: [ + buildInfoUI(provider,currentStepIndex), + const SizedBox(height: 20,), + bottomButton(false, provider, currentStepIndex), + const SizedBox(height: 50,), + ], + )), + ), ), - ) + ), ], ), ); } + Widget buildInfoUI(CreateContractViewModel provider, int currentStepIndex){ + if(currentStepIndex != -1){ + switch(currentStepIndex){ + case 0: + return buildGeneralInfo(provider); + case 1: + return buildGSTInfo(provider); + case 2: + return buildStoreDetails(provider); + case 3: + return buildTaxAndAccountInfo(provider); + case 4: + return buildShippingInfo(provider); + } + } + return Container(); + } + + + Widget buildGeneralInfo(CreateContractViewModel provider){ + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Center(child: Text(AppLocalizations.of(context).add_general_information, style: FontStyles.labelMedium.copyWith(fontWeight: FontWeight.bold, color: AppColors.primaryTextColor),)), + const SizedBox(height: 10,), + Center(child: Text(AppLocalizations.of(context).fill_out_the_basic_details_for_this_contract, style: FontStyles.labelSmall.copyWith(color: AppColors.defaultTextColor),)), + const SizedBox(height: 20,), + Text(AppLocalizations.of(context).first_name, style: FontStyles.labelSmall.copyWith(color: AppColors.primaryTextColor),), + const SizedBox(height: 10,), + InputTextField( + formKey: provider.firstNameFormKey, + controller: provider.firstNameController, + errorText: provider.firstNameErrorText, + hintText: '', + labelText: '', + cursorColor: AppColors.sellerTextFieldTextColor, + textColor: AppColors.sellerTextFieldTextColor, + focusedBorderColor: AppColors.sellerTextFieldBorder, + inactiveBorderColor: AppColors.sellerTextFieldBorder, + onTextChange: (value) { + // provider.validateEmail(context, value); + }, + ), + Text(AppLocalizations.of(context).last_name, style: FontStyles.labelSmall.copyWith(color: AppColors.primaryTextColor),), + const SizedBox(height: 10,), + InputTextField( + formKey: provider.lastNameFormKey, + controller: provider.lastNameController, + errorText: provider.lastNameErrorText, + hintText: '', + labelText: '', + cursorColor: AppColors.sellerTextFieldTextColor, + textColor: AppColors.sellerTextFieldTextColor, + focusedBorderColor: AppColors.sellerTextFieldBorder, + inactiveBorderColor: AppColors.sellerTextFieldBorder, + onTextChange: (value) { + // provider.validateEmail(context, value); + }, + ), + Text('${AppLocalizations.of(context).email} (${AppLocalizations.of(context).optional})', style: FontStyles.labelSmall.copyWith(color: AppColors.primaryTextColor),), + const SizedBox(height: 10,), + InputTextField( + formKey: provider.emailFormKey, + controller: provider.emailController, + errorText: provider.emailErrorText, + hintText: '', + labelText: '', + cursorColor: AppColors.sellerTextFieldTextColor, + textColor: AppColors.sellerTextFieldTextColor, + focusedBorderColor: AppColors.sellerTextFieldBorder, + inactiveBorderColor: AppColors.sellerTextFieldBorder, + onTextChange: (value) { + // provider.validateEmail(context, value); + }, + ), + Text(AppLocalizations.of(context).password, style: FontStyles.labelSmall.copyWith(color: AppColors.primaryTextColor),), + const SizedBox(height: 10,), + InputTextField( + formKey: provider.passwordFormKey, + controller: provider.passwordController, + errorText: provider.passwordErrorText, + hintText: '', + labelText: '', + cursorColor: AppColors.sellerTextFieldTextColor, + textColor: AppColors.sellerTextFieldTextColor, + focusedBorderColor: AppColors.sellerTextFieldBorder, + inactiveBorderColor: AppColors.sellerTextFieldBorder, + onTextChange: (value) { + // provider.validateEmail(context, value); + }, + ), + Text(AppLocalizations.of(context).confirm_password, style: FontStyles.labelSmall.copyWith(color: AppColors.primaryTextColor),), + const SizedBox(height: 10,), + InputTextField( + formKey: provider.confirmPasswordFormKey, + controller: provider.confirmPasswordController, + errorText: provider.confirmPasswordErrorText, + hintText: '', + labelText: '', + cursorColor: AppColors.sellerTextFieldTextColor, + textColor: AppColors.sellerTextFieldTextColor, + focusedBorderColor: AppColors.sellerTextFieldBorder, + inactiveBorderColor: AppColors.sellerTextFieldBorder, + onTextChange: (value) { + // provider.validateEmail(context, value); + }, + ), + ], + ); + } + + Widget buildGSTInfo(CreateContractViewModel provider){ + return Column( + children: [], + ); + } + Widget buildStoreDetails(CreateContractViewModel provider){ + return Column( + children: [], + ); + } + Widget buildTaxAndAccountInfo(CreateContractViewModel provider){ + return Column( + children: [], + ); + } + Widget buildShippingInfo(CreateContractViewModel provider){ + return Column( + children: [], + ); + } + + Widget bottomButton(bool isLoading, CreateContractViewModel provider, int currentIndex,){ + return SizedBox( + width: MediaQuery.of(context).size.width, + child: CustomButton( + buttonText: AppLocalizations.of(context).next_button_text, + onPressed: () { + provider.goToNextStep(currentIndex); + }, + isLoading: isLoading, + loadingColor: AppColors.white, + backgroundColor: provider.checkBasicDetails()? AppColors.primaryColor : AppColors.greyButtonBg, + textStyle: FontStyles.labelMedium + .copyWith(color: AppColors.white)), + ); + } + } diff --git a/lib/features/seller/authentication/view_model/create_contract_view_model.dart b/lib/features/seller/authentication/view_model/create_contract_view_model.dart index 2a19856..b0fdbf7 100644 --- a/lib/features/seller/authentication/view_model/create_contract_view_model.dart +++ b/lib/features/seller/authentication/view_model/create_contract_view_model.dart @@ -8,6 +8,21 @@ class CreateContractViewModel extends ChangeNotifier{ final int? steps = 5; final List stepperData = []; + final GlobalKey firstNameFormKey = GlobalKey(); + final GlobalKey lastNameFormKey = GlobalKey(); + final GlobalKey emailFormKey = GlobalKey(); + final GlobalKey passwordFormKey = GlobalKey(); + final GlobalKey confirmPasswordFormKey = GlobalKey(); + TextEditingController firstNameController = TextEditingController(text: ""); + TextEditingController lastNameController = TextEditingController(text: ""); + TextEditingController emailController = TextEditingController(text: ""); + TextEditingController passwordController = TextEditingController(text: ""); + TextEditingController confirmPasswordController = TextEditingController(text: ""); + String? firstNameErrorText; + String? lastNameErrorText; + String? emailErrorText; + String? passwordErrorText; + String? confirmPasswordErrorText; setInitialStepperData(){ stepperData.add(StepperData(headerTitle: 'Personal Information',isCurrentStep: false, stepCompleted: true)); @@ -42,4 +57,12 @@ class CreateContractViewModel extends ChangeNotifier{ } + bool checkBasicDetails(){ + + // TODO: + // logic to check all the fields is yet to be implemented. + return false; + } + + } diff --git a/lib/features/shared/custom_stepper.dart b/lib/features/shared/custom_stepper.dart index aed8e1b..4174cf1 100644 --- a/lib/features/shared/custom_stepper.dart +++ b/lib/features/shared/custom_stepper.dart @@ -40,10 +40,14 @@ class CustomStepper extends StatelessWidget { hoverColor: Colors.transparent, onTap: (){ if(i == 0){ - onChanged?.call(i); + if(data?[i].isCurrentStep == false){ + onChanged?.call(i); + } }else{ if(data?[i -1].stepCompleted ?? false){ - onChanged?.call(i); + if(data?[i].isCurrentStep == false){ + onChanged?.call(i); + } } } }, diff --git a/lib/localization/intl_de.arb b/lib/localization/intl_de.arb index 3125c35..82369a0 100644 --- a/lib/localization/intl_de.arb +++ b/lib/localization/intl_de.arb @@ -47,5 +47,10 @@ "not_receive_otp" : "OTP not received?", "send_again" : "Send again", "resend_otp_in" : "Resend OTP in", - "back_button_text": "Back" + "back_button_text": "Back", + "next_button_text" : "Next", + "add_general_information" : "Add General Information", + "fill_out_the_basic_details_for_this_contract" : "Fill out the basic details for this contract", + "fill_out_the_basic_details_for_this_contract" : "Fill out the basic details for this contract", + "optional" : "optional" } \ No newline at end of file diff --git a/lib/localization/intl_en.arb b/lib/localization/intl_en.arb index 791961c..cc8c484 100644 --- a/lib/localization/intl_en.arb +++ b/lib/localization/intl_en.arb @@ -47,5 +47,10 @@ "not_receive_otp" : "OTP not received?", "send_again" : "Send again", "resend_otp_in" : "Resend OTP in", - "back_button_text": "Back" + "back_button_text": "Back", + "next_button_text" : "Next", + "add_general_information" : "Add General Information", + "fill_out_the_basic_details_for_this_contract" : "Fill out the basic details for this contract", + "fill_out_the_basic_details_for_this_contract" : "Fill out the basic details for this contract", + "optional" : "optional" } \ No newline at end of file From 2b46918e7ba7077877e447111b88044d85d8b2cf Mon Sep 17 00:00:00 2001 From: Muthukaliswaran Date: Tue, 10 Dec 2024 17:08:25 +0530 Subject: [PATCH 4/7] Validations for basic details fields implemented. --- .../widget/shared/input_text_field.dart | 11 ++- .../view/create_contract_screen.dart | 44 +++++++--- .../create_contract_view_model.dart | 84 +++++++++++++++++-- 3 files changed, 119 insertions(+), 20 deletions(-) diff --git a/lib/common/widget/shared/input_text_field.dart b/lib/common/widget/shared/input_text_field.dart index 834a204..5a6cb7a 100644 --- a/lib/common/widget/shared/input_text_field.dart +++ b/lib/common/widget/shared/input_text_field.dart @@ -85,18 +85,23 @@ class _InputTextFieldState extends State { errorText: null, suffixIcon: widget.isObscureText ? InkWell( + splashColor: Colors.transparent, + canRequestFocus: false, + hoverColor: Colors.transparent, + focusColor: Colors.transparent, + highlightColor: Colors.transparent, onTap: () => changeVisiblity(), child: Container( padding: const EdgeInsets.only(right: 20), child: isPasswordVisible - ? const Icon(Icons.visibility) - : const Icon(Icons.visibility_off), + ? Icon(Icons.visibility,color: widget.textColor,) + : Icon(Icons.visibility_off,color: widget.textColor), ), ) : const SizedBox.shrink(), enabledBorder: OutlineInputBorder( borderSide: BorderSide( - color: _isHovered + color: (widget.errorText != null && widget.errorText!.isNotEmpty)? Colors.red : _isHovered ? widget.focusedBorderColor // Hover border color : widget.inactiveBorderColor, // Default border color ), diff --git a/lib/features/seller/authentication/view/create_contract_screen.dart b/lib/features/seller/authentication/view/create_contract_screen.dart index 4dbbe7a..d8a5121 100644 --- a/lib/features/seller/authentication/view/create_contract_screen.dart +++ b/lib/features/seller/authentication/view/create_contract_screen.dart @@ -241,7 +241,7 @@ class _CreateContractScreenState extends State { const SizedBox(height: 10,), Center(child: Text(AppLocalizations.of(context).fill_out_the_basic_details_for_this_contract, style: FontStyles.labelSmall.copyWith(color: AppColors.defaultTextColor),)), const SizedBox(height: 20,), - Text(AppLocalizations.of(context).first_name, style: FontStyles.labelSmall.copyWith(color: AppColors.primaryTextColor),), + Text('${AppLocalizations.of(context).first_name} *', style: FontStyles.labelSmall.copyWith(color: AppColors.primaryTextColor),), const SizedBox(height: 10,), InputTextField( formKey: provider.firstNameFormKey, @@ -254,10 +254,12 @@ class _CreateContractScreenState extends State { focusedBorderColor: AppColors.sellerTextFieldBorder, inactiveBorderColor: AppColors.sellerTextFieldBorder, onTextChange: (value) { - // provider.validateEmail(context, value); + provider.validateName(context, value, true); }, ), - Text(AppLocalizations.of(context).last_name, style: FontStyles.labelSmall.copyWith(color: AppColors.primaryTextColor),), + if(provider.firstNameErrorText?.isNotEmpty ?? false) + const SizedBox(height: 10,), + Text('${AppLocalizations.of(context).last_name} *', style: FontStyles.labelSmall.copyWith(color: AppColors.primaryTextColor),), const SizedBox(height: 10,), InputTextField( formKey: provider.lastNameFormKey, @@ -270,10 +272,12 @@ class _CreateContractScreenState extends State { focusedBorderColor: AppColors.sellerTextFieldBorder, inactiveBorderColor: AppColors.sellerTextFieldBorder, onTextChange: (value) { - // provider.validateEmail(context, value); + provider.validateName(context, value, false); }, ), - Text('${AppLocalizations.of(context).email} (${AppLocalizations.of(context).optional})', style: FontStyles.labelSmall.copyWith(color: AppColors.primaryTextColor),), + if(provider.lastNameErrorText?.isNotEmpty ?? false) + const SizedBox(height: 10,), + Text('${AppLocalizations.of(context).email} *', style: FontStyles.labelSmall.copyWith(color: AppColors.primaryTextColor),), const SizedBox(height: 10,), InputTextField( formKey: provider.emailFormKey, @@ -286,10 +290,12 @@ class _CreateContractScreenState extends State { focusedBorderColor: AppColors.sellerTextFieldBorder, inactiveBorderColor: AppColors.sellerTextFieldBorder, onTextChange: (value) { - // provider.validateEmail(context, value); + provider.validateEmail(context, value); }, ), - Text(AppLocalizations.of(context).password, style: FontStyles.labelSmall.copyWith(color: AppColors.primaryTextColor),), + if(provider.emailErrorText?.isNotEmpty ?? false) + const SizedBox(height: 10,), + Text('${AppLocalizations.of(context).password} *', style: FontStyles.labelSmall.copyWith(color: AppColors.primaryTextColor),), const SizedBox(height: 10,), InputTextField( formKey: provider.passwordFormKey, @@ -297,15 +303,21 @@ class _CreateContractScreenState extends State { errorText: provider.passwordErrorText, hintText: '', labelText: '', + isObscureText: true, cursorColor: AppColors.sellerTextFieldTextColor, textColor: AppColors.sellerTextFieldTextColor, focusedBorderColor: AppColors.sellerTextFieldBorder, inactiveBorderColor: AppColors.sellerTextFieldBorder, onTextChange: (value) { - // provider.validateEmail(context, value); + provider.validatePassword(context, value, true); + if(provider.confirmPasswordController.text.isNotEmpty){ + provider.validatePassword(context, provider.confirmPasswordController.text, false, password: value); + } }, ), - Text(AppLocalizations.of(context).confirm_password, style: FontStyles.labelSmall.copyWith(color: AppColors.primaryTextColor),), + if(provider.passwordErrorText?.isNotEmpty ?? false) + const SizedBox(height: 10,), + Text('${AppLocalizations.of(context).confirm_password} *', style: FontStyles.labelSmall.copyWith(color: AppColors.primaryTextColor),), const SizedBox(height: 10,), InputTextField( formKey: provider.confirmPasswordFormKey, @@ -313,12 +325,13 @@ class _CreateContractScreenState extends State { errorText: provider.confirmPasswordErrorText, hintText: '', labelText: '', + isObscureText: true, cursorColor: AppColors.sellerTextFieldTextColor, textColor: AppColors.sellerTextFieldTextColor, focusedBorderColor: AppColors.sellerTextFieldBorder, inactiveBorderColor: AppColors.sellerTextFieldBorder, onTextChange: (value) { - // provider.validateEmail(context, value); + provider.validatePassword(context, value, false, password: provider.passwordController.text); }, ), ], @@ -352,11 +365,18 @@ class _CreateContractScreenState extends State { child: CustomButton( buttonText: AppLocalizations.of(context).next_button_text, onPressed: () { - provider.goToNextStep(currentIndex); + if(currentIndex == 0){ + if(provider.checkBasicDetails(context)){ + provider.setStepCompleted(currentIndex, true); + provider.goToNextStep(currentIndex); + } + }else if(currentIndex == 1){ + // For index 1 + } }, isLoading: isLoading, loadingColor: AppColors.white, - backgroundColor: provider.checkBasicDetails()? AppColors.primaryColor : AppColors.greyButtonBg, + backgroundColor: provider.checkBasicDetails(context)? AppColors.primaryColor : AppColors.greyButtonBg, textStyle: FontStyles.labelMedium .copyWith(color: AppColors.white)), ); diff --git a/lib/features/seller/authentication/view_model/create_contract_view_model.dart b/lib/features/seller/authentication/view_model/create_contract_view_model.dart index b0fdbf7..ba33101 100644 --- a/lib/features/seller/authentication/view_model/create_contract_view_model.dart +++ b/lib/features/seller/authentication/view_model/create_contract_view_model.dart @@ -2,7 +2,9 @@ import 'package:flutter/material.dart'; +import '../../../../utils/validators/pattern_validator.dart'; import '../../../shared/model/stepper_data.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class CreateContractViewModel extends ChangeNotifier{ @@ -25,8 +27,9 @@ class CreateContractViewModel extends ChangeNotifier{ String? confirmPasswordErrorText; setInitialStepperData(){ - stepperData.add(StepperData(headerTitle: 'Personal Information',isCurrentStep: false, stepCompleted: true)); - stepperData.add(StepperData(headerTitle: 'GST Verification',isCurrentStep: true, stepCompleted: false)); + stepperData.clear(); + stepperData.add(StepperData(headerTitle: 'Personal Information',isCurrentStep: true, stepCompleted: false)); + stepperData.add(StepperData(headerTitle: 'GST Verification',isCurrentStep: false, stepCompleted: false)); stepperData.add(StepperData(headerTitle: 'Store Details',isCurrentStep: false, stepCompleted: false)); stepperData.add(StepperData(headerTitle: 'Tax and Account Information',isCurrentStep: false, stepCompleted: false)); stepperData.add(StepperData(headerTitle: 'Shipping Information',isCurrentStep: false, stepCompleted: false)); @@ -34,6 +37,71 @@ class CreateContractViewModel extends ChangeNotifier{ } + T validateEmail(context, String? value) { + emailErrorText = PatternValidator.isValidEmail( + context, + value, + AppLocalizations.of(context).email_mandatory, + AppLocalizations.of(context).invalid_email); + if(T == dynamic){ + notifyListeners(); + } + return (emailErrorText?.isEmpty?? true) as T; + } + + T validateName(context, String? value, bool isFirstName) { + if (isFirstName) { + firstNameErrorText = PatternValidator.isValidName( + context, + value, + AppLocalizations.of(context).first_name_mandatory, + AppLocalizations.of(context).invalid_first_name); + if(T == dynamic){ + notifyListeners(); + } + return (firstNameErrorText?.isEmpty ?? true) as T; + } else { + lastNameErrorText = PatternValidator.isValidName( + context, + value, + AppLocalizations.of(context).last_name_mandatory, + AppLocalizations.of(context).invalid_last_name); + if(T == dynamic){ + notifyListeners(); + } + return (lastNameErrorText?.isEmpty ?? true) as T; + } + } + + T validatePassword(context, String? value, bool isPassword, + {String password = ""}) { + if (isPassword) { + passwordErrorText = PatternValidator.isValidPassword( + context, + value, + AppLocalizations.of(context).password_mandatory, + AppLocalizations.of(context).invalid_password); + if(T == dynamic){ + notifyListeners(); + } + return (passwordErrorText?.isEmpty ?? true) as T; + } else { + confirmPasswordErrorText = PatternValidator.isValidPassword( + context, + value, + AppLocalizations.of(context).confirm_password_mandatory, + AppLocalizations.of(context).invalid_password, + confirmPassword: password, + misMatchErrorText: + AppLocalizations.of(context).password_confrim_password_match); + if(T == dynamic){ + notifyListeners(); + } + return (confirmPasswordErrorText?.isEmpty ?? true) as T; + } + } + + setStepCompleted(int index, bool isCompleted){ stepperData[index].stepCompleted = isCompleted; } @@ -57,10 +125,16 @@ class CreateContractViewModel extends ChangeNotifier{ } - bool checkBasicDetails(){ + bool checkBasicDetails(context){ + if(emailController.text.isEmpty || firstNameController.text.isEmpty || lastNameController.text.isEmpty || passwordController.text.isEmpty || confirmPasswordController.text.isEmpty){ + return false; + } + + if(validateEmail(context, emailController.text) && validateName(context, firstNameController.text, true) && validateName(context, lastNameController.text, false) && validatePassword(context, passwordController.text, true) && validatePassword(context, confirmPasswordController.text, false, password: passwordController.text)){ + + return true; + } - // TODO: - // logic to check all the fields is yet to be implemented. return false; } From 4c592fe2c7507a3816a983d303e3fd3bc4a9a2b1 Mon Sep 17 00:00:00 2001 From: Muthukaliswaran Date: Thu, 12 Dec 2024 17:26:48 +0530 Subject: [PATCH 5/7] Validations for GST page done. --- .../view/create_contract_screen.dart | 129 ++++++++++++++++-- .../create_contract_view_model.dart | 53 +++++++ lib/features/shared/model/stepper_data.dart | 3 +- lib/localization/intl_de.arb | 14 +- lib/localization/intl_en.arb | 12 +- lib/utils/validators/pattern_validator.dart | 10 ++ 6 files changed, 207 insertions(+), 14 deletions(-) diff --git a/lib/features/seller/authentication/view/create_contract_screen.dart b/lib/features/seller/authentication/view/create_contract_screen.dart index d8a5121..497156e 100644 --- a/lib/features/seller/authentication/view/create_contract_screen.dart +++ b/lib/features/seller/authentication/view/create_contract_screen.dart @@ -339,8 +339,106 @@ class _CreateContractScreenState extends State { } Widget buildGSTInfo(CreateContractViewModel provider){ - return Column( - children: [], + return ValueListenableBuilder( + valueListenable: provider.rxSellGstProducts, + builder: (context, value, child) { + if(value == 1){ + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Center(child: Text(AppLocalizations.of(context).add_gst_information, style: FontStyles.labelMedium.copyWith(fontWeight: FontWeight.bold, color: AppColors.primaryTextColor),)), + const SizedBox(height: 10,), + Center(child: Text(AppLocalizations.of(context).gst_number_mandatory_text, style: FontStyles.labelSmall.copyWith(color: AppColors.defaultTextColor),)), + const SizedBox(height: 20,), + Text('${AppLocalizations.of(context).enter_15_digit_gst_number} *', style: FontStyles.labelSmall.copyWith(color: AppColors.primaryTextColor),), + const SizedBox(height: 10,), + InputTextField( + formKey: provider.gstNoFormKey, + controller: provider.gstNoTextField, + errorText: provider.gstNoErrorText, + hintText: '', + labelText: '', + cursorColor: AppColors.sellerTextFieldTextColor, + textColor: AppColors.sellerTextFieldTextColor, + focusedBorderColor: AppColors.sellerTextFieldBorder, + inactiveBorderColor: AppColors.sellerTextFieldBorder, + onTextChange: (value) { + int cursorPosition = provider.gstNoTextField.selection.base.offset; + provider.gstNoTextField.text = value.toUpperCase(); + if (cursorPosition <= provider.gstNoTextField.text.length) { + provider.gstNoTextField.selection = TextSelection.collapsed(offset: cursorPosition); + } else { + provider.gstNoTextField.selection = TextSelection.collapsed(offset: provider.gstNoTextField.text.length); + } + provider.validGstNumber(context, value); + }, + ), + + + RadioListTile( + hoverColor: Colors.transparent, + dense: true, + activeColor: Colors.transparent, + title: Text(AppLocalizations.of(context).i_sell_only_books), + value: 2, groupValue: provider.rxSellGstProducts.value, onChanged: (value){ + provider.rxSellGstProducts.value = value ?? 0; + if(provider.rxSellGstProducts.value == 1){ + provider.validGstNumber(context, provider.gstNoTextField.text); + }else{ + provider.validPanNumber(context, provider.panNoTextField.text); + } + provider.notifyListener(); + }) + + ], + ); + }else{ + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Center(child: Text(AppLocalizations.of(context).add_pan_card_details, style: FontStyles.labelMedium.copyWith(fontWeight: FontWeight.bold, color: AppColors.primaryTextColor),)), + const SizedBox(height: 10,), + Center(child: Text(AppLocalizations.of(context).pan_number_mandatory_info, style: FontStyles.labelSmall.copyWith(color: AppColors.defaultTextColor),)), + const SizedBox(height: 20,), + Text('${AppLocalizations.of(context).enter_10_digit_pan_number} *', style: FontStyles.labelSmall.copyWith(color: AppColors.primaryTextColor),), + const SizedBox(height: 10,), + InputTextField( + formKey: provider.panNoFormKey, + controller: provider.panNoTextField, + errorText: provider.panNoErrorText, + hintText: '', + labelText: '', + cursorColor: AppColors.sellerTextFieldTextColor, + textColor: AppColors.sellerTextFieldTextColor, + focusedBorderColor: AppColors.sellerTextFieldBorder, + inactiveBorderColor: AppColors.sellerTextFieldBorder, + onTextChange: (value) { + int cursorPosition = provider.panNoTextField.selection.base.offset; + provider.panNoTextField.text = value.toUpperCase(); + if (cursorPosition <= provider.panNoTextField.text.length) { + provider.panNoTextField.selection = TextSelection.collapsed(offset: cursorPosition); + } else { + provider.panNoTextField.selection = TextSelection.collapsed(offset: provider.panNoTextField.text.length); + } + provider.validPanNumber(context, provider.panNoTextField.text); + }, + ), + RadioListTile( + hoverColor: Colors.transparent, + dense: true, + title: Text(AppLocalizations.of(context).i_have_a_gst_number), + value: 1, groupValue: provider.rxSellGstProducts.value, onChanged: (value){ + provider.rxSellGstProducts.value = value ?? 0; + if(provider.rxSellGstProducts.value == 1){ + provider.validGstNumber(context, provider.gstNoTextField.text); + }else{ + provider.validPanNumber(context, provider.panNoTextField.text); + } + }) + ], + ); + } + } ); } Widget buildStoreDetails(CreateContractViewModel provider){ @@ -365,22 +463,33 @@ class _CreateContractScreenState extends State { child: CustomButton( buttonText: AppLocalizations.of(context).next_button_text, onPressed: () { - if(currentIndex == 0){ - if(provider.checkBasicDetails(context)){ - provider.setStepCompleted(currentIndex, true); - provider.goToNextStep(currentIndex); - } - }else if(currentIndex == 1){ - // For index 1 + if(enableDisableButtonBg(currentIndex,provider)){ + provider.setStepCompleted(currentIndex, true); + provider.goToNextStep(currentIndex); } }, isLoading: isLoading, loadingColor: AppColors.white, - backgroundColor: provider.checkBasicDetails(context)? AppColors.primaryColor : AppColors.greyButtonBg, + backgroundColor: enableDisableButtonBg(currentIndex, provider)? AppColors.primaryColor : AppColors.greyButtonBg, textStyle: FontStyles.labelMedium .copyWith(color: AppColors.white)), ); } + bool enableDisableButtonBg(int currentIndex, CreateContractViewModel provider){ + if(currentIndex ==0){ + return provider.checkBasicDetails(context); + }else if (currentIndex == 1){ + if(provider.rxSellGstProducts.value == 1){ + return provider.validGstNumber(context, provider.gstNoTextField.text); + }else{ + return provider.validPanNumber(context, provider.panNoTextField.text); + } + } + + return false; + } + + } diff --git a/lib/features/seller/authentication/view_model/create_contract_view_model.dart b/lib/features/seller/authentication/view_model/create_contract_view_model.dart index ba33101..520d266 100644 --- a/lib/features/seller/authentication/view_model/create_contract_view_model.dart +++ b/lib/features/seller/authentication/view_model/create_contract_view_model.dart @@ -15,16 +15,23 @@ class CreateContractViewModel extends ChangeNotifier{ final GlobalKey emailFormKey = GlobalKey(); final GlobalKey passwordFormKey = GlobalKey(); final GlobalKey confirmPasswordFormKey = GlobalKey(); + final GlobalKey gstNoFormKey = GlobalKey(); + final GlobalKey panNoFormKey = GlobalKey(); TextEditingController firstNameController = TextEditingController(text: ""); TextEditingController lastNameController = TextEditingController(text: ""); TextEditingController emailController = TextEditingController(text: ""); TextEditingController passwordController = TextEditingController(text: ""); TextEditingController confirmPasswordController = TextEditingController(text: ""); + TextEditingController gstNoTextField = TextEditingController(text: ""); + TextEditingController panNoTextField = TextEditingController(text: ""); String? firstNameErrorText; String? lastNameErrorText; String? emailErrorText; String? passwordErrorText; String? confirmPasswordErrorText; + String? gstNoErrorText; + String? panNoErrorText; + ValueNotifier rxSellGstProducts = ValueNotifier(1); setInitialStepperData(){ stepperData.clear(); @@ -101,6 +108,48 @@ class CreateContractViewModel extends ChangeNotifier{ } } + T validGstNumber(context, String value){ + if(T == dynamic){ + if(PatternValidator.isValidGstNumber(context, value)?? false){ + gstNoErrorText = ''; + }else{ + gstNoErrorText = AppLocalizations.of(context).please_enter_a_valid_gst_number; + } + + notifyListeners(); + return (gstNoErrorText?.isEmpty ?? true) as T; + }else{ + String gstNoErrorText = ''; + if(PatternValidator.isValidGstNumber(context, value)?? false){ + gstNoErrorText = ''; + }else{ + gstNoErrorText = AppLocalizations.of(context).please_enter_a_valid_gst_number; + } + return (gstNoErrorText.isEmpty) as T; + } + } + + + T validPanNumber(context, String value){ + if(T == dynamic){ + if(PatternValidator.isValidPanNumber(context, value)?? false){ + panNoErrorText = ''; + }else{ + panNoErrorText = AppLocalizations.of(context).please_enter_a_valid_pan_card_number; + } + notifyListeners(); + return (panNoErrorText?.isEmpty ?? true) as T; + }else{ + String panNoErrorText = ''; + if(PatternValidator.isValidPanNumber(context, value)?? false){ + panNoErrorText = ''; + }else{ + panNoErrorText = AppLocalizations.of(context).please_enter_a_valid_pan_card_number; + } + return (panNoErrorText.isEmpty) as T; + } + } + setStepCompleted(int index, bool isCompleted){ stepperData[index].stepCompleted = isCompleted; @@ -139,4 +188,8 @@ class CreateContractViewModel extends ChangeNotifier{ } + void notifyListener(){ + notifyListeners(); + } + } diff --git a/lib/features/shared/model/stepper_data.dart b/lib/features/shared/model/stepper_data.dart index 035f2fe..a0840a2 100644 --- a/lib/features/shared/model/stepper_data.dart +++ b/lib/features/shared/model/stepper_data.dart @@ -2,7 +2,8 @@ class StepperData{ bool? stepCompleted; String? headerTitle; bool? isCurrentStep; + bool? canEdit; - StepperData({this.stepCompleted, this.headerTitle, this.isCurrentStep}); + StepperData({this.stepCompleted, this.headerTitle, this.isCurrentStep, this.canEdit}); } \ No newline at end of file diff --git a/lib/localization/intl_de.arb b/lib/localization/intl_de.arb index 82369a0..8ac8fae 100644 --- a/lib/localization/intl_de.arb +++ b/lib/localization/intl_de.arb @@ -51,6 +51,16 @@ "next_button_text" : "Next", "add_general_information" : "Add General Information", "fill_out_the_basic_details_for_this_contract" : "Fill out the basic details for this contract", - "fill_out_the_basic_details_for_this_contract" : "Fill out the basic details for this contract", - "optional" : "optional" + "fill_out_the_basic_details_for_this_contract" : "Fill out the basic details for this contract", + "optional" : "optional", +"add_gst_information" : "Add GST Information", +"gst_number_mandatory_text" : "GST Number is mandatory to sell online", +"enter_15_digit_gst_number" : "Enter 15 digit GST number", +"add_pan_card_details" : "Add PAN card details", +"pan_number_mandatory_info" : "Pan details is Mandatory to sell non-GST goods", +"enter_10_digit_pan_number" : "Enter 10 digit PAN card number", +"please_enter_a_valid_gst_number" : "Please enter valid GST number", + "please_enter_a_valid_pan_card_number" : "Please enter a valid PAN card number", + "i_have_a_gst_number" : "I have GST number", + "i_sell_only_books" : "I only sell non-GST categories, like Books etc" } \ No newline at end of file diff --git a/lib/localization/intl_en.arb b/lib/localization/intl_en.arb index cc8c484..e654ed7 100644 --- a/lib/localization/intl_en.arb +++ b/lib/localization/intl_en.arb @@ -52,5 +52,15 @@ "add_general_information" : "Add General Information", "fill_out_the_basic_details_for_this_contract" : "Fill out the basic details for this contract", "fill_out_the_basic_details_for_this_contract" : "Fill out the basic details for this contract", - "optional" : "optional" + "optional" : "optional", + "add_gst_information" : "Add GST Information", + "gst_number_mandatory_text" : "GST Number is mandatory to sell online", + "enter_15_digit_gst_number" : "Enter 15 digit GST number", + "add_pan_card_details" : "Add PAN card details", + "pan_number_mandatory_info" : "Pan details is Mandatory to sell non-GST goods", + "enter_10_digit_pan_number" : "Enter 10 digit PAN card number", + "please_enter_a_valid_gst_number" : "Please enter valid GST number", + "please_enter_a_valid_pan_card_number" : "Please enter a valid PAN card number", + "i_have_a_gst_number" : "I have GST number", + "i_sell_only_books" : "I only sell non-GST categories, like Books etc" } \ No newline at end of file diff --git a/lib/utils/validators/pattern_validator.dart b/lib/utils/validators/pattern_validator.dart index 9696653..5ca37a9 100644 --- a/lib/utils/validators/pattern_validator.dart +++ b/lib/utils/validators/pattern_validator.dart @@ -47,4 +47,14 @@ class PatternValidator { } + static bool? isValidGstNumber(context, String gstNumber){ + final regex = RegExp(r'^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[0-9]{1}[A-Z]{1}[0-9A-Z]{1}$'); + return regex.hasMatch(gstNumber); + } + + static bool? isValidPanNumber(context, String panNumber){ + final regex = RegExp(r'^[A-Z]{5}[0-9]{4}[A-Z]$'); + return regex.hasMatch(panNumber); + } + } \ No newline at end of file From 6ca069d8a7cc8769e69f0f509de71ecdc06a5d36 Mon Sep 17 00:00:00 2001 From: Muthukaliswaran Date: Thu, 12 Dec 2024 17:33:54 +0530 Subject: [PATCH 6/7] Validations for GST page done. --- .../seller/authentication/view/create_contract_screen.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/features/seller/authentication/view/create_contract_screen.dart b/lib/features/seller/authentication/view/create_contract_screen.dart index 497156e..6f58146 100644 --- a/lib/features/seller/authentication/view/create_contract_screen.dart +++ b/lib/features/seller/authentication/view/create_contract_screen.dart @@ -434,6 +434,7 @@ class _CreateContractScreenState extends State { }else{ provider.validPanNumber(context, provider.panNoTextField.text); } + provider.notifyListener(); }) ], ); From 701d80adf7c2766f1efeb0c9fd978ae138050f68 Mon Sep 17 00:00:00 2001 From: Muthukaliswaran Date: Fri, 13 Dec 2024 19:08:07 +0530 Subject: [PATCH 7/7] PAN details update page done. --- asset/images/file-type-pdf2.svg | 1 + asset/images/microsoft-word.svg | 1 + lib/constants/color_constants.dart | 1 + .../view/create_contract_screen.dart | 278 ++++++++++++++++-- .../create_contract_view_model.dart | 80 ++++- .../seller_register_view_model.dart | 2 +- lib/features/shared/dash_painter.dart | 151 ++++++++++ lib/features/shared/dotted_border.dart | 80 +++++ lib/localization/intl_de.arb | 15 +- lib/localization/intl_en.arb | 15 +- lib/route/router_config.dart | 3 +- lib/route/router_constant.dart | 4 + lib/utils/utils.dart | 6 + pubspec.lock | 152 ++++++---- pubspec.yaml | 3 + 15 files changed, 692 insertions(+), 100 deletions(-) create mode 100644 asset/images/file-type-pdf2.svg create mode 100644 asset/images/microsoft-word.svg create mode 100644 lib/features/shared/dash_painter.dart create mode 100644 lib/features/shared/dotted_border.dart diff --git a/asset/images/file-type-pdf2.svg b/asset/images/file-type-pdf2.svg new file mode 100644 index 0000000..e10b671 --- /dev/null +++ b/asset/images/file-type-pdf2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/asset/images/microsoft-word.svg b/asset/images/microsoft-word.svg new file mode 100644 index 0000000..dcfc1c3 --- /dev/null +++ b/asset/images/microsoft-word.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/lib/constants/color_constants.dart b/lib/constants/color_constants.dart index 0acbe86..5055ddf 100644 --- a/lib/constants/color_constants.dart +++ b/lib/constants/color_constants.dart @@ -33,4 +33,5 @@ class AppColors{ static const Color defaultTextColor = Color(0xff788295); static const Color sellerTextFieldBorder = Color(0xffe2e4e9); static const Color sellerTextFieldTextColor = Color(0xff848e9f); + static const Color greenColor = Color(0xff4a9d07); } \ No newline at end of file diff --git a/lib/features/seller/authentication/view/create_contract_screen.dart b/lib/features/seller/authentication/view/create_contract_screen.dart index 6f58146..a22a817 100644 --- a/lib/features/seller/authentication/view/create_contract_screen.dart +++ b/lib/features/seller/authentication/view/create_contract_screen.dart @@ -1,17 +1,28 @@ +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:desktop_drop/desktop_drop.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; import 'package:frontend_ecommerce/common/styles/font_style.dart'; import 'package:frontend_ecommerce/constants/color_constants.dart'; import 'package:frontend_ecommerce/features/shared/custom_stepper.dart'; +import 'package:frontend_ecommerce/features/shared/dotted_border.dart'; import 'package:provider/provider.dart'; import '../../../../common/widget/shared/custom_button.dart'; +import '../../../../common/widget/shared/custom_snackbar.dart'; import '../../../../common/widget/shared/input_text_field.dart'; import '../../../../constants/screen_size_constants.dart'; +import '../../../../utils/utils.dart'; import '../view_model/create_contract_view_model.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class CreateContractScreen extends StatefulWidget { - const CreateContractScreen({super.key}); + const CreateContractScreen({super.key, this.step = '1'}); + + final String? step; @override State createState() => _CreateContractScreenState(); @@ -23,7 +34,7 @@ class _CreateContractScreenState extends State { void initState() { WidgetsBinding.instance.addPostFrameCallback((timeStamp) { final auth = Provider.of(context, listen: false); - auth.setInitialStepperData(); + auth.setInitialStepperData(context,currentStep: int.tryParse(widget.step ?? '1') ?? 1); }); super.initState(); } @@ -374,21 +385,40 @@ class _CreateContractScreenState extends State { }, ), + Row( + children: [ + Radio(hoverColor: Colors.transparent, + value: 2, groupValue: provider.rxSellGstProducts.value, onChanged: (value){ + provider.rxSellGstProducts.value = value ?? 0; + if(provider.rxSellGstProducts.value == 1){ + provider.validGstNumber(context, provider.gstNoTextField.text); + }else{ + provider.validPanNumber(context, provider.panNoTextField.text); + } + provider.notifyListener(); + }), + Flexible(child: InkWell( + hoverColor: Colors.transparent, + highlightColor: Colors.transparent, + focusColor: Colors.transparent, + splashColor: Colors.transparent, + onTap: (){ + if(provider.rxSellGstProducts.value == 1){ + provider.rxSellGstProducts.value = 2; + }else{ + provider.rxSellGstProducts.value = 1; + } - RadioListTile( - hoverColor: Colors.transparent, - dense: true, - activeColor: Colors.transparent, - title: Text(AppLocalizations.of(context).i_sell_only_books), - value: 2, groupValue: provider.rxSellGstProducts.value, onChanged: (value){ - provider.rxSellGstProducts.value = value ?? 0; - if(provider.rxSellGstProducts.value == 1){ - provider.validGstNumber(context, provider.gstNoTextField.text); - }else{ - provider.validPanNumber(context, provider.panNoTextField.text); - } - provider.notifyListener(); - }) + if(provider.rxSellGstProducts.value == 1){ + provider.validGstNumber(context, provider.gstNoTextField.text); + }else{ + provider.validPanNumber(context, provider.panNoTextField.text); + } + provider.notifyListener(); + }, + child: Text(AppLocalizations.of(context).i_sell_only_books))), + ], + ) ], ); @@ -423,25 +453,213 @@ class _CreateContractScreenState extends State { provider.validPanNumber(context, provider.panNoTextField.text); }, ), - RadioListTile( - hoverColor: Colors.transparent, - dense: true, - title: Text(AppLocalizations.of(context).i_have_a_gst_number), - value: 1, groupValue: provider.rxSellGstProducts.value, onChanged: (value){ - provider.rxSellGstProducts.value = value ?? 0; - if(provider.rxSellGstProducts.value == 1){ - provider.validGstNumber(context, provider.gstNoTextField.text); - }else{ - provider.validPanNumber(context, provider.panNoTextField.text); - } - provider.notifyListener(); - }) + if(provider.rxSellGstProducts.value == 2 && provider.validPanNumber(context, provider.panNoTextField.text)) + panDetails(provider), + + Row( + children: [ + Radio(hoverColor: Colors.transparent, + value: 1, groupValue: provider.rxSellGstProducts.value, onChanged: (value){ + provider.rxSellGstProducts.value = value ?? 0; + if(provider.rxSellGstProducts.value == 1){ + provider.validGstNumber(context, provider.gstNoTextField.text); + }else{ + provider.validPanNumber(context, provider.panNoTextField.text); + } + provider.notifyListener(); + }), + Flexible(child: InkWell( + hoverColor: Colors.transparent, + highlightColor: Colors.transparent, + focusColor: Colors.transparent, + splashColor: Colors.transparent, + onTap: (){ + if(provider.rxSellGstProducts.value == 1){ + provider.rxSellGstProducts.value = 2; + }else{ + provider.rxSellGstProducts.value = 1; + } + + if(provider.rxSellGstProducts.value == 1){ + provider.validGstNumber(context, provider.gstNoTextField.text); + }else{ + provider.validPanNumber(context, provider.panNoTextField.text); + } + provider.notifyListener(); + + }, + child: Text(AppLocalizations.of(context).i_have_a_gst_number))), + ], + ) ], ); } } ); } + + + Widget panDetails(CreateContractViewModel provider){ + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('${AppLocalizations.of(context).pan_name} *', style: FontStyles.labelSmall.copyWith(color: AppColors.primaryTextColor),), + const SizedBox(height: 10,), + InputTextField( + formKey: provider.panNameFormKey, + controller: provider.panNameTextField, + errorText: provider.panNameErrorText, + hintText: '', + labelText: '', + cursorColor: AppColors.sellerTextFieldTextColor, + textColor: AppColors.sellerTextFieldTextColor, + focusedBorderColor: AppColors.sellerTextFieldBorder, + inactiveBorderColor: AppColors.sellerTextFieldBorder, + onTextChange: (value) { + int cursorPosition = provider.panNameTextField.selection.base.offset; + provider.panNameTextField.text = value.toUpperCase(); + if (cursorPosition <= provider.panNameTextField.text.length) { + provider.panNameTextField.selection = TextSelection.collapsed(offset: cursorPosition); + } else { + provider.panNameTextField.selection = TextSelection.collapsed(offset: provider.panNameTextField.text.length); + } + provider.validatePanName(context, provider.panNameTextField.text); + }, + ), + if(provider.panNameErrorText?.isNotEmpty ?? false) + const SizedBox(height: 10,), + + Text('${AppLocalizations.of(context).upload_pan_document} *', style: FontStyles.labelSmall.copyWith(color: AppColors.primaryTextColor, fontWeight: FontWeight.bold),), + const SizedBox(height: 10,), + ValueListenableBuilder( + valueListenable: provider.isDragging, + builder: (context,isDragging, child) { + return DottedBorder( + borderType: BorderType.RRect, + dashPattern: const [6, 6], + strokeWidth: 1, + color: isDragging ? AppColors.primaryColor : AppColors.focusedBorder, + child: provider.filePickerResult.count <= 0 ? DropTarget( + onDragEntered: (value){ + provider.isDragging.value =true; + }, + onDragExited: (value){ + provider.isDragging.value =false; + }, + onDragDone: (value) async{ + if(Utils.getFileExtension(value.files[0].name ?? '') != '.png' && Utils.getFileExtension(value.files[0].name ?? '') != '.jpg' && Utils.getFileExtension(value.files[0].name ?? '') != '.pdf' && Utils.getFileExtension(value.files[0].name ?? '') != '.doc'){ + CustomSnackbar( + message: AppLocalizations.of(context).the_selected_file_is_not_allowed, + context: context) + .showSnackbar(); + }else{ + provider.filePickerResult = FilePickerResult([PlatformFile.fromMap({'name' : value.files[0].name, 'size' : await value.files[0].length(), 'path' : value.files[0].path, 'bytes': await value.files[0].readAsBytes()})]); + provider.notifyListener(); + } + }, child: Stack( + children: [ + Row( + children: [ + Flexible( + child: InkWell( + onTap: (){ + FilePicker.platform.pickFiles( + allowMultiple: false, + type: FileType.custom, + allowedExtensions: ['.pdf','.jpg','.png','.doc'] + ).then((value){ + if(value?.xFiles.where((test)=> Utils.getFileExtension(test.name) != '.png' && Utils.getFileExtension(test.name) != '.jpg' && Utils.getFileExtension(test.name) != '.pdf' && Utils.getFileExtension(test.name) != '.doc').isNotEmpty?? false){ + CustomSnackbar( + message: AppLocalizations.of(context).the_selected_file_is_not_allowed, + context: context) + .showSnackbar(); + }else{ + provider.filePickerResult = value ?? const FilePickerResult([]); + provider.notifyListener(); + } + + }); + }, + child: Container( + decoration: const BoxDecoration( + color: AppColors.primaryColor, + ), + padding: const EdgeInsetsDirectional.only(start: 20, end: 20, top: 10, bottom: 10), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.upload, size: 20,), + Flexible(child: Text(AppLocalizations.of(context).upload)) + ], + ), + ), + ), + ), + const SizedBox(width: 10,), + Flexible(child: Text(AppLocalizations.of(context).drag_here_to_upload)) + ], + ), + if(isDragging)Positioned.fill( + child: Container( + decoration: BoxDecoration( + color: isDragging? AppColors.greenColor.withOpacity(0.5) : Colors.transparent + ), + ), + ) + ], + ), + ) : + Row( + children: [ + Expanded(child: Utils.getFileExtension(provider.filePickerResult.files[0].name ?? '') == '.png' || Utils.getFileExtension(provider.filePickerResult.files[0].name ?? '') == '.jpg'? Image.memory(Uint8List.fromList(provider.filePickerResult.files[0].bytes?.toList() ?? []),height: 300,) : + Utils.getFileExtension(provider.filePickerResult.files[0].name ?? '') == '.pdf'? Column( + children: [ + SvgPicture.asset(height: 100,'asset/images/file-type-pdf2.svg'), + const SizedBox(height: 5,), + Text(provider.filePickerResult.files[0].name, style: FontStyles.labelSmall,) + ], + ) : Column( + children: [ + SvgPicture.asset(height: 100,'asset/images/microsoft-word.svg'), + const SizedBox(height: 5,), + Text(provider.filePickerResult.files[0].name, style: FontStyles.labelSmall,) + ], + ) + ) + ], + ) + ); + } + ), + const SizedBox(height: 5,), + InkWell( + onTap: (){ + provider.filePickerResult = const FilePickerResult([]); + provider.notifyListener(); + }, + child: Text( + AppLocalizations.of(context).clear_file, + style: FontStyles.labelMedium.copyWith(color: AppColors.primaryColor), + ), + ), + const SizedBox(height: 10,), + RichText(text: TextSpan( + text: '${AppLocalizations.of(context).allowed_extension}: ', + style: FontStyles.labelMedium.copyWith(fontWeight: FontWeight.bold), + children: const [ + TextSpan( + text: 'pdf, jpg, png, doc', + style: FontStyles.labelSmall, + ) + ] + )), + const SizedBox(height: 20,), + ], + ); + } + + + Widget buildStoreDetails(CreateContractViewModel provider){ return Column( children: [], @@ -485,7 +703,7 @@ class _CreateContractScreenState extends State { if(provider.rxSellGstProducts.value == 1){ return provider.validGstNumber(context, provider.gstNoTextField.text); }else{ - return provider.validPanNumber(context, provider.panNoTextField.text); + return provider.validPanNumber(context, provider.panNoTextField.text) && provider.validatePanName(context, provider.panNameTextField.text, canShowError: false) && provider.validatePanDocument(context); } } diff --git a/lib/features/seller/authentication/view_model/create_contract_view_model.dart b/lib/features/seller/authentication/view_model/create_contract_view_model.dart index 520d266..b7517d0 100644 --- a/lib/features/seller/authentication/view_model/create_contract_view_model.dart +++ b/lib/features/seller/authentication/view_model/create_contract_view_model.dart @@ -1,10 +1,13 @@ +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; +import 'package:frontend_ecommerce/route/router_constant.dart'; import '../../../../utils/validators/pattern_validator.dart'; import '../../../shared/model/stepper_data.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'dart:html' as html; class CreateContractViewModel extends ChangeNotifier{ @@ -17,6 +20,7 @@ class CreateContractViewModel extends ChangeNotifier{ final GlobalKey confirmPasswordFormKey = GlobalKey(); final GlobalKey gstNoFormKey = GlobalKey(); final GlobalKey panNoFormKey = GlobalKey(); + final GlobalKey panNameFormKey = GlobalKey(); TextEditingController firstNameController = TextEditingController(text: ""); TextEditingController lastNameController = TextEditingController(text: ""); TextEditingController emailController = TextEditingController(text: ""); @@ -24,6 +28,7 @@ class CreateContractViewModel extends ChangeNotifier{ TextEditingController confirmPasswordController = TextEditingController(text: ""); TextEditingController gstNoTextField = TextEditingController(text: ""); TextEditingController panNoTextField = TextEditingController(text: ""); + TextEditingController panNameTextField = TextEditingController(text: ""); String? firstNameErrorText; String? lastNameErrorText; String? emailErrorText; @@ -31,15 +36,36 @@ class CreateContractViewModel extends ChangeNotifier{ String? confirmPasswordErrorText; String? gstNoErrorText; String? panNoErrorText; + String? panNameErrorText; ValueNotifier rxSellGstProducts = ValueNotifier(1); + FilePickerResult filePickerResult = const FilePickerResult([]); + ValueNotifier isDragging = ValueNotifier(false); - setInitialStepperData(){ + setInitialStepperData(context, {int currentStep = -1}){ stepperData.clear(); - stepperData.add(StepperData(headerTitle: 'Personal Information',isCurrentStep: true, stepCompleted: false)); - stepperData.add(StepperData(headerTitle: 'GST Verification',isCurrentStep: false, stepCompleted: false)); - stepperData.add(StepperData(headerTitle: 'Store Details',isCurrentStep: false, stepCompleted: false)); - stepperData.add(StepperData(headerTitle: 'Tax and Account Information',isCurrentStep: false, stepCompleted: false)); - stepperData.add(StepperData(headerTitle: 'Shipping Information',isCurrentStep: false, stepCompleted: false)); + stepperData.add(StepperData(headerTitle: AppLocalizations.of(context).personal_information,isCurrentStep: false, stepCompleted: false)); + stepperData.add(StepperData(headerTitle: AppLocalizations.of(context).gst_verification,isCurrentStep: false, stepCompleted: false)); + stepperData.add(StepperData(headerTitle: AppLocalizations.of(context).store_details,isCurrentStep: false, stepCompleted: false)); + stepperData.add(StepperData(headerTitle: AppLocalizations.of(context).tax_and_account_information,isCurrentStep: false, stepCompleted: false)); + stepperData.add(StepperData(headerTitle: AppLocalizations.of(context).shipping_information,isCurrentStep: false, stepCompleted: false)); + + + if(currentStep > stepperData.length){ + /// Todo: check from the api and set the current step and also how many steps completed + stepperData[0].isCurrentStep =true; + }else{ + for(int i = 0; i <= currentStep; i++){ + if(i + 1 == currentStep){ + stepperData[i].isCurrentStep = true; + } + + // Todo: To implement the steps completed from api. + // For now i have implemented like, if current step is 3 then all the previous steps by default set as completed. + if(i < (currentStep -1)){ + stepperData[i].stepCompleted = true; + } + } + } notifyListeners(); } @@ -151,25 +177,59 @@ class CreateContractViewModel extends ChangeNotifier{ } + bool validatePanName(context, String value, {bool canShowError = true}){ + if(value.isNotEmpty){ + panNameErrorText = ''; + if(canShowError){ + notifyListeners(); + } + return true; + } + if(canShowError){ + panNameErrorText = AppLocalizations.of(context).please_enter_pan_name; + notifyListeners(); + } + return false; + } + + bool validatePanDocument(context){ + if(filePickerResult.count > 0){ + return true; + } + return false; + } + + setStepCompleted(int index, bool isCompleted){ stepperData[index].stepCompleted = isCompleted; } goToNextStep(int currentIndex){ - for(int i = 0; i< stepperData.length; i++){ - stepperData[i].isCurrentStep = false; - } + if(currentIndex < (stepperData.length - 1)){ + for(int i = 0; i< stepperData.length; i++){ + stepperData[i].isCurrentStep = false; + } stepperData[currentIndex + 1].isCurrentStep = true; + updateStepInUrl(currentIndex+2); + notifyListeners(); + }else{ + /// Todo: Go to next page after full completion of create contract; } - notifyListeners(); } + + void updateStepInUrl(int step){ + html.window.history.pushState({}, '', '/#${AppPages.auth}${AppPages.sellerCreateContract}?step=$step'); + } + + goToSelectedStepperIndex(int index){ for(int i = 0; i< stepperData.length; i++){ stepperData[i].isCurrentStep = false; } stepperData[index].isCurrentStep = true; + updateStepInUrl(index + 1); notifyListeners(); } diff --git a/lib/features/seller/authentication/view_model/seller_register_view_model.dart b/lib/features/seller/authentication/view_model/seller_register_view_model.dart index 0f261f6..caac63a 100644 --- a/lib/features/seller/authentication/view_model/seller_register_view_model.dart +++ b/lib/features/seller/authentication/view_model/seller_register_view_model.dart @@ -280,7 +280,7 @@ class SellerRegisterViewModel extends ChangeNotifier{ verifyOtpLoading = false; notifyListeners(); if(onValue.sellerVerifyOTPRegisterModel?.status == 200 ){ - context.go(AppPages.auth+AppPages.sellerCreateContract); + context.goNamed(NamedRoute.sellerContractCreate,queryParameters: {'step' : '1'}); }else{ CustomSnackbar( message: onValue.errorResponseModel!.message ?? "", diff --git a/lib/features/shared/dash_painter.dart b/lib/features/shared/dash_painter.dart new file mode 100644 index 0000000..39ce093 --- /dev/null +++ b/lib/features/shared/dash_painter.dart @@ -0,0 +1,151 @@ +part of 'dotted_border.dart'; + +typedef PathBuilder = Path Function(Size); + +class _DashPainter extends CustomPainter { + final double strokeWidth; + final List dashPattern; + final Color color; + final BorderType borderType; + final Radius radius; + final StrokeCap strokeCap; + final PathBuilder? customPath; + final EdgeInsets padding; + + _DashPainter({ + this.strokeWidth = 2, + this.dashPattern = const [3, 1], + this.color = Colors.black, + this.borderType = BorderType.Rect, + this.radius = const Radius.circular(0), + this.strokeCap = StrokeCap.butt, + this.customPath, + this.padding = EdgeInsets.zero, + }) { + assert(dashPattern.isNotEmpty, 'Dash Pattern cannot be empty'); + } + + @override + void paint(Canvas canvas, Size originalSize) { + final Size size; + if (padding == EdgeInsets.zero) { + size = originalSize; + } else { + canvas.translate(padding.left, padding.top); + size = Size( + originalSize.width - padding.horizontal, + originalSize.height - padding.vertical, + ); + } + + Paint paint = Paint() + ..strokeWidth = strokeWidth + ..color = color + ..strokeCap = strokeCap + ..style = PaintingStyle.stroke; + + Path _path; + if (customPath != null) { + _path = dashPath( + customPath!(size), + dashArray: CircularIntervalList(dashPattern), + ); + } else { + _path = _getPath(size); + } + + canvas.drawPath(_path, paint); + } + + /// Returns a [Path] based on the the [borderType] parameter + Path _getPath(Size size) { + Path path; + switch (borderType) { + case BorderType.Circle: + path = _getCirclePath(size); + break; + case BorderType.RRect: + path = _getRRectPath(size, radius); + break; + case BorderType.Rect: + path = _getRectPath(size); + break; + case BorderType.Oval: + path = _getOvalPath(size); + break; + } + + return dashPath(path, dashArray: CircularIntervalList(dashPattern)); + } + + /// Returns a circular path of [size] + Path _getCirclePath(Size size) { + double w = size.width; + double h = size.height; + double s = size.shortestSide; + + return Path() + ..addRRect( + RRect.fromRectAndRadius( + Rect.fromLTWH( + w > s ? (w - s) / 2 : 0, + h > s ? (h - s) / 2 : 0, + s, + s, + ), + Radius.circular(s / 2), + ), + ); + } + + /// Returns a Rounded Rectangular Path with [radius] of [size] + Path _getRRectPath(Size size, Radius radius) { + return Path() + ..addRRect( + RRect.fromRectAndRadius( + Rect.fromLTWH( + 0, + 0, + size.width, + size.height, + ), + radius, + ), + ); + } + + /// Returns a path of [size] + Path _getRectPath(Size size) { + return Path() + ..addRect( + Rect.fromLTWH( + 0, + 0, + size.width, + size.height, + ), + ); + } + + /// Return an oval path of [size] + Path _getOvalPath(Size size) { + return Path() + ..addOval( + Rect.fromLTWH( + 0, + 0, + size.width, + size.height, + ), + ); + } + + @override + bool shouldRepaint(_DashPainter oldDelegate) { + return oldDelegate.strokeWidth != this.strokeWidth || + oldDelegate.color != this.color || + oldDelegate.dashPattern != this.dashPattern || + oldDelegate.padding != this.padding || + oldDelegate.borderType != this.borderType; + } +} diff --git a/lib/features/shared/dotted_border.dart b/lib/features/shared/dotted_border.dart new file mode 100644 index 0000000..fdc3732 --- /dev/null +++ b/lib/features/shared/dotted_border.dart @@ -0,0 +1,80 @@ +library dotted_border; + +import 'package:flutter/material.dart'; +import 'package:path_drawing/path_drawing.dart'; + +part 'dash_painter.dart'; + +/// Add a dotted border around any [child] widget. The [strokeWidth] property +/// defines the width of the dashed border and [color] determines the stroke +/// paint color. [CircularIntervalList] is populated with the [dashPattern] to +/// render the appropriate pattern. The [radius] property is taken into account +/// only if the [borderType] is [BorderType.RRect]. A [customPath] can be passed in +/// as a parameter if you want to draw a custom shaped border. +class DottedBorder extends StatelessWidget { + final Widget child; + final EdgeInsets padding; + final EdgeInsets borderPadding; + final double strokeWidth; + final Color color; + final List dashPattern; + final BorderType borderType; + final Radius radius; + final StrokeCap strokeCap; + final PathBuilder? customPath; + + DottedBorder({ + required this.child, + this.color = Colors.black, + this.strokeWidth = 1, + this.borderType = BorderType.Rect, + this.dashPattern = const [3, 1], + this.padding = const EdgeInsets.all(2), + this.borderPadding = EdgeInsets.zero, + this.radius = const Radius.circular(0), + this.strokeCap = StrokeCap.butt, + this.customPath, + }) { + assert(_isValidDashPattern(dashPattern), 'Invalid dash pattern'); + } + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Positioned.fill( + child: CustomPaint( + painter: _DashPainter( + padding: borderPadding, + strokeWidth: strokeWidth, + radius: radius, + color: color, + borderType: borderType, + dashPattern: dashPattern, + customPath: customPath, + strokeCap: strokeCap, + ), + ), + ), + Padding( + padding: padding, + child: child, + ), + ], + ); + } + + /// Compute if [dashPattern] is valid. The following conditions need to be met + /// * Cannot be null or empty + /// * If [dashPattern] has only 1 element, it cannot be 0 + bool _isValidDashPattern(List? dashPattern) { + Set? _dashSet = dashPattern?.toSet(); + if (_dashSet == null) return false; + if (_dashSet.length == 1 && _dashSet.elementAt(0) == 0.0) return false; + if (_dashSet.length == 0) return false; + return true; + } +} + +/// The different supported BorderTypes +enum BorderType { Circle, RRect, Rect, Oval } diff --git a/lib/localization/intl_de.arb b/lib/localization/intl_de.arb index 8ac8fae..d6e8959 100644 --- a/lib/localization/intl_de.arb +++ b/lib/localization/intl_de.arb @@ -62,5 +62,18 @@ "please_enter_a_valid_gst_number" : "Please enter valid GST number", "please_enter_a_valid_pan_card_number" : "Please enter a valid PAN card number", "i_have_a_gst_number" : "I have GST number", - "i_sell_only_books" : "I only sell non-GST categories, like Books etc" + "i_sell_only_books" : "I only sell non-GST categories, like Books etc", + "pan_name" : "PAN name", + "upload_pan_document" : "Upload PAN document", + "upload" : "Upload", + "drag_here_to_upload" : "Drag file here to upload", + "the_selected_file_is_not_allowed" : "The selected file is not allowed", + "clear_file" : "Clear file", + "allowed_extension" : "Allowed Extensions", + "please_enter_pan_name" : "Please enter PAN name", + "personal_information" : "Personal Information", + "gst_verification" : "GST Verification", + "store_details" : "Store Details", + "tax_and_account_information" : "Tax and Account Information", + "shipping_information" : "Shipping Information" } \ No newline at end of file diff --git a/lib/localization/intl_en.arb b/lib/localization/intl_en.arb index e654ed7..6fbf2e1 100644 --- a/lib/localization/intl_en.arb +++ b/lib/localization/intl_en.arb @@ -62,5 +62,18 @@ "please_enter_a_valid_gst_number" : "Please enter valid GST number", "please_enter_a_valid_pan_card_number" : "Please enter a valid PAN card number", "i_have_a_gst_number" : "I have GST number", - "i_sell_only_books" : "I only sell non-GST categories, like Books etc" + "i_sell_only_books" : "I only sell non-GST categories, like Books etc", + "pan_name" : "PAN name", + "upload_pan_document" : "Upload PAN document", + "upload" : "Upload", + "drag_here_to_upload" : "Drag file here to upload", + "the_selected_file_is_not_allowed" : "The selected file is not allowed", + "clear_file" : "Clear file", + "allowed_extension" : "Allowed Extensions", + "please_enter_pan_name" : "Please enter PAN name", + "personal_information" : "Personal Information", + "gst_verification" : "GST Verification", + "store_details" : "Store Details", + "tax_and_account_information" : "Tax and Account Information", + "shipping_information" : "Shipping Information" } \ No newline at end of file diff --git a/lib/route/router_config.dart b/lib/route/router_config.dart index 2d80af7..76ea230 100644 --- a/lib/route/router_config.dart +++ b/lib/route/router_config.dart @@ -43,8 +43,9 @@ class AppRouter { ), GoRoute( path: AppPages.auth+AppPages.sellerCreateContract, + name: NamedRoute.sellerContractCreate, builder: (BuildContext context, GoRouterState state) { - return CreateContractScreen(); + return CreateContractScreen(step: state.uri.queryParameters['step']); } ), ], diff --git a/lib/route/router_constant.dart b/lib/route/router_constant.dart index 1ec9ecc..8002d8b 100644 --- a/lib/route/router_constant.dart +++ b/lib/route/router_constant.dart @@ -9,4 +9,8 @@ class AppPages{ static const String buyerLogin = '/buyer/login'; static const String buyerDashboard = '/buyer/dashboard'; static const String sellerCreateContract = '/seller/creating-contract'; +} + +class NamedRoute { + static const String sellerContractCreate = 'seller-contract-create'; } \ No newline at end of file diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 5a3b45f..78ca85c 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -1,3 +1,4 @@ +import 'package:path/path.dart' as p; class Utils{ static List getDialCodes(){ @@ -203,4 +204,9 @@ class Utils{ return dialCodes; } + + + static String getFileExtension(String path, [int level = 1]) => + p.extension(path, level); + } \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index cc17d24..796d27f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: "5534e701a2c505fed1f0799e652dd6ae23bd4d2c4cf797220e5ced5764a7c1c2" + sha256: eae3133cbb06de9205899b822e3897fc6a8bc278ad4c944b4ce612689369694b url: "https://pub.dev" source: hosted - version: "1.3.44" + version: "1.3.47" args: dependency: transitive description: name: args - sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.6.0" async: dependency: transitive description: @@ -65,6 +65,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" + url: "https://pub.dev" + source: hosted + version: "0.3.4+2" cupertino_icons: dependency: "direct main" description: @@ -73,6 +81,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" + desktop_drop: + dependency: "direct main" + description: + name: desktop_drop + sha256: "03abf1c0443afdd1d65cf8fa589a2f01c67a11da56bbb06f6ea1de79d5628e94" + url: "https://pub.dev" + source: hosted + version: "0.5.0" dio: dependency: "direct main" description: @@ -105,54 +121,62 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.3" + file_picker: + dependency: "direct main" + description: + name: file_picker + sha256: "89500471922dd3a89ab0d6e13ab4a2268c25474bff4ca7c628f55c76e0ced1de" + url: "https://pub.dev" + source: hosted + version: "8.1.5" firebase_auth: dependency: "direct main" description: name: firebase_auth - sha256: d453acec0d958ba0e25d41a9901b32cb77d1535766903dea7a61b2788c304596 + sha256: "03483af6e67b7c4b696ca9386989a6cd5593569e1ac5af6907ea5f7fd9c16d8b" url: "https://pub.dev" source: hosted - version: "5.3.1" + version: "5.3.4" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface - sha256: "78966c2ef774f5bf2a8381a307222867e9ece3509110500f7a138c115926aa65" + sha256: "3e1409f48c48930635705b1237ebbdee8c54c19106a0a4fb321dbb4b642820c4" url: "https://pub.dev" source: hosted - version: "7.4.7" + version: "7.4.10" firebase_auth_web: dependency: transitive description: name: firebase_auth_web - sha256: "77ad3b252badedd3f08dfa21a4c7fe244be96c6da3a4067f253b13ea5d32424c" + sha256: d83fe95c44d73c9c29b006ac7df3aa5e1b8ce92b62edc44e8f86250951fe2cd0 url: "https://pub.dev" source: hosted - version: "5.13.2" + version: "5.13.5" firebase_core: dependency: "direct main" description: name: firebase_core - sha256: "51dfe2fbf3a984787a2e7b8592f2f05c986bfedd6fdacea3f9e0a7beb334de96" + sha256: fef81a53ba1ca618def1f8bef4361df07968434e62cb204c1fb90bb880a03da2 url: "https://pub.dev" source: hosted - version: "3.6.0" + version: "3.8.1" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface - sha256: e30da58198a6d4b49d5bce4e852f985c32cb10db329ebef9473db2b9f09ce810 + sha256: b94b217e3ad745e784960603d33d99471621ecca151c99c670869b76e50ad2a6 url: "https://pub.dev" source: hosted - version: "5.3.0" + version: "5.3.1" firebase_core_web: dependency: transitive description: name: firebase_core_web - sha256: f967a7138f5d2ffb1ce15950e2a382924239eaa521150a8f144af34e68b3b3e5 + sha256: "9e69806bb3d905aeec3c1242e0e1475de6ea6d48f456af29d598fb229a2b4e5e" url: "https://pub.dev" source: hosted - version: "2.18.1" + version: "2.18.2" flutter: dependency: "direct main" description: flutter @@ -171,6 +195,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: "9b78450b89f059e96c9ebb355fa6b3df1d6b330436e0b885fb49594c41721398" + url: "https://pub.dev" + source: hosted + version: "2.0.23" flutter_screenutil: dependency: "direct main" description: @@ -231,10 +263,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2" + sha256: "54900a1a1243f3c4a5506d853a2b5c2dbc38d5f27e52a52618a8054401431123" url: "https://pub.dev" source: hosted - version: "2.0.10+1" + version: "2.0.16" flutter_test: dependency: "direct dev" description: flutter @@ -249,42 +281,42 @@ packages: dependency: "direct main" description: name: go_router - sha256: "2ddb88e9ad56ae15ee144ed10e33886777eb5ca2509a914850a5faa7b52ff459" + sha256: "2fd11229f59e23e967b0775df8d5948a519cd7e1e8b6e849729e010587b46539" url: "https://pub.dev" source: hosted - version: "14.2.7" + version: "14.6.2" google_identity_services_web: dependency: transitive description: name: google_identity_services_web - sha256: "5be191523702ba8d7a01ca97c17fca096822ccf246b0a9f11923a6ded06199b6" + sha256: "55580f436822d64c8ff9a77e37d61f5fb1e6c7ec9d632a43ee324e2a05c3c6c9" url: "https://pub.dev" source: hosted - version: "0.3.1+4" + version: "0.3.3" google_sign_in: dependency: "direct main" description: name: google_sign_in - sha256: "0b8787cb9c1a68ad398e8010e8c8766bfa33556d2ab97c439fb4137756d7308f" + sha256: fad6ddc80c427b0bba705f2116204ce1173e09cf299f85e053d57a55e5b2dd56 url: "https://pub.dev" source: hosted - version: "6.2.1" + version: "6.2.2" google_sign_in_android: dependency: transitive description: name: google_sign_in_android - sha256: "0608de03fc541ece4f91ba3e01a68b17cce7a6cf42bd59e40bbe5c55cc3a49d8" + sha256: "3b96f9b6cf61915f73cbe1218a192623e296a9b8b31965702503649477761e36" url: "https://pub.dev" source: hosted - version: "6.1.30" + version: "6.1.34" google_sign_in_ios: dependency: transitive description: name: google_sign_in_ios - sha256: "4898410f55440049e1ba8f15411612d9f89299d89c61cd9baf7e02d56ff81ac7" + sha256: "83f015169102df1ab2905cf8abd8934e28f87db9ace7a5fa676998842fed228a" url: "https://pub.dev" source: hosted - version: "5.7.7" + version: "5.7.8" google_sign_in_platform_interface: dependency: transitive description: @@ -297,10 +329,10 @@ packages: dependency: transitive description: name: google_sign_in_web - sha256: "042805a21127a85b0dc46bba98a37926f17d2439720e8a459d27045d8ef68055" + sha256: ada595df6c30cead48e66b1f3a050edf0c5cf2ba60c185d69690e08adcc6281b url: "https://pub.dev" source: hosted - version: "0.12.4+2" + version: "0.12.4+3" http: dependency: transitive description: @@ -369,18 +401,18 @@ packages: dependency: "direct main" description: name: logger - sha256: "697d067c60c20999686a0add96cf6aba723b3aa1f83ecf806a8097231529ec32" + sha256: be4b23575aac7ebf01f225a241eb7f6b5641eeaf43c6a8613510fc2f8cf187d1 url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.5.0" logging: dependency: transitive description: name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" matcher: dependency: transitive description: @@ -421,38 +453,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" + path_drawing: + dependency: "direct main" + description: + name: path_drawing + sha256: bbb1934c0cbb03091af082a6389ca2080345291ef07a5fa6d6e078ba8682f977 + url: "https://pub.dev" + source: hosted + version: "1.0.1" path_parsing: dependency: transitive description: name: path_parsing - sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.1.0" path_provider: dependency: transitive description: name: path_provider - sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.5" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: c464428172cb986b758c6d1724c603097febb8fb855aa265aeecc9280c294d4a + sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2" url: "https://pub.dev" source: hosted - version: "2.2.12" + version: "2.2.15" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.1" path_provider_linux: dependency: transitive description: @@ -489,10 +529,10 @@ packages: dependency: transitive description: name: platform - sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" url: "https://pub.dev" source: hosted - version: "3.1.5" + version: "3.1.6" plugin_platform_interface: dependency: transitive description: @@ -566,34 +606,34 @@ packages: dependency: transitive description: name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" vector_graphics: dependency: transitive description: name: vector_graphics - sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3" + sha256: "27d5fefe86fb9aace4a9f8375b56b3c292b64d8c04510df230f849850d912cb7" url: "https://pub.dev" source: hosted - version: "1.1.11+1" + version: "1.1.15" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da + sha256: "2430b973a4ca3c4dbc9999b62b8c719a160100dcbae5c819bae0cacce32c9cdb" url: "https://pub.dev" source: hosted - version: "1.1.11+1" + version: "1.1.12" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81" + sha256: "1b4b9e706a10294258727674a340ae0d6e64a7231980f9f9a3d12e4b42407aad" url: "https://pub.dev" source: hosted - version: "1.1.11+1" + version: "1.1.16" vector_math: dependency: transitive description: @@ -614,18 +654,18 @@ packages: dependency: transitive description: name: web - sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062 + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.0" win32: dependency: transitive description: name: win32 - sha256: "4d45dc9069dba4619dc0ebd93c7cec5e66d8482cb625a370ac806dcc8165f2ec" + sha256: "8b338d4486ab3fbc0ba0db9f9b4f5239b6697fcee427939a40e720cbb9ee0a69" url: "https://pub.dev" source: hosted - version: "5.5.5" + version: "5.9.0" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 909ca2d..82a1c47 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -49,6 +49,9 @@ dependencies: firebase_core: ^3.6.0 flutter_secure_storage: ^9.2.2 carousel_slider: ^5.0.0 + file_picker: ^8.1.5 + path_drawing: ^1.0.1 + desktop_drop: ^0.5.0