diff --git a/android/app/build.gradle b/android/app/build.gradle index fe59029..037adf7 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -28,7 +28,7 @@ if (flutterVersionName == null) { } android { - namespace = "com.example.cheminova" + namespace = "com.example.tm_cheminova" compileSdk = flutter.compileSdkVersion ndkVersion = flutter.ndkVersion @@ -39,7 +39,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId = "com.example.cheminova" + applicationId = "com.example.tm_cheminova" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdk = flutter.minSdkVersion diff --git a/android/app/google-services.json b/android/app/google-services.json index 40ed628..3017e22 100644 --- a/android/app/google-services.json +++ b/android/app/google-services.json @@ -9,7 +9,7 @@ "client_info": { "mobilesdk_app_id": "1:242950171023:android:7fdc614b0429b52445c3fa", "android_client_info": { - "package_name": "com.example.cheminova" + "package_name": "com.example.tm_cheminova" } }, "oauth_client": [], diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 88adec2..c5a5c4e 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,5 @@ - + @@ -7,7 +8,7 @@ + android:icon="@mipmap/launcher_icon"> getTask() async { + // setLoading(true); + // try { + // Response response = await _apiClient.get(ApiUrls.salesTaskUrl); + // setLoading(false); + // if (response.statusCode == 200) { + // final data = SalesTaskResponse.fromJson(response.data); + // tasksList = data.products ?? []; + // notifyListeners(); + // } + // } catch (e) { + // String error = "Something went wrong"; + // if (e is DioException) { + // error = e.response!.data['message'] ?? "Something went wrong"; + // } + // ScaffoldMessenger.of( + // navigatorKey.currentContext!, + // ).showSnackBar( + // SnackBar( + // content: Text( + // error, + // ), + // ), + // ); + // setLoading(false); + // } + // } + + Future getTask() async { - setLoading(true); - try { - Response response = await _apiClient.get(ApiUrls.salesTaskUrl); - setLoading(false); + bool moreDataAvailable = true; + int page = 1; + + // Loop to fetch all pages + while (moreDataAvailable) { + Response response = await _apiClient.get( + '${ApiUrls.salesTaskUrl}?page=$page'); if (response.statusCode == 200) { + // productResponse = ProductResponse.fromJson(response.data); final data = SalesTaskResponse.fromJson(response.data); - tasksList = data.products ?? []; - notifyListeners(); + tasksList.addAll(data.products ?? []); // Add new products to the list + moreDataAvailable = data.totalPages! > page; + page++; + notifyListeners(); // Notify listeners to update the UI with the new product list } - } catch (e) { - String error = "Something went wrong"; - if (e is DioException) { - error = e.response!.data['message'] ?? "Something went wrong"; - } - ScaffoldMessenger.of( - navigatorKey.currentContext!, - ).showSnackBar( - SnackBar( - content: Text( - error, - ), - ), - ); - setLoading(false); } } + void filterProducts(String val) { tasksList = tasksList.where((element) { final productNameLower = element.ProductName!.toLowerCase(); diff --git a/lib/provider/notification_provider.dart b/lib/provider/notification_provider.dart index 8bbcbfa..1511c5f 100644 --- a/lib/provider/notification_provider.dart +++ b/lib/provider/notification_provider.dart @@ -10,7 +10,7 @@ import '../services/api_urls.dart'; class NotificationProvider extends ChangeNotifier { NotificationProvider() { - getNotification(); + // getNotification(); } final _apiClient = ApiClient(); @@ -41,24 +41,44 @@ class NotificationProvider extends ChangeNotifier { void setDate(String date) { _selectedDate = date; notifyListeners(); - getNotification(); + //getNotification(); } - Future getNotification() async { - clearLists(); + // Future getNotification() async { + // clearLists(); + // setLoading(true); + // try { + // Response response = await _apiClient + // .get('${ApiUrls.notificationUrl}?Date=$_selectedDate'); + // setLoading(false); + // if (response.statusCode == 200) { + // final data = NotificationListResponse.fromJson(response.data); + // notificationList = data.notifications ?? []; + // notifyListeners(); + // } + // } catch (e) { + // setLoading(false); + // } + // } + + Future getNotification(String date) async { + if (date.isEmpty) return; // Invalid call ko prevent karein + final String url = "${ApiUrls.notificationUrl}?Date=$date"; + setLoading(true); try { - Response response = await _apiClient - .get('${ApiUrls.notificationUrl}?Date=$_selectedDate'); - setLoading(false); + Response response = await _apiClient.get(url); if (response.statusCode == 200) { final data = NotificationListResponse.fromJson(response.data); notificationList = data.notifications ?? []; - notifyListeners(); + } else { + notificationList = []; } } catch (e) { - setLoading(false); + notificationList = []; } + setLoading(false); + notifyListeners(); } Future getAnnouncement(int page, int rowsPerPage) async { diff --git a/lib/provider/product_provider.dart b/lib/provider/product_provider.dart index dbdfe4a..4c96684 100644 --- a/lib/provider/product_provider.dart +++ b/lib/provider/product_provider.dart @@ -7,7 +7,7 @@ import 'package:flutter/material.dart'; class ProductProvider extends ChangeNotifier { ProductProvider() { - getProducts(); + getProducts(1); } final _apiClient = ApiClient(); @@ -26,34 +26,71 @@ class ProductProvider extends ChangeNotifier { notifyListeners(); } - Future getProducts() async { - setLoading(true); + // Future?> getProducts(int page) async { + // setLoading(true); + // try { + // Response response = await _apiClient.get("${ApiUrls.getProducts}?page=$page"); + // setLoading(false); + // if (response.statusCode == 200) { + // productResponse = ProductResponse.fromJson(response.data); + // productList = productResponse!.products; + // //notifyListeners(); + // } + // } catch (e) { + // setLoading(false); + // String error = "Something went wrong"; + // if (e is DioException) { + // error = e.response!.data['message'] ?? "Something went wrong"; + // } + // ScaffoldMessenger.of( + // navigatorKey.currentContext!, + // ).showSnackBar( + // SnackBar( + // content: Text( + // error, + // ), + // ), + // ); + // } + // } + + + Future getProducts(int page) async { + // if (_isLoading) return; // Prevent duplicate requests + // setLoading(true); try { - Response response = await _apiClient.get(ApiUrls.getProducts); - setLoading(false); + Response response = await _apiClient.get("${ApiUrls.getProducts}?page=$page"); if (response.statusCode == 200) { - productResponse = ProductResponse.fromJson(response.data); - productList = productResponse!.products; - notifyListeners(); + final newProductResponse = ProductResponse.fromJson(response.data); + if (newProductResponse.products.isNotEmpty) { + // if (page == 6) { + // // Clear the list if it's the first page + // productList = newProductResponse.products; + // } else { + // Append new products for subsequent pages + productList.addAll(newProductResponse.products); + } + // _isLoading = false; + // notifyListeners(); + // } } } catch (e) { - setLoading(false); String error = "Something went wrong"; if (e is DioException) { - error = e.response!.data['message'] ?? "Something went wrong"; + error = e.response?.data['message'] ?? "Something went wrong"; } - ScaffoldMessenger.of( - navigatorKey.currentContext!, - ).showSnackBar( + ScaffoldMessenger.of(navigatorKey.currentContext!).showSnackBar( SnackBar( - content: Text( - error, - ), + content: Text(error), ), ); + } finally { + setLoading(false); } } + + Future submitSelectedProducts( String addedFor, String addedForId) async { setLoading(true); @@ -99,3 +136,125 @@ class ProductProvider extends ChangeNotifier { } } } + + + +// import 'package:cheminova/constants/constant.dart'; +// import 'package:cheminova/models/product_model.dart'; +// import 'package:cheminova/services/api_client.dart'; +// import 'package:cheminova/services/api_urls.dart'; +// import 'package:dio/dio.dart'; +// import 'package:flutter/material.dart'; +// +// class ProductProvider extends ChangeNotifier { +// ProductProvider() { +// // Initialize with the first page of products +// getProducts(1); +// } +// +// final _apiClient = ApiClient(); +// ProductResponse? productResponse; // To parse and store API response +// List productList = []; // Main list of products +// final List _selectedProducts = []; // For user-selected products +// +// bool _isLoading = false; +// bool _hasMoreData = true; // Track if more data is available +// +// bool get isLoading => _isLoading; +// bool get hasMoreData => _hasMoreData; +// +// List get selectedProducts => _selectedProducts; +// +// void setLoading(bool loading) { +// _isLoading = loading; +// notifyListeners(); +// } +// +// Future getProducts(int page) async { +// if (_isLoading || !_hasMoreData) return; // Prevent duplicate requests +// setLoading(true); +// +// try { +// final String url = "${ApiUrls.getProducts}?page=$page"; +// Response response = await _apiClient.get(url); +// +// if (response.statusCode == 200) { +// productResponse = ProductResponse.fromJson(response.data); +// +// if (productResponse!.products.isNotEmpty) { +// +// selectedProducts.addAll(productResponse!.products); +// // if (page <= productResponse!.totalPages) { +// // // Replace the list if fetching the first page +// // productList = productResponse!.products; +// // } else { +// // // Append new products for subsequent pages +// // productList.addAll(productResponse!.products); +// // } +// notifyListeners(); +// } else { +// // No more products to load +// _hasMoreData = false; +// } +// } +// } catch (e) { +// String error = "Something went wrong"; +// if (e is DioException) { +// error = e.response?.data['message'] ?? "Something went wrong"; +// } +// ScaffoldMessenger.of( +// navigatorKey.currentContext!, +// ).showSnackBar( +// SnackBar( +// content: Text(error), +// ), +// ); +// } finally { +// setLoading(false); +// } +// } +// +// Future submitSelectedProducts(String addedFor, String addedForId) async { +// setLoading(true); +// +// try { +// final data = { +// "products": selectedProducts.map((product) { +// return { +// "SKU": product.SKU, +// "ProductName": product.name, +// "Sale": product.sale, +// "Inventory": product.inventory, +// "productid": product.id, +// }; +// }).toList(), +// "addedFor": addedFor, +// "addedForId": addedForId, +// }; +// +// Response response = +// await _apiClient.post(ApiUrls.submitProducts, data: data); +// +// if (response.statusCode == 201) { +// return true; +// } else { +// return false; +// } +// } catch (e) { +// String error = "Something went wrong"; +// if (e is DioException) { +// error = e.response?.data['message'] ?? "Something went wrong"; +// } +// ScaffoldMessenger.of( +// navigatorKey.currentContext!, +// ).showSnackBar( +// SnackBar( +// content: Text(error), +// ), +// ); +// return false; +// } finally { +// setLoading(false); +// } +// } +// } diff --git a/lib/screens/add_products_screen.dart b/lib/screens/add_products_screen.dart index 1b29c8f..0ff3d76 100644 --- a/lib/screens/add_products_screen.dart +++ b/lib/screens/add_products_screen.dart @@ -10,6 +10,9 @@ import 'package:cheminova/widgets/common_elevated_button.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; + + + class AddProductsScreen extends StatefulWidget { final PdRdResponseModel distributor; final String distributorType; @@ -26,20 +29,100 @@ class AddProductsScreen extends StatefulWidget { class _AddProductsScreenState extends State { List filteredProducts = []; final searchController = TextEditingController(); - late ProductProvider provider; - + final ScrollController scrollController = ScrollController(); + late ProductProvider provider; + int currentpage = 1; + bool hasMoreData = true; // To track if more data is available + bool isLoading = false; // To track loading state @override void initState() { super.initState(); - loadProducts(); + provider = Provider.of(context, listen: false); + loadProducts(); + + scrollController.addListener(() { + if (scrollController.position.pixels == + scrollController.position.maxScrollExtent) { + loadProducts(); + } + }); + } + + + + + + + // Future loadProducts() async { + // // provider = Provider.of(context, listen: false); + // await provider.getProducts(currentpage); + // setState(() { + // //provider.selectedProducts.clear(); + // // isLoading = true; + // currentpage++; + // filteredProducts = provider.productList; + // }); + // } + + + // Future loadProducts() async { + // if (isLoading || !hasMoreData) return; // Prevent multiple simultaneous calls + // + // setState(() { + // isLoading = true; // Show loading state + // }); + // + // // Fetch products from the provider + // await provider.getProducts(currentpage); + // + // setState(() { + // final newProducts = provider.productList; + // + // // Add only unique products to the list + // for (var product in newProducts) { + // if (!filteredProducts.any((p) => p.id == product.id)) { + // filteredProducts.add(product); + // + // } + // } + // + // // Check if more data is available + // hasMoreData = newProducts.isNotEmpty; + // + // isLoading = false; // Remove loading state + // currentpage++; + // }); + // } + + Future loadProducts() async { - provider = Provider.of(context, listen: false); - await provider.getProducts(); + if (isLoading || !hasMoreData) return; // Prevent duplicate API calls setState(() { - provider.selectedProducts.clear(); - filteredProducts = provider.productList; + isLoading = true; + }); + + // Fetch products + await provider.getProducts(currentpage); + + setState(() { + final newProducts = provider.productList; + + // Add only unique products to filteredProducts + for (var product in newProducts) { + if (!filteredProducts.any((p) => p.id == product.id)) { + filteredProducts.add(product); + } + } + + currentpage++; + isLoading = false; + + // If no new products are returned, stop further API calls + if (newProducts.isEmpty) { + hasMoreData = false; + } }); } @@ -121,84 +204,94 @@ class _AddProductsScreenState extends State { isScrollControlled: true, constraints: BoxConstraints( maxHeight: - MediaQuery.of(context).size.height * 0.9, + MediaQuery.of(context).size.height * 0.9, ), context: context, builder: (BuildContext context) { return Consumer( builder: (context, value, child) => StatefulBuilder( - builder: (context, setState) { - return Column( - children: [ - Padding( - padding: const EdgeInsets.all(18.0), - child: TextField( - controller: searchController, - decoration: const InputDecoration( - labelText: + builder: (context, setState) { + return Column( + children: [ + Padding( + padding: const EdgeInsets.all(18.0), + child: TextField( + controller: searchController, + decoration: const InputDecoration( + labelText: 'Search by name or SKU', - border: OutlineInputBorder(), - prefixIcon: Icon(Icons.search), + border: OutlineInputBorder(), + prefixIcon: Icon(Icons.search), + ), + onChanged: (value) { + filterProducts(value); + setState(() {}); + }, + ), ), - onChanged: (value) { - filterProducts(value); - setState(() {}); - }, - ), - ), - Expanded( - child: ListView.builder( - itemCount: - filteredProducts.length, - itemBuilder: (context, index) { - bool isAlreadySelected = + Expanded( + child: ListView.builder( + controller: scrollController, + itemCount: + filteredProducts.length +(isLoading ? 1 : 0), + itemBuilder: (context, index) { + if (index == filteredProducts.length) { + // Show a loading indicator while fetching more products + return const Center( + child: Padding( + padding: EdgeInsets.all(16.0), + child: CircularProgressIndicator(), + ), + ); + } + bool isAlreadySelected = provider.selectedProducts .contains( - filteredProducts[ - index]); - return Card( - child: ListTile( - title: Text( - filteredProducts[index] - .name - .capitalize(), - style: TextStyle( - color: isAlreadySelected - ? Colors.grey - : Colors.black, + filteredProducts[ + index]); + return Card( + child: ListTile( + title: Text( + filteredProducts[index] + .name + .capitalize(), + style: TextStyle( + color: isAlreadySelected + ? Colors.grey + : Colors.black, + ), + ), + subtitle: Text( + filteredProducts[index] + .SKU, + style: TextStyle( + color: isAlreadySelected + ? Colors.grey + : Colors.black, + ), + ), + onTap: isAlreadySelected + ? null + : () { + setState(() { + provider + .selectedProducts + .add(filteredProducts[ + index]); + }); + Navigator.pop( + context); + }, ), - ), - subtitle: Text( - filteredProducts[index] - .SKU, - style: TextStyle( - color: isAlreadySelected - ? Colors.grey - : Colors.black, - ), - ), - onTap: isAlreadySelected - ? null - : () { - setState(() { - provider - .selectedProducts - .add(filteredProducts[ - index]); - }); - Navigator.pop( - context); - }, - ), - ); - }, - ), - ), - ], - ); - }, - ), + ); + }, + ), + ), + ], + ); + }, + ), ); }, ).whenComplete(() { @@ -223,18 +316,18 @@ class _AddProductsScreenState extends State { onPressed: () { provider .submitSelectedProducts( - (widget.distributorType == - 'Principal Distributor') - ? 'PrincipalDistributor' - : 'RetailDistributor', - widget.distributor.id!) + (widget.distributorType == + 'Principal Distributor') + ? 'PrincipalDistributor' + : 'RetailDistributor', + widget.distributor.id!) .then((value) { if (value) { Navigator.push( context, MaterialPageRoute( builder: (context) => - const DataSubmitSuccessfull(), + const DataSubmitSuccessfull(), ), ); } else { @@ -261,6 +354,306 @@ class _AddProductsScreenState extends State { } } + +// class AddProductsScreen extends StatefulWidget { +// final PdRdResponseModel distributor; +// final String distributorType; +// const AddProductsScreen({ +// super.key, +// required this.distributor, +// required this.distributorType, +// }); +// +// @override +// State createState() => _AddProductsScreenState(); +// } + + +// class _AddProductsScreenState extends State { +// List filteredProducts = []; +// final searchController = TextEditingController(); +// late ProductProvider provider; +// int currentPage = 1; +// bool isLoadingMore = false; +// bool hasMoreProducts = true; +// final ScrollController scrollController = ScrollController(); +// +// @override +// void initState() { +// super.initState(); +// provider = Provider.of(context, listen: false); +// // loadProducts(); +// +// provider.getProducts(currentPage); +// scrollController.addListener(() { +// if (scrollController.position.pixels == scrollController.position.maxScrollExtent) { +// // Load the next page when reaching the bottom +// currentPage++; +// provider.getProducts(currentPage); +// } +// }); +// } +// +// Future loadProducts() async { +// await provider.getProducts(currentPage); +// +// setState(() { +// filteredProducts = provider.productList; +// }); +// } +// +// Future loadMoreProducts() async { +// setState(() { +// isLoadingMore = true; +// }); +// +// currentPage++; +// await provider.getProducts(currentPage); +// +// setState(() { +// // Check if more products were fetched +// if (provider.productList.isEmpty) { +// hasMoreProducts = false; // No more products to load +// } else { +// filteredProducts.addAll(provider.productList); +// } +// isLoadingMore = false; +// }); +// } +// +// void filterProducts(String query) { +// setState(() { +// filteredProducts = provider.productList.where((product) { +// final productNameLower = product.name.toLowerCase(); +// final productSkuLower = product.SKU.toLowerCase(); +// final searchLower = query.toLowerCase(); +// +// return productNameLower.contains(searchLower) || +// productSkuLower.contains(searchLower); +// }).toList(); +// }); +// } +// +// @override +// Widget build(BuildContext context) { +// return CommonBackground( +// child: Scaffold( +// backgroundColor: Colors.transparent, +// appBar: CommonAppBar( +// actions: [ +// IconButton( +// onPressed: () { +// Navigator.pop(context); +// }, +// icon: Image.asset('assets/Back_attendance.png'), +// padding: const EdgeInsets.only(right: 20), +// ), +// ], +// title: Text( +// widget.distributor.name!.capitalize(), +// style: const TextStyle( +// fontSize: 20, +// color: Colors.black, +// fontWeight: FontWeight.w400, +// fontFamily: 'Anek', +// ), +// ), +// backgroundColor: Colors.transparent, +// elevation: 0, +// ), +// drawer: const CommonDrawer(), +// body: Consumer( +// builder: (context, provider, child) { +// if (provider.isLoading && provider.productList.isEmpty) { +// return const Center(child: CircularProgressIndicator()); +// } +// +// return Stack( +// children: [ +// Column( +// children: [ +// if (provider.selectedProducts.isNotEmpty) +// Expanded( +// child: ListView.builder( +// //controller: scrollController, +// itemCount: filteredProducts.length , // Add 1 for the loading indicator +// itemBuilder: (context, index) { +// final product = provider.productList[index]; +// if (index < filteredProducts.length) { +// return ProductBlock(index: index); +// } else if (isLoadingMore) { +// return const Center( +// child: Padding( +// padding: EdgeInsets.all(16.0), +// child: CircularProgressIndicator(), +// ), +// ); +// } else { +// return const SizedBox(); // Empty widget when no more products to load +// } +// }, +// ), +// ), +// ], +// ), +// Align( +// alignment: provider.selectedProducts.isEmpty +// ? Alignment.center +// : Alignment.bottomCenter, +// child: Padding( +// padding: const EdgeInsets.all(16.0), +// child: Column( +// mainAxisSize: MainAxisSize.min, +// children: [ +// FloatingActionButton.extended( +// onPressed: () { +// showModalBottomSheet( +// isScrollControlled: true, +// constraints: BoxConstraints( +// maxHeight: +// MediaQuery.of(context).size.height * 0.9, +// ), +// context: context, +// builder: (BuildContext context) { +// return Consumer( +// builder: (context, value, child) => +// StatefulBuilder( +// builder: (context, setState) { +// return Column( +// children: [ +// Padding( +// padding: const EdgeInsets.all(18.0), +// child: TextField( +// controller: searchController, +// decoration: const InputDecoration( +// labelText: +// 'Search by name or SKU', +// border: OutlineInputBorder(), +// prefixIcon: Icon(Icons.search), +// ), +// onChanged: (value) { +// filterProducts(value); +// setState(() {}); +// }, +// ), +// ), +// Expanded( +// child: ListView.builder( +// controller: scrollController, +// itemCount: provider.productList.length, +// itemBuilder: (context, index) { +// final product = provider.productList[index]; +// bool isAlreadySelected = provider +// .selectedProducts +// .contains( +// product); +// return Card( +// child: ListTile( +// title: Text( +// product +// .name +// .capitalize(), +// style: TextStyle( +// color: isAlreadySelected +// ? Colors.grey +// : Colors.black, +// ), +// ), +// subtitle: Text( +// product.SKU, +// style: TextStyle( +// color: isAlreadySelected +// ? Colors.grey +// : Colors.black, +// ), +// ), +// onTap: isAlreadySelected +// ? null +// : () { +// setState(() { +// provider +// .selectedProducts +// .add( +// product); +// }); +// Navigator.pop( +// context); +// }, +// ), +// ); +// }, +// ), +// ), +// ], +// ); +// }, +// ), +// ); +// }, +// ).whenComplete(() { +// setState(() {}); +// }); +// }, +// backgroundColor: Colors.white, +// icon: const Icon(Icons.add, color: Colors.black), +// label: const Text( +// 'Add Products', +// style: TextStyle(color: Colors.black), +// ), +// ), +// if (provider.selectedProducts.isNotEmpty) ...[ +// const SizedBox(height: 16.0), +// CommonElevatedButton( +// borderRadius: 30, +// width: double.infinity, +// height: kToolbarHeight - 10, +// text: 'SUBMIT', +// backgroundColor: const Color(0xff004791), +// onPressed: () { +// provider +// .submitSelectedProducts( +// (widget.distributorType == +// 'Principal Distributor') +// ? 'PrincipalDistributor' +// : 'RetailDistributor', +// widget.distributor.id!) +// .then((value) { +// if (value) { +// Navigator.push( +// context, +// MaterialPageRoute( +// builder: (context) => +// const DataSubmitSuccessfull(), +// ), +// ); +// } else { +// ScaffoldMessenger.of(context).showSnackBar( +// const SnackBar( +// content: Text("Insufficient stock"), +// ), +// ); +// } +// }); +// }, +// ), +// ], +// ], +// ), +// ), +// ), +// ], +// ); +// }, +// ), +// ), +// ); +// } +// } + + + + + class ProductBlock extends StatefulWidget { final int index; @@ -326,7 +719,7 @@ class _ProductBlockState extends State { enabled: true, onChanged: (_) => validateInput() ? provider.selectedProducts[widget.index].sale = - int.parse(saleController.text) + int.parse(saleController.text) : null, ), TextField( @@ -337,7 +730,7 @@ class _ProductBlockState extends State { enabled: true, onChanged: (_) => validateInput() ? provider.selectedProducts[widget.index].inventory = - int.parse(inventoryController.text) + int.parse(inventoryController.text) : null, ), if (errorMessage != null) @@ -354,3 +747,96 @@ class _ProductBlockState extends State { ); } } +// class ProductBlock extends StatefulWidget { +// final int index; +// +// const ProductBlock({super.key, required this.index}); +// +// @override +// State createState() => _ProductBlockState(); +// } +// +// class _ProductBlockState extends State { +// final saleController = TextEditingController(); +// final inventoryController = TextEditingController(); +// String? errorMessage; +// late ProductProvider provider; +// +// @override +// void initState() { +// super.initState(); +// provider = Provider.of(context, listen: false); +// } +// +// bool validateInput() { +// setState(() { +// if (saleController.text.isNotEmpty && +// inventoryController.text.isNotEmpty) { +// // Check if both inputs are valid integers +// try { +// int.parse(saleController.text); +// int.parse(inventoryController.text); +// } catch (e) { +// // Handle the case where input is not a valid integer +// errorMessage = 'Please enter valid integer values for both fields'; +// } +// } else { +// errorMessage = null; +// } +// }); +// return errorMessage == null; +// } +// +// @override +// Widget build(BuildContext context) { +// return Card( +// // color: !widget.product.isPurchased ? Colors.white54 : Colors.white, +// color: Colors.white, +// margin: const EdgeInsets.all(8), +// child: Padding( +// padding: const EdgeInsets.all(16), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Text( +// 'Product: ${provider.productList[widget.index].name.capitalize()}', +// style: const TextStyle(fontSize: 16)), +// Text('SKU: ${provider.productList[widget.index].SKU}', +// style: const TextStyle(fontSize: 15)), +// const SizedBox(height: 8), +// TextField( +// controller: saleController, +// decoration: const InputDecoration(labelText: 'Sale'), +// keyboardType: TextInputType.number, +// // enabled: widget.product.isPurchased, +// enabled: true, +// onChanged: (_) => validateInput() +// ? provider.productList[widget.index].sale = +// int.parse(saleController.text) +// : null, +// ), +// TextField( +// controller: inventoryController, +// decoration: const InputDecoration(labelText: 'Inventory'), +// keyboardType: TextInputType.number, +// // enabled: widget.product.isPurchased, +// enabled: true, +// onChanged: (_) => validateInput() +// ? provider.productList[widget.index].inventory = +// int.parse(inventoryController.text) +// : null, +// ), +// if (errorMessage != null) +// Padding( +// padding: const EdgeInsets.only(top: 8.0), +// child: Text( +// errorMessage!, +// style: const TextStyle(color: Colors.red), +// ), +// ), +// ], +// ), +// ), +// ); +// } +// } diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 532363b..cdb1f91 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -173,15 +173,15 @@ class _HomePageState extends State { Expanded( child: _buildCustomCard('Update\nSales Data\n', '', onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - const SelectDistributerScreen( - task: "Update Sales", - ), - ), - ); + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (context) => + // const SelectDistributerScreen( + // task: "Update Sales", + // ), + // ), + // ); }), ), ], diff --git a/lib/screens/notification_screen.dart b/lib/screens/notification_screen.dart index 56cdec7..147331a 100644 --- a/lib/screens/notification_screen.dart +++ b/lib/screens/notification_screen.dart @@ -7,6 +7,8 @@ import 'package:cheminova/widgets/common_elevated_button.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; +import '../models/notification_list_response.dart'; + class NotificationScreen extends StatefulWidget { const NotificationScreen({super.key}); @@ -23,16 +25,33 @@ class NotificationScreenState extends State { super.initState(); } + // Future _selectDate(BuildContext context) async { + // final provider = context.read(); + // final dateSelected = await showDatePicker( + // context: context, + // initialDate: DateTime.now(), + // firstDate: DateTime(2000), + // lastDate: DateTime(2025), + // ); + // if (dateSelected != null) { + // provider.setDate(DateFormat('dd/MM/yyyy').format(dateSelected)); + // } + // } + + Future _selectDate(BuildContext context) async { - final provider = context.read(); - final dateSelected = await showDatePicker( + DateTime? selectedDate = await showDatePicker( context: context, initialDate: DateTime.now(), - firstDate: DateTime(2000), - lastDate: DateTime(2025), + firstDate: DateTime(2020), // Adjust the start date as needed + lastDate: DateTime.now(), ); - if (dateSelected != null) { - provider.setDate(DateFormat('dd/MM/yyyy').format(dateSelected)); + + if (selectedDate != null) { + // Format the date to dd/MM/yyyy + String formattedDate = DateFormat('dd/MM/yyyy').format(selectedDate); + // Call the API with the selected date + _notificationProvider.getNotification(formattedDate); } } @@ -61,39 +80,77 @@ class NotificationScreenState extends State { fontFamily: 'Anek')), backgroundColor: Colors.transparent, elevation: 0, - ), - drawer: const CommonDrawer(), - body: Consumer( - builder: (context, value, child) => value.isLoading - ? const Center(child: CircularProgressIndicator()) - : Column( + bottom: PreferredSize( + preferredSize: Size.fromHeight(80), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: TextButton( + onPressed: () { + _selectDate(context); + }, + child: + Row( children: [ - Container( - margin: const EdgeInsets.all(10), - padding: const EdgeInsets.symmetric(horizontal: 10), - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.all(Radius.circular(10)), - ), - child: Row( - // mainAxisAlignment: MainAxisAlignment.spaceAround, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - value.selectedDate, - ), - IconButton( - onPressed: () { - _selectDate(context); - }, - icon: const Icon(Icons.calendar_month), - ), - ], - ), + Text("Select Date: ",style: TextStyle(fontWeight: FontWeight.bold,fontSize: 18),), + IconButton( + icon: const Icon(Icons.calendar_today), + onPressed: () { + _selectDate(context); + }, ), - MyListView(value: value), ], ), + ), + ), + ), + ), + drawer: const CommonDrawer(), + body: + // Consumer( + // // builder: (context, value, child) => value.isLoading + // // ? const Center(child: CircularProgressIndicator()) + // // : Column( + // // children: [ + // // Container( + // // margin: const EdgeInsets.all(10), + // // padding: const EdgeInsets.symmetric(horizontal: 10), + // // decoration: const BoxDecoration( + // // color: Colors.white, + // // borderRadius: BorderRadius.all(Radius.circular(10)), + // // ), + // // child: Row( + // // // mainAxisAlignment: MainAxisAlignment.spaceAround, + // // mainAxisAlignment: MainAxisAlignment.center, + // // children: [ + // // Text( + // // value.selectedDate, + // // ), + // // IconButton( + // // onPressed: () { + // // _selectDate(context); + // // }, + // // icon: const Icon(Icons.calendar_month), + // // ), + // // ], + // // ), + // // ), + // // MyListView(value: value), + // // ], + // // ), + // // ), + + Consumer( + builder: (context, value, child) { + if (value.isLoading) { + return const Center(child: CircularProgressIndicator()); + } else if (value.notificationList.isEmpty) { + // Handle empty notification list + return const Center(child: Text("No notifications available")); + } else { + // Show the notification list once data is fetched + return MyListView(value: value); + } + }, ), ), ), @@ -125,38 +182,95 @@ class MyListView extends StatelessWidget { @override Widget build(BuildContext context) { - return Expanded( - child: (value.notificationList.isEmpty) - ? const Center( - child: Text( - 'No notifications for selected date', - style: TextStyle(fontSize: 20, color: Colors.white), - ), - ) - : ListView.builder( - padding: const EdgeInsets.only(top: 15), - itemCount: value.notificationList.length, - itemBuilder: (context, index) { - return Padding( - padding: - const EdgeInsets.only(bottom: 10, left: 10, right: 10), - child: Padding( - padding: const EdgeInsets.only(bottom: 10), - child: ExpansionTile( - collapsedBackgroundColor: Colors.white, - backgroundColor: Colors.white, - trailing: const SizedBox.shrink(), - title: Text( - value.notificationList[index].title ?? '', - style: const TextStyle( - fontSize: 17, fontWeight: FontWeight.w500), - ), - subtitle: Text(value.notificationList[index].msg ?? ''), - ), + + + Map> groupedNotifications = {}; + + // Iterate over the notification list and group by formatted date + for (var notification in value.notificationList) { + String date = DateFormat("dd MMM yyyy").format(DateTime.parse(notification.createdAt ?? '')); + if (!groupedNotifications.containsKey(date)) { + groupedNotifications[date] = []; + } + groupedNotifications[date]!.add(notification); + } + if (groupedNotifications.isEmpty) { + return const Center(child: Text("No notifications available")); + } + + return + // Expanded( + // child: (value.notificationList.isEmpty) + // ? const Center( + // child: Text( + // 'No notifications available', + // style: TextStyle(fontSize: 20, color: Colors.white), + // ), + // ) + // ListView.builder( + // padding: const EdgeInsets.only(top: 15), + // itemCount: value.notificationList.length, + // itemBuilder: (context, index) { + // return Padding( + // padding: + // const EdgeInsets.only(bottom: 10, left: 10, right: 10), + // child: Padding( + // padding: const EdgeInsets.only(bottom: 10), + // child: ExpansionTile( + // collapsedBackgroundColor: Colors.white, + // backgroundColor: Colors.white, + // trailing: const SizedBox.shrink(), + // title: Text( + // value.notificationList[index].title ?? '', + // style: const TextStyle( + // fontSize: 17, fontWeight: FontWeight.w500), + // ), + // subtitle: Text(value.notificationList[index].msg ?? ''), + // ), + // ), + // ); + // }, + // + // ); + + ListView.builder( + padding: const EdgeInsets.only(top: 15), + itemCount: groupedNotifications.length, // Number of date groups + itemBuilder: (context, index) { + String date = groupedNotifications.keys.elementAt(index); + List notificationsForDate = groupedNotifications[date]!; + + return Padding( + padding: const EdgeInsets.only(bottom: 10, left: 10, right: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Display the date for each group of notifications + Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Text( + date, + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), - ); - }, + ), + // Display each notification in an expandable tile + ...notificationsForDate.map((item) => Padding( + padding: const EdgeInsets.only(bottom: 10), + child: ExpansionTile( + collapsedBackgroundColor: Colors.white, + backgroundColor: Colors.white, + trailing: const SizedBox.shrink(), // Remove trailing icon + title: Text( + item.title ?? '', + style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w500), + ), + subtitle: Text(item.msg ?? ''), + ), + )), + ], ), - ); + ); + }, + ); } } diff --git a/lib/widgets/common_app_bar.dart b/lib/widgets/common_app_bar.dart index f614528..9fff211 100644 --- a/lib/widgets/common_app_bar.dart +++ b/lib/widgets/common_app_bar.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; class CommonAppBar extends StatelessWidget implements PreferredSizeWidget { final Widget title; final List? actions; - final TabBar? bottom; + final PreferredSizeWidget? bottom; const CommonAppBar({super.key, required this.title, this.actions, required Color backgroundColor, required int elevation, this.bottom}); @@ -24,6 +24,8 @@ class CommonAppBar extends StatelessWidget implements PreferredSizeWidget { actions: actions); } + // @override + // Size get preferredSize => const Size.fromHeight(kToolbarHeight); @override - Size get preferredSize => const Size.fromHeight(kToolbarHeight); + Size get preferredSize => Size.fromHeight(kToolbarHeight + (bottom?.preferredSize.height ?? 0)); } diff --git a/lib/widgets/common_drawer.dart b/lib/widgets/common_drawer.dart index a3273a4..88fce20 100644 --- a/lib/widgets/common_drawer.dart +++ b/lib/widgets/common_drawer.dart @@ -1,22 +1,180 @@ +// import 'package:cheminova/provider/user_provider.dart'; +// import 'package:cheminova/screens/change_password_screen.dart'; +// import 'package:cheminova/screens/home_screen.dart'; +// import 'package:cheminova/screens/login_screen.dart'; +// import 'package:cheminova/screens/profile_screen.dart'; +// import 'package:flutter/material.dart'; +// import 'package:provider/provider.dart'; +// +// class CommonDrawer extends StatelessWidget { +// const CommonDrawer({super.key}); +// +// @override +// Widget build(BuildContext context) { +// return Drawer( +// child: ListView( +// padding: EdgeInsets.zero, +// children: [ +// SizedBox( +// height: 150, +// child: DrawerHeader( +// decoration: const BoxDecoration( +// color: Colors.black87, +// ), +// child: Consumer( +// builder: (context, userProvider, child) { +// if (userProvider.isLoading) { +// return const Center(child: CircularProgressIndicator()); +// } else if (userProvider.user != null) { +// return Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// mainAxisAlignment: MainAxisAlignment.start, +// children: [ +// Text( +// userProvider.user!.name, +// style: const TextStyle( +// color: Colors.white, +// fontSize: 18, +// ), +// ), +// Text( +// userProvider.user!.uniqueId, +// style: const TextStyle( +// color: Colors.white, +// fontSize: 20, +// ), +// ), +// ], +// ); +// } else { +// return const Text( +// 'No User Data', +// style: TextStyle( +// color: Colors.white, +// fontSize: 18, +// ), +// ); +// } +// }, +// ), +// ), +// ), +// ListTile( +// leading: const Icon(Icons.home), +// title: const Text('Home'), +// onTap: () { +// Navigator.pushReplacement( +// context, +// MaterialPageRoute(builder: (context) => const HomePage()), +// ); +// }, +// ), +// ListTile( +// leading: const Icon(Icons.account_circle), +// title: const Text('Profile'), +// onTap: () { +// Navigator.push( +// context, +// MaterialPageRoute( +// builder: (context) { +// return const ProfileScreen(); +// }, +// ), +// ); +// }, +// ), +// ListTile( +// leading: const Icon(Icons.settings), +// title: const Text('Change Password'), +// onTap: () { +// Navigator.push( +// context, +// MaterialPageRoute( +// builder: (context) => const ChangePasswordPage()), +// ); +// }, +// ), +// ListTile( +// leading: const Icon(Icons.exit_to_app), +// title: const Text('Logout'), +// onTap: () { +// WidgetsBinding.instance.addPostFrameCallback((_) { +// Provider.of(context, listen: false) +// .clearUserProfile(); +// }); +// Navigator.pushReplacement( +// context, +// MaterialPageRoute(builder: (context) => const LoginPage()), +// ); +// }, +// ), +// +// +// SizedBox( +// height: 700, +// child: Padding( +// padding: const EdgeInsets.all(8.0), +// child: Center( +// child: Text( +// 'Version 2.0.0', +// style: TextStyle(color: Colors.grey[600], fontSize: 12), +// ), +// ), +// ), +// ), +// +// ], +// ), +// ); +// } +// } + + + import 'package:cheminova/provider/user_provider.dart'; import 'package:cheminova/screens/change_password_screen.dart'; import 'package:cheminova/screens/home_screen.dart'; import 'package:cheminova/screens/login_screen.dart'; import 'package:cheminova/screens/profile_screen.dart'; import 'package:flutter/material.dart'; +import 'package:package_info_plus/package_info_plus.dart'; import 'package:provider/provider.dart'; -class CommonDrawer extends StatelessWidget { +class CommonDrawer extends StatefulWidget { const CommonDrawer({super.key}); + @override + State createState() => _CommonDrawerState(); +} + +class _CommonDrawerState extends State { + + + String _appVersion = ''; + + @override + void initState() { + super.initState(); + _getAppVersion(); + } + + Future _getAppVersion() async { + PackageInfo packageInfo = await PackageInfo.fromPlatform( + + ); + setState(() { + _appVersion = packageInfo.version; // 1.0.0 + }); + } + @override Widget build(BuildContext context) { return Drawer( - child: ListView( - padding: EdgeInsets.zero, + child: Column( children: [ SizedBox( height: 150, + width:double.infinity, child: DrawerHeader( decoration: const BoxDecoration( color: Colors.black87, @@ -108,6 +266,14 @@ class CommonDrawer extends StatelessWidget { ); }, ), + const Spacer(), // Push the version text to the bottom + Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: Text( + 'Version: $_appVersion', + style: TextStyle(color: Colors.grey[600], fontSize: 12), + ), + ), ], ), ); diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 36ffc87..caa3990 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -14,6 +14,7 @@ import firebase_messaging import flutter_local_notifications import flutter_secure_storage_macos import geolocator_apple +import package_info_plus import path_provider_foundation import syncfusion_pdfviewer_macos import url_launcher_macos @@ -28,6 +29,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) + FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SyncfusionFlutterPdfViewerPlugin.register(with: registry.registrar(forPlugin: "SyncfusionFlutterPdfViewerPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 8af8eb8..aac7b7c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -25,6 +25,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.4.1" + archive: + dependency: transitive + description: + name: archive + sha256: "6199c74e3db4fbfbd04f66d739e72fe11c8a8957d5f219f1f4482dbde6420b5a" + url: "https://pub.dev" + source: hosted + version: "4.0.2" args: dependency: transitive description: @@ -49,70 +57,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" - build: - dependency: transitive - description: - name: build - sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - build_config: - dependency: transitive - description: - name: build_config - sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 - url: "https://pub.dev" - source: hosted - version: "1.1.1" - build_daemon: - dependency: transitive - description: - name: build_daemon - sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" - url: "https://pub.dev" - source: hosted - version: "4.0.2" - build_resolvers: - dependency: transitive - description: - name: build_resolvers - sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" - url: "https://pub.dev" - source: hosted - version: "2.4.2" - build_runner: - dependency: "direct dev" - description: - name: build_runner - sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7" - url: "https://pub.dev" - source: hosted - version: "2.4.11" - build_runner_core: - dependency: transitive - description: - name: build_runner_core - sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe - url: "https://pub.dev" - source: hosted - version: "7.3.1" - built_collection: - dependency: transitive - description: - name: built_collection - sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://pub.dev" - source: hosted - version: "5.1.1" - built_value: - dependency: transitive - description: - name: built_value - sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb - url: "https://pub.dev" - source: hosted - version: "8.9.2" characters: dependency: transitive description: @@ -129,6 +73,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c + url: "https://pub.dev" + source: hosted + version: "0.4.2" clock: dependency: transitive description: @@ -137,14 +89,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" - code_builder: - dependency: transitive - description: - name: code_builder - sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 - url: "https://pub.dev" - source: hosted - version: "4.10.0" collection: dependency: transitive description: @@ -430,22 +374,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.6.0" - flutter_gen_runner: + flutter_launcher_icons: dependency: "direct dev" description: - name: flutter_gen_runner - sha256: "931b03f77c164df0a4815aac0efc619a6ac8ec4cada55025119fca4894dada90" + name: flutter_launcher_icons + sha256: bfa04787c85d80ecb3f8777bde5fc10c3de809240c48fa061a2c2bf15ea5211c url: "https://pub.dev" source: hosted - version: "5.6.0" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" - url: "https://pub.dev" - source: hosted - version: "4.0.0" + version: "0.14.3" flutter_local_notifications: dependency: "direct main" description: @@ -544,14 +480,6 @@ packages: description: flutter source: sdk version: "0.0.0" - frontend_server_client: - dependency: transitive - description: - name: frontend_server_client - sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 - url: "https://pub.dev" - source: hosted - version: "4.0.0" geocoding: dependency: "direct main" description: @@ -640,14 +568,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" - graphs: - dependency: transitive - description: - name: graphs - sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" - url: "https://pub.dev" - source: hosted - version: "2.3.2" hashcodes: dependency: transitive description: @@ -664,14 +584,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.2" - http_multi_server: - dependency: transitive - description: - name: http_multi_server - sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" - url: "https://pub.dev" - source: hosted - version: "3.2.1" http_parser: dependency: transitive description: @@ -680,6 +592,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + image: + dependency: transitive + description: + name: image + sha256: "8346ad4b5173924b5ddddab782fc7d8a6300178c8b1dc427775405a01701c4a6" + url: "https://pub.dev" + source: hosted + version: "4.5.2" image_picker: dependency: "direct main" description: @@ -760,14 +680,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.19.0" - io: - dependency: transitive - description: - name: io - sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" - url: "https://pub.dev" - source: hosted - version: "1.0.4" js: dependency: transitive description: @@ -808,22 +720,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" - lints: - dependency: transitive - description: - name: lints - sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - logging: - dependency: transitive - description: - name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" - url: "https://pub.dev" - source: hosted - version: "1.2.0" matcher: dependency: transitive description: @@ -872,6 +768,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + sha256: "739e0a5c3c4055152520fa321d0645ee98e932718b4c8efeeb51451968fe0790" + url: "https://pub.dev" + source: hosted + version: "8.1.3" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: a5ef9986efc7bf772f2696183a3992615baa76c1ffb1189318dd8803778fb05b + url: "https://pub.dev" + source: hosted + version: "3.0.2" path: dependency: transitive description: @@ -1016,14 +928,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" - pool: + posix: dependency: transitive description: - name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + name: posix + sha256: a0117dc2167805aa9125b82eee515cc891819bac2f538c83646d355b16f58b9a url: "https://pub.dev" source: hosted - version: "1.5.1" + version: "6.0.1" pretty_dio_logger: dependency: "direct main" description: @@ -1048,14 +960,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - pubspec_parse: - dependency: transitive - description: - name: pubspec_parse - sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 - url: "https://pub.dev" - source: hosted - version: "1.3.0" searchfield: dependency: "direct main" description: @@ -1064,22 +968,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.9" - shelf: - dependency: transitive - description: - name: shelf - sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 - url: "https://pub.dev" - source: hosted - version: "1.4.1" - shelf_web_socket: - dependency: transitive - description: - name: shelf_web_socket - sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" - url: "https://pub.dev" - source: hosted - version: "2.0.0" simple_gesture_detector: dependency: transitive description: @@ -1125,14 +1013,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" - stream_transform: - dependency: transitive - description: - name: stream_transform - sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" - url: "https://pub.dev" - source: hosted - version: "2.1.0" string_scanner: dependency: transitive description: @@ -1245,14 +1125,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.9.4" - timing: - dependency: transitive - description: - name: timing - sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" - url: "https://pub.dev" - source: hosted - version: "1.0.1" typed_data: dependency: transitive description: @@ -1397,22 +1269,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.1" - web_socket: - dependency: transitive - description: - name: web_socket - sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" - url: "https://pub.dev" - source: hosted - version: "0.1.6" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" - url: "https://pub.dev" - source: hosted - version: "3.0.1" win32: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 62ee9ad..bf69650 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.0+1 +version: 2.0.1+1 environment: sdk: '>=3.4.3 <4.0.0' @@ -52,10 +52,18 @@ dependencies: firebase_analytics: ^11.2.1 searchfield: ^1.0.9 syncfusion_flutter_pdfviewer: ^26.2.11 + package_info_plus: ^8.1.3 dev_dependencies: flutter_test: sdk: flutter + flutter_launcher_icons: ^0.14.3 + + +flutter_launcher_icons: + android: "launcher_icon" + ios: true + image_path: "assets/app-icon-territory-manager.png" flutter_lints: ^4.0.0 build_runner: