google location module added

master
Sneha 3 months ago
parent d4966a9113
commit 7f738be8ee

@ -1,58 +1,72 @@
plugins {
id "com.android.application"
id "kotlin-android"
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id "dev.flutter.flutter-gradle-plugin"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file("local.properties")
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader("UTF-8") { reader ->
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterVersionCode = localProperties.getProperty("flutter.versionCode")
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = "1"
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty("flutter.versionName")
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = "1.0"
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
namespace = "com.arminta.supplier_new"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileSdkVersion 34
ndkVersion flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
freeCompilerArgs += ['-Xskip-metadata-version-check']
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.arminta.supplier_new"
applicationId "com.arminta.supplier_new"
// 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
targetSdk = flutter.targetSdkVersion
versionCode = flutterVersionCode.toInteger()
versionName = flutterVersionName
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
minSdkVersion 28
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.debug
signingConfig signingConfigs.debug
}
}
}
flutter {
source = "../.."
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

@ -1,7 +1,11 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.arminta.supplier_new">>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:label="supplier_new"
android:label="@string/appName"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
@ -31,6 +35,11 @@
<meta-data
android:name="flutterEmbedding"
android:value="2" />
<meta-data android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyDJpK9RVhlBejtJu9xSGfneuTN6HOfJgSM"/>
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-3940256099942544~4354546703"/>
</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

@ -1,3 +1,16 @@
buildscript {
ext.kotlin_version = '1.9.0'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
@ -5,12 +18,21 @@ allprojects {
}
}
rootProject.buildDir = "../build"
// Add this block to apply the Kotlin flag to all subprojects
subprojects {
afterEvaluate { project ->
project.tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { task ->
task.kotlinOptions.freeCompilerArgs += ['-Xskip-metadata-version-check']
}
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(":app")
project.evaluationDependsOn(':app')
}
tasks.register("clean", Delete) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 826 B

@ -427,7 +427,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@ -484,7 +484,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 759 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 522 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 B

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 759 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 762 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

@ -5,7 +5,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Supplier New</string>
<string>${PRODUCT_NAME}</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
@ -46,4 +46,4 @@
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>
</plist>

@ -0,0 +1,98 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:geocoding/geocoding.dart';
class LocationPage extends StatefulWidget {
const LocationPage({Key? key}) : super(key: key);
@override
State<LocationPage> createState() => _LocationPageState();
}
class _LocationPageState extends State<LocationPage> {
String? _currentAddress;
Position? _currentPosition;
Future<bool> _handleLocationPermission() async {
bool serviceEnabled;
LocationPermission permission;
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text(
'Location services are disabled. Please enable the services')));
return false;
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Location permissions are denied')));
return false;
}
}
if (permission == LocationPermission.deniedForever) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text(
'Location permissions are permanently denied, we cannot request permissions.')));
return false;
}
return true;
}
Future<void> _getCurrentPosition() async {
final hasPermission = await _handleLocationPermission();
if (!hasPermission) return;
await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high)
.then((Position position) {
setState(() => _currentPosition = position);
_getAddressFromLatLng(_currentPosition!);
}).catchError((e) {
debugPrint(e);
});
}
Future<void> _getAddressFromLatLng(Position position) async {
await placemarkFromCoordinates(
_currentPosition!.latitude, _currentPosition!.longitude)
.then((List<Placemark> placemarks) {
Placemark place = placemarks[0];
_currentAddress = '${place.street}, ${place.subLocality}, ${place.locality}, ${place.postalCode}, ${place.country}';
print(place);
setState(() {
});
}).catchError((e) {
debugPrint(e);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Location Page")),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('LAT: ${_currentPosition?.latitude ?? ""}'),
Text('LNG: ${_currentPosition?.longitude ?? ""}'),
Text('ADDRESS: ${_currentAddress ?? ""}'),
const SizedBox(height:32),
ElevatedButton(
onPressed: _getCurrentPosition,
child: const Text("Get Current Location"),
)
],
),
),
),
);
}
}

@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:image_picker/image_picker.dart';
import 'package:supplier_new/common/settings.dart';
import 'package:supplier_new/financials/financial_main_screen.dart';
import 'package:supplier_new/orders/all_orders.dart';
import 'package:supplier_new/orders/order_requests.dart';
import 'package:supplier_new/plans/all_plans.dart';
@ -39,7 +40,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
AllOrders(),
AllPlans(),
FleetStep1Page(),
HomeScreen(),
FinancialMainScreen(),
];
// List of bottom navigation bar items
final List<BottomNavigationBarItem> _bottomNavItems = [

@ -0,0 +1,6 @@
class APIKeys {
APIKeys._();
static String androidApiKey = "AIzaSyDJpK9RVhlBejtJu9xSGfneuTN6HOfJgSM";
static String iosApiKey = "YOUR IOS KEY HERE";
}

@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:fluttertoast/fluttertoast.dart';
import 'package:intl/intl.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:supplier_new/common/preloader.dart';
@ -122,9 +123,9 @@ class AppSettings{
static String email = '';
static String phoneNumber = '';
static String accessToken = '';
static String customerId = '';
static double userLatitude = 0;
static double userLongitude = 0;
static String supplierId = '';
static double supplierLatitude = 0;
static double supplierLongitude = 0;
static String customerIdsign = '';
static String profileImage = '';
static String preloadText = 'Please wait';
@ -139,7 +140,20 @@ class AppSettings{
static String uploadPicUrl = host + 'uploads-user';
static String getOrderRequestsFromUsersUrl = host + 'getuserRequestbookingsforsupplier';
static String formDouble(dynamic s) {
var comma = NumberFormat('#,##,###.##', 'en_IN');
if (s == null) return "0.00";
String str = s.toString().trim();
if (str.isEmpty) return "0.00";
try {
return comma.format(double.parse(str));
} catch (e) {
return "0.00"; // fallback if it's not a valid number
}
}
// Shared preferences save,get and clear data
static saveData(String _key, _value, type) async {
@ -256,7 +270,7 @@ class AppSettings{
}
static Future<bool> resetToken() async {
var uri = Uri.parse(resetTokenUrl + '/' + customerId);
var uri = Uri.parse(resetTokenUrl + '/' + supplierId);
try {
// var response = await http.get(uri, headers: await buildPutRequestHeaders());
@ -335,24 +349,24 @@ class AppSettings{
static Future<String> uploadrofileImageHTTPNew(file) async {
var request = http.MultipartRequest(
'POST', Uri.parse(uploadPicUrl + '/' + customerId));
'POST', Uri.parse(uploadPicUrl + '/' + supplierId));
request.files.add(await http.MultipartFile.fromPath('picture', file.path));
var res = await request.send();
var response = await http.Response.fromStream(res);
return response.body;
}
static Future<String> getOrderRequestsFromUsers(payload) async {
var uri = Uri.parse(getOrderRequestsFromUsersUrl);
static Future<String> getOrderRequestsFromUsers() async {
var uri = Uri.parse(getOrderRequestsFromUsersUrl+'/'+supplierId);
//uri = uri.replace(query: 'customerId=$customerId');
var response = await http.post(uri, body: json.encode(payload), headers: await buildRequestHeaders());
var response = await http.get(uri, headers: await buildRequestHeaders());
if (response.statusCode == 200) {
return response.body;
} else if (response.statusCode == 401) {
bool status = await AppSettings.resetToken();
if (status) {
response = await http.post(uri, headers: await buildRequestHeaders());
response = await http.get(uri, headers: await buildRequestHeaders());
if (response.statusCode == 200) {
return response.body;
} else {
@ -381,7 +395,7 @@ class AppSettings{
'access_token', input['simplydata']['access_token'], 'STRING');
await saveData('phone', input['simplydata']['phone'], 'STRING');
await saveData('email', input['simplydata']['email'][0]['email'], 'STRING');
await saveData('customerId', input['simplydata']['customerId'], 'STRING');
await saveData('supplierId', input['simplydata']['supplierId'], 'STRING');
await saveData('profile', input['simplydata']['picture'], 'STRING');
await saveData('user_address', input['simplydata']['address1'], 'STRING');
if(input['simplydata']['latitude']==0){
@ -417,9 +431,9 @@ class AppSettings{
email = await getData('email', 'STRING');
userAddress = await getData('user_address', 'STRING');
phoneNumber = await getData('phone', 'STRING');
customerId = await getData('customerId', 'STRING');
userLatitude = await getData('latitude', 'DOUBLE');
userLongitude = await getData('longitude', 'DOUBLE');
supplierId = await getData('supplierId', 'STRING');
supplierLatitude = await getData('latitude', 'DOUBLE');
supplierLongitude = await getData('longitude', 'DOUBLE');
//profilePictureUrl = await getData('profile', 'STRING');
// fcmId = await getData('fcmId', 'STRING');
//profileImage=await getData('profile', 'STRING');
@ -548,4 +562,53 @@ class AppSettings{
],
);
}
static SupplierAppBarWithHelpAction(String title,context) {
title = title ?? '';
return AppBar(
backgroundColor: Colors.white,
elevation: 0,
scrolledUnderElevation: 0,
title: Text(
title,
style: fontTextStyle(16, Color(0XFF2A2A2A), FontWeight.w600),
),
iconTheme: IconThemeData(color: Color(0XFF2A2A2A)),
actions: [
Row(
children: [
Padding(
padding: EdgeInsets.fromLTRB(0, 10, 10, 10),
child: IconButton(
icon: Image.asset(
'images/help_appbar.png',
height: 20,
width: 20,// Example URL image
),
onPressed: () {},
),
)
],
)
],
leading: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Padding(
padding:
const EdgeInsets.fromLTRB(8, 8, 8, 8), // Add padding if needed
child: Image.asset(
'images/backbutton_appbar.png', // Replace with your image path
fit: BoxFit.contain,
color: Color(0XFF2A2A2A),
height: 24,
width: 24,
),
),
),
);
}
}

@ -0,0 +1,237 @@
import 'package:flutter/material.dart';
import 'package:supplier_new/common/settings.dart';
class FinancialMainScreen extends StatefulWidget {
const FinancialMainScreen({super.key});
@override
State<FinancialMainScreen> createState() => _FinancialMainScreenState();
}
class _FinancialMainScreenState extends State<FinancialMainScreen>
with SingleTickerProviderStateMixin {
late TabController _tabController;
final transactions = [
{
"name": "My Home Bhooja",
"date": "21 August",
"amount": "+ ₹2,580",
"color": Colors.green,
"status": "success",
},
{
"name": "Ramakrishna",
"date": "20 August",
"amount": "- ₹748",
"color": Colors.red,
"status": "success",
},
{
"name": "Malleshan Water Supplies",
"date": "21 August",
"amount": "- ₹10,000",
"color": Colors.red,
"status": "success",
},
{
"name": "My Home Bhooja",
"date": "21 August",
"amount": "+ ₹2,580",
"color": Colors.grey,
"status": "failed",
},
{
"name": "My Home Bhooja",
"date": "21 August",
"amount": "+ ₹2,580",
"color": Colors.green,
"status": "success",
},
];
@override
void initState() {
super.initState();
_tabController = TabController(length: 2, vsync: this);
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
Widget Transactions(){
return Container(
color: Color(0XFFFFFFFF),
child:Column(
children: [
// Filters row
SingleChildScrollView(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Row(
children: [
_buildFilterChip("Status"),
_buildFilterChip("Payment Method"),
_buildFilterChip("Date"),
_buildFilterChip("Amount"),
],
),
),
const Divider(height: 1),
// Transactions list
Expanded(
child: ListView.builder(
itemCount: transactions.length,
itemBuilder: (context, index) {
final txn = transactions[index];
return ListTile(
leading: const CircleAvatar(
backgroundColor: Colors.blue,
child: Icon(Icons.person, color: Colors.white),
),
title: Text(txn["name"].toString()),
subtitle: Text(txn["date"].toString()),
trailing: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
txn["amount"].toString(),
style: TextStyle(
color: txn["color"] as Color,
fontWeight: FontWeight.bold,
),
),
if (txn["status"] == "failed")
const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.error, color: Colors.red, size: 14),
SizedBox(width: 4),
Text(
"Failed",
style: TextStyle(
color: Colors.red, fontSize: 12),
),
],
),
],
),
);
},
),
),
],
),
);
}
Widget CreditAccounts(){
return Container(
color: Color(0XFFFFFFFF),
child:Center(
child: Text("Credit Accounts Coming Soon..."),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar:AppBar(
elevation: 0,
backgroundColor: Colors.white,
title: Text(
'Financials',
style: fontTextStyle(14, Color(0XFF2A2A2A), FontWeight.w500),
),
iconTheme: IconThemeData(color: Color(0XFF2A2A2A)),
leading: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Padding(
padding: const EdgeInsets.fromLTRB(
8, 8, 8, 8), // Add padding if needed
child: Image.asset(
'images/backbutton_appbar.png',
height: 24,
width: 24,// Replace with your image path
fit: BoxFit.contain, // Adjust the fit
),
),
),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(50.0),
child: Container(
height: 38,
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: AnimatedBuilder(
animation: _tabController,
builder: (context, _) {
return TabBar(
controller: _tabController,
indicatorColor: Colors.transparent, // remove underline
dividerColor: Colors.transparent,
isScrollable: false,
overlayColor: MaterialStateProperty.all(Colors.transparent),// equal width
tabs: List.generate(2, (index) {
final labels = ['Transactions', 'Credit Accounts'];
final isSelected = _tabController.index == index;
return Container(
decoration: BoxDecoration(
color: isSelected ? const Color(0XFFF1F1F1) : Colors.transparent,
borderRadius: BorderRadius.circular(27),
),
alignment: Alignment.center,
child: Text(
labels[index],
style: fontTextStyle(
12,
const Color(0XFF101214),
FontWeight.w600,
),
),
);
}),
);
},
),
),
),
),
body: TabBarView(
controller: _tabController,
children: [
// Tab 1: Transactions
Transactions(),
// Tab 2: Credit Accounts
CreditAccounts()
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.add),
),
);
}
Widget _buildFilterChip(String text) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: FilterChip(
label: Text(text),
onSelected: (val) {},
),
);
}
}

@ -0,0 +1,7 @@
library google_maps_place_picker_mb;
export 'src/models/pick_result.dart';
export 'src/components/floating_card.dart';
export 'src/components/rounded_frame.dart';
export 'src/models/circle_area.dart';
export 'src/place_picker.dart';

@ -0,0 +1,161 @@
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:google_maps_webservice/geocoding.dart';
import 'package:google_maps_webservice/places.dart';
import 'package:http/http.dart';
import 'package:provider/provider.dart';
import '../src/models/pick_result.dart';
import '../src/place_picker.dart';
class PlaceProvider extends ChangeNotifier {
PlaceProvider(
String apiKey,
String? proxyBaseUrl,
Client? httpClient,
Map<String, dynamic> apiHeaders,
) {
places = GoogleMapsPlaces(
apiKey: apiKey,
baseUrl: proxyBaseUrl,
httpClient: httpClient,
apiHeaders: apiHeaders as Map<String, String>?,
);
geocoding = GoogleMapsGeocoding(
apiKey: apiKey,
baseUrl: proxyBaseUrl,
httpClient: httpClient,
apiHeaders: apiHeaders as Map<String, String>?,
);
}
static PlaceProvider of(BuildContext context, {bool listen = true}) =>
Provider.of<PlaceProvider>(context, listen: listen);
late GoogleMapsPlaces places;
late GoogleMapsGeocoding geocoding;
String? sessionToken;
bool isOnUpdateLocationCooldown = false;
LocationAccuracy? desiredAccuracy;
bool isAutoCompleteSearching = false;
Future<void> updateCurrentLocation(bool forceAndroidLocationManager) async {
bool serviceEnabled;
LocationPermission permission;
// Test if location services are enabled.
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
// Location services are not enabled don't continue
// accessing the position and request users of the
// App to enable the location services.
return Future.error('Location services are disabled.');
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
// Permissions are denied, next time you could try
// requesting permissions again (this is also where
// Android's shouldShowRequestPermissionRationale
// returned true. According to Android guidelines
// your App should show an explanatory UI now.
return Future.error('Location permissions are denied');
}
}
if (permission == LocationPermission.deniedForever) {
// Permissions are denied forever, handle appropriately.
return Future.error(
'Location permissions are permanently denied, we cannot request permissions.');
}
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.best,
);
currentPosition = position;
// notifyListeners();
}
Position? _currentPosition;
Position? get currentPosition => _currentPosition;
set currentPosition(Position? newPosition) {
_currentPosition = newPosition;
notifyListeners();
}
Timer? _debounceTimer;
Timer? get debounceTimer => _debounceTimer;
set debounceTimer(Timer? timer) {
_debounceTimer = timer;
notifyListeners();
}
CameraPosition? _previousCameraPosition;
CameraPosition? get prevCameraPosition => _previousCameraPosition;
setPrevCameraPosition(CameraPosition? prePosition) {
_previousCameraPosition = prePosition;
}
CameraPosition? _currentCameraPosition;
CameraPosition? get cameraPosition => _currentCameraPosition;
setCameraPosition(CameraPosition? newPosition) {
_currentCameraPosition = newPosition;
}
PickResult? _selectedPlace;
PickResult? get selectedPlace => _selectedPlace;
set selectedPlace(PickResult? result) {
_selectedPlace = result;
notifyListeners();
}
SearchingState _placeSearchingState = SearchingState.Idle;
SearchingState get placeSearchingState => _placeSearchingState;
set placeSearchingState(SearchingState newState) {
_placeSearchingState = newState;
notifyListeners();
}
GoogleMapController? _mapController;
GoogleMapController? get mapController => _mapController;
set mapController(GoogleMapController? controller) {
_mapController = controller;
notifyListeners();
}
PinState _pinState = PinState.Preparing;
PinState get pinState => _pinState;
set pinState(PinState newState) {
_pinState = newState;
notifyListeners();
}
bool _isSeachBarFocused = false;
bool get isSearchBarFocused => _isSeachBarFocused;
set isSearchBarFocused(bool focused) {
_isSeachBarFocused = focused;
notifyListeners();
}
MapType _mapType = MapType.normal;
MapType get mapType => _mapType;
setMapType(MapType mapType, {bool notify = false}) {
_mapType = mapType;
if (notify) notifyListeners();
}
switchMapType() {
_mapType = MapType.values[(_mapType.index + 1) % MapType.values.length];
if (_mapType == MapType.none) _mapType = MapType.normal;
notifyListeners();
}
}

@ -0,0 +1,16 @@
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
class SearchProvider extends ChangeNotifier {
static SearchProvider of(BuildContext context, {bool listen = true}) =>
Provider.of<SearchProvider>(context, listen: listen);
String prevSearchTerm = "";
String _searchTerm = "";
String get searchTerm => _searchTerm;
set searchTerm(String newValue) {
_searchTerm = newValue;
notifyListeners();
}
}

@ -0,0 +1,341 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:google_maps_webservice/places.dart';
import 'package:provider/provider.dart';
import 'package:supplier_new/google_maps_place_picker_mb/google_maps_place_picker.dart';
import 'package:supplier_new/google_maps_place_picker_mb/providers/place_provider.dart';
import 'package:supplier_new/google_maps_place_picker_mb/providers/search_provider.dart';
import 'package:supplier_new/google_maps_place_picker_mb/src/components/prediction_tile.dart';
import 'package:supplier_new/google_maps_place_picker_mb/src/components/rounded_frame.dart';
import 'package:supplier_new/google_maps_place_picker_mb/src/controllers/autocomplete_search_controller.dart';
class AutoCompleteSearch extends StatefulWidget {
const AutoCompleteSearch(
{Key? key,
required this.sessionToken,
required this.onPicked,
required this.appBarKey,
this.hintText = "Search here",
this.searchingText = "Searching...",
this.hidden = false,
this.height = 40,
this.contentPadding = EdgeInsets.zero,
this.debounceMilliseconds,
this.onSearchFailed,
required this.searchBarController,
this.autocompleteOffset,
this.autocompleteRadius,
this.autocompleteLanguage,
this.autocompleteComponents,
this.autocompleteTypes,
this.strictbounds,
this.region,
this.initialSearchString,
this.searchForInitialValue,
this.autocompleteOnTrailingWhitespace})
: super(key: key);
final String? sessionToken;
final String? hintText;
final String? searchingText;
final bool hidden;
final double height;
final EdgeInsetsGeometry contentPadding;
final int? debounceMilliseconds;
final ValueChanged<Prediction> onPicked;
final ValueChanged<String>? onSearchFailed;
final SearchBarController searchBarController;
final num? autocompleteOffset;
final num? autocompleteRadius;
final String? autocompleteLanguage;
final List<String>? autocompleteTypes;
final List<Component>? autocompleteComponents;
final bool? strictbounds;
final String? region;
final GlobalKey appBarKey;
final String? initialSearchString;
final bool? searchForInitialValue;
final bool? autocompleteOnTrailingWhitespace;
@override
AutoCompleteSearchState createState() => AutoCompleteSearchState();
}
class AutoCompleteSearchState extends State<AutoCompleteSearch> {
TextEditingController controller = TextEditingController();
FocusNode focus = FocusNode();
OverlayEntry? overlayEntry;
SearchProvider provider = SearchProvider();
@override
void initState() {
super.initState();
if (widget.initialSearchString != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
controller.text = widget.initialSearchString!;
if (widget.searchForInitialValue!) {
_onSearchInputChange();
}
});
}
controller.addListener(_onSearchInputChange);
focus.addListener(_onFocusChanged);
widget.searchBarController.attach(this);
}
@override
void dispose() {
controller.removeListener(_onSearchInputChange);
controller.dispose();
focus.removeListener(_onFocusChanged);
focus.dispose();
_clearOverlay();
super.dispose();
}
@override
Widget build(BuildContext context) {
return !widget.hidden
? ChangeNotifierProvider.value(
value: provider,
child: RoundedFrame(
height: widget.height,
padding: const EdgeInsets.only(right: 10),
color: Theme.of(context).brightness == Brightness.dark
? Colors.black54
: Colors.white,
borderRadius: BorderRadius.circular(20),
elevation: 4.0,
child: Row(
children: <Widget>[
SizedBox(width: 10),
Icon(Icons.search),
SizedBox(width: 10),
Expanded(child: _buildSearchTextField()),
_buildTextClearIcon(),
],
),
),
)
: Container();
}
Widget _buildSearchTextField() {
return TextField(
controller: controller,
focusNode: focus,
decoration: InputDecoration(
hintText: widget.hintText,
border: InputBorder.none,
errorBorder: InputBorder.none,
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
disabledBorder: InputBorder.none,
focusedErrorBorder: InputBorder.none,
isDense: true,
contentPadding: widget.contentPadding,
),
);
}
Widget _buildTextClearIcon() {
return Selector<SearchProvider, String>(
selector: (_, provider) => provider.searchTerm,
builder: (_, data, __) {
if (data.length > 0) {
return Padding(
padding: const EdgeInsets.only(right: 8.0),
child: GestureDetector(
child: Icon(
Icons.clear,
color: Theme.of(context).brightness == Brightness.dark
? Colors.white
: Colors.black,
),
onTap: () {
clearText();
},
),
);
} else {
return SizedBox(width: 10);
}
});
}
_onSearchInputChange() {
if (!mounted) return;
this.provider.searchTerm = controller.text;
PlaceProvider provider = PlaceProvider.of(context, listen: false);
if (controller.text.isEmpty) {
provider.debounceTimer?.cancel();
_searchPlace(controller.text);
return;
}
if (controller.text.trim() == this.provider.prevSearchTerm.trim()) {
provider.debounceTimer?.cancel();
return;
}
if (!widget.autocompleteOnTrailingWhitespace! &&
controller.text.substring(controller.text.length - 1) == " ") {
provider.debounceTimer?.cancel();
return;
}
if (provider.debounceTimer?.isActive ?? false) {
provider.debounceTimer!.cancel();
}
provider.debounceTimer =
Timer(Duration(milliseconds: widget.debounceMilliseconds!), () {
_searchPlace(controller.text.trim());
});
}
_onFocusChanged() {
PlaceProvider provider = PlaceProvider.of(context, listen: false);
provider.isSearchBarFocused = focus.hasFocus;
provider.debounceTimer?.cancel();
provider.placeSearchingState = SearchingState.Idle;
}
_searchPlace(String searchTerm) {
this.provider.prevSearchTerm = searchTerm;
_clearOverlay();
if (searchTerm.length < 1) return;
_displayOverlay(_buildSearchingOverlay());
_performAutoCompleteSearch(searchTerm);
}
_clearOverlay() {
if (overlayEntry != null) {
overlayEntry!.remove();
overlayEntry = null;
}
}
_displayOverlay(Widget overlayChild) {
_clearOverlay();
final RenderBox? appBarRenderBox =
widget.appBarKey.currentContext!.findRenderObject() as RenderBox?;
final translation = appBarRenderBox?.getTransformTo(null).getTranslation();
final Offset offset = translation != null
? Offset(translation.x, translation.y)
: Offset(0.0, 0.0);
final screenWidth = MediaQuery.of(context).size.width;
overlayEntry = OverlayEntry(
builder: (context) => Positioned(
top: appBarRenderBox!.paintBounds.shift(offset).top +
appBarRenderBox.size.height,
left: screenWidth * 0.025,
right: screenWidth * 0.025,
child: Material(
elevation: 4.0,
child: overlayChild,
),
),
);
Overlay.of(context)!.insert(overlayEntry!);
}
Widget _buildSearchingOverlay() {
return Container(
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24),
child: Row(
children: <Widget>[
SizedBox(
height: 24,
width: 24,
child: CircularProgressIndicator(strokeWidth: 3),
),
SizedBox(width: 24),
Expanded(
child: Text(
widget.searchingText ?? "Searching...",
style: TextStyle(fontSize: 16),
),
)
],
),
);
}
Widget _buildPredictionOverlay(List<Prediction> predictions) {
return ListBody(
children: predictions
.map(
(p) => PredictionTile(
prediction: p,
onTap: (selectedPrediction) {
resetSearchBar();
widget.onPicked(selectedPrediction);
},
),
)
.toList(),
);
}
_performAutoCompleteSearch(String searchTerm) async {
PlaceProvider provider = PlaceProvider.of(context, listen: false);
if (searchTerm.isNotEmpty) {
final PlacesAutocompleteResponse response =
await provider.places.autocomplete(
searchTerm,
sessionToken: widget.sessionToken,
location: provider.currentPosition == null
? null
: Location(
lat: provider.currentPosition!.latitude,
lng: provider.currentPosition!.longitude),
offset: widget.autocompleteOffset,
radius: widget.autocompleteRadius,
language: widget.autocompleteLanguage,
types: widget.autocompleteTypes ?? const [],
components: widget.autocompleteComponents ?? const [],
strictbounds: widget.strictbounds ?? false,
region: widget.region,
);
if (response.errorMessage?.isNotEmpty == true ||
response.status == "REQUEST_DENIED") {
if (widget.onSearchFailed != null) {
widget.onSearchFailed!(response.status);
}
return;
}
_displayOverlay(_buildPredictionOverlay(response.predictions));
}
}
clearText() {
provider.searchTerm = "";
controller.clear();
}
resetSearchBar() {
clearText();
focus.unfocus();
}
clearOverlay() {
_clearOverlay();
}
}

@ -0,0 +1,58 @@
import 'package:flutter/material.dart';
class AnimatedPin extends StatefulWidget {
AnimatedPin({
Key? key,
this.child,
});
final Widget? child;
@override
_AnimatedPinState createState() => _AnimatedPinState();
}
class _AnimatedPinState extends State<AnimatedPin>
with TickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
)..repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return JumpingContainer(controller: _controller, child: widget.child);
}
}
class JumpingContainer extends AnimatedWidget {
const JumpingContainer({
Key? key,
required AnimationController controller,
this.child,
}) : super(key: key, listenable: controller);
final Widget? child;
Animation<double> get _progress => listenable as Animation<double>;
@override
Widget build(BuildContext context) {
return Transform.translate(
offset: Offset(0, -10 + _progress.value * 10),
child: child,
);
}
}

@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
import 'package:supplier_new/google_maps_place_picker_mb/src/components/rounded_frame.dart';
class FloatingCard extends StatelessWidget {
const FloatingCard({
Key? key,
this.topPosition,
this.leftPosition,
this.rightPosition,
this.bottomPosition,
this.width,
this.height,
this.borderRadius = BorderRadius.zero,
this.elevation = 0.0,
this.color,
this.child,
}) : super(key: key);
final double? topPosition;
final double? leftPosition;
final double? bottomPosition;
final double? rightPosition;
final double? width;
final double? height;
final BorderRadius borderRadius;
final double elevation;
final Color? color;
final Widget? child;
@override
Widget build(BuildContext context) {
return Positioned(
top: topPosition,
left: leftPosition,
right: rightPosition,
bottom: bottomPosition,
child: RoundedFrame(
width: width,
height: height,
borderRadius: borderRadius,
elevation: elevation,
color: color,
child: child,
),
);
}
}

@ -0,0 +1,81 @@
import 'package:flutter/material.dart';
import 'package:google_maps_webservice/places.dart';
class PredictionTile extends StatelessWidget {
final Prediction prediction;
final ValueChanged<Prediction>? onTap;
PredictionTile({required this.prediction, this.onTap});
@override
Widget build(BuildContext context) {
return ListTile(
leading: Icon(Icons.location_on),
title: RichText(
text: TextSpan(
children: _buildPredictionText(context),
),
),
onTap: () {
if (onTap != null) {
onTap!(prediction);
}
},
);
}
List<TextSpan> _buildPredictionText(BuildContext context) {
final List<TextSpan> result = <TextSpan>[];
final textColor = Colors.black;
if (prediction.matchedSubstrings.length > 0) {
MatchedSubstring matchedSubString = prediction.matchedSubstrings[0];
// There is no matched string at the beginning.
if (matchedSubString.offset > 0) {
result.add(
TextSpan(
text: prediction.description
?.substring(0, matchedSubString.offset as int?),
style: TextStyle(
color: textColor, fontSize: 16, fontWeight: FontWeight.w300),
),
);
}
// Matched strings.
result.add(
TextSpan(
text: prediction.description?.substring(
matchedSubString.offset as int,
matchedSubString.offset + matchedSubString.length as int?),
style: TextStyle(
color: textColor, fontSize: 16, fontWeight: FontWeight.w500),
),
);
// Other strings.
if (matchedSubString.offset + matchedSubString.length <
(prediction.description?.length ?? 0)) {
result.add(
TextSpan(
text: prediction.description?.substring(
matchedSubString.offset + matchedSubString.length as int),
style: TextStyle(
color: textColor, fontSize: 16, fontWeight: FontWeight.w300),
),
);
}
// If there is no matched strings, but there are predicts. (Not sure if this happens though)
} else {
result.add(
TextSpan(
text: prediction.description,
style: TextStyle(
color: textColor, fontSize: 16, fontWeight: FontWeight.w300),
),
);
}
return result;
}
}

@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
class RoundedFrame extends StatelessWidget {
const RoundedFrame({
Key? key,
this.margin,
this.padding,
this.width,
this.height,
this.child,
this.color,
this.borderRadius = BorderRadius.zero,
this.borderColor = Colors.transparent,
this.elevation = 0.0,
this.materialType,
}) : super(key: key);
final EdgeInsetsGeometry? margin;
final EdgeInsetsGeometry? padding;
final double? width;
final double? height;
final Widget? child;
final Color? color;
final Color borderColor;
final BorderRadius borderRadius;
final double elevation;
final MaterialType? materialType;
@override
Widget build(BuildContext context) {
return Container(
width: width,
height: height,
margin: margin,
padding: padding,
child: Material(
type: color == Colors.transparent
? MaterialType.transparency
: materialType ?? MaterialType.canvas,
color: color,
shape: RoundedRectangleBorder(
borderRadius: borderRadius, side: BorderSide(color: borderColor)),
elevation: elevation,
child: ClipRRect(
borderRadius: borderRadius,
child: child,
),
),
);
}
}

@ -0,0 +1,24 @@
import 'package:flutter/cupertino.dart';
import 'package:supplier_new/google_maps_place_picker_mb/src/autocomplete_search.dart';
class SearchBarController extends ChangeNotifier {
late AutoCompleteSearchState _autoCompleteSearch;
attach(AutoCompleteSearchState searchWidget) {
_autoCompleteSearch = searchWidget;
}
/// Just clears text.
clear() {
_autoCompleteSearch.clearText();
}
/// Clear and remove focus (Dismiss keyboard)
reset() {
_autoCompleteSearch.resetSearchBar();
}
clearOverlay() {
_autoCompleteSearch.clearOverlay();
}
}

@ -0,0 +1,612 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:google_maps_webservice/geocoding.dart';
import 'package:google_maps_webservice/places.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
import 'package:supplier_new/google_maps_place_picker_mb/google_maps_place_picker.dart';
import 'package:supplier_new/google_maps_place_picker_mb/providers/place_provider.dart';
import 'package:supplier_new/google_maps_place_picker_mb/src/components/animated_pin.dart';
typedef SelectedPlaceWidgetBuilder = Widget Function(
BuildContext context,
PickResult? selectedPlace,
SearchingState state,
bool isSearchBarFocused,
);
typedef PinBuilder = Widget Function(
BuildContext context,
PinState state,
);
class GoogleMapPlacePicker extends StatelessWidget {
const GoogleMapPlacePicker({
Key? key,
required this.initialTarget,
required this.appBarKey,
this.selectedPlaceWidgetBuilder,
this.pinBuilder,
this.onSearchFailed,
this.onMoveStart,
this.onMapCreated,
this.debounceMilliseconds,
this.enableMapTypeButton,
this.enableMyLocationButton,
this.onToggleMapType,
this.onMyLocation,
this.onPlacePicked,
this.usePinPointingSearch,
this.usePlaceDetailSearch,
this.selectInitialPosition,
this.language,
this.pickArea,
this.forceSearchOnZoomChanged,
this.hidePlaceDetailsWhenDraggingPin,
this.onCameraMoveStarted,
this.onCameraMove,
this.onCameraIdle,
this.selectText,
this.outsideOfPickAreaText,
this.zoomGesturesEnabled = true,
this.zoomControlsEnabled = false,
this.fullMotion = false,
}) : super(key: key);
final LatLng initialTarget;
final GlobalKey appBarKey;
final SelectedPlaceWidgetBuilder? selectedPlaceWidgetBuilder;
final PinBuilder? pinBuilder;
final ValueChanged<String>? onSearchFailed;
final VoidCallback? onMoveStart;
final MapCreatedCallback? onMapCreated;
final VoidCallback? onToggleMapType;
final VoidCallback? onMyLocation;
final ValueChanged<PickResult>? onPlacePicked;
final int? debounceMilliseconds;
final bool? enableMapTypeButton;
final bool? enableMyLocationButton;
final bool? usePinPointingSearch;
final bool? usePlaceDetailSearch;
final bool? selectInitialPosition;
final String? language;
final CircleArea? pickArea;
final bool? forceSearchOnZoomChanged;
final bool? hidePlaceDetailsWhenDraggingPin;
/// GoogleMap pass-through events:
final Function(PlaceProvider)? onCameraMoveStarted;
final CameraPositionCallback? onCameraMove;
final Function(PlaceProvider)? onCameraIdle;
// strings
final String? selectText;
final String? outsideOfPickAreaText;
/// Zoom feature toggle
final bool zoomGesturesEnabled;
final bool zoomControlsEnabled;
/// Use never scrollable scroll-view with maximum dimensions to prevent unnecessary re-rendering.
final bool fullMotion;
_searchByCameraLocation(PlaceProvider provider) async {
// We don't want to search location again if camera location is changed by zooming in/out.
if (forceSearchOnZoomChanged == false &&
provider.prevCameraPosition != null &&
provider.prevCameraPosition!.target.latitude ==
provider.cameraPosition!.target.latitude &&
provider.prevCameraPosition!.target.longitude ==
provider.cameraPosition!.target.longitude) {
provider.placeSearchingState = SearchingState.Idle;
return;
}
provider.placeSearchingState = SearchingState.Searching;
final GeocodingResponse response =
await provider.geocoding.searchByLocation(
Location(
lat: provider.cameraPosition!.target.latitude,
lng: provider.cameraPosition!.target.longitude),
language: language,
);
if (response.errorMessage?.isNotEmpty == true ||
response.status == "REQUEST_DENIED") {
print("Camera Location Search Error: " + response.errorMessage!);
if (onSearchFailed != null) {
onSearchFailed!(response.status);
}
provider.placeSearchingState = SearchingState.Idle;
return;
}
if (usePlaceDetailSearch!) {
final PlacesDetailsResponse detailResponse =
await provider.places.getDetailsByPlaceId(
response.results[0].placeId,
language: language,
);
if (detailResponse.errorMessage?.isNotEmpty == true ||
detailResponse.status == "REQUEST_DENIED") {
print("Fetching details by placeId Error: " +
detailResponse.errorMessage!);
if (onSearchFailed != null) {
onSearchFailed!(detailResponse.status);
}
provider.placeSearchingState = SearchingState.Idle;
return;
}
provider.selectedPlace =
PickResult.fromPlaceDetailResult(detailResponse.result);
} else {
provider.selectedPlace =
PickResult.fromGeocodingResult(response.results[0]);
}
provider.placeSearchingState = SearchingState.Idle;
}
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
if (this.fullMotion)
SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(),
child: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Stack(
alignment: AlignmentDirectional.center,
children: [
_buildGoogleMap(context),
_buildPin(),
],
))),
if (!this.fullMotion) _buildGoogleMap(context),
if (!this.fullMotion) _buildPin(),
_buildFloatingCard(),
_buildMapIcons(context),
_buildZoomButtons()
],
);
}
Widget _buildGoogleMapInner(PlaceProvider? provider, MapType mapType) {
CameraPosition initialCameraPosition =
CameraPosition(target: this.initialTarget, zoom: 15);
return GoogleMap(
zoomGesturesEnabled: this.zoomGesturesEnabled,
zoomControlsEnabled:
false, // we use our own implementation that supports iOS as well, see _buildZoomButtons()
myLocationButtonEnabled: false,
compassEnabled: false,
mapToolbarEnabled: false,
initialCameraPosition: initialCameraPosition,
mapType: mapType,
myLocationEnabled: true,
circles: pickArea != null && pickArea!.radius > 0
? Set<Circle>.from([pickArea])
: Set<Circle>(),
onMapCreated: (GoogleMapController controller) {
if (provider == null) return;
provider.mapController = controller;
provider.setCameraPosition(null);
provider.pinState = PinState.Idle;
// When select initialPosition set to true.
if (selectInitialPosition!) {
provider.setCameraPosition(initialCameraPosition);
_searchByCameraLocation(provider);
}
if (onMapCreated != null) {
onMapCreated!(controller);
}
},
onCameraIdle: () {
if (provider == null) return;
if (provider.isAutoCompleteSearching) {
provider.isAutoCompleteSearching = false;
provider.pinState = PinState.Idle;
provider.placeSearchingState = SearchingState.Idle;
return;
}
// Perform search only if the setting is to true.
if (usePinPointingSearch!) {
// Search current camera location only if camera has moved (dragged) before.
if (provider.pinState == PinState.Dragging) {
// Cancel previous timer.
if (provider.debounceTimer?.isActive ?? false) {
provider.debounceTimer!.cancel();
}
provider.debounceTimer =
Timer(Duration(milliseconds: debounceMilliseconds!), () {
_searchByCameraLocation(provider);
});
}
}
provider.pinState = PinState.Idle;
if (onCameraIdle != null) {
onCameraIdle!(provider);
}
},
onCameraMoveStarted: () {
if (provider == null) return;
if (onCameraMoveStarted != null) {
onCameraMoveStarted!(provider);
}
provider.setPrevCameraPosition(provider.cameraPosition);
// Cancel any other timer.
provider.debounceTimer?.cancel();
// Update state, dismiss keyboard and clear text.
provider.pinState = PinState.Dragging;
// Begins the search state if the hide details is enabled
if (this.hidePlaceDetailsWhenDraggingPin!) {
provider.placeSearchingState = SearchingState.Searching;
}
onMoveStart!();
},
onCameraMove: (CameraPosition position) {
if (provider == null) return;
provider.setCameraPosition(position);
if (onCameraMove != null) {
onCameraMove!(position);
}
},
// gestureRecognizers make it possible to navigate the map when it's a
// child in a scroll view e.g ListView, SingleChildScrollView...
gestureRecognizers: Set()
..add(Factory<EagerGestureRecognizer>(() => EagerGestureRecognizer())),
);
}
Widget _buildGoogleMap(BuildContext context) {
return Selector<PlaceProvider, MapType>(
selector: (_, provider) => provider.mapType,
builder: (_, data, __) => this._buildGoogleMapInner(
PlaceProvider.of(context, listen: false), data));
}
Widget _buildPin() {
return Center(
child: Selector<PlaceProvider, PinState>(
selector: (_, provider) => provider.pinState,
builder: (context, state, __) {
if (pinBuilder == null) {
return _defaultPinBuilder(context, state);
} else {
return Builder(
builder: (builderContext) =>
pinBuilder!(builderContext, state));
}
},
),
);
}
Widget _defaultPinBuilder(BuildContext context, PinState state) {
if (state == PinState.Preparing) {
return Container();
} else if (state == PinState.Idle) {
return Stack(
children: <Widget>[
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.place, size: 36, color: Colors.red),
SizedBox(height: 42),
],
),
),
Center(
child: Container(
width: 5,
height: 5,
decoration: BoxDecoration(
color: Colors.black,
shape: BoxShape.circle,
),
),
),
],
);
} else {
return Stack(
children: <Widget>[
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedPin(
child: Icon(Icons.place, size: 36, color: Colors.red)),
SizedBox(height: 42),
],
),
),
Center(
child: Container(
width: 5,
height: 5,
decoration: BoxDecoration(
color: Colors.black,
shape: BoxShape.circle,
),
),
),
],
);
}
}
Widget _buildFloatingCard() {
return Selector<PlaceProvider,
Tuple4<PickResult?, SearchingState, bool, PinState>>(
selector: (_, provider) => Tuple4(
provider.selectedPlace,
provider.placeSearchingState,
provider.isSearchBarFocused,
provider.pinState),
builder: (context, data, __) {
if ((data.item1 == null && data.item2 == SearchingState.Idle) ||
data.item3 == true ||
data.item4 == PinState.Dragging &&
this.hidePlaceDetailsWhenDraggingPin!) {
return Container();
} else {
if (selectedPlaceWidgetBuilder == null) {
return _defaultPlaceWidgetBuilder(context, data.item1, data.item2);
} else {
return Builder(
builder: (builderContext) => selectedPlaceWidgetBuilder!(
builderContext, data.item1, data.item2, data.item3));
}
}
},
);
}
Widget _buildZoomButtons() {
return Selector<PlaceProvider, Tuple2<GoogleMapController?, LatLng?>>(
selector: (_, provider) => new Tuple2<GoogleMapController?, LatLng?>(
provider.mapController, provider.cameraPosition?.target),
builder: (context, data, __) {
if (!this.zoomControlsEnabled ||
data.item1 == null ||
data.item2 == null) {
return Container();
} else {
return Positioned(
bottom: 50,
right: 10,
child: Card(
elevation: 4.0,
child: Container(
width: 40,
height: 100,
child: Column(
children: <Widget>[
IconButton(
icon: Icon(Icons.add),
onPressed: () async {
double currentZoomLevel =
await data.item1!.getZoomLevel();
currentZoomLevel = currentZoomLevel + 2;
data.item1!.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: data.item2!,
zoom: currentZoomLevel,
),
),
);
}),
SizedBox(height: 2),
IconButton(
icon: Icon(Icons.remove),
onPressed: () async {
double currentZoomLevel =
await data.item1!.getZoomLevel();
currentZoomLevel = currentZoomLevel - 2;
if (currentZoomLevel < 0) currentZoomLevel = 0;
data.item1!.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: data.item2!,
zoom: currentZoomLevel,
),
),
);
}),
],
),
),
),
);
}
},
);
}
Widget _defaultPlaceWidgetBuilder(
BuildContext context, PickResult? data, SearchingState state) {
return FloatingCard(
bottomPosition: MediaQuery.of(context).size.height * 0.1,
leftPosition: MediaQuery.of(context).size.width * 0.15,
rightPosition: MediaQuery.of(context).size.width * 0.15,
width: MediaQuery.of(context).size.width * 0.7,
borderRadius: BorderRadius.circular(12.0),
elevation: 4.0,
color: Theme.of(context).cardColor,
child: state == SearchingState.Searching
? _buildLoadingIndicator()
: _buildSelectionDetails(context, data!),
);
}
Widget _buildLoadingIndicator() {
return Container(
height: 48,
child: const Center(
child: SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(),
),
),
);
}
Widget _buildSelectionDetails(BuildContext context, PickResult result) {
bool canBePicked = pickArea == null ||
pickArea!.radius <= 0 ||
Geolocator.distanceBetween(
pickArea!.center.latitude,
pickArea!.center.longitude,
result.geometry!.location.lat,
result.geometry!.location.lng) <=
pickArea!.radius;
MaterialStateColor buttonColor = MaterialStateColor.resolveWith(
(states) => canBePicked ? Colors.lightGreen : Colors.red);
return Container(
margin: EdgeInsets.all(10),
child: Column(
children: <Widget>[
Text(
result.types!.length==1?
result.formattedAddress!:result.name!+', '+result.formattedAddress!,
style: TextStyle(fontSize: 18),
textAlign: TextAlign.center,
),
SizedBox(height: 10),
(canBePicked && (selectText?.isEmpty ?? true)) ||
(!canBePicked && (outsideOfPickAreaText?.isEmpty ?? true))
? SizedBox.fromSize(
size: Size(56, 56), // button width and height
child: ClipOval(
child: Material(
child: InkWell(
overlayColor: buttonColor,
onTap: () {
if (canBePicked) {
onPlacePicked!(result);
}
},
child: Icon(
canBePicked
? Icons.check_sharp
: Icons.app_blocking_sharp,
color: buttonColor)),
),
),
)
: SizedBox.fromSize(
size: Size(MediaQuery.of(context).size.width * 0.8,
56), // button width and height
child: ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: Material(
child: InkWell(
overlayColor: buttonColor,
onTap: () {
if (canBePicked) {
onPlacePicked!(result);
}
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
canBePicked
? Icons.check_sharp
: Icons.app_blocking_sharp,
color: buttonColor),
SizedBox.fromSize(size: new Size(10, 0)),
Text(
canBePicked
? selectText!
: outsideOfPickAreaText!,
style: TextStyle(color: buttonColor))
],
)),
),
),
)
],
),
);
}
Widget _buildMapIcons(BuildContext context) {
if (appBarKey.currentContext == null) {
return Container();
}
final RenderBox appBarRenderBox =
appBarKey.currentContext!.findRenderObject() as RenderBox;
return Positioned(
top: appBarRenderBox.size.height,
right: 15,
child: Column(
children: <Widget>[
enableMapTypeButton!
? Container(
width: 35,
height: 35,
child: RawMaterialButton(
shape: CircleBorder(),
fillColor: Theme.of(context).brightness == Brightness.dark
? Colors.black54
: Colors.white,
elevation: 4.0,
onPressed: onToggleMapType,
child: Icon(Icons.layers),
),
)
: Container(),
SizedBox(height: 10),
enableMyLocationButton!
? Container(
width: 35,
height: 35,
child: RawMaterialButton(
shape: CircleBorder(),
fillColor: Theme.of(context).brightness == Brightness.dark
? Colors.black54
: Colors.white,
elevation: 4.0,
onPressed: onMyLocation,
child: Icon(Icons.my_location),
),
)
: Container(),
],
),
);
}
}

@ -0,0 +1,22 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:uuid/uuid.dart';
class CircleArea extends Circle {
CircleArea({
required LatLng center,
required double radius,
Color? fillColor,
Color? strokeColor,
int strokeWidth = 2,
}) : super(
circleId: CircleId(Uuid().v4()),
center: center,
radius: radius,
fillColor: fillColor ?? Colors.blue.withAlpha(32),
strokeColor: strokeColor ?? Colors.blue.withAlpha(192),
strokeWidth: strokeWidth,
);
}

@ -0,0 +1,91 @@
import 'package:google_maps_webservice/geocoding.dart';
import 'package:google_maps_webservice/places.dart';
class PickResult {
PickResult({
this.placeId,
this.geometry,
this.formattedAddress,
this.types,
this.addressComponents,
this.adrAddress,
this.formattedPhoneNumber,
this.id,
this.reference,
this.icon,
this.name,
this.openingHours,
this.photos,
this.internationalPhoneNumber,
this.priceLevel,
this.rating,
this.scope,
this.url,
this.vicinity,
this.utcOffset,
this.website,
this.reviews,
});
final String? placeId;
final Geometry? geometry;
final String? formattedAddress;
final List<String>? types;
final List<AddressComponent>? addressComponents;
// Below results will not be fetched if 'usePlaceDetailSearch' is set to false (Defaults to false).
final String? adrAddress;
final String? formattedPhoneNumber;
final String? id;
final String? reference;
final String? icon;
final String? name;
final OpeningHoursDetail? openingHours;
final List<Photo>? photos;
final String? internationalPhoneNumber;
final PriceLevel? priceLevel;
final num? rating;
final String? scope;
final String? url;
final String? vicinity;
final num? utcOffset;
final String? website;
final List<Review>? reviews;
factory PickResult.fromGeocodingResult(GeocodingResult result) {
return PickResult(
placeId: result.placeId,
geometry: result.geometry,
formattedAddress: result.formattedAddress,
types: result.types,
addressComponents: result.addressComponents,
);
}
factory PickResult.fromPlaceDetailResult(PlaceDetails result) {
return PickResult(
placeId: result.placeId,
geometry: result.geometry,
formattedAddress: result.formattedAddress,
types: result.types,
addressComponents: result.addressComponents,
adrAddress: result.adrAddress,
formattedPhoneNumber: result.formattedPhoneNumber,
id: result.id,
reference: result.reference,
icon: result.icon,
name: result.name,
openingHours: result.openingHours,
photos: result.photos,
internationalPhoneNumber: result.internationalPhoneNumber,
priceLevel: result.priceLevel,
rating: result.rating,
scope: result.scope,
url: result.url,
vicinity: result.vicinity,
utcOffset: result.utcOffset,
website: result.website,
reviews: result.reviews,
);
}
}

