Commit 97403192 authored by Sai Srinivas's avatar Sai Srinivas Committed by Sai Srinivas
Browse files

Icon inserted and few changes

parent 5a62c920
class allJobCardListResponse {
String? error;
List<JobCardList>? jobCardList;
String? message;
allJobCardListResponse({this.error, this.jobCardList, this.message});
allJobCardListResponse.fromJson(Map<String, dynamic> json) {
error = json['error'];
if (json['job_card_list'] != null) {
jobCardList = <JobCardList>[];
json['job_card_list'].forEach((v) {
jobCardList!.add(new JobCardList.fromJson(v));
});
}
message = json['message'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['error'] = this.error;
if (this.jobCardList != null) {
data['job_card_list'] = this.jobCardList!.map((v) => v.toJson()).toList();
}
data['message'] = this.message;
return data;
}
}
class JobCardList {
String? id;
String? complaintId;
String? extraDescription;
String? date;
String? totalPrice;
JobCardList(
{this.id,
this.complaintId,
this.extraDescription,
this.date,
this.totalPrice});
JobCardList.fromJson(Map<String, dynamic> json) {
id = json['id'];
complaintId = json['complaint_id'];
extraDescription = json['extra_description'];
date = json['date'];
totalPrice = json['total_price'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['complaint_id'] = this.complaintId;
data['extra_description'] = this.extraDescription;
data['date'] = this.date;
data['total_price'] = this.totalPrice;
return data;
}
}
class allServiceListResponse {
String? error;
TechDetails? techDetails;
List<AllServiceList>? allServiceList;
String? message;
allServiceListResponse(
{this.error, this.techDetails, this.allServiceList, this.message});
allServiceListResponse.fromJson(Map<String, dynamic> json) {
error = json['error'];
techDetails = json['tech_details'] != null
? new TechDetails.fromJson(json['tech_details'])
: null;
if (json['all_service_list'] != null) {
allServiceList = <AllServiceList>[];
json['all_service_list'].forEach((v) {
allServiceList!.add(new AllServiceList.fromJson(v));
});
}
message = json['message'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['error'] = this.error;
if (this.techDetails != null) {
data['tech_details'] = this.techDetails!.toJson();
}
if (this.allServiceList != null) {
data['all_service_list'] =
this.allServiceList!.map((v) => v.toJson()).toList();
}
data['message'] = this.message;
return data;
}
}
class TechDetails {
String? id;
String? empName;
String? techRoleName;
String? mobNum;
String? lat;
String? lng;
String? profileImg;
TechDetails(
{this.id,
this.empName,
this.techRoleName,
this.mobNum,
this.lat,
this.lng,
this.profileImg});
TechDetails.fromJson(Map<String, dynamic> json) {
id = json['id'];
empName = json['emp_name'];
techRoleName = json['tech_role_name'];
mobNum = json['mob_num'];
lat = json['lat'];
lng = json['lng'];
profileImg = json['profile_img'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['emp_name'] = this.empName;
data['tech_role_name'] = this.techRoleName;
data['mob_num'] = this.mobNum;
data['lat'] = this.lat;
data['lng'] = this.lng;
data['profile_img'] = this.profileImg;
return data;
}
}
class AllServiceList {
String? id;
String? empName;
String? date;
String? techRoleName;
String? mobNum;
String? profileImg;
String? feedback;
String? openStatus;
String? customerServiceRating;
String? inOrOutTime;
String? runningHrs;
String? fsrExt;
String? fsrNo;
AllServiceList(
{this.id,
this.empName,
this.date,
this.techRoleName,
this.mobNum,
this.profileImg,
this.feedback,
this.openStatus,
this.customerServiceRating,
this.inOrOutTime,
this.runningHrs,
this.fsrExt,
this.fsrNo});
AllServiceList.fromJson(Map<String, dynamic> json) {
id = json['id'];
empName = json['emp_name'];
date = json['date'];
techRoleName = json['tech_role_name'];
mobNum = json['mob_num'];
profileImg = json['profile_img'];
feedback = json['feedback'];
openStatus = json['open_status'];
customerServiceRating = json['customer_service_rating'];
inOrOutTime = json['in_or_out_time'];
runningHrs = json['running_hrs'];
fsrExt = json['fsr_ext'];
fsrNo = json['fsr_no'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['emp_name'] = this.empName;
data['date'] = this.date;
data['tech_role_name'] = this.techRoleName;
data['mob_num'] = this.mobNum;
data['profile_img'] = this.profileImg;
data['feedback'] = this.feedback;
data['open_status'] = this.openStatus;
data['customer_service_rating'] = this.customerServiceRating;
data['in_or_out_time'] = this.inOrOutTime;
data['running_hrs'] = this.runningHrs;
data['fsr_ext'] = this.fsrExt;
data['fsr_no'] = this.fsrNo;
return data;
}
}
class complaintDetailsResponse {
String? error;
List<ComplaintDetails>? complaintDetails;
List<JobCardList>? jobCardList;
List<ServiceDetails>? serviceDetails;
String? message;
String? sessionExists;
complaintDetailsResponse(
{this.error,
this.complaintDetails,
this.jobCardList,
this.serviceDetails,
this.message,
this.sessionExists});
complaintDetailsResponse.fromJson(Map<String, dynamic> json) {
error = json['error'];
if (json['complaint_details'] != null) {
complaintDetails = <ComplaintDetails>[];
json['complaint_details'].forEach((v) {
complaintDetails!.add(new ComplaintDetails.fromJson(v));
});
}
if (json['job_card_list'] != null) {
jobCardList = <JobCardList>[];
json['job_card_list'].forEach((v) {
jobCardList!.add(new JobCardList.fromJson(v));
});
}
if (json['service_details'] != null) {
serviceDetails = <ServiceDetails>[];
json['service_details'].forEach((v) {
serviceDetails!.add(new ServiceDetails.fromJson(v));
});
}
message = json['message'];
sessionExists = json['session_exists'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['error'] = this.error;
if (this.complaintDetails != null) {
data['complaint_details'] =
this.complaintDetails!.map((v) => v.toJson()).toList();
}
if (this.jobCardList != null) {
data['job_card_list'] = this.jobCardList!.map((v) => v.toJson()).toList();
}
if (this.serviceDetails != null) {
data['service_details'] =
this.serviceDetails!.map((v) => v.toJson()).toList();
}
data['message'] = this.message;
data['session_exists'] = this.sessionExists;
return data;
}
}
class ComplaintDetails {
String? id;
String? openStatus;
String? registredDate;
String? hashId;
String? productName;
String? complaintName;
String? complaintDesc;
String? complaintNote;
ComplaintDetails(
{this.id,
this.openStatus,
this.registredDate,
this.hashId,
this.productName,
this.complaintName,
this.complaintDesc,
this.complaintNote});
ComplaintDetails.fromJson(Map<String, dynamic> json) {
id = json['id'];
openStatus = json['open_status'];
registredDate = json['registred_date'];
hashId = json['hash_id'];
productName = json['product_name'];
complaintName = json['complaint_name'];
complaintDesc = json['complaint_desc'];
complaintNote = json['complaint_note'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['open_status'] = this.openStatus;
data['registred_date'] = this.registredDate;
data['hash_id'] = this.hashId;
data['product_name'] = this.productName;
data['complaint_name'] = this.complaintName;
data['complaint_desc'] = this.complaintDesc;
data['complaint_note'] = this.complaintNote;
return data;
}
}
class JobCardList {
String? id;
String? complaintId;
String? extraDescription;
String? date;
String? totalPrice;
JobCardList(
{this.id,
this.complaintId,
this.extraDescription,
this.date,
this.totalPrice});
JobCardList.fromJson(Map<String, dynamic> json) {
id = json['id'];
complaintId = json['complaint_id'];
extraDescription = json['extra_description'];
date = json['date'];
totalPrice = json['total_price'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['complaint_id'] = this.complaintId;
data['extra_description'] = this.extraDescription;
data['date'] = this.date;
data['total_price'] = this.totalPrice;
return data;
}
}
class ServiceDetails {
String? id;
String? empName;
String? lat;
String? lng;
String? paymentStatus;
String? date;
String? techRoleName;
String? mobNum;
String? profileImg;
String? feedback;
String? openStatus;
String? rating;
String? inOrOutTime;
String? runningHrs;
String? fsrExt;
String? fsrNo;
ServiceDetails(
{this.id,
this.empName,
this.lat,
this.lng,
this.paymentStatus,
this.date,
this.techRoleName,
this.mobNum,
this.profileImg,
this.feedback,
this.openStatus,
this.rating,
this.inOrOutTime,
this.runningHrs,
this.fsrExt,
this.fsrNo});
ServiceDetails.fromJson(Map<String, dynamic> json) {
id = json['id'];
empName = json['emp_name'];
lat = json['lat'];
lng = json['lng'];
paymentStatus = json['payment_status'];
date = json['date'];
techRoleName = json['tech_role_name'];
mobNum = json['mob_num'];
profileImg = json['profile_img'];
feedback = json['feedback'];
openStatus = json['open_status'];
rating = json['rating'];
inOrOutTime = json['in_or_out_time'];
runningHrs = json['running_hrs'];
fsrExt = json['fsr_ext'];
fsrNo = json['fsr_no'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['emp_name'] = this.empName;
data['lat'] = this.lat;
data['lng'] = this.lng;
data['payment_status'] = this.paymentStatus;
data['date'] = this.date;
data['tech_role_name'] = this.techRoleName;
data['mob_num'] = this.mobNum;
data['profile_img'] = this.profileImg;
data['feedback'] = this.feedback;
data['open_status'] = this.openStatus;
data['rating'] = this.rating;
data['in_or_out_time'] = this.inOrOutTime;
data['running_hrs'] = this.runningHrs;
data['fsr_ext'] = this.fsrExt;
data['fsr_no'] = this.fsrNo;
return data;
}
}
class jobCardProductsResponse {
String? error;
List<JobCardProducts>? jobCardProducts;
String? message;
jobCardProductsResponse({this.error, this.jobCardProducts, this.message});
jobCardProductsResponse.fromJson(Map<String, dynamic> json) {
error = json['error'];
if (json['job_card_products'] != null) {
jobCardProducts = <JobCardProducts>[];
json['job_card_products'].forEach((v) {
jobCardProducts!.add(new JobCardProducts.fromJson(v));
});
}
message = json['message'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['error'] = this.error;
if (this.jobCardProducts != null) {
data['job_card_products'] =
this.jobCardProducts!.map((v) => v.toJson()).toList();
}
data['message'] = this.message;
return data;
}
}
class JobCardProducts {
String? id;
String? partName;
String? qty;
String? price;
String? totalPrice;
JobCardProducts(
{this.id, this.partName, this.qty, this.price, this.totalPrice});
JobCardProducts.fromJson(Map<String, dynamic> json) {
id = json['id'];
partName = json['part_name'];
qty = json['qty'];
price = json['price'];
totalPrice = json['total_price'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['part_name'] = this.partName;
data['qty'] = this.qty;
data['price'] = this.price;
data['total_price'] = this.totalPrice;
return data;
}
}
class ratingResponse {
String? error;
String? message;
String? sessionExists;
ratingResponse({this.error, this.message, this.sessionExists});
ratingResponse.fromJson(Map<String, dynamic> json) {
error = json['error'];
message = json['message'];
sessionExists = json['session_exists'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['error'] = this.error;
data['message'] = this.message;
data['session_exists'] = this.sessionExists;
return data;
}
}
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:gen_service/Models/HelpAndComplaintModels/complaintDetailsResponse.dart';
import 'package:gen_service/Models/HelpAndComplaintModels/jobCardProductsResponse.dart';
import 'package:gen_service/Services/api_calling.dart'; import 'package:gen_service/Services/api_calling.dart';
import 'package:provider/provider.dart';
import '../Models/CommonResponse.dart'; import '../Models/CommonResponse.dart';
import '../Models/HelpAndComplaintModels/ComplaintListResponse.dart'; import '../Models/HelpAndComplaintModels/ComplaintListResponse.dart';
...@@ -12,6 +16,9 @@ class HelpAndComplaintProvider extends ChangeNotifier { ...@@ -12,6 +16,9 @@ class HelpAndComplaintProvider extends ChangeNotifier {
String? _errorMessage; String? _errorMessage;
ComplaintListResponse? _complaintListResponse; ComplaintListResponse? _complaintListResponse;
complaintDetailsResponse? _complaintDetailsResponse;
jobCardProductsResponse? _jobCardResponse;
bool get isLoading => _isLoading; bool get isLoading => _isLoading;
String? get errorMessage => _errorMessage; String? get errorMessage => _errorMessage;
ComplaintListResponse? get complaintListResponse => _complaintListResponse; ComplaintListResponse? get complaintListResponse => _complaintListResponse;
...@@ -23,6 +30,36 @@ class HelpAndComplaintProvider extends ChangeNotifier { ...@@ -23,6 +30,36 @@ class HelpAndComplaintProvider extends ChangeNotifier {
DropDownsListResponse? _dropDownsListResponse; DropDownsListResponse? _dropDownsListResponse;
DropDownsListResponse? get dropDownsListResponse => _dropDownsListResponse; DropDownsListResponse? get dropDownsListResponse => _dropDownsListResponse;
complaintDetailsResponse? get compDetailsResponse => _complaintDetailsResponse;
jobCardProductsResponse? get jobCardResponse => _jobCardResponse;
List<bool> _starStates = [false, false, false, false, false];
var _rating = 0;
List<bool> get starStates => _starStates;
int get rating => _rating;
set rating(int value) {
_rating = value;
notifyListeners();
}
set starStates(List<bool> value) {
_starStates = value;
notifyListeners();
}
void updateStarStates(int rating, List<bool> starStates) {
starStates.fillRange(0, starStates.length, false);
for (int i = 0; i < rating; i++) {
starStates[i] = true;
}
notifyListeners();
}
///---------------------------------------------- ///----------------------------------------------
/// Fetch Complaints List /// Fetch Complaints List
...@@ -140,6 +177,80 @@ class HelpAndComplaintProvider extends ChangeNotifier { ...@@ -140,6 +177,80 @@ class HelpAndComplaintProvider extends ChangeNotifier {
Future<void> fetchComplaintDetailsAPI(String accId, String sessionId,complaintId) async {
_isLoading = true;
_errorMessage = null;
notifyListeners();
try {
final response = await ApiCalling.complaintDetailsAPI(accId, sessionId,complaintId);
if (response != null) {
if (response.error == "0") {
_complaintDetailsResponse = response;
updateStarStates(int.parse(response.serviceDetails?.firstOrNull?.rating.toString()??"0"), _starStates);
} else {
_errorMessage = response.message ?? "Something went wrong!";
}
} else {
_errorMessage = "No response from server.";
}
} catch (e) {
_errorMessage = "Failed to fetch dashboard: $e";
} finally {
_isLoading = false;
notifyListeners();
}
}
Future<void> updateRatingForTechnician(String accId, String sessionId,complaintId,rating) async {
// _errorMessage = null;
notifyListeners();
try {
final response = await ApiCalling.updateTechRatingAPI(accId, sessionId,complaintId,rating);
if (response != null) {
if (response.error == "0") {
fetchComplaintDetailsAPI(accId, sessionId,complaintId);
} else {
// _errorMessage = response.message ?? "Something went wrong!";
}
} else {
// _errorMessage = "No response from server.";
}
} catch (e) {
_errorMessage = "Failed to fetch dashboard: $e";
} finally {
_isLoading = false;
notifyListeners();
}
}
Future<void> fetchJobCardProductDetails(String accId, String sessionId,jobCardId) async {
_errorMessage = null;
notifyListeners();
try {
final response = await ApiCalling.jobCardProductDetailsAPI(accId, sessionId,jobCardId);
if (response != null) {
if (response.error == "0") {
_jobCardResponse = response;
} else {
_errorMessage = response.message ?? "Something went wrong!";
}
} else {
_errorMessage = "No response from server.";
}
} catch (e) {
_errorMessage = "Failed to fetch dashboard: $e";
} finally {
_isLoading = false;
notifyListeners();
}
}
///---------------------------------------------- ///----------------------------------------------
/// Private Helpers /// Private Helpers
......
import 'dart:async';
import 'dart:io';
import 'dart:ui' as ui;
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:gen_service/Models/GetInTouchListResponse.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:geocoding/geocoding.dart' as geocoding;
import 'package:location/location.dart' as Location;
import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
import '../Services/api_calling.dart';
class MapProvider extends ChangeNotifier {
GetInTouchListResponse? _response;
List<GetInTouchList> _locationsList = [];
final String _googleApikey = "AIzaSyBGzvgMMKwPBAANTwaoRsAnrCpiWCj8wVs";
GoogleMapController? _mapController;
CameraPosition? _cameraPosition;
final LatLng _startLocation = const LatLng(
17.439112226708446,
78.43292499146135,
);
String _latlongs = "";
List<Marker> _markers = [];
List<String> _addresses = [];
Location.LocationData? _currentLocation;
bool _isLocationEnabled = false;
bool _hasLocationPermission = false;
Timer? _timer;
bool _isLoading = true;
Timer? _debounceTimer;
LatLng? _mapCenter;
GetInTouchListResponse? get response => _response;
List<GetInTouchList> get locationsList => _locationsList;
String get googleAPIKey => _googleApikey;
GoogleMapController? get mapController => _mapController;
CameraPosition? get cameraPosition => _cameraPosition;
LatLng get startLocation => _startLocation;
String get latlongs => _latlongs;
List<Marker> get markers => _markers;
List<String> get addresses => _addresses;
Location.LocationData? get currentLocation => _currentLocation;
bool get isLocationEnabled => _isLocationEnabled;
bool get hasLocationPermission => _hasLocationPermission;
bool get isLoading => _isLoading;
Timer? get timer => _timer;
set markers(List<Marker> value) {
_markers = value;
if (value.isNotEmpty) {
_isLoading = false; // Mark screen as loaded when markers are added
}
notifyListeners();
}
set mapController(GoogleMapController? value) {
_mapController = value;
notifyListeners();
}
set mapCenter(LatLng? value) {
_mapCenter = value;
_latlongs = value != null ? '${value.latitude},${value.longitude}' : '';
notifyListeners();
}
set isLoading(bool value) {
_isLoading = value;
notifyListeners();
}
void resetAll() {
_markers = [];
_addresses = [];
_mapCenter = null;
_isLoading = true;
notifyListeners();
}
Future<void> getLocationPermission(BuildContext context, empId,
session,) async {
_isLocationEnabled = await Geolocator.isLocationServiceEnabled();
LocationPermission permission = await Geolocator.checkPermission();
_hasLocationPermission =
permission == LocationPermission.always ||
permission == LocationPermission.whileInUse;
final Location.Location location = Location.Location();
bool serviceEnabled;
Location.PermissionStatus permissionGranted;
serviceEnabled = await location.serviceEnabled();
if (!serviceEnabled) {
serviceEnabled = await location.requestService();
if (!serviceEnabled) {
_isLoading = false;
return;
}
}
permissionGranted = await location.hasPermission();
if (permissionGranted == Location.PermissionStatus.denied) {
permissionGranted = await location.requestPermission();
if (permissionGranted != Location.PermissionStatus.granted) {
// toast(context, 'Location permission denied.');
_isLoading = false;
return;
}
}
final Location.LocationData locData = await location.getLocation();
_currentLocation = locData;
if (_currentLocation != null && _mapCenter == null) {
_mapCenter = LatLng(
_currentLocation!.latitude!,
_currentLocation!.longitude!,
);
_latlongs =
'${_currentLocation!.latitude},${_currentLocation!.longitude}';
_mapController?.animateCamera(CameraUpdate.newLatLng(_mapCenter!));
await nearbyServiceLocations(context, empId,
session,);
}
notifyListeners();
}
void onCameraMove(BuildContext context, CameraPosition position, empId,
session) {
_timer?.cancel();
_mapCenter = position.target;
_latlongs = '${_mapCenter!.latitude},${_mapCenter!.longitude}';
_timer = Timer(Duration(seconds: 1), () {
nearbyServiceLocations(context, empId,
session,);
});
notifyListeners();
}
void debounce(VoidCallback callback, Duration duration) {
_debounceTimer?.cancel();
_debounceTimer = Timer(duration, callback);
}
Future<void> nearbyServiceLocations(
BuildContext context,
empId,
session,
) async {
try {
final data = await ApiCalling.fetchGetInTouchListApi(
empId,
session,
);
if (data != null) {
if (data.error == "0") {
_isLoading = false;
_response = data;
_locationsList = data.getInTouchList ?? [];
await updateMarkersFromApiResponse(context, data.getInTouchList??[]);
if (_locationsList.isEmpty) {
// toast(context, 'No leads found within the selected radius.');
}
}
else {
// toast(context, data.message ?? 'Failed to load leads.');
_isLoading = false;
notifyListeners();
}
} else {
// toast(context, "Something went wrong, please try again.");
_isLoading = false;
notifyListeners();
}
} on Exception catch (e, s) {
print("nearbyServiceLocations error: $e, stack: $s");
// toast(context, 'An error occurred while loading leads.');
_isLoading = false;
notifyListeners();
}
}
Future<void> updateMarkersFromApiResponse(
BuildContext context,
List<GetInTouchList> locationsList,
) async {
print("markers updating");
_markers = await createMarkersFromApiResponse(context, locationsList);
_addresses.clear();
await Future.forEach(locationsList, (store) async {
String address = await _getAddressFromLatLng(store.loc);
_addresses.add(address);
print(_addresses);
});
notifyListeners();
}
Future<List<Marker>> createMarkersFromApiResponse(
BuildContext context,
List<GetInTouchList> locationsList,
) async {
List<Marker> markers = [];
ByteData data = await rootBundle.load("assets/images/maps_ic.png");
Uint8List bytes = data.buffer.asUint8List();
await Future.forEach(locationsList, (leads) async {
if (leads.loc == null || leads.id == null || leads.branchName == null) {
print(
'Skipping invalid lead: id=${leads.id}, name=${leads.branchName}, loc=${leads.loc}',
);
return;
}
ui.Codec codec = await ui.instantiateImageCodec(bytes);
ui.FrameInfo fi = await codec.getNextFrame();
Uint8List resizedBytes =
(await fi.image.toByteData(
format: ui.ImageByteFormat.png,
))!.buffer.asUint8List();
markers.add(
Marker(
markerId: MarkerId(leads.id.toString()),
position: _parseLatLng(leads.loc),
icon: BitmapDescriptor.fromBytes(resizedBytes),
infoWindow: InfoWindow(
title: leads.branchName ?? 'Unknown Lead',
snippet: leads.address ?? 'No address available',
anchor: const Offset(0.5, 1.0),
),
zIndex: 100,
onTap: () {
_mapController?.showMarkerInfoWindow(MarkerId(leads.id.toString()));
print('Marker tapped: id=${leads.id}, name=${leads.branchName}');
},
),
);
});
return markers;
}
LatLng _parseLatLng(String? location) {
if (location != null) {
List<String> parts = location.split(',');
if (parts.length == 2) {
double lat = double.tryParse(parts[0]) ?? 0.0;
double lng = double.tryParse(parts[1]) ?? 0.0;
if (lat != 0.0 && lng != 0.0) {
return LatLng(lat, lng);
}
}
}
print('Invalid location string: $location');
return const LatLng(0.0, 0.0);
}
Future<String> _getAddressFromLatLng(String? location) async {
if (location != null) {
List<String> parts = location.split(',');
if (parts.length == 2) {
double lat = double.tryParse(parts[0]) ?? 0.0;
double lng = double.tryParse(parts[1]) ?? 0.0;
if (lat != 0.0 && lng != 0.0) {
try {
List<geocoding.Placemark> placemarks = await geocoding
.placemarkFromCoordinates(lat, lng);
if (placemarks.isNotEmpty) {
final placemark = placemarks.first;
String address =
'${placemark.street ?? ''}, '
'${placemark.thoroughfare ?? ''} '
'${placemark.subLocality ?? ''}, '
'${placemark.locality ?? ''}, '
'${placemark.administrativeArea ?? ''}, '
'${placemark.subAdministrativeArea ?? ''} '
'${placemark.postalCode ?? ''}, '
'${placemark.country ?? ''}';
return address.trim();
}
} catch (e) {
print('Geocoding error for location $location: $e');
}
}
}
}
return "Address not found";
}
}
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:gen_service/Models/HelpAndComplaintModels/allJobCardListResponse.dart';
import 'package:gen_service/Models/HelpAndComplaintModels/allServiceListResponse.dart';
import 'package:gen_service/Services/api_calling.dart';
class ServiceAndJobCardListProvider extends ChangeNotifier {
bool _isLoading = false;
String? _errorMessage;
allJobCardListResponse? _allJobCardResponse;
allServiceListResponse? _allServiceResponse;
bool get isLoading => _isLoading;
String? get errorMessage => _errorMessage;
allJobCardListResponse? get allJobCardResponse => _allJobCardResponse;
allServiceListResponse? get allServiceResponse => _allServiceResponse;
Map<String, int> _localRatings = {};
Map<String, List<bool>> _starStates = {};
int getRating(String serviceId) => _localRatings[serviceId] ?? 0;
List<bool> getStarStates(String serviceId) {
return _starStates.putIfAbsent(serviceId, () => List.filled(5, false));
}
void setRating(String serviceId, int rating, {bool notify = true}) {
_localRatings[serviceId] = rating;
final stars = getStarStates(serviceId);
for (int i = 0; i < 5; i++) {
stars[i] = i < rating;
}
if (notify) notifyListeners();
}
void initRatingsFromData(List<AllServiceList> services) {
for (var service in services) {
final id = service.id?.toString() ?? service.id.toString();
final backendRating = int.parse(service.customerServiceRating.toString()) ?? 0;
setRating(id, backendRating, notify: false);
}
notifyListeners();
}
Future<void> fetchAllJobCardsListAPI(String accId, String sessionId,complaintId) async {
_isLoading = true;
_errorMessage = null;
notifyListeners();
try {
final response = await ApiCalling.jobCardsListAPI(accId, sessionId,complaintId);
if (response != null) {
if (response.error == "0") {
_allJobCardResponse = response;
} else {
_errorMessage = response.message ?? "Something went wrong!";
}
} else {
_errorMessage = "No response from server.";
}
} catch (e) {
_errorMessage = "Failed to fetch dashboard: $e";
} finally {
_isLoading = false;
notifyListeners();
}
}
Future<void> fetchServiceDetailsAPI(String accId, String sessionId,complaintId) async {
_isLoading = true;
_errorMessage = null;
notifyListeners();
try {
final response = await ApiCalling.serviceListAPI(accId, sessionId,complaintId);
if (response != null) {
if (response.error == "0") {
_allServiceResponse = response;
initRatingsFromData(response.allServiceList??[]);
} else {
_errorMessage = response.message ?? "Something went wrong!";
}
} else {
_errorMessage = "No response from server.";
}
} catch (e) {
_errorMessage = "Failed to fetch dashboard: $e";
} finally {
_isLoading = false;
notifyListeners();
}
}
Future<void> updateRatingForTechnician(String accId, String sessionId,complaintId,rating) async {
// _errorMessage = null;
notifyListeners();
try {
final response = await ApiCalling.updateTechRatingAPI(accId, sessionId,complaintId,rating);
if (response != null) {
if (response.error == "0") {
fetchServiceDetailsAPI(accId, sessionId,complaintId);
setRating(complaintId, rating);
} else {
// _errorMessage = response.message ?? "Something went wrong!";
}
} else {
// _errorMessage = "No response from server.";
}
} catch (e) {
_errorMessage = "Failed to fetch dashboard: $e";
} finally {
_isLoading = false;
notifyListeners();
}
}
void _setLoading(bool value) {
_isLoading = value;
notifyListeners();
}
void clearData() {
_allJobCardResponse = null;
_allServiceResponse = null;
_errorMessage = null;
notifyListeners();
}
}
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final TextEditingController _mobileController = TextEditingController();
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return Scaffold(
resizeToAvoidBottomInset: true,
backgroundColor: Colors.blue,
body: Stack(
children: [
/// 🔹 Background image
Container(
width: double.infinity,
height: double.infinity,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/images/background_png.png"),
fit: BoxFit.cover,
),
),
),
/// 🔹 Main content (scrollable & keyboard-safe)
SafeArea(
child: LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom, // moves up with keyboard
),
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: IntrinsicHeight(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const SizedBox(height: 80),
/// 🔹 Logo
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(width: 20,),
SvgPicture.asset(
"assets/svg/genesis_logo_2io.svg",
height: 48,
color: Colors.white,
),
]
),
const SizedBox(height: 12),
const Spacer(),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 26, vertical: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Login to",
style: TextStyle(
fontSize: 48,
fontFamily: "PoppinsLight",
fontWeight: FontWeight.w100,
color: Colors.white,
),
),
Text(
"continue",
style: TextStyle(
fontSize: 48,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
],
),
),
],
),
const SizedBox(height: 20),
/// 🔹 Bottom Sheet style area
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 30),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24),
),
),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
const Text(
"Enter Registered Mobile No.",
style: TextStyle(
fontSize: 14,
color: Colors.black54,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 10),
/// 🔹 Mobile Field
TextFormField(
controller: _mobileController,
keyboardType: TextInputType.phone,
maxLength: 10,
decoration: InputDecoration(
hintText: "Enter Mobile No.",
counterText: "",
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(50),
borderSide: BorderSide(color: Colors.grey.shade300),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(50),
borderSide: const BorderSide(color: Colors.blue, width: 1.2),
),
filled: true,
fillColor: Colors.grey.shade100,
),
validator: (value) {
if (value == null || value.isEmpty) {
return "Please enter mobile number";
} else if (value.length != 10) {
return "Enter valid 10-digit number";
}
return null;
},
),
const SizedBox(height: 20),
/// 🔹 Continue Button (Always visible)
SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF0086F1),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
),
onPressed: () {
if (_formKey.currentState!.validate()) {
FocusScope.of(context).unfocus();
// TODO: Add API call here
}
},
child: const Text(
"Continue",
style: TextStyle(
fontSize: 16,
color: Colors.white,
fontWeight: FontWeight.w600,
),
),
),
),
],
),
),
),
],
),
),
),
);
},
),
),
],
),
);
}
}
...@@ -5,6 +5,7 @@ import 'package:gen_service/Screens/HelpAndComplaintScreens/SelectOrderHelpScree ...@@ -5,6 +5,7 @@ import 'package:gen_service/Screens/HelpAndComplaintScreens/SelectOrderHelpScree
import 'package:gen_service/Screens/ProfileScreen.dart'; import 'package:gen_service/Screens/ProfileScreen.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../Utility/AppColors.dart'; import '../../Utility/AppColors.dart';
import 'ComplaintDetailsScreen.dart';
class ComplaintListScreen extends StatefulWidget { class ComplaintListScreen extends StatefulWidget {
final String accId; final String accId;
...@@ -27,8 +28,10 @@ class _ComplaintListScreenState extends State<ComplaintListScreen> { ...@@ -27,8 +28,10 @@ class _ComplaintListScreenState extends State<ComplaintListScreen> {
void initState() { void initState() {
super.initState(); super.initState();
Future.microtask(() { Future.microtask(() {
final provider = final provider = Provider.of<HelpAndComplaintProvider>(
Provider.of<HelpAndComplaintProvider>(context, listen: false); context,
listen: false,
);
provider.fetchComplaintsList( provider.fetchComplaintsList(
accId: widget.accId, accId: widget.accId,
sessionId: widget.sessionId, sessionId: widget.sessionId,
...@@ -57,28 +60,133 @@ class _ComplaintListScreenState extends State<ComplaintListScreen> { ...@@ -57,28 +60,133 @@ class _ComplaintListScreenState extends State<ComplaintListScreen> {
return Scaffold( return Scaffold(
backgroundColor: AppColors.backgroundRegular, backgroundColor: AppColors.backgroundRegular,
body: Center( body: Center(
child: Text( child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Error Icon
Container(
width: 120,
height: 120,
decoration: BoxDecoration(
color: Colors.red.withOpacity(0.1),
shape: BoxShape.circle,
),
child: const Icon(
Icons.error_outline_rounded,
size: 60,
color: Colors.red,
),
),
const SizedBox(height: 24),
// Error Title
const Text(
"Oops! Something went wrong",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color: Colors.black87,
fontFamily: "Poppins",
),
),
const SizedBox(height: 12),
// Error Message
Text(
error, error,
style: const TextStyle(color: Colors.red, fontSize: 16), textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 14,
color: Colors.grey,
fontFamily: "Poppins",
height: 1.4,
), ),
), ),
const SizedBox(height: 32),
// Retry Button
ElevatedButton.icon(
onPressed: () async {
// Show loading state
setState(() {});
await Future.delayed(const Duration(milliseconds: 300));
// Retry fetching data
final provider = Provider.of<HelpAndComplaintProvider>(
context,
listen: false,
); );
} provider.fetchComplaintsList(
accId: widget.accId,
sessionId: widget.sessionId,
);
},
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.buttonColor,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 12,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25),
),
elevation: 2,
),
icon: const Icon(Icons.refresh_rounded, size: 20),
label: const Text(
"Try Again",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
fontFamily: "Poppins",
),
),
),
const SizedBox(height: 16),
// Alternative Action
TextButton(
onPressed: () {
// Go back or navigate to home
Navigator.pop(context);
},
child: const Text(
"Go Back",
style: TextStyle(
fontSize: 14,
color: Colors.grey,
fontFamily: "Poppins",
),
),
),
],
),
),
),
);
}
if (data == null) { if (data == null) {
return const Scaffold( return const Scaffold(
backgroundColor: AppColors.backgroundRegular, backgroundColor: AppColors.backgroundRegular,
body: Center( body: Center(child: Text("No data found.")),
child: Text("No data found."),
),
); );
} }
// Separate open and closed complaints // Separate open and closed complaints
final openComplaints = data.complaintList! final openComplaints =
data.complaintList!
.where((c) => c.openStatus?.toLowerCase() == "open") .where((c) => c.openStatus?.toLowerCase() == "open")
.toList(); .toList();
final closedComplaints = data.complaintList! final closedComplaints =
data.complaintList!
.where((c) => c.openStatus?.toLowerCase() == "closed") .where((c) => c.openStatus?.toLowerCase() == "closed")
.toList(); .toList();
...@@ -113,12 +221,12 @@ class _ComplaintListScreenState extends State<ComplaintListScreen> { ...@@ -113,12 +221,12 @@ class _ComplaintListScreenState extends State<ComplaintListScreen> {
StretchMode.blurBackground, StretchMode.blurBackground,
], ],
background: Container( background: Container(
decoration: decoration: const BoxDecoration(color: AppColors.primary),
const BoxDecoration(
gradient: AppColors.commonAppBarGradient
),
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 16,
),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
...@@ -151,9 +259,12 @@ class _ComplaintListScreenState extends State<ComplaintListScreen> { ...@@ -151,9 +259,12 @@ class _ComplaintListScreenState extends State<ComplaintListScreen> {
SliverToBoxAdapter( SliverToBoxAdapter(
child: Container( child: Container(
padding: const EdgeInsets.only(top: 1), padding: const EdgeInsets.only(top: 1),
color: AppColors.backgroundBottom, color: AppColors.primary,
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20), padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 20,
),
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: AppColors.backgroundRegular, color: AppColors.backgroundRegular,
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
...@@ -169,7 +280,13 @@ class _ComplaintListScreenState extends State<ComplaintListScreen> { ...@@ -169,7 +280,13 @@ class _ComplaintListScreenState extends State<ComplaintListScreen> {
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute(builder: (context)=> SelectOrderHelpScreen(accId: widget.accId, sessionId: widget.sessionId)) MaterialPageRoute(
builder:
(context) => SelectOrderHelpScreen(
accId: widget.accId,
sessionId: widget.sessionId,
),
),
).then((_) async { ).then((_) async {
await provider.fetchComplaintsList( await provider.fetchComplaintsList(
accId: widget.accId, accId: widget.accId,
...@@ -179,7 +296,9 @@ class _ComplaintListScreenState extends State<ComplaintListScreen> { ...@@ -179,7 +296,9 @@ class _ComplaintListScreenState extends State<ComplaintListScreen> {
}, },
child: Container( child: Container(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
vertical: 16, horizontal: 14), vertical: 16,
horizontal: 14,
),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.circular(14), borderRadius: BorderRadius.circular(14),
...@@ -247,6 +366,8 @@ class _ComplaintListScreenState extends State<ComplaintListScreen> { ...@@ -247,6 +366,8 @@ class _ComplaintListScreenState extends State<ComplaintListScreen> {
const SizedBox(height: 8), const SizedBox(height: 8),
...openComplaints.map( ...openComplaints.map(
(c) => ComplaintCard( (c) => ComplaintCard(
accId: widget.accId,
sessionId: widget.sessionId,
title: c.complaintName ?? "-", title: c.complaintName ?? "-",
id: c.id ?? "-", id: c.id ?? "-",
product: c.productName ?? "", product: c.productName ?? "",
...@@ -272,6 +393,8 @@ class _ComplaintListScreenState extends State<ComplaintListScreen> { ...@@ -272,6 +393,8 @@ class _ComplaintListScreenState extends State<ComplaintListScreen> {
const SizedBox(height: 8), const SizedBox(height: 8),
...closedComplaints.map( ...closedComplaints.map(
(c) => ComplaintCard( (c) => ComplaintCard(
accId: widget.accId,
sessionId: widget.sessionId,
title: c.complaintName ?? "-", title: c.complaintName ?? "-",
id: c.id ?? "", id: c.id ?? "",
product: c.productName ?? "", product: c.productName ?? "",
...@@ -294,7 +417,8 @@ class _ComplaintListScreenState extends State<ComplaintListScreen> { ...@@ -294,7 +417,8 @@ class _ComplaintListScreenState extends State<ComplaintListScreen> {
/// Reusable Complaint Item Card /// Reusable Complaint Item Card
class ComplaintCard extends StatelessWidget { class ComplaintCard extends StatelessWidget {
final String accId;
final String sessionId;
final String title; final String title;
final String id; final String id;
final String product; final String product;
...@@ -310,24 +434,40 @@ class ComplaintCard extends StatelessWidget { ...@@ -310,24 +434,40 @@ class ComplaintCard extends StatelessWidget {
required this.status, required this.status,
required this.date, required this.date,
required this.engModel, required this.engModel,
required this.accId,
required this.sessionId,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return InkResponse(
onTap: () async {
await Navigator.push(
context,
MaterialPageRoute(
builder:
(context) => ComplaintDetailsScreen(
accId: accId,
sessionId: sessionId,
complaintId: id,
),
),
);
},
child: Container(
margin: const EdgeInsets.symmetric(vertical: 6), margin: const EdgeInsets.symmetric(vertical: 6),
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14), padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
// border: Border.all(color: Colors.grey.shade200), border: Border.all(color: Colors.grey.shade200),
// boxShadow: [ boxShadow: [
// BoxShadow( BoxShadow(
// color: Colors.grey.shade200, color: Colors.grey.shade200,
// blurRadius: 4, blurRadius: 4,
// offset: const Offset(0, 2), offset: const Offset(0, 2),
// ) ),
// ], ],
), ),
child: Column( child: Column(
children: [ children: [
...@@ -370,13 +510,15 @@ class ComplaintCard extends StatelessWidget { ...@@ -370,13 +510,15 @@ class ComplaintCard extends StatelessWidget {
), ),
), ),
const SizedBox(height: 2), const SizedBox(height: 2),
], ],
), ),
Container( Container(
padding: EdgeInsets.symmetric(horizontal: 15, vertical: 7), padding: EdgeInsets.symmetric(horizontal: 15, vertical: 7),
decoration: BoxDecoration( decoration: BoxDecoration(
color: status == "Open" ? AppColors.successBG : AppColors.yellowBG, color:
status == "Open"
? AppColors.successBG
: AppColors.yellowBG,
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
child: Text( child: Text(
...@@ -385,7 +527,10 @@ class ComplaintCard extends StatelessWidget { ...@@ -385,7 +527,10 @@ class ComplaintCard extends StatelessWidget {
fontFamily: "Poppins", fontFamily: "Poppins",
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
color: status == "Open" ? AppColors.success : AppColors.normalText, color:
status == "Open"
? AppColors.success
: AppColors.normalText,
), ),
), ),
), ),
...@@ -421,6 +566,7 @@ class ComplaintCard extends StatelessWidget { ...@@ -421,6 +566,7 @@ class ComplaintCard extends StatelessWidget {
), ),
], ],
), ),
),
); );
} }
} }
...@@ -58,9 +58,115 @@ class _SelectOrderHelpScreenState extends State<SelectOrderHelpScreen> { ...@@ -58,9 +58,115 @@ class _SelectOrderHelpScreenState extends State<SelectOrderHelpScreen> {
return Scaffold( return Scaffold(
backgroundColor: AppColors.backgroundRegular, backgroundColor: AppColors.backgroundRegular,
body: Center( body: Center(
child: Text( child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Error Icon
Container(
width: 120,
height: 120,
decoration: BoxDecoration(
color: Colors.red.withOpacity(0.1),
shape: BoxShape.circle,
),
child: const Icon(
Icons.error_outline_rounded,
size: 60,
color: Colors.red,
),
),
const SizedBox(height: 24),
// Error Title
const Text(
"Oops! Something went wrong",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color: Colors.black87,
fontFamily: "Poppins",
),
),
const SizedBox(height: 12),
// Error Message
Text(
error, error,
style: const TextStyle(color: Colors.red, fontSize: 16), textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 14,
color: Colors.grey,
fontFamily: "Poppins",
height: 1.4,
),
),
const SizedBox(height: 32),
// Retry Button
ElevatedButton.icon(
onPressed: () async {
// Show loading state
setState(() {});
await Future.delayed(const Duration(milliseconds: 300));
// Retry fetching data
final provider = Provider.of<HelpAndComplaintProvider>(
context,
listen: false,
);
provider.fetchGeneratorList(
accId: widget.accId,
sessionId: widget.sessionId,
);
},
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.buttonColor,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 12,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25),
),
elevation: 2,
),
icon: const Icon(Icons.refresh_rounded, size: 20),
label: const Text(
"Try Again",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
fontFamily: "Poppins",
),
),
),
const SizedBox(height: 16),
// Alternative Action
TextButton(
onPressed: () {
// Go back or navigate to home
Navigator.pop(context);
},
child: const Text(
"Go Back",
style: TextStyle(
fontSize: 14,
color: Colors.grey,
fontFamily: "Poppins",
),
),
),
],
),
), ),
), ),
); );
......
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment