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/common/widget/shared/input_text_field.dart b/lib/common/widget/shared/input_text_field.dart
index 131314e..5a6cb7a 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,
@@ -75,20 +85,25 @@ 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
- ? AppColors.focusedBorder // Hover border color
- : AppColors.inActiveBorder, // Default border color
+ color: (widget.errorText != null && widget.errorText!.isNotEmpty)? Colors.red : _isHovered
+ ? widget.focusedBorderColor // Hover border color
+ : widget.inactiveBorderColor, // Default border color
),
),
focusedBorder: OutlineInputBorder(
@@ -96,12 +111,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 0783794..5055ddf 100644
--- a/lib/constants/color_constants.dart
+++ b/lib/constants/color_constants.dart
@@ -28,4 +28,10 @@ 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);
+ static const Color defaultIconColor = Color(0xff788295);
+ 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/data/config/dio_client.dart b/lib/data/config/dio_client.dart
index 0fd660c..8a4fb38 100644
--- a/lib/data/config/dio_client.dart
+++ b/lib/data/config/dio_client.dart
@@ -3,6 +3,7 @@ import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:frontend_ecommerce/config/endpoints_config.dart';
+import 'package:frontend_ecommerce/data/secured_storage/secured_storage.dart';
class ApiService {
final Dio _dio = Dio();
@@ -15,9 +16,9 @@ class ApiService {
// Add interceptors
_dio.interceptors.add(
InterceptorsWrapper(
- onRequest: (options, handler) {
+ onRequest: (options, handler) async{
// Modify the request (e.g., add headers or tokens)
- options.headers['Authorization'] = 'Bearer your_token';
+ options.headers['authorization'] = await getAuthorizationHeader();
return handler.next(options); // Continue with the request
},
onResponse: (response, handler) {
@@ -81,4 +82,19 @@ class ApiService {
rethrow;
}
}
+
+
+
+ Future getAuthorizationHeader() async {
+ final String accessToken = 'Bearer ${await _getToken()}';
+ return accessToken;
+ }
+
+
+ Future _getToken() async{
+ final SecureStorage secureStorage = SecureStorage();
+ return await secureStorage.getAccessToken();
+ }
+
+
}
diff --git a/lib/data/data_source/seller/seller_api_endpoints.dart b/lib/data/data_source/seller/seller_api_endpoints.dart
index ae25f23..2f08738 100644
--- a/lib/data/data_source/seller/seller_api_endpoints.dart
+++ b/lib/data/data_source/seller/seller_api_endpoints.dart
@@ -6,4 +6,6 @@ class SellerApiEndpoints {
static const String sellerVerifyOTP = "${EndpointsConfig.backendUrl}seller/verify-otp";
static const String sellerRegisterSocial = "${EndpointsConfig.backendUrl}seller/register-social";
static const String sellerLogin = "${EndpointsConfig.backendUrl}seller/login";
+ static const String sellerCreateContract = "${EndpointsConfig.backendUrl}seller/creating-contract";
+ static const String getCreateContractDetails = "${EndpointsConfig.backendUrl}seller/get-contract-details";
}
\ No newline at end of file
diff --git a/lib/data/data_source/seller/seller_data_source.dart b/lib/data/data_source/seller/seller_data_source.dart
index 0865985..ffe212a 100644
--- a/lib/data/data_source/seller/seller_data_source.dart
+++ b/lib/data/data_source/seller/seller_data_source.dart
@@ -1,6 +1,8 @@
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:frontend_ecommerce/data/data_source/seller/seller_api_endpoints.dart';
+import 'package:frontend_ecommerce/features/seller/authentication/model/seller_create_contract_request_model.dart';
+import 'package:frontend_ecommerce/features/seller/authentication/model/seller_create_contract_response_model.dart';
import 'package:frontend_ecommerce/features/seller/authentication/model/seller_otp_verify_request_model.dart';
import 'package:frontend_ecommerce/features/seller/authentication/model/seller_otp_verify_response_model.dart';
@@ -16,6 +18,8 @@ abstract class BuyerDataSource {
Future? sellerRegister(BuildContext context, SellerRegisterRequestModel data);
Future? generateOtp(BuildContext context, SellerOTPRequestModel data);
Future? verifyOtp(BuildContext context, SellerOTPVerifyRequestModel data);
+ Future? createContract(BuildContext context, SellerCreateContractRequestModel data);
+ Future? getContractDetails(BuildContext context);
}
class SellerDataSourceImpl implements BuyerDataSource{
@@ -82,4 +86,48 @@ class SellerDataSourceImpl implements BuyerDataSource{
}
+ @override
+ Future? createContract(BuildContext context, SellerCreateContractRequestModel data) async{
+ try{
+ final apiService = ApiService(context);
+ ErrorResponseModel? errorResponseModel;
+ SellerCreateContractModel? sellerCreateContractModel;
+
+ Response response = await apiService.post(SellerApiEndpoints.sellerCreateContract, data.toJson());
+ if(response.statusCode == 200){
+ sellerCreateContractModel = SellerCreateContractModel.fromJson(response.data);
+ } else{
+ errorResponseModel = ErrorResponseModel.fromJson(response.data);
+ }
+
+ return SellerCreateContractResponseModel(sellerCreateContractModel: sellerCreateContractModel, errorResponseModel: errorResponseModel);
+ }catch(e){
+ rethrow;
+ }
+
+ }
+
+
+ @override
+ Future? getContractDetails(BuildContext context) async{
+ try{
+ final apiService = ApiService(context);
+ ErrorResponseModel? errorResponseModel;
+ SellerCreateContractModel? sellerCreateContractModel;
+
+ Response response = await apiService.get(SellerApiEndpoints.getCreateContractDetails);
+ if(response.statusCode == 200){
+ sellerCreateContractModel = SellerCreateContractModel.fromJson(response.data);
+ } else{
+ errorResponseModel = ErrorResponseModel.fromJson(response.data);
+ }
+
+ return SellerCreateContractResponseModel(sellerCreateContractModel: sellerCreateContractModel, errorResponseModel: errorResponseModel);
+ }catch(e){
+ rethrow;
+ }
+
+ }
+
+
}
\ No newline at end of file
diff --git a/lib/features/seller/authentication/model/seller_create_contract_request_model.dart b/lib/features/seller/authentication/model/seller_create_contract_request_model.dart
new file mode 100644
index 0000000..d3c90bf
--- /dev/null
+++ b/lib/features/seller/authentication/model/seller_create_contract_request_model.dart
@@ -0,0 +1,80 @@
+class SellerCreateContractRequestModel {
+ final String? firstName;
+ final String? lastName;
+ final String? email;
+ final String? password;
+ final String? confirmPassword;
+ final int? stepIndex;
+ final String? panName;
+ final String? panNumber;
+ final String? gstNumber;
+
+ SellerCreateContractRequestModel({
+ this.firstName,
+ this.lastName,
+ this.email,
+ this.password,
+ this.confirmPassword,
+ this.stepIndex,
+ this.panName,
+ this.panNumber,
+ this.gstNumber
+ });
+
+ SellerCreateContractRequestModel copyWith({
+ String? firstName,
+ String? lastName,
+ String? email,
+ String? password,
+ String? confirmPassword,
+ int? stepIndex,
+ String? panName,
+ String? panNumber,
+ String? gstNumber,
+ }) {
+ return SellerCreateContractRequestModel(
+ firstName: firstName ?? this.firstName,
+ lastName: lastName ?? this.lastName,
+ email: email ?? this.email,
+ password: password ?? this.password,
+ confirmPassword: confirmPassword ?? this.confirmPassword,
+ stepIndex: stepIndex ?? this.stepIndex,
+ gstNumber: gstNumber ?? this.gstNumber,
+ panName: panName ?? this.panName,
+ panNumber: panNumber ?? this.panNumber
+ );
+ }
+
+ factory SellerCreateContractRequestModel.fromJson(Map json){
+ return SellerCreateContractRequestModel(
+ firstName: json["firstName"],
+ lastName: json["lastName"],
+ email: json["email"],
+ password: json["password"],
+ confirmPassword: json["confirm_password"],
+ stepIndex: json["stepIndex"],
+ gstNumber: json["gstNumber"],
+ panNumber: json["panNumber"],
+ panName: json["panName"]
+ );
+ }
+
+ Map toJson() {
+ return {
+ "firstName": firstName,
+ "lastName": lastName,
+ "email": email,
+ "password": password,
+ "confirm_password": confirmPassword,
+ "stepIndex": stepIndex,
+ "panNumber": panNumber,
+ "panName": panName,
+ "gstNumber": gstNumber
+ };
+ }
+
+ @override
+ String toString(){
+ return "$firstName $lastName $email $password $confirmPassword $stepIndex";
+ }
+}
\ No newline at end of file
diff --git a/lib/features/seller/authentication/model/seller_create_contract_response_model.dart b/lib/features/seller/authentication/model/seller_create_contract_response_model.dart
new file mode 100644
index 0000000..e0e3209
--- /dev/null
+++ b/lib/features/seller/authentication/model/seller_create_contract_response_model.dart
@@ -0,0 +1,51 @@
+import 'package:frontend_ecommerce/features/seller/authentication/model/seller_register_model.dart';
+
+import '../../../shared/model/error_response.dart';
+
+class SellerCreateContractModel {
+ SellerCreateContractModel({
+ required this.status,
+ required this.data
+ });
+
+ final int? status;
+ final Data? data;
+
+ SellerCreateContractModel copyWith({
+ int? status,
+ Data? data,
+ String? token
+ }) {
+ return SellerCreateContractModel(
+ status: status ?? this.status,
+ data: data ?? this.data
+ );
+ }
+
+ factory SellerCreateContractModel.fromJson(Map json){
+ return SellerCreateContractModel(
+ status: json["status"],
+ data: json["result"] == null ? null : Data.fromJson(json["result"]),
+ );
+ }
+
+ Map toJson() => {
+ "status": status,
+ "result": data?.toJson(),
+ };
+
+ @override
+ String toString(){
+ return "$status, $data";
+ }
+}
+
+class SellerCreateContractResponseModel{
+ final SellerCreateContractModel? sellerCreateContractModel;
+ final ErrorResponseModel? errorResponseModel;
+
+ SellerCreateContractResponseModel({
+ required this.sellerCreateContractModel,
+ required this.errorResponseModel
+ });
+}
\ No newline at end of file
diff --git a/lib/features/seller/authentication/model/seller_otp_verify_response_model.dart b/lib/features/seller/authentication/model/seller_otp_verify_response_model.dart
index 546cc48..1e391a0 100644
--- a/lib/features/seller/authentication/model/seller_otp_verify_response_model.dart
+++ b/lib/features/seller/authentication/model/seller_otp_verify_response_model.dart
@@ -6,31 +6,37 @@ class SellerVerifyOTPRegisterModel {
SellerVerifyOTPRegisterModel({
required this.status,
required this.data,
+ required this.token
});
final int? status;
final Data? data;
+ final String? token;
SellerVerifyOTPRegisterModel copyWith({
int? status,
- Data? data
+ Data? data,
+ String? token
}) {
return SellerVerifyOTPRegisterModel(
status: status ?? this.status,
- data: data ?? this.data
+ data: data ?? this.data,
+ token: token ?? this.token
);
}
factory SellerVerifyOTPRegisterModel.fromJson(Map json){
return SellerVerifyOTPRegisterModel(
status: json["status"],
- data: json["result"] == null ? null : Data.fromJson(json["result"])
+ data: json["result"] == null ? null : Data.fromJson(json["result"]),
+ token: json["token"]
);
}
Map toJson() => {
"status": status,
- "result": data?.toJson()
+ "result": data?.toJson(),
+ "token" : token
};
@override
diff --git a/lib/features/seller/authentication/model/seller_register_model.dart b/lib/features/seller/authentication/model/seller_register_model.dart
index 7674037..66178e9 100644
--- a/lib/features/seller/authentication/model/seller_register_model.dart
+++ b/lib/features/seller/authentication/model/seller_register_model.dart
@@ -62,6 +62,7 @@ class Data {
required this.shippingMethod,
required this.shippingCharge,
required this.authToken,
+ required this.stepIndex
});
final int? id;
@@ -74,13 +75,14 @@ class Data {
final DateTime? createdAt;
final String? phoneNumber;
final bool? isOtpVerified;
- final bool? gstNumber;
+ final String? gstNumber;
final bool? gstOtpVerified;
final String? storeName;
final Pickup? pickUp;
final String? shippingMethod;
final String? shippingCharge;
final String? authToken;
+ final int? stepIndex;
Data copyWith({
int? id,
@@ -93,13 +95,14 @@ class Data {
DateTime? createdAt,
String? phoneNumber,
bool? isOtpVerified,
- bool? gstNumber,
+ String? gstNumber,
bool? gstOtpVerified,
String? storeName,
Pickup? pickUp,
String? shippingMethod,
String? shippingCharge,
String? authToken,
+ int? stepIndex
}) {
return Data(
id: id ?? this.id,
@@ -119,6 +122,7 @@ class Data {
shippingMethod: shippingMethod ?? this.shippingMethod,
shippingCharge: shippingCharge ?? this.shippingCharge,
authToken: authToken ?? this.authToken,
+ stepIndex : stepIndex ?? this.stepIndex
);
}
@@ -141,6 +145,7 @@ class Data {
shippingMethod: json["shippingMethod"],
shippingCharge: json["shippingCharge"],
authToken: json["authToken"],
+ stepIndex: json["stepIndex"]
);
}
@@ -162,11 +167,12 @@ class Data {
"shippingMethod": shippingMethod,
"shippingCharge": shippingCharge,
"authToken": authToken,
+ "stepIndex" : stepIndex
};
@override
String toString() {
- return "$id, $firstName, $lastName, $email, $password, $confirmPassword, $updatedAt, $createdAt, $phoneNumber, $isOtpVerified, $gstNumber, $gstOtpVerified, $storeName, ${pickUp?.toString()}, $shippingMethod, $shippingCharge, $authToken";
+ return "$id, $firstName, $lastName, $email, $password, $confirmPassword, $updatedAt, $createdAt, $phoneNumber, $isOtpVerified, $gstNumber, $gstOtpVerified, $storeName, ${pickUp?.toString()}, $shippingMethod, $shippingCharge, $authToken, $stepIndex";
}
}
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..f978be7
--- /dev/null
+++ b/lib/features/seller/authentication/view/create_contract_screen.dart
@@ -0,0 +1,747 @@
+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/details_footer_section.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, this.step = '1'});
+
+ final String? step;
+
+ @override
+ State createState() => _CreateContractScreenState();
+}
+
+class _CreateContractScreenState extends State {
+
+ @override
+ void initState() {
+ WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
+ final auth = Provider.of(context, listen: false);
+ auth.setInitialStepperData(context,currentStep: int.tryParse(widget.step ?? '1') ?? 1);
+ });
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Material(
+ color: AppColors.white,
+ child: Scaffold(
+ backgroundColor: AppColors.white,
+ appBar: AppBar(
+ backgroundColor: AppColors.inActiveBorder,
+ surfaceTintColor: AppColors.inActiveBorder,
+ title: InkWell(
+ onTap: (){},
+ focusColor: Colors.transparent,
+ hoverColor: Colors.transparent,
+ highlightColor: Colors.transparent,
+ splashColor: Colors.transparent,
+ child: Padding(
+ padding: const EdgeInsetsDirectional.only(start: 20),
+ child: Text('ChoiceUs INC',style: FontStyles.labelLarge.copyWith(fontWeight: FontWeight.bold,color: AppColors.primaryColor),),
+ ),
+ ),
+ titleSpacing: 0,
+ centerTitle: false,
+ actions: [
+ InkWell(
+ onTap: (){
+ },
+ focusColor: Colors.transparent,
+ hoverColor: Colors.transparent,
+ highlightColor: Colors.transparent,
+ splashColor: Colors.transparent,
+ child: Padding(
+ padding: const EdgeInsetsDirectional.only(end: 20),
+ child: Text('Settings',style: FontStyles.labelMedium.copyWith(fontWeight: FontWeight.bold),),
+ ),
+ )
+ ],
+ ),
+ body: 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);
+ }
+ }
+ );}
+ ),
+ bottomNavigationBar: const DetailsFooterSection(),
+ ));
+ }
+
+ 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: Column(
+ children: [
+ 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(height: 30,),
+ Expanded(
+ 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: Column(
+ children: [
+ 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(height: 30,),
+ Expanded(
+ 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: Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ Expanded(
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Flexible(
+ child: CustomStepper(data: provider.stepperData,onChanged: (stepIndex){
+ provider.goToSelectedStepperIndex(stepIndex);
+ },),
+ ),
+ ],
+ ),
+ )
+ ],
+ ),
+ const SizedBox(height: 30,),
+ Expanded(
+ 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.validateName(context, value, true);
+ },
+ ),
+ 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,
+ controller: provider.lastNameController,
+ errorText: provider.lastNameErrorText,
+ hintText: '',
+ labelText: '',
+ cursorColor: AppColors.sellerTextFieldTextColor,
+ textColor: AppColors.sellerTextFieldTextColor,
+ focusedBorderColor: AppColors.sellerTextFieldBorder,
+ inactiveBorderColor: AppColors.sellerTextFieldBorder,
+ onTextChange: (value) {
+ provider.validateName(context, value, false);
+ },
+ ),
+ 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,
+ 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);
+ },
+ ),
+ 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,
+ controller: provider.passwordController,
+ errorText: provider.passwordErrorText,
+ hintText: '',
+ labelText: '',
+ isObscureText: true,
+ cursorColor: AppColors.sellerTextFieldTextColor,
+ textColor: AppColors.sellerTextFieldTextColor,
+ focusedBorderColor: AppColors.sellerTextFieldBorder,
+ inactiveBorderColor: AppColors.sellerTextFieldBorder,
+ onTextChange: (value) {
+ provider.validatePassword(context, value, true);
+ if(provider.confirmPasswordController.text.isNotEmpty){
+ provider.validatePassword(context, provider.confirmPasswordController.text, false, password: value);
+ }
+ },
+ ),
+ 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,
+ controller: provider.confirmPasswordController,
+ errorText: provider.confirmPasswordErrorText,
+ hintText: '',
+ labelText: '',
+ isObscureText: true,
+ cursorColor: AppColors.sellerTextFieldTextColor,
+ textColor: AppColors.sellerTextFieldTextColor,
+ focusedBorderColor: AppColors.sellerTextFieldBorder,
+ inactiveBorderColor: AppColors.sellerTextFieldBorder,
+ onTextChange: (value) {
+ provider.validatePassword(context, value, false, password: provider.passwordController.text);
+ },
+ ),
+ ],
+ );
+ }
+
+ Widget buildGSTInfo(CreateContractViewModel provider){
+ 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, provider.gstNoTextField.text);
+ },
+ ),
+
+ 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;
+ }
+
+ 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))),
+ ],
+ )
+
+ ],
+ );
+ }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);
+ },
+ ),
+ 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: [],
+ );
+ }
+ 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: ValueListenableBuilder(valueListenable: provider.isBottomButtonLoading, builder: (context, value, child){
+ return CustomButton(
+ buttonText: AppLocalizations.of(context).next_button_text,
+ onPressed: () {
+ if(enableDisableButtonBg(currentIndex,provider)){
+ provider.updateDetails(context,currentIndex);
+ }
+ },
+ isLoading: value,
+ loadingColor: AppColors.white,
+ 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) && provider.validatePanName(context, provider.panNameTextField.text, canShowError: false) && provider.validatePanDocument(context);
+ }
+ }
+
+ 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
new file mode 100644
index 0000000..fbde551
--- /dev/null
+++ b/lib/features/seller/authentication/view_model/create_contract_view_model.dart
@@ -0,0 +1,347 @@
+
+
+import 'package:dio/dio.dart';
+import 'package:file_picker/file_picker.dart';
+import 'package:flutter/material.dart';
+import 'package:frontend_ecommerce/features/seller/authentication/model/seller_create_contract_request_model.dart';
+import 'package:frontend_ecommerce/route/router_constant.dart';
+
+import '../../../../common/widget/shared/custom_snackbar.dart';
+import '../../../../data/data_source/seller/seller_data_source.dart';
+import '../../../../utils/utils.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{
+ final SellerDataSourceImpl sellerDataSource = SellerDataSourceImpl();
+ 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();
+ final GlobalKey gstNoFormKey = GlobalKey();
+ final GlobalKey panNoFormKey = GlobalKey();
+ final GlobalKey panNameFormKey = 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: "");
+ TextEditingController panNameTextField = TextEditingController(text: "");
+ String? firstNameErrorText;
+ String? lastNameErrorText;
+ String? emailErrorText;
+ String? passwordErrorText;
+ String? confirmPasswordErrorText;
+ String? gstNoErrorText;
+ String? panNoErrorText;
+ String? panNameErrorText;
+ ValueNotifier rxSellGstProducts = ValueNotifier(1);
+ FilePickerResult filePickerResult = const FilePickerResult([]);
+ ValueNotifier isDragging = ValueNotifier(false);
+ ValueNotifier isBottomButtonLoading = ValueNotifier(false);
+
+ setInitialStepperData(context, {int currentStep = -1}){
+ stepperData.clear();
+ 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));
+
+
+
+ getContractDetails(context);
+ }
+
+
+ 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;
+ }
+ }
+
+ 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;
+ }
+ }
+
+
+ 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){
+
+ 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;
+ }
+ }
+
+
+ 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();
+ }
+
+
+ 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;
+ }
+
+ return false;
+ }
+
+
+ void notifyListener(){
+ notifyListeners();
+ }
+
+
+ void getContractDetails(BuildContext context) async{
+ try{
+
+ await sellerDataSource
+ .getContractDetails(context)
+ ?.then((onValue) async {
+ isBottomButtonLoading.value = false;
+ if (onValue.sellerCreateContractModel != null) {
+ if (onValue.sellerCreateContractModel?.status == 200) {
+ int currentStep = onValue.sellerCreateContractModel?.data?.stepIndex ?? 1;
+ updateStepInUrl(currentStep);
+ for(int i = 0; i <= currentStep; i++){
+ if(i + 1 == currentStep){
+ stepperData[i].isCurrentStep = true;
+ }
+
+ if(i < (currentStep -1)){
+ stepperData[i].stepCompleted = true;
+ }
+ }
+ notifyListeners();
+ } else {
+ CustomSnackbar(
+ message: onValue.errorResponseModel!.message ?? "",
+ context: context)
+ .showSnackbar();
+ }
+ } else {
+ CustomSnackbar(
+ message: onValue.errorResponseModel!.message ?? "",
+ context: context)
+ .showSnackbar();
+ }
+ });
+
+
+ }on DioException catch(e){
+ Utils.showApiExceptionError(e, context);
+ }
+ }
+
+
+
+
+
+
+ void updateDetails(BuildContext context, int currentIndex) async {
+ try {
+ SellerCreateContractRequestModel sellerCreateContractRequestModel =
+ SellerCreateContractRequestModel();
+
+ if (currentIndex == 0) {
+ sellerCreateContractRequestModel = SellerCreateContractRequestModel(
+ firstName: firstNameController.text,
+ lastName: lastNameController.text,
+ email: emailController.text,
+ password: passwordController.text,
+ confirmPassword: confirmPasswordController.text,
+ stepIndex: currentIndex + 2);
+ } else if (currentIndex == 1) {
+ if (rxSellGstProducts.value == 1) {
+ sellerCreateContractRequestModel = SellerCreateContractRequestModel(
+ stepIndex: currentIndex + 2, gstNumber: gstNoTextField.text);
+ } else {
+ sellerCreateContractRequestModel = SellerCreateContractRequestModel(
+ stepIndex: currentIndex + 2,
+ panNumber: panNoTextField.text,
+ panName: panNameTextField.text);
+ }
+ } else if (currentIndex == 2) {
+ } else if (currentIndex == 3) {
+ } else if (currentIndex == 4) {}
+
+ isBottomButtonLoading.value = true;
+ await sellerDataSource
+ .createContract(context, sellerCreateContractRequestModel)
+ ?.then((onValue) async {
+ isBottomButtonLoading.value = false;
+ if (onValue.sellerCreateContractModel != null) {
+ if (onValue.sellerCreateContractModel?.status == 200) {
+ setStepCompleted(currentIndex, true);
+ goToNextStep(currentIndex);
+ } else {
+ CustomSnackbar(
+ message: onValue.errorResponseModel!.message ?? "",
+ context: context)
+ .showSnackbar();
+ }
+ } else {
+ CustomSnackbar(
+ message: onValue.errorResponseModel!.message ?? "",
+ context: context)
+ .showSnackbar();
+ }
+ });
+ } on DioException catch (e) {
+ isBottomButtonLoading.value = false;
+ Utils.showApiExceptionError(e, context);
+ }
+ }
+}
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..9fdf8fd 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
@@ -3,12 +3,14 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:frontend_ecommerce/features/seller/authentication/model/seller_otp_request_model.dart';
import 'package:frontend_ecommerce/features/seller/authentication/model/seller_otp_verify_request_model.dart';
+import 'package:frontend_ecommerce/features/seller/authentication/model/seller_otp_verify_response_model.dart';
import 'package:go_router/go_router.dart';
import '../../../../common/widget/shared/custom_snackbar.dart';
import '../../../../data/data_source/seller/seller_data_source.dart';
import '../../../../data/secured_storage/secured_storage.dart';
import '../../../../route/router_constant.dart';
+import '../../../../utils/utils.dart';
import '../../../../utils/validators/pattern_validator.dart';
import '../model/seller_register_model.dart';
import '../model/seller_register_request_model.dart';
@@ -210,7 +212,7 @@ class SellerRegisterViewModel extends ChangeNotifier{
}on DioException catch (e){
registerLoading = false;
notifyListeners();
- showApiExceptionError(e, context);
+ Utils.showApiExceptionError(e, context);
}
} else {
if(phoneNumber.isNotEmpty){
@@ -262,12 +264,12 @@ class SellerRegisterViewModel extends ChangeNotifier{
}on DioException catch (e) {
registerLoading = false;
notifyListeners();
- showApiExceptionError(e, context);
+ Utils.showApiExceptionError(e, context);
}
}
- verifyOtp(context) async{
+ verifyOtp(BuildContext context) async{
if(otp.length >= 6){
verifyOtpLoading = true;
notifyListeners();
@@ -280,11 +282,8 @@ class SellerRegisterViewModel extends ChangeNotifier{
verifyOtpLoading = false;
notifyListeners();
if(onValue.sellerVerifyOTPRegisterModel?.status == 200 ){
- // Move to Home page
- CustomSnackbar(
- message: 'OTP Verified',
- context: context)
- .showSnackbar();
+ setStorageValues(onValue);
+ context.goNamed(NamedRoute.sellerContractCreate,queryParameters: {'step' : '1'});
}else{
CustomSnackbar(
message: onValue.errorResponseModel!.message ?? "",
@@ -303,7 +302,7 @@ class SellerRegisterViewModel extends ChangeNotifier{
}on DioException catch (e) {
verifyOtpLoading = false;
notifyListeners();
- showApiExceptionError(e, context);
+ Utils.showApiExceptionError(e, context);
}
}else{
CustomSnackbar(
@@ -314,11 +313,11 @@ class SellerRegisterViewModel extends ChangeNotifier{
}
- void setStorageValues(SellerRegisterModel? registerData) async{
- _secureStorage.setUserEmail(registerData?.data?.email ?? '');
- _secureStorage.setUserFirstName(registerData?.data?.firstName ?? '');
- _secureStorage.setUserLastName(registerData?.data?.lastName ?? '');
- _secureStorage.setAccessToken(registerData?.accessToken ?? '');
+ void setStorageValues(SellerVerifyOTPResponseModel? registerData) async{
+ _secureStorage.setUserEmail(registerData?.sellerVerifyOTPRegisterModel?.data?.email ?? '');
+ _secureStorage.setUserFirstName(registerData?.sellerVerifyOTPRegisterModel?.data?.firstName ?? '');
+ _secureStorage.setUserLastName(registerData?.sellerVerifyOTPRegisterModel?.data?.lastName ?? '');
+ _secureStorage.setAccessToken(registerData?.sellerVerifyOTPRegisterModel?.token ?? '');
}
void toggleOtpScreen(bool? openVerifyOtpScreen){
@@ -327,12 +326,5 @@ class SellerRegisterViewModel extends ChangeNotifier{
}
- void showApiExceptionError(dynamic error, BuildContext context){
- CustomSnackbar(
- message: error.response?.data != null ? (error.response?.data is Map && (error.response?.data as Map).containsKey('message') ? error.response?.data['message'] : 'Something went wrong') : 'Something went wrong',
- context: context)
- .showSnackbar();
- }
-
}
\ No newline at end of file
diff --git a/lib/features/shared/custom_stepper.dart b/lib/features/shared/custom_stepper.dart
new file mode 100644
index 0000000..bbb48bf
--- /dev/null
+++ b/lib/features/shared/custom_stepper.dart
@@ -0,0 +1,110 @@
+import 'package:flutter/material.dart';
+import 'package:frontend_ecommerce/common/styles/font_style.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: (){
+ if(data?[i].stepCompleted == null || data?[i].stepCompleted == false){
+ if(i == 0){
+ if(data?[i].isCurrentStep == false){
+ onChanged?.call(i);
+ }
+ }else{
+ if(data?[i -1].stepCompleted ?? false){
+ if(data?[i].isCurrentStep == false){
+ onChanged?.call(i);
+ }
+ }
+ }
+ }
+ },
+ child: Column(children: [
+ Padding(
+ padding: const EdgeInsetsDirectional.only(start: 20,end: 20),
+ 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(
+ 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/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/details_footer_section.dart b/lib/features/shared/details_footer_section.dart
new file mode 100644
index 0000000..2bba81d
--- /dev/null
+++ b/lib/features/shared/details_footer_section.dart
@@ -0,0 +1,76 @@
+import 'package:flutter/material.dart';
+import 'package:frontend_ecommerce/common/styles/font_style.dart';
+
+import '../../constants/color_constants.dart';
+import 'package:flutter_gen/gen_l10n/app_localizations.dart';
+
+class DetailsFooterSection extends StatefulWidget {
+ const DetailsFooterSection({super.key});
+
+ @override
+ State createState() => _DetailsFooterSectionState();
+}
+
+class _DetailsFooterSectionState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsetsDirectional.only(start: 25,end: 25),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const SizedBox(height: 10,),
+ const Divider(
+ height: 1,
+ color: AppColors.inActiveBorder,
+ ),
+ const SizedBox(height: 10,),
+ Row(
+ children: [
+ const Icon(Icons.lock),
+ const SizedBox(width: 5,),
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsetsDirectional.only(top: 3),
+ child: Text(AppLocalizations.of(context).save_information_policy_msg,style: FontStyles.labelMedium,),
+ ),
+ ),
+ ],
+ ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Padding(
+ padding: const EdgeInsetsDirectional.only(bottom: 0),
+ child: InkWell(
+ onTap: (){},
+ focusColor: Colors.transparent,
+ hoverColor: Colors.transparent,
+ highlightColor: Colors.transparent,
+ splashColor: Colors.transparent,
+ child: Text(AppLocalizations.of(context).contact_seller_support, style: FontStyles.labelMedium.copyWith(color: AppColors.primaryColor),)),
+ ),
+ Column(
+ children: [
+ Text(AppLocalizations.of(context).copy_right_text,style: FontStyles.labelSmall,),
+ const SizedBox(height: 5,),
+ InkWell(
+ onTap: (){},
+ child: Container(
+ decoration: const BoxDecoration(
+ color: AppColors.primaryColor,
+ borderRadius: BorderRadius.only(topLeft: Radius.circular(10), topRight: Radius.circular(10))
+ ),
+ padding: const EdgeInsetsDirectional.only(top: 5, bottom: 5, start: 20, end: 20),
+ child: Text(AppLocalizations.of(context).feedback_capital, style: FontStyles.labelSmall.copyWith(color: AppColors.white),)),
+ ),
+ ],
+ )
+ ],
+ )
+ ],
+ ),
+ );
+ }
+}
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/features/shared/model/stepper_data.dart b/lib/features/shared/model/stepper_data.dart
new file mode 100644
index 0000000..a0840a2
--- /dev/null
+++ b/lib/features/shared/model/stepper_data.dart
@@ -0,0 +1,9 @@
+class StepperData{
+ bool? stepCompleted;
+ String? headerTitle;
+ bool? isCurrentStep;
+ bool? canEdit;
+
+ 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 3125c35..18e7387 100644
--- a/lib/localization/intl_de.arb
+++ b/lib/localization/intl_de.arb
@@ -47,5 +47,37 @@
"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",
+"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",
+ "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",
+ "save_information_policy_msg" : "This information will be securely saved as per ChoiceUS''s Terms of Service and Privacy Policy",
+ "contact_seller_support" : "Contact Seller Support",
+ "copy_right_text" : "Ⓒ 1999 - 2023 ChoiceUS.com , Inc. or its affiliates",
+ "feedback_capital" : "FEEDBACK"
}
\ No newline at end of file
diff --git a/lib/localization/intl_en.arb b/lib/localization/intl_en.arb
index 791961c..ba2ad54 100644
--- a/lib/localization/intl_en.arb
+++ b/lib/localization/intl_en.arb
@@ -47,5 +47,37 @@
"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",
+ "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",
+ "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",
+ "save_information_policy_msg" : "This information will be securely saved as per ChoiceUS''s Terms of Service and Privacy Policy",
+ "contact_seller_support" : "Contact Seller Support",
+ "copy_right_text" : "Ⓒ 1999 - 2023 ChoiceUS.com , Inc. or its affiliates",
+ "feedback_capital" : "FEEDBACK"
}
\ 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..76ea230 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,13 @@ class AppRouter {
return SellerRegister();
}
),
+ GoRoute(
+ path: AppPages.auth+AppPages.sellerCreateContract,
+ name: NamedRoute.sellerContractCreate,
+ builder: (BuildContext context, GoRouterState state) {
+ return CreateContractScreen(step: state.uri.queryParameters['step']);
+ }
+ ),
],
);
}
diff --git a/lib/route/router_constant.dart b/lib/route/router_constant.dart
index 01dcfea..8002d8b 100644
--- a/lib/route/router_constant.dart
+++ b/lib/route/router_constant.dart
@@ -8,4 +8,9 @@ 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';
+}
+
+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..4c7face 100644
--- a/lib/utils/utils.dart
+++ b/lib/utils/utils.dart
@@ -1,3 +1,7 @@
+import 'package:flutter/material.dart';
+import 'package:path/path.dart' as p;
+
+import '../common/widget/shared/custom_snackbar.dart';
class Utils{
static List getDialCodes(){
@@ -203,4 +207,17 @@ class Utils{
return dialCodes;
}
+
+
+ static String getFileExtension(String path, [int level = 1]) =>
+ p.extension(path, level);
+
+ static showApiExceptionError(dynamic error, BuildContext context){
+ CustomSnackbar(
+ message: error.response?.data != null ? (error.response?.data is Map && (error.response?.data as Map).containsKey('message') ? error.response?.data['message'] : 'Something went wrong') : 'Something went wrong',
+ context: context)
+ .showSnackbar();
+ }
+
+
}
\ 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
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