@ -0,0 +1,535 @@
import 'dart:async';
import 'dart:io' show Platform;
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_api_headers/google_api_headers.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:google_maps_webservice/places.dart';
import 'package:http/http.dart';
import 'package:provider/provider.dart';
import 'package:uuid/uuid.dart';
import 'package:supplier_new/google_maps_place_picker_mb/google_maps_place_picker.dart';
import 'package:supplier_new/google_maps_place_picker_mb/providers/place_provider.dart';
import 'package:supplier_new/google_maps_place_picker_mb/src/autocomplete_search.dart';
import 'package:supplier_new/google_maps_place_picker_mb/src/controllers/autocomplete_search_controller.dart';
import 'package:supplier_new/google_maps_place_picker_mb/src/google_map_place_picker.dart';
typedef IntroModalWidgetBuilder = Widget Function(
BuildContext context,
Function? close,
);
enum PinState { Preparing, Idle, Dragging }
enum SearchingState { Idle, Searching }
class PlacePicker extends StatefulWidget {
const PlacePicker({
Key? key,
required this.apiKey,
this.onPlacePicked,
required this.initialPosition,
this.useCurrentLocation,
this.desiredLocationAccuracy = LocationAccuracy.high,
this.onMapCreated,
this.hintText,
this.searchingText,
this.selectText,
this.outsideOfPickAreaText,
this.onAutoCompleteFailed,
this.onGeocodingSearchFailed,
this.proxyBaseUrl,
this.httpClient,
this.selectedPlaceWidgetBuilder,
this.pinBuilder,
this.introModalWidgetBuilder,
this.autoCompleteDebounceInMilliseconds = 500,
this.cameraMoveDebounceInMilliseconds = 750,
this.initialMapType = MapType.normal,
this.enableMapTypeButton = true,
this.enableMyLocationButton = true,
this.myLocationButtonCooldown = 10,
this.usePinPointingSearch = true,
this.usePlaceDetailSearch = false,
this.autocompleteOffset,
this.autocompleteRadius,
this.autocompleteLanguage,
this.autocompleteComponents,
this.autocompleteTypes,
this.strictbounds,
this.region,
this.pickArea,
this.selectInitialPosition = false,
this.resizeToAvoidBottomInset = true,
this.initialSearchString,
this.searchForInitialValue = false,
this.forceAndroidLocationManager = false,
this.forceSearchOnZoomChanged = false,
this.automaticallyImplyAppBarLeading = true,
this.autocompleteOnTrailingWhitespace = false,
this.hidePlaceDetailsWhenDraggingPin = true,
this.onTapBack,
this.onCameraMoveStarted,
this.onCameraMove,
this.onCameraIdle,
this.onMapTypeChanged,
this.zoomGesturesEnabled = true,
this.zoomControlsEnabled = false,
}) : super(key: key);
final String apiKey;
final LatLng initialPosition;
final bool? useCurrentLocation;
final LocationAccuracy desiredLocationAccuracy;
final String? hintText;
final String? searchingText;
final String? selectText;
final String? outsideOfPickAreaText;
final ValueChanged<String>? onAutoCompleteFailed;
final ValueChanged<String>? onGeocodingSearchFailed;
final int autoCompleteDebounceInMilliseconds;
final int cameraMoveDebounceInMilliseconds;
final MapType initialMapType;
final bool enableMapTypeButton;
final bool enableMyLocationButton;
final int myLocationButtonCooldown;
final bool usePinPointingSearch;
final bool usePlaceDetailSearch;
final num? autocompleteOffset;
final num? autocompleteRadius;
final String? autocompleteLanguage;
final List<String>? autocompleteTypes;
final List<Component>? autocompleteComponents;
final bool? strictbounds;
final String? region;
/// If set the picker can only pick addresses in the given circle area.
/// The section will be highlighted.
final CircleArea? pickArea;
/// If true the [body] and the scaffold's floating widgets should size
/// themselves to avoid the onscreen keyboard whose height is defined by the
/// ambient [MediaQuery]'s [MediaQueryData.viewInsets] `bottom` property.
///
/// For example, if there is an onscreen keyboard displayed above the
/// scaffold, the body can be resized to avoid overlapping the keyboard, which
/// prevents widgets inside the body from being obscured by the keyboard.
///
/// Defaults to true.
final bool resizeToAvoidBottomInset;
final bool selectInitialPosition;
/// By using default setting of Place Picker, it will result result when user hits the select here button.
///
/// If you managed to use your own [selectedPlaceWidgetBuilder], then this WILL NOT be invoked, and you need use data which is
/// being sent with [selectedPlaceWidgetBuilder].
final ValueChanged<PickResult>? onPlacePicked;
/// optional - builds selected place's UI
///
/// It is provided by default if you leave it as a null.
/// INPORTANT: If this is non-null, [onPlacePicked] will not be invoked, as there will be no default 'Select here' button.
final SelectedPlaceWidgetBuilder? selectedPlaceWidgetBuilder;
/// optional - builds customized pin widget which indicates current pointing position.
///
/// It is provided by default if you leave it as a null.
final PinBuilder? pinBuilder;
/// optional - builds customized introduction panel.
///
/// None is provided / the map is instantly accessible if you leave it as a null.
final IntroModalWidgetBuilder? introModalWidgetBuilder;
/// optional - sets 'proxy' value in google_maps_webservice
///
/// In case of using a proxy the baseUrl can be set.
/// The apiKey is not required in case the proxy sets it.
/// (Not storing the apiKey in the app is good practice)
final String? proxyBaseUrl;
/// optional - set 'client' value in google_maps_webservice
///
/// In case of using a proxy url that requires authentication
/// or custom configuration
final BaseClient? httpClient;
/// Initial value of autocomplete search
final String? initialSearchString;
/// Whether to search for the initial value or not
final bool searchForInitialValue;
/// On Android devices you can set [forceAndroidLocationManager]
/// to true to force the plugin to use the [LocationManager] to determine the
/// position instead of the [FusedLocationProviderClient]. On iOS this is ignored.
final bool forceAndroidLocationManager;
/// Allow searching place when zoom has changed. By default searching is disabled when zoom has changed in order to prevent unwilling API usage.
final bool forceSearchOnZoomChanged;
/// Whether to display appbar backbutton. Defaults to true.
final bool automaticallyImplyAppBarLeading;
/// Will perform an autocomplete search, if set to true. Note that setting
/// this to true, while providing a smoother UX experience, may cause
/// additional unnecessary queries to the Places API.
///
/// Defaults to false.
final bool autocompleteOnTrailingWhitespace;
final bool hidePlaceDetailsWhenDraggingPin;
// Raised when clicking on the back arrow.
// This will not listen for the system back button on Android devices.
// If this is not set, but the back button is visible through automaticallyImplyLeading,
// the Navigator will try to pop instead.
final VoidCallback? onTapBack;
/// GoogleMap pass-through events:
/// Callback method for when the map is ready to be used.
///
/// Used to receive a [GoogleMapController] for this [GoogleMap].
final MapCreatedCallback? onMapCreated;
/// Called when the camera starts moving.
///
/// This can be initiated by the following:
/// 1. Non-gesture animation initiated in response to user actions.
/// For example: zoom buttons, my location button, or marker clicks.
/// 2. Programmatically initiated animation.
/// 3. Camera motion initiated in response to user gestures on the map.
/// For example: pan, tilt, pinch to zoom, or rotate.
final Function(PlaceProvider)? onCameraMoveStarted;
/// Called repeatedly as the camera continues to move after an
/// onCameraMoveStarted call.
///
/// This may be called as often as once every frame and should
/// not perform expensive operations.
final CameraPositionCallback? onCameraMove;
/// Called when camera movement has ended, there are no pending
/// animations and the user has stopped interacting with the map.
final Function(PlaceProvider)? onCameraIdle;
/// Called when the map type has been changed.
final Function(MapType)? onMapTypeChanged;
/// Allow user to make visible the zoom button & toggle on & off zoom gestures
final bool zoomGesturesEnabled;
final bool zoomControlsEnabled;
@override
_PlacePickerState createState() => _PlacePickerState();
}
class _PlacePickerState extends State<PlacePicker> {
GlobalKey appBarKey = GlobalKey();
late final Future<PlaceProvider> _futureProvider;
PlaceProvider? provider;
SearchBarController searchBarController = SearchBarController();
bool showIntroModal = true;
@override
void initState() {
super.initState();
_futureProvider = _initPlaceProvider();
}
@override
void dispose() {
searchBarController.dispose();
super.dispose();
}
Future<PlaceProvider> _initPlaceProvider() async {
final headers = await const GoogleApiHeaders().getHeaders();
final provider = PlaceProvider(
widget.apiKey,
widget.proxyBaseUrl,
widget.httpClient,
headers,
);
provider.sessionToken = const Uuid().v4();
provider.desiredAccuracy = widget.desiredLocationAccuracy;
provider.setMapType(widget.initialMapType);
if (widget.useCurrentLocation != null && widget.useCurrentLocation!) {
await provider.updateCurrentLocation(widget.forceAndroidLocationManager);
}
return provider;
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {
searchBarController.clearOverlay();
return Future.value(true);
},
child: FutureBuilder<PlaceProvider>(
future: _futureProvider,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasData) {
provider = snapshot.data;
return MultiProvider(
providers: [
ChangeNotifierProvider<PlaceProvider>.value(value: provider!),
],
child: Stack(children: [
Scaffold(
key: ValueKey<int>(provider.hashCode),
resizeToAvoidBottomInset: widget.resizeToAvoidBottomInset,
extendBodyBehindAppBar: true,
appBar: AppBar(
key: appBarKey,
automaticallyImplyLeading: false,
iconTheme: Theme.of(context).iconTheme,
elevation: 0,
backgroundColor: Colors.transparent,
titleSpacing: 0.0,
title: _buildSearchBar(context),
),
body: _buildMapWithLocation(),
),
_buildIntroModal(context),
]),
);
}
final children = <Widget>[];
if (snapshot.hasError) {
children.addAll([
Icon(
Icons.error_outline,
color: Colors.red,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('Error: ${snapshot.error}'),
)
]);
} else {
children.add(CircularProgressIndicator());
}
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: children,
),
),
);
},
));
}
Widget _buildSearchBar(BuildContext context) {
return Row(
children: <Widget>[
widget.automaticallyImplyAppBarLeading || widget.onTapBack != null
? IconButton(
onPressed: () {
if (!showIntroModal ||
widget.introModalWidgetBuilder == null) {
if (widget.onTapBack != null) {
widget.onTapBack!();
return;
}
Navigator.maybePop(context);
}
},
icon: Icon(
Platform.isIOS ? Icons.arrow_back_ios : Icons.arrow_back,
),
color: Colors.black.withAlpha(128),
padding: EdgeInsets.zero)
: SizedBox(width: 15),
Expanded(
child: AutoCompleteSearch(
appBarKey: appBarKey,
searchBarController: searchBarController,
sessionToken: provider!.sessionToken,
hintText: widget.hintText,
searchingText: widget.searchingText,
debounceMilliseconds: widget.autoCompleteDebounceInMilliseconds,
onPicked: (prediction) {
_pickPrediction(prediction);
},
onSearchFailed: (status) {
if (widget.onAutoCompleteFailed != null) {
widget.onAutoCompleteFailed!(status);
}
},
autocompleteOffset: widget.autocompleteOffset,
autocompleteRadius: widget.autocompleteRadius,
autocompleteLanguage: widget.autocompleteLanguage,
autocompleteComponents: widget.autocompleteComponents,
autocompleteTypes: widget.autocompleteTypes,
strictbounds: widget.strictbounds,
region: widget.region,
initialSearchString: widget.initialSearchString,
searchForInitialValue: widget.searchForInitialValue,
autocompleteOnTrailingWhitespace:
widget.autocompleteOnTrailingWhitespace),
),
SizedBox(width: 5),
],
);
}
_pickPrediction(Prediction prediction) async {
provider!.placeSearchingState = SearchingState.Searching;
final PlacesDetailsResponse response =
await provider!.places.getDetailsByPlaceId(
prediction.placeId!,
sessionToken: provider!.sessionToken,
language: widget.autocompleteLanguage,
);
if (response.errorMessage?.isNotEmpty == true ||
response.status == "REQUEST_DENIED") {
if (widget.onAutoCompleteFailed != null) {
widget.onAutoCompleteFailed!(response.status);
}
return;
}
provider!.selectedPlace = PickResult.fromPlaceDetailResult(response.result);
// Prevents searching again by camera movement.
provider!.isAutoCompleteSearching = true;
await _moveTo(provider!.selectedPlace!.geometry!.location.lat,
provider!.selectedPlace!.geometry!.location.lng);
provider!.placeSearchingState = SearchingState.Idle;
}
_moveTo(double latitude, double longitude) async {
GoogleMapController? controller = provider!.mapController;
if (controller == null) return;
await controller.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(latitude, longitude),
zoom: 16,
),
),
);
}
_moveToCurrentPosition() async {
if (provider!.currentPosition != null) {
await _moveTo(provider!.currentPosition!.latitude,
provider!.currentPosition!.longitude);
}
}
Widget _buildMapWithLocation() {
if (provider!.currentPosition == null) {
return _buildMap(widget.initialPosition);
}
return _buildMap(LatLng(provider!.currentPosition!.latitude,
provider!.currentPosition!.longitude));
}
Widget _buildMap(LatLng initialTarget) {
return GoogleMapPlacePicker(
fullMotion: !widget.resizeToAvoidBottomInset,
initialTarget: initialTarget,
appBarKey: appBarKey,
selectedPlaceWidgetBuilder: widget.selectedPlaceWidgetBuilder,
pinBuilder: widget.pinBuilder,
onSearchFailed: widget.onGeocodingSearchFailed,
debounceMilliseconds: widget.cameraMoveDebounceInMilliseconds,
enableMapTypeButton: widget.enableMapTypeButton,
enableMyLocationButton: widget.enableMyLocationButton,
usePinPointingSearch: widget.usePinPointingSearch,
usePlaceDetailSearch: widget.usePlaceDetailSearch,
onMapCreated: widget.onMapCreated,
selectInitialPosition: widget.selectInitialPosition,
language: widget.autocompleteLanguage,
pickArea: widget.pickArea,
forceSearchOnZoomChanged: widget.forceSearchOnZoomChanged,
hidePlaceDetailsWhenDraggingPin: widget.hidePlaceDetailsWhenDraggingPin,
selectText: widget.selectText,
outsideOfPickAreaText: widget.outsideOfPickAreaText,
onToggleMapType: () {
provider!.switchMapType();
if (widget.onMapTypeChanged != null) {
widget.onMapTypeChanged!(provider!.mapType);
}
},
onMyLocation: () async {
// Prevent to click many times in short period.
if (provider!.isOnUpdateLocationCooldown == false) {
provider!.isOnUpdateLocationCooldown = true;
Timer(Duration(seconds: widget.myLocationButtonCooldown), () {
provider!.isOnUpdateLocationCooldown = false;
});
await provider!
.updateCurrentLocation(widget.forceAndroidLocationManager);
await _moveToCurrentPosition();
}
},
onMoveStart: () {
searchBarController.reset();
},
onPlacePicked: widget.onPlacePicked,
onCameraMoveStarted: widget.onCameraMoveStarted,
onCameraMove: widget.onCameraMove,
onCameraIdle: widget.onCameraIdle,
zoomGesturesEnabled: widget.zoomGesturesEnabled,
zoomControlsEnabled: widget.zoomControlsEnabled,
);
}
Widget _buildIntroModal(BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return showIntroModal && widget.introModalWidgetBuilder != null
? Stack(children: [
Positioned(
top: 0,
right: 0,
bottom: 0,
left: 0,
child: Material(
type: MaterialType.canvas,
color: Color.fromARGB(128, 0, 0, 0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.zero,
),
child: ClipRect(),
),
),
widget.introModalWidgetBuilder!(context, () {
setState(() {
showIntroModal = false;
});
})
])
: Container();
});
}
}

@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
import 'helper.dart';
class AppColors {
AppColors._();
static const Color primaryColor = Color(0xffed1846);
static const Color secondaryColor = Color(0xff5bcb84);
static const Color tColor = Color(0xfff087ca);
static const Color rColor = Color(0xfff08c87);
static const Color statusColorPending = Color(0xfff8c942);
static const Color statusColorInProgress = Color(0xff587add);
static const Color statusColorConfirm = Color(0xff30d300);
static const Color pageBgColor = Color(0xFFF6F6F6);
static const Color grey100Color = Color(0xFFEEEEEE);
static const Color grey200Color = Color(0xFFEEEEEE);
static const Color grey300Color = Color(0xFFE0E0E0);
static const Color grey400Color = Color(0xFFBDBDBD);
static const Color grey500Color = Color(0xFF9E9E9E);
static const Color grey600Color = Color(0xFF757575);
static const Color grey700Color = Color(0xFF616161);
static const Color grey800Color = Color(0xFF424242);
static const Color grey900Color = Color(0xFF212121);
static const Color errorColor = Color(0xFFD50000);
static const Color error100Color = Color(0xffFF5252);
static const Color mainBgColor = Color(0xfff7f7f7);
static const Color logoColor = Color(0xfff1ea0c);
static MaterialColor primaryMaterialColor = getSwatch(primaryColor);
static MaterialColor errorMaterialColor = getSwatch(errorColor);
static MaterialColor tableRowMaterialColor = getSwatch(grey500Color);
}

@ -0,0 +1,4 @@
class AppImages {
static const String pickupMarker = "assets/images/pickup_marker.png";
static const String dropMarker = "assets/images/drop_marker.png";
}

@ -0,0 +1,25 @@
import 'dart:ui';
import 'package:get/get.dart';
class AppSizes {
// get height and width from getX
static final double deviceHeight = Get.height;
static final double deviceWidth = Get.width;
static const int height1060 = 1060;
static const int height880 = 880;
static const int height740 = 740;
static const int height490 = 490;
static const int width1060 = 1060;
static const int width880 = 880;
static const int width740 = 740;
static const int width490 = 490;
static const int screen720x1280 = 490;
static final double mapPinSize = (deviceWidth * window.devicePixelRatio);
}

@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
MaterialColor getSwatch(Color color) {
final hslColor = HSLColor.fromColor(color);
final lightness = hslColor.lightness;
/// if [500] is the default color, there are at LEAST five
/// steps below [500]. (i.e. 400, 300, 200, 100, 50.) A
/// divisor of 5 would mean [50] is a lightness of 1.0 or
/// a color of #ffffff. A value of six would be near white
/// but not quite.
const lowDivisor = 6;
/// if [500] is the default color, there are at LEAST four
/// steps above [500]. A divisor of 4 would mean [900] is
/// a lightness of 0.0 or color of #000000
const highDivisor = 5;
final lowStep = (1.0 - lightness) / lowDivisor;
final highStep = lightness / highDivisor;
return MaterialColor(color.value, {
50: (hslColor.withLightness(lightness + (lowStep * 5))).toColor(),
100: (hslColor.withLightness(lightness + (lowStep * 4))).toColor(),
200: (hslColor.withLightness(lightness + (lowStep * 3))).toColor(),
300: (hslColor.withLightness(lightness + (lowStep * 2))).toColor(),
400: (hslColor.withLightness(lightness + lowStep)).toColor(),
500: (hslColor.withLightness(lightness)).toColor(),
600: (hslColor.withLightness(lightness - highStep)).toColor(),
700: (hslColor.withLightness(lightness - (highStep * 2))).toColor(),
800: (hslColor.withLightness(lightness - (highStep * 3))).toColor(),
900: (hslColor.withLightness(lightness - (highStep * 4))).toColor(),
});
}

@ -0,0 +1,120 @@
import 'dart:developer';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:location/location.dart';
import 'app_images.dart';
import 'app_sizes.dart';
import 'permission_alert.dart';
class LocationController<T> extends GetxController {
Location location = Location();
// final Rx<LatLng?> locationPosition = const LatLng(0.0, 0.0).obs;
/*final Rx<LatLng?> locationPosition =
const LatLng(12.90618717, 77.5844983).obs;*/
final Rx<LatLng?> locationPosition =
const LatLng(0, 0).obs;
bool locationServiceActive = true;
BitmapDescriptor? pickupMarker;
BitmapDescriptor? dropMarker;
@override
void onInit() async {
await _getBytesFromAsset(AppImages.pickupMarker, AppSizes.mapPinSize * 0.1);
await _getBytesFromAsset(AppImages.dropMarker, AppSizes.mapPinSize * 0.05);
super.onInit();
refreshToLiveLocation();
}
Future<void> _getBytesFromAsset(String path, double size) async {
ByteData data = await rootBundle.load(path);
ui.Codec codec = await ui.instantiateImageCodec(
data.buffer.asUint8List(),
targetWidth: size.toInt(),
allowUpscaling: true,
);
ui.FrameInfo fi = await codec.getNextFrame();
if (path == AppImages.pickupMarker) {
pickupMarker = BitmapDescriptor.fromBytes(
(await fi.image.toByteData(format: ui.ImageByteFormat.png))!
.buffer
.asUint8List());
} else if (path == AppImages.dropMarker) {
dropMarker = BitmapDescriptor.fromBytes(
(await fi.image.toByteData(format: ui.ImageByteFormat.png))!
.buffer
.asUint8List());
} else {}
}
refreshToLiveLocation() async {
log("initiating");
bool serviceEnabled;
PermissionStatus permissionGranted;
serviceEnabled = await location.serviceEnabled();
if (!serviceEnabled) {
serviceEnabled = await location.requestService();
locationPosition.value = null;
return;
}
log("permission check");
permissionGranted = await location.hasPermission();
if (permissionGranted == PermissionStatus.denied) {
permissionGranted = await location.requestPermission();
if (permissionGranted != PermissionStatus.granted) {
showPermissionAlertDialog(
requestMsg:
"Location access needed. Go to Android settings, tap App permissions and tap Allow.",
barrierDismissible: false,
);
} else {
await location.changeSettings(
accuracy: LocationAccuracy.high,
interval: 2000,
distanceFilter: 2);
location.onLocationChanged.listen((LocationData currentLocation) async {
var lat = currentLocation.latitude;
var long = currentLocation.longitude;
if (lat != null && long != null) {
locationPosition.value = LatLng(
lat,
long,
);
log("live location ${locationPosition.value}");
}
});
}
} else {
await location.changeSettings(
accuracy: LocationAccuracy.high,
interval: 2000,
distanceFilter: 2);
location.onLocationChanged.listen((LocationData currentLocation) async {
var lat = currentLocation.latitude;
var long = currentLocation.longitude;
if (lat != null && long != null) {
locationPosition.value = LatLng(
lat,
long,
);
log("live location ${locationPosition.value}");
}
});
}
}
}

@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
import 'helper.dart';
class AppColors {
AppColors._();
static const Color primaryColor = Color(0xffed1846);
static const Color secondaryColor = Color(0xff5bcb84);
static const Color tColor = Color(0xfff087ca);
static const Color rColor = Color(0xfff08c87);
static const Color statusColorPending = Color(0xfff8c942);
static const Color statusColorInProgress = Color(0xff587add);
static const Color statusColorConfirm = Color(0xff30d300);
static const Color pageBgColor = Color(0xFFF6F6F6);
static const Color grey100Color = Color(0xFFEEEEEE);
static const Color grey200Color = Color(0xFFEEEEEE);
static const Color grey300Color = Color(0xFFE0E0E0);
static const Color grey400Color = Color(0xFFBDBDBD);
static const Color grey500Color = Color(0xFF9E9E9E);
static const Color grey600Color = Color(0xFF757575);
static const Color grey700Color = Color(0xFF616161);
static const Color grey800Color = Color(0xFF424242);
static const Color grey900Color = Color(0xFF212121);
static const Color errorColor = Color(0xFFD50000);
static const Color error100Color = Color(0xffFF5252);
static const Color mainBgColor = Color(0xfff7f7f7);
static const Color logoColor = Color(0xfff1ea0c);
static MaterialColor primaryMaterialColor = getSwatch(primaryColor);
static MaterialColor errorMaterialColor = getSwatch(errorColor);
static MaterialColor tableRowMaterialColor = getSwatch(grey500Color);
}

@ -0,0 +1,4 @@
class AppImages {
static const String pickupMarker = "assets/images/pickup_marker.png";
static const String dropMarker = "assets/images/drop_marker.png";
}

@ -0,0 +1,25 @@
import 'dart:ui';
import 'package:get/get.dart';
class AppSizes {
// get height and width from getX
static final double deviceHeight = Get.height;
static final double deviceWidth = Get.width;
static const int height1060 = 1060;
static const int height880 = 880;
static const int height740 = 740;
static const int height490 = 490;
static const int width1060 = 1060;
static const int width880 = 880;
static const int width740 = 740;
static const int width490 = 490;
static const int screen720x1280 = 490;
static final double mapPinSize = (deviceWidth * window.devicePixelRatio);
}

@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
MaterialColor getSwatch(Color color) {
final hslColor = HSLColor.fromColor(color);
final lightness = hslColor.lightness;
/// if [500] is the default color, there are at LEAST five
/// steps below [500]. (i.e. 400, 300, 200, 100, 50.) A
/// divisor of 5 would mean [50] is a lightness of 1.0 or
/// a color of #ffffff. A value of six would be near white
/// but not quite.
const lowDivisor = 6;
/// if [500] is the default color, there are at LEAST four
/// steps above [500]. A divisor of 4 would mean [900] is
/// a lightness of 0.0 or color of #000000
const highDivisor = 5;
final lowStep = (1.0 - lightness) / lowDivisor;
final highStep = lightness / highDivisor;
return MaterialColor(color.value, {
50: (hslColor.withLightness(lightness + (lowStep * 5))).toColor(),
100: (hslColor.withLightness(lightness + (lowStep * 4))).toColor(),
200: (hslColor.withLightness(lightness + (lowStep * 3))).toColor(),
300: (hslColor.withLightness(lightness + (lowStep * 2))).toColor(),
400: (hslColor.withLightness(lightness + lowStep)).toColor(),
500: (hslColor.withLightness(lightness)).toColor(),
600: (hslColor.withLightness(lightness - highStep)).toColor(),
700: (hslColor.withLightness(lightness - (highStep * 2))).toColor(),
800: (hslColor.withLightness(lightness - (highStep * 3))).toColor(),
900: (hslColor.withLightness(lightness - (highStep * 4))).toColor(),
});
}

@ -0,0 +1,120 @@
import 'dart:developer';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:location/location.dart';
import 'app_images.dart';
import 'app_sizes.dart';
import 'permission_alert.dart';
class LocationController<T> extends GetxController {
Location location = Location();
// final Rx<LatLng?> locationPosition = const LatLng(0.0, 0.0).obs;
/*final Rx<LatLng?> locationPosition =
const LatLng(12.90618717, 77.5844983).obs;*/
final Rx<LatLng?> locationPosition =
const LatLng(0, 0).obs;
bool locationServiceActive = true;
BitmapDescriptor? pickupMarker;
BitmapDescriptor? dropMarker;
@override
void onInit() async {
await _getBytesFromAsset(AppImages.pickupMarker, AppSizes.mapPinSize * 0.1);
await _getBytesFromAsset(AppImages.dropMarker, AppSizes.mapPinSize * 0.05);
super.onInit();
refreshToLiveLocation();
}
Future<void> _getBytesFromAsset(String path, double size) async {
ByteData data = await rootBundle.load(path);
ui.Codec codec = await ui.instantiateImageCodec(
data.buffer.asUint8List(),
targetWidth: size.toInt(),
allowUpscaling: true,
);
ui.FrameInfo fi = await codec.getNextFrame();
if (path == AppImages.pickupMarker) {
pickupMarker = BitmapDescriptor.fromBytes(
(await fi.image.toByteData(format: ui.ImageByteFormat.png))!
.buffer
.asUint8List());
} else if (path == AppImages.dropMarker) {
dropMarker = BitmapDescriptor.fromBytes(
(await fi.image.toByteData(format: ui.ImageByteFormat.png))!
.buffer
.asUint8List());
} else {}
}
refreshToLiveLocation() async {
log("initiating");
bool serviceEnabled;
PermissionStatus permissionGranted;
serviceEnabled = await location.serviceEnabled();
if (!serviceEnabled) {
serviceEnabled = await location.requestService();
locationPosition.value = null;
return;
}
log("permission check");
permissionGranted = await location.hasPermission();
if (permissionGranted == PermissionStatus.denied) {
permissionGranted = await location.requestPermission();
if (permissionGranted != PermissionStatus.granted) {
showPermissionAlertDialog(
requestMsg:
"Location access needed. Go to Android settings, tap App permissions and tap Allow.",
barrierDismissible: false,
);
} else {
await location.changeSettings(
accuracy: LocationAccuracy.high,
interval: 2000,
distanceFilter: 2);
location.onLocationChanged.listen((LocationData currentLocation) async {
var lat = currentLocation.latitude;
var long = currentLocation.longitude;
if (lat != null && long != null) {
locationPosition.value = LatLng(
lat,
long,
);
log("live location ${locationPosition.value}");
}
});
}
} else {
await location.changeSettings(
accuracy: LocationAccuracy.high,
interval: 2000,
distanceFilter: 2);
location.onLocationChanged.listen((LocationData currentLocation) async {
var lat = currentLocation.latitude;
var long = currentLocation.longitude;
if (lat != null && long != null) {
locationPosition.value = LatLng(
lat,
long,
);
log("live location ${locationPosition.value}");
}
});
}
}
}

@ -0,0 +1,252 @@
import 'dart:async';
import 'dart:developer';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:location/location.dart';
import 'location_controller.dart';
class OrderTrackingPage extends StatefulWidget {
var lat;
var lng;
var d_lat;
var d_lng;
var u_address;
OrderTrackingPage({
this.lat,
this.lng,
this.d_lat,
this.d_lng,
this.u_address
});
@override
OrderTrackingPageState createState() => OrderTrackingPageState();
}
class OrderTrackingPageState extends State<OrderTrackingPage> {
final Completer<GoogleMapController> mapController = Completer();
PolylinePoints polylinePoints = PolylinePoints();
double latitude=0;
double longitude=0;
double d_latitude=0;
double d_longitude=0;
String u_address = '';
LocationData? currentLocation;
String googleAPiKey ="AIzaSyDJpK9RVhlBejtJu9xSGfneuTN6HOfJgSM";
Set<Marker> markers = {};
Map<PolylineId, Polyline> polylines = {};
late LatLng startLocation ;
late LatLng user_location;
LocationController locationController = Get.put(LocationController());
double distance = 0.0;
@override
void initState() {
super.initState();
latitude=widget.lat;
longitude=widget.lng;
d_latitude=widget.d_lat;
d_longitude=widget.d_lng;
u_address=widget.u_address;
user_location = LatLng(widget.lat,widget.lng);
startLocation = LatLng(widget.d_lat,widget.d_lng);
LatLng delivery_Location = LatLng(widget.d_lat,widget.d_lng);
//LatLng endLocation = LatLng(17.4968,78.3614);
ever<LatLng?>(locationController.locationPosition, (value) {
if (value != null) {
// log("${value.latitude} ${value.longitude}");
var latitude = value.latitude;
var longitude = value.longitude;
startLocation = LatLng(widget.d_lat, widget.d_lng);
getDirections(user_location);
}
});
getDirections(user_location); //fetch direction polylines from Google API
}
getDirections(user_location) async {
markers.clear();
markers.add(Marker(
markerId: MarkerId(startLocation.toString()),
position: startLocation,
infoWindow: const InfoWindow(
title: 'Starting Point',
snippet: 'Start Marker',
),
icon: locationController.pickupMarker ?? BitmapDescriptor.defaultMarker,
));
markers.add(Marker(
markerId: MarkerId(user_location.toString()),
position: user_location, //position of marker
infoWindow: const InfoWindow(
title: 'Destination Point ',
snippet: 'Destination Marker',
),
icon: locationController.dropMarker ?? BitmapDescriptor.defaultMarker,
));
List<LatLng> polylineCoordinates = [];
PolylineResult result = await polylinePoints.getRouteBetweenCoordinates(
googleAPiKey,
PointLatLng(startLocation.latitude, startLocation.longitude),
PointLatLng(user_location.latitude, user_location.longitude),
travelMode: TravelMode.driving,
);
if (result.points.isNotEmpty) {
for (var point in result.points) {
polylineCoordinates.add(LatLng(point.latitude, point.longitude));
}
} else {
// log(result.errorMessage ?? "Something went wrong");
}
//polulineCoordinates is the List of longitute and latidtude.
double totalDistance = 0;
for(var i = 0; i < polylineCoordinates.length-1; i++){
totalDistance += calculateDistance(
polylineCoordinates[i].latitude,
polylineCoordinates[i].longitude,
polylineCoordinates[i+1].latitude,
polylineCoordinates[i+1].longitude);
}
print(totalDistance);
setState(() {
distance = totalDistance;
});
addPolyLine(polylineCoordinates);
}
addPolyLine(List<LatLng> polylineCoordinates) async {
PolylineId id = const PolylineId("poly");
Polyline polyline = Polyline(
polylineId: id,
color: Colors.deepPurpleAccent,
points: polylineCoordinates,
width:8,
);
polylines[id] =polyline;
var position = CameraPosition(
target: LatLng(latitude,longitude),
zoom: 16);
final GoogleMapController controller = await mapController.future;
controller.animateCamera(CameraUpdate.newCameraPosition(position));
setState(() {});
}
double calculateDistance(lat1, lon1, lat2, lon2){
var p = 0.017453292519943295;
var a = 0.5 - cos((lat2 - lat1) * p)/2 +
cos(lat1 * p) * cos(lat2 * p) *
(1 - cos((lon2 - lon1) * p))/2;
return 12742 * asin(sqrt(a));
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
GoogleMap(
//Map widget from google_maps_flutter package
zoomGesturesEnabled: true,
//enable Zoom in, out on map
initialCameraPosition: CameraPosition(
//innital position in map
target: startLocation, //initial position
zoom: 8.0, //initial zoom level
),
markers: markers,
//markers to show on map
polylines: Set<Polyline>.of(polylines.values),
//polylines
mapType: MapType.normal,
//map type
onMapCreated: (controller) {
//method called when map is created
if (!mapController.isCompleted) {
mapController.complete(controller);
}
},
),
const SizedBox(
height: 95,
),
Positioned(
bottom: 100,
left: 50,
child: Container(
child: Card(
child: Container(
padding: EdgeInsets.all(20),
child: Text("Total Distance: " + distance.toStringAsFixed(2) + " KM",
style: TextStyle(fontSize: 20, fontWeight:FontWeight.bold))
),
)
)),
/* const SizedBox(
height: 30,
),
Positioned(
bottom: 80,
left: 0,
child: Container(
child: Card(
child: Container(
padding: EdgeInsets.all(20),
child: Text("User Address: " + u_address.toString() ,
style: TextStyle(fontSize: 15, fontWeight:FontWeight.bold,overflow: TextOverflow.ellipsis))
),
)
)),
const SizedBox(
height: 30,
),
Positioned(
bottom: 10,
left: 50,
child: Container(
child: Card(
child: Container(
padding: EdgeInsets.all(20),
child: Text("Total DistanceD: " + distance.toStringAsFixed(2) + " KM",
style: TextStyle(fontSize: 20, fontWeight:FontWeight.bold))
),
)
)),*/
],
)
);
}
}

@ -0,0 +1 @@
enum PaddingType { symmetric, only }

@ -0,0 +1,53 @@
import 'package:flutter/material.dart';
import 'package:get/get_core/src/get_main.dart';
import 'package:get/get_navigation/get_navigation.dart';
import 'package:permission_handler/permission_handler.dart';
import 'app_colors.dart';
import 'primary_button.dart';
import 'primary_text.dart';
void showPermissionAlertDialog({
String title = "Need Permission",
required String requestMsg,
bool barrierDismissible = true,
}) {
Get.defaultDialog(
title: title,
middleText: "",
backgroundColor: Colors.white,
contentPadding: const EdgeInsets.only(top: 30, bottom: 30.0),
radius: 10,
barrierDismissible: barrierDismissible,
titlePadding: const EdgeInsets.only(top: 15),
titleStyle: const TextStyle(
color: AppColors.grey900Color,
fontSize: 18,
fontWeight: FontWeight.w600,
),
/*cancel: PrimaryButton(
title: "DISMISS",
onPressed: () {},
textSize: AppSizes.font_13,
bgColor: AppColors.grey500Color,
),*/
confirm: PrimaryButton(
title: "GO TO SETTINGS",
onPressed: () {
openAppSettings();
Get.back();
},
textSize: 13,
),
content: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: PrimaryText(
requestMsg,
textAlign: TextAlign.center,
fontColor: AppColors.grey800Color,
fontSize: 15,
fontWeight: FontWeight.w500,
),
),
);
}

@ -0,0 +1,65 @@
import 'package:flutter/material.dart';
import 'app_colors.dart';
import 'primary_text.dart';
class PrimaryButton extends StatelessWidget {
final String title;
final VoidCallback onPressed;
final double verticalPadding;
final bool textAllCaps;
final double textSize;
final FontWeight textWeight;
final Color textColor;
final TextDecoration textDecoration;
final int letterSpacing;
final bool isResponsive;
final Color bgColor;
final double horizontalPadding;
const PrimaryButton({
Key? key,
required this.title,
required this.onPressed,
this.verticalPadding = 10.0,
this.textAllCaps = true,
this.textSize = 14,
this.textWeight = FontWeight.w500,
this.textColor = Colors.white,
this.textDecoration = TextDecoration.none,
this.letterSpacing = 1,
this.isResponsive = true,
this.bgColor = AppColors.primaryColor,
this.horizontalPadding = 25.0,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(40),
),
color: bgColor,
child: InkWell(
onTap: onPressed,
child: Padding(
padding: EdgeInsets.symmetric(
vertical: verticalPadding,
horizontal: horizontalPadding,
),
child: PrimaryText(
textAllCaps ? title.toUpperCase() : title,
// title.toUpperCase(),
textAlign: TextAlign.center,
fontSize: textSize,
fontWeight: textWeight,
fontColor: textColor,
textDecoration: textDecoration,
isResponsive: isResponsive,
),
),
),
);
}
}

@ -0,0 +1,88 @@
import 'package:flutter/material.dart';
import 'app_sizes.dart';
import 'padding_type_enum.dart';
class PrimaryText extends StatelessWidget {
final String text;
final Color? fontColor;
final double fontSize;
final FontWeight fontWeight;
final double horizontalPadding;
final double verticalPadding;
final TextAlign textAlign;
final bool isResponsive;
final TextDecoration? textDecoration;
final PaddingType paddingType;
final double leftPadding;
final double rightPadding;
final double lineHeight;
final FontStyle fontStyle;
final TextOverflow textOverflow;
final bool textAllCaps;
final double letterSpacing;
const PrimaryText(
this.text, {
Key? key,
this.fontColor = Colors.black,
this.fontSize = 16,
this.fontWeight = FontWeight.w600,
this.horizontalPadding = 0.0,
this.verticalPadding = 0.0,
this.textAlign = TextAlign.start,
this.isResponsive = true,
this.textDecoration,
this.paddingType = PaddingType.symmetric,
this.leftPadding = 0.0,
this.rightPadding = 0.0,
this.lineHeight = 1.5,
this.fontStyle = FontStyle.normal,
this.textOverflow = TextOverflow.visible,
this.textAllCaps = false,
this.letterSpacing = 0,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: paddingType == PaddingType.symmetric
? EdgeInsets.symmetric(
horizontal: horizontalPadding,
vertical: verticalPadding,
)
: EdgeInsets.only(
left: leftPadding,
right: rightPadding,
),
child: Text(
textAllCaps ? text.toUpperCase() : text,
style: TextStyle(
fontSize: responsiveTextSize(),
fontWeight: fontWeight,
color: fontColor,
decoration: textDecoration,
height: lineHeight,
fontStyle: fontStyle,
letterSpacing: letterSpacing,
),
textScaleFactor: 1,
textAlign: textAlign,
),
);
}
double responsiveTextSize() {
if (isResponsive) {
if (AppSizes.deviceHeight < AppSizes.height490) {
return fontSize - 3;
} else if (AppSizes.deviceHeight < AppSizes.height740) {
return fontSize - 2;
} else if (AppSizes.deviceHeight < AppSizes.height880) {
return fontSize - 1;
}
}
return fontSize;
}
}

@ -0,0 +1 @@
enum PaddingType { symmetric, only }

@ -0,0 +1,53 @@
import 'package:flutter/material.dart';
import 'package:get/get_core/src/get_main.dart';
import 'package:get/get_navigation/get_navigation.dart';
import 'package:permission_handler/permission_handler.dart';
import 'app_colors.dart';
import 'primary_button.dart';
import 'primary_text.dart';
void showPermissionAlertDialog({
String title = "Need Permission",
required String requestMsg,
bool barrierDismissible = true,
}) {
Get.defaultDialog(
title: title,
middleText: "",
backgroundColor: Colors.white,
contentPadding: const EdgeInsets.only(top: 30, bottom: 30.0),
radius: 10,
barrierDismissible: barrierDismissible,
titlePadding: const EdgeInsets.only(top: 15),
titleStyle: const TextStyle(
color: AppColors.grey900Color,
fontSize: 18,
fontWeight: FontWeight.w600,
),
/*cancel: PrimaryButton(
title: "DISMISS",
onPressed: () {},
textSize: AppSizes.font_13,
bgColor: AppColors.grey500Color,
),*/
confirm: PrimaryButton(
title: "GO TO SETTINGS",
onPressed: () {
openAppSettings();
Get.back();
},
textSize: 13,
),
content: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: PrimaryText(
requestMsg,
textAlign: TextAlign.center,
fontColor: AppColors.grey800Color,
fontSize: 15,
fontWeight: FontWeight.w500,
),
),
);
}

@ -0,0 +1,65 @@
import 'package:flutter/material.dart';
import 'app_colors.dart';
import 'primary_text.dart';
class PrimaryButton extends StatelessWidget {
final String title;
final VoidCallback onPressed;
final double verticalPadding;
final bool textAllCaps;
final double textSize;
final FontWeight textWeight;
final Color textColor;
final TextDecoration textDecoration;
final int letterSpacing;
final bool isResponsive;
final Color bgColor;
final double horizontalPadding;
const PrimaryButton({
Key? key,
required this.title,
required this.onPressed,
this.verticalPadding = 10.0,
this.textAllCaps = true,
this.textSize = 14,
this.textWeight = FontWeight.w500,
this.textColor = Colors.white,
this.textDecoration = TextDecoration.none,
this.letterSpacing = 1,
this.isResponsive = true,
this.bgColor = AppColors.primaryColor,
this.horizontalPadding = 25.0,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(40),
),
color: bgColor,
child: InkWell(
onTap: onPressed,
child: Padding(
padding: EdgeInsets.symmetric(
vertical: verticalPadding,
horizontal: horizontalPadding,
),
child: PrimaryText(
textAllCaps ? title.toUpperCase() : title,
// title.toUpperCase(),
textAlign: TextAlign.center,
fontSize: textSize,
fontWeight: textWeight,
fontColor: textColor,
textDecoration: textDecoration,
isResponsive: isResponsive,
),
),
),
);
}
}

@ -0,0 +1,88 @@
import 'package:flutter/material.dart';
import 'app_sizes.dart';
import 'padding_type_enum.dart';
class PrimaryText extends StatelessWidget {
final String text;
final Color? fontColor;
final double fontSize;
final FontWeight fontWeight;
final double horizontalPadding;
final double verticalPadding;
final TextAlign textAlign;
final bool isResponsive;
final TextDecoration? textDecoration;
final PaddingType paddingType;
final double leftPadding;
final double rightPadding;
final double lineHeight;
final FontStyle fontStyle;
final TextOverflow textOverflow;
final bool textAllCaps;
final double letterSpacing;
const PrimaryText(
this.text, {
Key? key,
this.fontColor = Colors.black,
this.fontSize = 16,
this.fontWeight = FontWeight.w600,
this.horizontalPadding = 0.0,
this.verticalPadding = 0.0,
this.textAlign = TextAlign.start,
this.isResponsive = true,
this.textDecoration,
this.paddingType = PaddingType.symmetric,
this.leftPadding = 0.0,
this.rightPadding = 0.0,
this.lineHeight = 1.5,
this.fontStyle = FontStyle.normal,
this.textOverflow = TextOverflow.visible,
this.textAllCaps = false,
this.letterSpacing = 0,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: paddingType == PaddingType.symmetric
? EdgeInsets.symmetric(
horizontal: horizontalPadding,
vertical: verticalPadding,
)
: EdgeInsets.only(
left: leftPadding,
right: rightPadding,
),
child: Text(
textAllCaps ? text.toUpperCase() : text,
style: TextStyle(
fontSize: responsiveTextSize(),
fontWeight: fontWeight,
color: fontColor,
decoration: textDecoration,
height: lineHeight,
fontStyle: fontStyle,
letterSpacing: letterSpacing,
),
textScaleFactor: 1,
textAlign: textAlign,
),
);
}
double responsiveTextSize() {
if (isResponsive) {
if (AppSizes.deviceHeight < AppSizes.height490) {
return fontSize - 3;
} else if (AppSizes.deviceHeight < AppSizes.height740) {
return fontSize - 2;
} else if (AppSizes.deviceHeight < AppSizes.height880) {
return fontSize - 1;
}
}
return fontSize;
}
}

@ -0,0 +1,234 @@
import 'package:flutter/material.dart';
import 'package:supplier_new/orders/edit_order_requests.dart';
class AcceptOrderRequests extends StatefulWidget {
var order;
AcceptOrderRequests({this.order});
@override
State<AcceptOrderRequests> createState() => _AcceptOrderRequestsState();
}
class _AcceptOrderRequestsState extends State<AcceptOrderRequests> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.black),
onPressed: () => Navigator.pop(context),
),
actions: [
IconButton(
icon: const Icon(Icons.help_outline, color: Colors.black),
onPressed: () {},
),
],
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// 🔹 Top Card with Image
Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.asset(
"images/building.png", // replace with network image
height: 180,
width: double.infinity,
fit: BoxFit.cover,
),
),
/// Status Chip
Positioned(
top: 12,
left: 12,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: const Text(
"New",
style: TextStyle(
color: Colors.blue,
fontWeight: FontWeight.w600,
fontSize: 12,
),
),
),
),
],
),
/// Title + Location + Distance
Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
"Club Kohinoor",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
SizedBox(height: 4),
Text(
"Banjara Hills, Hyderabad • 5.5 Km",
style: TextStyle(
fontSize: 13,
color: Colors.grey,
),
),
],
),
),
const Divider(),
/// 🔹 Order Details
Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"ORDER DETAILS",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 12),
_detailRow("Tanker Price", "${widget.order.quoted_amount}"),
_detailRow("Water Type", " ${widget.order.type_of_water}"),
_detailRow("Date of Delivery", "${widget.order.time}"),
_detailRow("Capacity", "${widget.order.capacity}"),
_detailRow("Time of Delivery", "${widget.order.averageTime}"),
_detailRow("Quantity", "${widget.order.quantity}"),
_detailRow("Advance", "10%"),
],
),
),
const Divider(),
/// 🔹 Additional Details
const Padding(
padding: EdgeInsets.all(12),
child: Text(
"ADDITIONAL DETAILS\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit, "
"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "
"Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut "
"aliquip ex ea commodo consequat.",
style: TextStyle(fontSize: 13, color: Colors.black87, height: 1.4),
),
),
const Divider(),
/// 🔹 Payment Summary
Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"PAYMENT SUMMARY",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 12),
_detailRow("Tanker Price", "₹ 1,500"),
_detailRow("Advance", "10%"),
],
),
),
const SizedBox(height: 80), // space for bottom buttons
],
),
),
/// 🔹 Bottom Action Buttons
bottomNavigationBar: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: Colors.white,
border: Border(top: BorderSide(color: Colors.grey.shade300)),
),
child: Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => EditOrderRequests(order: widget.order,advance:"10%" ,),
),
);
},
child: const Text("Edit Order"),
),
),
const SizedBox(width: 8),
Expanded(
child: OutlinedButton(
style: OutlinedButton.styleFrom(
foregroundColor: Colors.red,
side: const BorderSide(color: Colors.red),
),
onPressed: () {},
child: const Text("Reject"),
),
),
const SizedBox(width: 8),
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
),
onPressed: () {},
child: const Text("Accept"),
),
),
],
),
),
);
}
/// 🔹 Helper widget for rows
Widget _detailRow(String title, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(title,
style: const TextStyle(fontSize: 13, color: Colors.grey)),
Text(value,
style: const TextStyle(
fontSize: 13, fontWeight: FontWeight.w600)),
],
),
);
}
}

@ -0,0 +1,216 @@
import 'package:flutter/material.dart';
class EditOrderRequests extends StatefulWidget {
var order;
String? advance;
EditOrderRequests({this.order,this.advance});
@override
State<EditOrderRequests> createState() => _EditOrderRequestsState();
}
class _EditOrderRequestsState extends State<EditOrderRequests> {
final TextEditingController tankerPriceController = TextEditingController();
final TextEditingController waterTypeController = TextEditingController();
final TextEditingController capacityController = TextEditingController();
final TextEditingController timeController = TextEditingController();
final TextEditingController quantityController = TextEditingController();
final TextEditingController advanceController = TextEditingController();
final TextEditingController dateController = TextEditingController();
@override
void initState() {
super.initState();
tankerPriceController.text='${widget.order.quoted_amount}';
waterTypeController.text='${widget.order.type_of_water}';
quantityController.text='${widget.order.quantity}';
capacityController.text='${widget.order.capacity}';
timeController.text='${widget.order.averageTime}';
dateController.text='${widget.order.time}';
advanceController.text='${widget.advance}';
// Update summary in real-time as user types
tankerPriceController.addListener(() => setState(() {}));
capacityController.addListener(() => setState(() {}));
quantityController.addListener(() => setState(() {}));
dateController.addListener(() => setState(() {}));
timeController.addListener(() => setState(() {}));
waterTypeController.addListener(() => setState(() {}));
advanceController.addListener(() => setState(() {}));
}
@override
Widget build(BuildContext context) {
int tankerPrice = int.tryParse(tankerPriceController.text) ?? 0;
int updatedQuantity=int.tryParse(quantityController.text) ?? 0;
String updatedCapacity=capacityController.text ?? '';
int totalPrice = tankerPrice * updatedQuantity;
double advancePercent =
double.tryParse(advanceController.text.replaceAll('%', '')) ?? 0;
int advancePayable = (totalPrice * (advancePercent / 100)).round();
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
leading: IconButton(
icon: const Icon(Icons.close, color: Colors.black),
onPressed: () => Navigator.pop(context),
),
title: const Text(
"Edit Order",
style: TextStyle(color: Colors.black, fontWeight: FontWeight.w600),
),
centerTitle: true,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 🔹 Club Info Card
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.shade300),
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Chip(
label: Text("New",
style: TextStyle(
color: Colors.blue,
fontSize: 12,
fontWeight: FontWeight.w600)),
backgroundColor: Color(0xFFEAF3FF),
padding: EdgeInsets.symmetric(horizontal: 4),
),
Text("Club Kohinoor",
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.w600)),
SizedBox(height: 4),
Text("Banjara Hills, Hyderabad",
style: TextStyle(color: Colors.grey, fontSize: 13)),
],
),
),
const Text("5.5 Km", style: TextStyle(color: Colors.black54)),
],
),
),
const SizedBox(height: 20),
const Text("ORDER DETAILS",
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 14)),
const SizedBox(height: 12),
// 🔹 Two in a row
_twoFields(tankerPriceController, "Tanker Price", null, null),
_twoFields(capacityController, "Capacity", quantityController, "Quantity"),
_twoFields(waterTypeController, "Water Type",advanceController, "Advance"),
_twoFields(dateController, "Date",timeController, "Time Of Delivery"),
const SizedBox(height: 20),
const Text("UPDATED PAYMENT SUMMARY",
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 14)),
const SizedBox(height: 12),
_summaryRow("Tanker Price", "$tankerPrice"),
_summaryRow("Quantity", " $updatedQuantity"),
_summaryRow("Capacity", "$updatedCapacity"),
_summaryRow("Total Price", "$totalPrice"),
const Divider(),
_summaryRow("Advance", advanceController.text),
_summaryRow("Advance Payable", "$advancePayable"),
],
),
),
bottomNavigationBar: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
border: Border(top: BorderSide(color: Colors.grey.shade300)),
),
child: Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: () => Navigator.pop(context),
child: const Text("Cancel"),
),
),
const SizedBox(width: 8),
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.deepPurple,
foregroundColor: Colors.white,
),
onPressed: () {
// 🔹 Collect updated values
print("Tanker Price: ${tankerPriceController.text}");
print("Water Type: ${waterTypeController.text}");
// Save logic here
},
child: const Text("Send ➤"),
),
),
],
),
),
);
}
/// 🔹 Two fields side by side
Widget _twoFields(TextEditingController? controller1, String? label1,
TextEditingController? controller2, String? label2) {
return Row(
children: [
Expanded(
child: _textField(controller1, label1),
),
const SizedBox(width: 10),
if (controller2 != null)
Expanded(
child: _textField(controller2, label2),
),
],
);
}
/// 🔹 Custom text field
Widget _textField(TextEditingController? controller, String? label) {
return Container(
margin: const EdgeInsets.only(bottom: 12),
child: TextField(
controller: controller,
decoration: InputDecoration(
labelText: label,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
contentPadding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
),
),
);
}
/// 🔹 Summary row
Widget _summaryRow(String title, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(title, style: const TextStyle(color: Colors.black54)),
Text(value,
style: const TextStyle(
fontWeight: FontWeight.w600, color: Colors.black)),
],
),
);
}
}

@ -1,4 +1,9 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:supplier_new/common/settings.dart';
import 'package:supplier_new/orders/order_requests_model.dart';
import 'accept_order_requests.dart';
class OrderRequestsPage extends StatefulWidget {
const OrderRequestsPage({super.key});
@ -9,154 +14,123 @@ class OrderRequestsPage extends StatefulWidget {
class _OrderRequestsPageState extends State<OrderRequestsPage> {
final TextEditingController searchController = TextEditingController();
List<OrderRequestsModel> orderRequestsList = [];
bool isLoading = false;
// Sample orders list
final List<OrderModel> allOrders = [
OrderModel(
status: "New",
title: "Green Valley Apartments",
location: "Gachibowli",
description: "10,000 L - Drinking water",
price: "₹3,400",
),
OrderModel(
status: "Expires in 15m",
title: "Lakeview Towers",
location: "Madhapur",
description: "8,000 L - Borewell water",
price: "₹2,700",
),
OrderModel(
status: "Expired",
title: "Sunrise Residency",
location: "Kukatpally",
description: "12,000 L - Tanker water",
price: "₹4,000",
),
OrderModel(
status: "Rejected",
title: "Skyline Apartments",
location: "Kompally",
description: "5,000 L - Drinking water",
price: "₹1,600",
),
OrderModel(
status: "Pending",
title: "Skyline Apartments",
location: "Kompally",
description: "5,000 L - Drinking water",
price: "₹1,600",
),
OrderModel(
status: "Rejected",
title: "Elite Towers",
location: "Hitech City",
description: "20,000 L - Borewell water",
price: "₹6,000",
),
];
@override
void initState() {
super.initState();
_fetchOrders();
}
Future<void> _fetchOrders() async {
setState(() => isLoading = true);
try {
final response = await AppSettings.getOrderRequestsFromUsers();
final data = (jsonDecode(response)['data'] as List)
.map((e) => OrderRequestsModel.fromJson(e))
.toList();
setState(() {
orderRequestsList = data;
isLoading = false;
});
} catch (e) {
debugPrint("⚠️ Error fetching orders: $e");
setState(() => isLoading = false);
}
}
/// 🔹 Helper to get status based on order time
Map<String, dynamic> getOrderStatus(String orderTimeStr) {
String status = "Invalid time";
Color color = Colors.grey;
if (orderTimeStr.isEmpty) return {"status": status, "color": color};
try {
final format = DateFormat("dd-MM-yyyy HH:mm");
final orderTime = format.parse(orderTimeStr);
final now = DateTime.now();
final difference = now.difference(orderTime);
if (difference.inHours < 2) {
status = "New";
color = const Color(0XFF1D7AFC); // Blue
} else if (difference.inHours < 24) {
int remaining = 24 - difference.inHours;
status = "Expires in ${remaining}h";
if (difference.inHours < 6) {
color = const Color(0XFFE56910); // Less urgent
} else {
color = const Color(0XFFE2483D); // More urgent
}
}
else {
status = "Expired";
color = const Color(0XFF757575); // Grey
}
} catch (e) {
debugPrint("⚠️ Error parsing time: $e");
}
return {"status": status, "color": color};
}
@override
Widget build(BuildContext context) {
// Split orders into rejected and others
final rejectedOrders =
allOrders.where((o) => o.status.toLowerCase() == "rejected").toList();
final otherOrders =
allOrders.where((o) => o.status.toLowerCase() != "rejected").toList();
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.black),
onPressed: () => Navigator.pop(context),
),
title: const Text(
"Order Requests",
style: TextStyle(
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
centerTitle: false,
actions: [
IconButton(
icon: const Icon(Icons.help_outline, color: Colors.black),
onPressed: () {},
),
const SizedBox(width: 8),
],
),
appBar: AppSettings.SupplierAppBarWithHelpAction('Order Requests', context),
body: Padding(
padding: const EdgeInsets.all(12.0),
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
/// Search bar
Row(
children: [
Expanded(
child: Container(
height: 42,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
border: Border.all(color: Colors.grey.shade300),
color: Colors.white,
),
child: TextField(
controller: searchController,
decoration: const InputDecoration(
hintText: "Search",
prefixIcon: Icon(Icons.search, color: Colors.grey),
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 10),
),
),
),
),
const SizedBox(width: 10),
Container(
height: 42,
width: 42,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade300),
color: Colors.white,
),
child: IconButton(
icon: const Icon(Icons.sort, size: 20, color: Colors.black),
onPressed: () {},
),
),
],
),
/// 🔹 Search Bar
_buildSearchBar(),
const SizedBox(height: 16),
/// Orders List
/// 🔹 Orders List
Expanded(
child: ListView(
children: [
// Active / Other Orders
...otherOrders.map((o) => OrderCard(order: o)),
child: isLoading
? const Center(child: CircularProgressIndicator())
: ListView.builder(
itemCount: orderRequestsList.length,
itemBuilder: (context, index) {
final order = orderRequestsList[index];
final statusMap = getOrderStatus(order.time ?? "");
final status = statusMap['status'];
final color = statusMap['color'];
// Rejected Orders Section
if (rejectedOrders.isNotEmpty) ...[
const SizedBox(height: 12),
const Text(
"Rejected Requests",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
const SizedBox(height: 8),
...rejectedOrders.map((o) => OrderCard(order: o)),
],
],
final cardModel = OrderCardModel(
status: status,
statusColor: color,
title: order.building_name ?? "",
location: order.displayAddress ?? "",
description: "${order.capacity ?? ''} - ${order.type_of_water ?? ''}",
price: "${AppSettings.formDouble(order.quoted_amount) ?? ''}",
);
final isExpired = status.toLowerCase() == "expired";
final card = OrderCard(order: cardModel);
return isExpired
? card
: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => AcceptOrderRequests(order: order),
),
);
},
child: card,
);
},
),
),
],
@ -164,18 +138,97 @@ class _OrderRequestsPageState extends State<OrderRequestsPage> {
),
);
}
Widget _buildSearchBar() {
return Row(
children: [
Expanded(
child: Container(
height: 42,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(22),
border: Border.all(color: const Color(0XFF939495)),
),
child: TextField(
controller: searchController,
decoration: InputDecoration(
hintText: "Search",
hintStyle: fontTextStyle(16, const Color(0XFF646566), FontWeight.w400),
prefixIcon: SizedBox(
height: 20,
width: 20,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Image.asset(
'images/search.png',
fit: BoxFit.contain,
),
),
),
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(vertical: 10),
),
),
),
),
const SizedBox(width: 10),
Container(
height: 42,
width: 42,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade300),
color: Color(0XFFF5F6F6),
),
child: IconButton(
icon: Image.asset(
'images/filter.png', // your image path
height: 20,
width: 20,
fit: BoxFit.contain,
),
onPressed: () {
// Your onPressed action
},
)
),
Container(
height: 42,
width: 42,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade300),
color: Color(0XFFF5F6F6),
),
child: IconButton(
icon: Image.asset(
'images/sort.png', // your image path
height: 20,
width: 20,
fit: BoxFit.contain,
),
onPressed: () {
// Your onPressed action
},
)
),
],
);
}
}
/// Order Model
class OrderModel {
/// 🔹 UI Model for rendering OrderCard
class OrderCardModel {
final String status;
final Color statusColor;
final String title;
final String location;
final String description;
final String price;
OrderModel({
OrderCardModel({
required this.status,
required this.statusColor,
required this.title,
required this.location,
required this.description,
@ -183,109 +236,73 @@ class OrderModel {
});
}
/// Order Card widget
/// 🔹 Card widget
class OrderCard extends StatelessWidget {
final OrderModel order;
final OrderCardModel order;
const OrderCard({super.key, required this.order});
Color _getStatusColor() {
switch (order.status.toLowerCase()) {
case "new":
return Colors.blue;
case "expires in 15m":
case "expires in 5m":
return Colors.orange;
case "expired":
return Colors.grey;
case "rejected":
return Colors.red;
default:
return Colors.black;
}
}
@override
Widget build(BuildContext context) {
final statusColor = _getStatusColor();
return Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
color: Color(0XFFF6F6F6),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade300),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
/// Left content
/// 🔹 Left content
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Status chip
/// Status chip
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: statusColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
color: Color(0XFFF6F6F6),
borderRadius: BorderRadius.circular(4),
border: Border.all(
color: order.statusColor,
width: 0.5,
),
),
child: Text(
order.status,
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: statusColor,
),
style: fontTextStyle(12, order.statusColor, FontWeight.w500)
),
),
const SizedBox(height: 6),
SizedBox(height:MediaQuery.of(context).size.height * .008,),
// Title
/// Title
Text(
order.title,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
),
style: fontTextStyle(16, Color(0XFF2D2E30), FontWeight.w600)
),
// Location
/// Location
Text(
order.location,
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade600,
),
style: fontTextStyle(10, Color(0XFF939495), FontWeight.w400)
),
const SizedBox(height: 4),
// Description
/// Description
Text(
order.description,
style: const TextStyle(
fontSize: 12,
color: Colors.blue,
fontWeight: FontWeight.w500,
),
style: fontTextStyle(14, Color(0XFF8270DB), FontWeight.w500)
),
],
),
),
/// Price
/// 🔹 Price
Text(
order.price,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
),
style: fontTextStyle(16, Color(0XFF444444), FontWeight.w600)
),
],
),

@ -0,0 +1,51 @@
import 'package:supplier_new/common/settings.dart';
import 'package:geolocator/geolocator.dart';
class OrderRequestsModel {
String building_name = '';
String address = '';
String type_of_water = '';
String capacity = '';
String quantity = '';
String time = '';
String averageTime = '';
String quoted_amount = '';
String displayAddress='';
double lat=0;
double lng=0;
double distanceInMeters=0;
OrderRequestsModel();
factory OrderRequestsModel.fromJson(Map<String, dynamic> json){
OrderRequestsModel rtvm = new OrderRequestsModel();
rtvm.building_name = json['customer_details']['buildingName'] ?? '';
rtvm.address = json['customer_details']['profile']['address1'] ?? '';
rtvm.type_of_water = json['type_of_water'] ?? '';
rtvm.capacity = json['capacity'] ?? '';
rtvm.quantity = json['quantity']?? '';
rtvm.averageTime = json['time'] ?? '';
rtvm.time = json['my_supplier_entry']['time'] ?? '';
rtvm.quoted_amount = json['my_supplier_entry']['quoted_amount'].toString() ?? '';
// Split and trim
List<String> parts = rtvm.address.split(',').map((e) => e.trim()).toList();
// Usually, the locality is the part before the main city (Hyderabad)displayAddress = "";
if (parts.length >= 2) {
rtvm.displayAddress = parts[parts.length -4]; // "Banjara Hills"
}
/* rtvm.distanceInMeters = double.parse((Geolocator.distanceBetween(
rtvm.lat,
rtvm.lng,
AppSettings.supplierLatitude,
AppSettings.supplierLongitude
) / 1000).toStringAsFixed(2));*/
return rtvm;
}
Map<String, dynamic> toJson() => {
"boreName":this.building_name,
};
}

@ -0,0 +1,250 @@
import 'package:flutter/material.dart';
import 'package:google_maps_place_picker_mb/google_maps_place_picker.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'dart:io' show Platform;
import 'package:google_maps_flutter_android/google_maps_flutter_android.dart';
import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
class GooglePlacePicker extends StatefulWidget {
GooglePlacePicker({Key? key}) : super(key: key);
static final kInitialPosition = LatLng(17.4167312, 78.4519109);
final GoogleMapsFlutterPlatform mapsImplementation =
GoogleMapsFlutterPlatform.instance;
@override
State<GooglePlacePicker> createState() => _GooglePlacePickerState();
}
class _GooglePlacePickerState extends State<GooglePlacePicker> {
PickResult? selectedPlace;
bool _showPlacePickerInContainer = false;
bool _showGoogleMapInContainer = false;
bool _mapsInitialized = false;
String _mapsRenderer = "latest";
void initRenderer() {
if (_mapsInitialized) return;
if (widget.mapsImplementation is GoogleMapsFlutterAndroid) {
switch (_mapsRenderer) {
case "legacy":
(widget.mapsImplementation as GoogleMapsFlutterAndroid)
.initializeWithRenderer(AndroidMapRenderer.legacy);
break;
case "latest":
(widget.mapsImplementation as GoogleMapsFlutterAndroid)
.initializeWithRenderer(AndroidMapRenderer.latest);
break;
}
}
setState(() {
_mapsInitialized = true;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Google Map Place Picker Demo"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (!_mapsInitialized &&
widget.mapsImplementation
is GoogleMapsFlutterAndroid) ...[
Switch(
value: (widget.mapsImplementation
as GoogleMapsFlutterAndroid)
.useAndroidViewSurface,
onChanged: (value) {
setState(() {
(widget.mapsImplementation
as GoogleMapsFlutterAndroid)
.useAndroidViewSurface = value;
});
}),
Text("Hybrid Composition"),
]
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (!_mapsInitialized &&
widget.mapsImplementation
is GoogleMapsFlutterAndroid) ...[
Text("Renderer: "),
Radio(
groupValue: _mapsRenderer,
value: "auto",
onChanged: (value) {
setState(() {
_mapsRenderer = "auto";
});
}),
Text("Auto"),
Radio(
groupValue: _mapsRenderer,
value: "legacy",
onChanged: (value) {
setState(() {
_mapsRenderer = "legacy";
});
}),
Text("Legacy"),
Radio(
groupValue: _mapsRenderer,
value: "latest",
onChanged: (value) {
setState(() {
_mapsRenderer = "latest";
});
}),
Text("Latest"),
]
],
),
!_showPlacePickerInContainer
? ElevatedButton(
child: Text("Load Place Picker"),
onPressed: () {
initRenderer();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return PlacePicker(
resizeToAvoidBottomInset:
false, // only works in page mode, less flickery
apiKey: Platform.isAndroid
? "AIzaSyB4FhQFeC9JMeIadUwIO6HkoD4UFG6N-HM"
: "IOS API KEY",
hintText: "Find a place ...",
searchingText: "Please wait ...",
selectText: "Select place",
outsideOfPickAreaText: "Place not in area",
initialPosition: GooglePlacePicker.kInitialPosition,
useCurrentLocation: true,
selectInitialPosition: true,
usePinPointingSearch: true,
usePlaceDetailSearch: true,
zoomGesturesEnabled: true,
zoomControlsEnabled: true,
onMapCreated: (GoogleMapController controller) {
print("Map created");
},
onPlacePicked: (PickResult result) {
print(
"Place picked: ${result.formattedAddress}");
setState(() {
selectedPlace = result;
Navigator.of(context).pop();
});
},
onMapTypeChanged: (MapType mapType) {
print(
"Map type changed to ${mapType.toString()}");
},
);
},
),
);
},
)
: Container(),
!_showPlacePickerInContainer
? ElevatedButton(
child: Text("Load Place Picker in Container"),
onPressed: () {
initRenderer();
setState(() {
_showPlacePickerInContainer = true;
});
},
)
: Container(
width: MediaQuery.of(context).size.width * 0.75,
height: MediaQuery.of(context).size.height * 0.35,
child: PlacePicker(
//forceAndroidLocationManager: true,
apiKey: Platform.isAndroid
? "AIzaSyB4FhQFeC9JMeIadUwIO6HkoD4UFG6N-HM"
: "IOS API KEY",
hintText: "Find a place ...",
searchingText: "Please wait ...",
selectText: "Select place",
initialPosition: GooglePlacePicker.kInitialPosition,
useCurrentLocation: true,
selectInitialPosition: true,
usePinPointingSearch: true,
usePlaceDetailSearch: true,
zoomGesturesEnabled: true,
zoomControlsEnabled: true,
onPlacePicked: (PickResult result) {
setState(() {
selectedPlace = result;
_showPlacePickerInContainer = false;
});
},
onTapBack: () {
setState(() {
_showPlacePickerInContainer = false;
});
})),
if (selectedPlace != null) ...[
Text(selectedPlace!.formattedAddress!),
Text("(lat: " +
selectedPlace!.geometry!.location.lat.toString() +
", lng: " +
selectedPlace!.geometry!.location.lng.toString() +
")"),
],
// #region Google Map Example without provider
_showPlacePickerInContainer
? Container()
: ElevatedButton(
child: Text("Toggle Google Map w/o Provider"),
onPressed: () {
initRenderer();
setState(() {
_showGoogleMapInContainer =
!_showGoogleMapInContainer;
});
},
),
!_showGoogleMapInContainer
? Container()
: Container(
width: MediaQuery.of(context).size.width * 0.75,
height: MediaQuery.of(context).size.height * 0.25,
child: GoogleMap(
zoomGesturesEnabled: false,
zoomControlsEnabled: false,
myLocationButtonEnabled: false,
compassEnabled: false,
mapToolbarEnabled: false,
initialCameraPosition: new CameraPosition(
target: GooglePlacePicker.kInitialPosition, zoom: 15),
mapType: MapType.normal,
myLocationEnabled: true,
onMapCreated: (GoogleMapController controller) {},
onCameraIdle: () {},
onCameraMoveStarted: () {},
onCameraMove: (CameraPosition position) {},
)),
!_showGoogleMapInContainer ? Container() : TextField(),
// #endregion
],
),
));
}
}

@ -0,0 +1,221 @@
import 'package:flutter/material.dart';
class EditPlanRequests extends StatefulWidget {
final dynamic order; // Pass order data here
const EditPlanRequests({super.key, this.order});
@override
State<EditPlanRequests> createState() => _EditPlanRequestsState();
}
class _EditPlanRequestsState extends State<EditPlanRequests> {
final TextEditingController tankerPriceController =
TextEditingController(text: "2000");
final TextEditingController waterTypeController =
TextEditingController(text: "Drinking Water");
final TextEditingController frequencyController =
TextEditingController(text: "4/week");
final TextEditingController capacityController =
TextEditingController(text: "10000L");
final TextEditingController timeController =
TextEditingController(text: "10:00 AM - 5:00 PM");
final TextEditingController quantityController =
TextEditingController(text: "1/day");
final TextEditingController advanceController =
TextEditingController(text: "40%");
final TextEditingController startDateController =
TextEditingController(text: "12 July 2025");
final TextEditingController endDateController =
TextEditingController(text: "21 July 2025");
@override
void initState() {
super.initState();
// Update summary in real-time as user types
tankerPriceController.addListener(() => setState(() {}));
advanceController.addListener(() => setState(() {}));
}
@override
Widget build(BuildContext context) {
int tankerPrice = int.tryParse(tankerPriceController.text) ?? 0;
int totalWeeks = 12; // static for now
int weeklyPrice = tankerPrice * 4; // assuming 4 tankers/week
int totalPrice = weeklyPrice * totalWeeks;
double advancePercent =
double.tryParse(advanceController.text.replaceAll('%', '')) ?? 0;
int advancePayable = (totalPrice * (advancePercent / 100)).round();
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
leading: IconButton(
icon: const Icon(Icons.close, color: Colors.black),
onPressed: () => Navigator.pop(context),
),
title: const Text(
"Edit Order",
style: TextStyle(color: Colors.black, fontWeight: FontWeight.w600),
),
centerTitle: true,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 🔹 Club Info Card
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.shade300),
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Chip(
label: Text("New",
style: TextStyle(
color: Colors.blue,
fontSize: 12,
fontWeight: FontWeight.w600)),
backgroundColor: Color(0xFFEAF3FF),
padding: EdgeInsets.symmetric(horizontal: 4),
),
Text("Club Kohinoor",
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.w600)),
SizedBox(height: 4),
Text("Banjara Hills, Hyderabad",
style: TextStyle(color: Colors.grey, fontSize: 13)),
],
),
),
const Text("5.5 Km", style: TextStyle(color: Colors.black54)),
],
),
),
const SizedBox(height: 20),
const Text("ORDER DETAILS",
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 14)),
const SizedBox(height: 12),
// 🔹 Two in a row
_twoFields(tankerPriceController, "Tanker Price",
waterTypeController, "Water Type"),
_twoFields(frequencyController, "Frequency", capacityController,
"Capacity"),
_twoFields(timeController, "Time of Delivery", quantityController,
"Quantity"),
_twoFields(advanceController, "Advance", startDateController,
"Start Date"),
_twoFields(endDateController, "End Date", null, null),
const SizedBox(height: 20),
const Text("UPDATED PAYMENT SUMMARY",
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 14)),
const SizedBox(height: 12),
_summaryRow("Tanker Price", "$tankerPrice"),
_summaryRow("Tankers per week", "04"),
_summaryRow("Weekly Price", "$weeklyPrice"),
_summaryRow("Total weeks", "$totalWeeks"),
_summaryRow("Total Price", "$totalPrice"),
const Divider(),
_summaryRow("Advance", advanceController.text),
_summaryRow("Advance Payable", "$advancePayable"),
],
),
),
bottomNavigationBar: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
border: Border(top: BorderSide(color: Colors.grey.shade300)),
),
child: Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: () => Navigator.pop(context),
child: const Text("Cancel"),
),
),
const SizedBox(width: 8),
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.deepPurple,
foregroundColor: Colors.white,
),
onPressed: () {
// 🔹 Collect updated values
print("Tanker Price: ${tankerPriceController.text}");
print("Water Type: ${waterTypeController.text}");
print("Frequency: ${frequencyController.text}");
// Save logic here
},
child: const Text("Send ➤"),
),
),
],
),
),
);
}
/// 🔹 Two fields side by side
Widget _twoFields(TextEditingController? controller1, String? label1,
TextEditingController? controller2, String? label2) {
return Row(
children: [
Expanded(
child: _textField(controller1, label1),
),
const SizedBox(width: 10),
if (controller2 != null)
Expanded(
child: _textField(controller2, label2),
),
],
);
}
/// 🔹 Custom text field
Widget _textField(TextEditingController? controller, String? label) {
return Container(
margin: const EdgeInsets.only(bottom: 12),
child: TextField(
controller: controller,
decoration: InputDecoration(
labelText: label,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
contentPadding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
),
),
);
}
/// 🔹 Summary row
Widget _summaryRow(String title, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(title, style: const TextStyle(color: Colors.black54)),
Text(value,
style: const TextStyle(
fontWeight: FontWeight.w600, color: Colors.black)),
],
),
);
}
}

@ -9,6 +9,9 @@ import file_selector_macos
import firebase_core
import firebase_messaging
import flutter_secure_storage_macos
import geolocator_apple
import location
import package_info_plus
import path_provider_foundation
import shared_preferences_foundation
@ -17,6 +20,9 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
LocationPlugin.register(with: registry.registrar(forPlugin: "LocationPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
}

@ -9,6 +9,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.35"
archive:
dependency: transitive
description:
name: archive
sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd"
url: "https://pub.dev"
source: hosted
version: "4.0.7"
args:
dependency: transitive
description:
name: args
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
url: "https://pub.dev"
source: hosted
version: "2.7.0"
async:
dependency: transitive
description:
@ -33,6 +49,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
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:
@ -65,6 +97,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.6"
csslib:
dependency: transitive
description:
name: csslib
sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
cupertino_icons:
dependency: "direct main"
description:
@ -182,6 +222,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_launcher_icons:
dependency: "direct dev"
description:
name: flutter_launcher_icons
sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea"
url: "https://pub.dev"
source: hosted
version: "0.13.1"
flutter_lints:
dependency: "direct dev"
description:
@ -198,6 +246,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.22"
flutter_polyline_points:
dependency: "direct main"
description:
name: flutter_polyline_points
sha256: "02699e69142f51a248d784b6e3eec524194467fca5f7c4da19699ce2368b6980"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
flutter_secure_storage:
dependency: "direct main"
description:
@ -264,6 +320,102 @@ packages:
url: "https://pub.dev"
source: hosted
version: "8.2.8"
geocoding:
dependency: "direct main"
description:
name: geocoding
sha256: d580c801cba9386b4fac5047c4c785a4e19554f46be42f4f5e5b7deacd088a66
url: "https://pub.dev"
source: hosted
version: "3.0.0"
geocoding_android:
dependency: transitive
description:
name: geocoding_android
sha256: "1b13eca79b11c497c434678fed109c2be020b158cec7512c848c102bc7232603"
url: "https://pub.dev"
source: hosted
version: "3.3.1"
geocoding_ios:
dependency: transitive
description:
name: geocoding_ios
sha256: "18ab1c8369e2b0dcb3a8ccc907319334f35ee8cf4cfef4d9c8e23b13c65cb825"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
geocoding_platform_interface:
dependency: transitive
description:
name: geocoding_platform_interface
sha256: "8c2c8226e5c276594c2e18bfe88b19110ed770aeb7c1ab50ede570be8b92229b"
url: "https://pub.dev"
source: hosted
version: "3.2.0"
geolocator:
dependency: "direct main"
description:
name: geolocator
sha256: "5c23f3613f50586c0bbb2b8f970240ae66b3bd992088cf60dd5ee2e6f7dde3a8"
url: "https://pub.dev"
source: hosted
version: "9.0.2"
geolocator_android:
dependency: transitive
description:
name: geolocator_android
sha256: "93906636752ea4d4e778afa981fdfe7409f545b3147046300df194330044d349"
url: "https://pub.dev"
source: hosted
version: "4.3.1"
geolocator_apple:
dependency: transitive
description:
name: geolocator_apple
sha256: c4ecead17985ede9634f21500072edfcb3dba0ef7b97f8d7bc556d2d722b3ba3
url: "https://pub.dev"
source: hosted
version: "2.3.9"
geolocator_platform_interface:
dependency: transitive
description:
name: geolocator_platform_interface
sha256: "386ce3d9cce47838355000070b1d0b13efb5bc430f8ecda7e9238c8409ace012"
url: "https://pub.dev"
source: hosted
version: "4.2.4"
geolocator_web:
dependency: transitive
description:
name: geolocator_web
sha256: "102e7da05b48ca6bf0a5bda0010f886b171d1a08059f01bfe02addd0175ebece"
url: "https://pub.dev"
source: hosted
version: "2.2.1"
geolocator_windows:
dependency: transitive
description:
name: geolocator_windows
sha256: "4f4218f122a6978d0ad655fa3541eea74c67417440b09f0657238810d5af6bdc"
url: "https://pub.dev"
source: hosted
version: "0.1.3"
get:
dependency: "direct main"
description:
name: get
sha256: c79eeb4339f1f3deffd9ec912f8a923834bec55f7b49c9e882b8fef2c139d425
url: "https://pub.dev"
source: hosted
version: "4.7.2"
google_api_headers:
dependency: "direct main"
description:
name: google_api_headers
sha256: b27a55935d5c51cedda8a925f5df8388cc327c94a47fef5a4335e8707e089878
url: "https://pub.dev"
source: hosted
version: "1.6.0"
google_fonts:
dependency: "direct main"
description:
@ -272,6 +424,78 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.4"
google_maps:
dependency: transitive
description:
name: google_maps
sha256: "5d410c32112d7c6eb7858d359275b2aa04778eed3e36c745aeae905fb2fa6468"
url: "https://pub.dev"
source: hosted
version: "8.2.0"
google_maps_flutter:
dependency: "direct main"
description:
name: google_maps_flutter
sha256: "621125e35e81ca39ef600e45243d2be93167e61def72bc7207b0c4a635c58506"
url: "https://pub.dev"
source: hosted
version: "2.10.1"
google_maps_flutter_android:
dependency: transitive
description:
name: google_maps_flutter_android
sha256: "10cf27bee8c560f8e69992b3a0f27ddf1d7acbea622ddb13ef3f587848a73f26"
url: "https://pub.dev"
source: hosted
version: "2.14.7"
google_maps_flutter_ios:
dependency: transitive
description:
name: google_maps_flutter_ios
sha256: c7433645c4c9b61c587938cb06072f3dad601239e596b090c0f8f206c1f2ade7
url: "https://pub.dev"
source: hosted
version: "2.15.2"
google_maps_flutter_platform_interface:
dependency: transitive
description:
name: google_maps_flutter_platform_interface
sha256: "970c8f766c02909c7be282dea923c971f83a88adaf07f8871d0aacebc3b07bb2"
url: "https://pub.dev"
source: hosted
version: "2.11.1"
google_maps_flutter_web:
dependency: transitive
description:
name: google_maps_flutter_web
sha256: a45786ea6691cc7cdbe2cf3ce2c2daf4f82a885745666b4a36baada3a4e12897
url: "https://pub.dev"
source: hosted
version: "0.5.12"
google_maps_place_picker_mb:
dependency: "direct main"
description:
name: google_maps_place_picker_mb
sha256: "09b62d033857fa3a69fb1afb720b091c0fcbecdf73bc34833448d2809469af13"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
google_maps_webservice:
dependency: "direct main"
description:
name: google_maps_webservice
sha256: d0ae4e4508afd74a3f051565261a3cdbae59db29448f9b6e6beb5674545e1eb7
url: "https://pub.dev"
source: hosted
version: "0.0.20-nullsafety.5"
html:
dependency: transitive
description:
name: html
sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602"
url: "https://pub.dev"
source: hosted
version: "0.15.6"
http:
dependency: "direct main"
description:
@ -288,6 +512,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.2"
image:
dependency: transitive
description:
name: image
sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928"
url: "https://pub.dev"
source: hosted
version: "4.5.4"
image_picker:
dependency: "direct main"
description:
@ -368,6 +600,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.6.7"
json_annotation:
dependency: transitive
description:
name: json_annotation
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
url: "https://pub.dev"
source: hosted
version: "4.9.0"
launcher_name:
dependency: "direct dev"
description:
name: launcher_name
sha256: "5fc9a8b8de9e255d5f21effc33632b5620771c9b2310d459a7710b725353b305"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
leak_tracker:
dependency: transitive
description:
@ -400,6 +648,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.0"
location:
dependency: "direct main"
description:
name: location
sha256: "6463a242973bf247e3fb1c7722919521b98026978ee3b5177202e103a39c145e"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
location_platform_interface:
dependency: transitive
description:
name: location_platform_interface
sha256: "1e535ccc8b4a9612de4e4319871136b45d2b5d1fb0c2a8bf99687242bf7ca5f7"
url: "https://pub.dev"
source: hosted
version: "5.0.0"
location_web:
dependency: transitive
description:
name: location_web
sha256: "613597b489beb396f658c6f4358dd383c5ed0a1402d95e287642a5f2d8171cb0"
url: "https://pub.dev"
source: hosted
version: "5.0.3"
matcher:
dependency: transitive
description:
@ -432,6 +704,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.0"
nested:
dependency: transitive
description:
name: nested
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
package_info_plus:
dependency: transitive
description:
name: package_info_plus
sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017"
url: "https://pub.dev"
source: hosted
version: "4.2.0"
package_info_plus_platform_interface:
dependency: transitive
description:
name: package_info_plus_platform_interface
sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
path:
dependency: transitive
description:
@ -488,6 +784,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.0"
permission_handler:
dependency: "direct main"
description:
name: permission_handler
sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb"
url: "https://pub.dev"
source: hosted
version: "11.3.1"
permission_handler_android:
dependency: transitive
description:
name: permission_handler_android
sha256: "71bbecfee799e65aff7c744761a57e817e73b738fedf62ab7afd5593da21f9f1"
url: "https://pub.dev"
source: hosted
version: "12.0.13"
permission_handler_apple:
dependency: transitive
description:
name: permission_handler_apple
sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023
url: "https://pub.dev"
source: hosted
version: "9.4.7"
permission_handler_html:
dependency: transitive
description:
name: permission_handler_html
sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24"
url: "https://pub.dev"
source: hosted
version: "0.1.3+5"
permission_handler_platform_interface:
dependency: transitive
description:
name: permission_handler_platform_interface
sha256: e9c8eadee926c4532d0305dff94b85bf961f16759c3af791486613152af4b4f9
url: "https://pub.dev"
source: hosted
version: "4.2.3"
permission_handler_windows:
dependency: transitive
description:
name: permission_handler_windows
sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
url: "https://pub.dev"
source: hosted
version: "0.2.1"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
url: "https://pub.dev"
source: hosted
version: "6.0.2"
platform:
dependency: transitive
description:
@ -504,6 +856,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.8"
posix:
dependency: transitive
description:
name: posix
sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61"
url: "https://pub.dev"
source: hosted
version: "6.0.3"
provider:
dependency: "direct main"
description:
name: provider
sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272"
url: "https://pub.dev"
source: hosted
version: "6.1.5+1"
sanitize_html:
dependency: transitive
description:
name: sanitize_html
sha256: "12669c4a913688a26555323fb9cec373d8f9fbe091f2d01c40c723b33caa8989"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
shared_preferences:
dependency: "direct main"
description:
@ -589,6 +965,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.2"
stream_transform:
dependency: transitive
description:
name: stream_transform
sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871
url: "https://pub.dev"
source: hosted
version: "2.1.1"
string_scanner:
dependency: transitive
description:
@ -613,6 +997,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.0"
tuple:
dependency: transitive
description:
name: tuple
sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
url: "https://pub.dev"
source: hosted
version: "2.0.2"
typed_data:
dependency: transitive
description:
@ -621,6 +1013,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.2"
uuid:
dependency: transitive
description:
name: uuid
sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313"
url: "https://pub.dev"
source: hosted
version: "3.0.7"
vector_math:
dependency: transitive
description:
@ -661,6 +1061,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.0"
xml:
dependency: transitive
description:
name: xml
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
url: "https://pub.dev"
source: hosted
version: "6.5.0"
yaml:
dependency: transitive
description:
name: yaml
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
url: "https://pub.dev"
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.4.4 <4.0.0"
flutter: ">=3.22.0"

@ -21,12 +21,37 @@ dependencies:
firebase_messaging: ^14.9.1
image_picker: ^1.1.2
intl: ^0.17.0
location: ^7.0.0
geolocator: ^9.0.2
google_api_headers: ^1.2.0
google_maps_flutter: ^2.2.3
google_maps_webservice: ^0.0.20-nullsafety.5
get: ^4.6.5
permission_handler: ^11.3.1
flutter_polyline_points: ^1.0.0
geocoding: ^3.0.0
provider: ^6.0.5
google_maps_place_picker_mb: ^3.0.2
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.0
flutter_launcher_icons: ^0.13.1
launcher_name: ^1.0.2
flutter_icons:
image_path: 'images/appiconn.png'
android: true
ios: true
remove_alpha_ios: true
flutter_launcher_name:
name: "Aquick Supplier"
launcher_name:
default: "Aquick Supplier"
flutter:

@ -9,6 +9,8 @@
#include <file_selector_windows/file_selector_windows.h>
#include <firebase_core/firebase_core_plugin_c_api.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <geolocator_windows/geolocator_windows.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FileSelectorWindowsRegisterWithRegistrar(
@ -17,4 +19,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
GeolocatorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("GeolocatorWindows"));
PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
}

@ -6,6 +6,8 @@ list(APPEND FLUTTER_PLUGIN_LIST
file_selector_windows
firebase_core
flutter_secure_storage_windows
geolocator_windows
permission_handler_windows
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST

Loading…
Cancel
Save