import 'dart:io'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/svg.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../Notifiers/mapProvider.dart'; import '../../Utility/AppColors.dart'; class ContactMap extends StatefulWidget { final accId; final sessionId; const ContactMap({super.key, this.accId, this.sessionId}); @override State createState() => _ContactMapState(); } class _ContactMapState extends State { @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { final provider = Provider.of(context, listen: false); provider.getLocationPermission(context, widget.accId, widget.sessionId); provider.nearbyServiceLocations(context, widget.accId, widget.sessionId); }); } @override Widget build(BuildContext context) { return Consumer( builder: (context, provider, child) { final isLoading = provider.isLoading; final data = provider.response?.getInTouchList; if (isLoading) { return const Scaffold( backgroundColor: AppColors.backgroundRegular, body: Center( child: CircularProgressIndicator(color: AppColors.buttonColor), ), ); } if (data == null || data.isEmpty) { return const Scaffold( backgroundColor: AppColors.backgroundRegular, body: Center(child: Text("No data found.")), ); } return SafeArea( child: Scaffold( resizeToAvoidBottomInset: true, backgroundColor: AppColors.backgroundRegular, body: CustomScrollView( slivers: [ SliverAppBar( leading: Container(), stretch: true, backgroundColor: AppColors.backgroundRegular, onStretchTrigger: () async { final provider = Provider.of( context, listen: false, ); provider.nearbyServiceLocations( context, widget.accId, widget.sessionId, ); }, stretchTriggerOffset: 300.0, expandedHeight: MediaQuery.of(context).size.height * 0.5, flexibleSpace: FlexibleSpaceBar( // stretchModes: const [ // StretchMode.zoomBackground, // StretchMode.zoomBackground, // ], background: Container( width: double.infinity, decoration: const BoxDecoration( gradient: AppColors.successGradient, ), child: Column( children: [ SizedBox( height: MediaQuery.of(context).size.height * 0.5, child: Stack( children: [ GoogleMap( myLocationEnabled: true, zoomGesturesEnabled: true, zoomControlsEnabled: true, gestureRecognizers: { Factory( () => EagerGestureRecognizer(), ), Factory( () => PanGestureRecognizer(), ), Factory( () => ScaleGestureRecognizer(), ), }, initialCameraPosition: CameraPosition( target: provider.startLocation, zoom: 14.0, ), markers: provider.markers.toSet(), mapType: MapType.normal, onMapCreated: (controller) { provider.mapController = controller; }, onCameraMove: (position) { provider.onCameraMove( context, position, widget.accId, widget.sessionId, ); }, onTap: (position) { provider.mapController ?.hideMarkerInfoWindow( provider.markers.isNotEmpty ? provider.markers.first.markerId : MarkerId(''), ); }, ), Container( height: 75, decoration: BoxDecoration( gradient: LinearGradient( colors: [ Color(0xFFFFFFFF), Color(0xFFFFFFFF), Color(0xFFFFFFFF).withAlpha(0), ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 20, ), child: SizedBox( child: Row( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ InkResponse( onTap: () { HapticFeedback.selectionClick(); Navigator.pop(context, true); }, child: SvgPicture.asset( "assets/svg/appbar_back.svg", height: 25, color: AppColors.nearDarkText, ), ), SizedBox(width: 10), Expanded( flex: 4, child: InkResponse( onTap: () { HapticFeedback.selectionClick(); Navigator.pop(context, true); }, child: Text( "Contact Us", overflow: TextOverflow.ellipsis, maxLines: 1, style: TextStyle( fontSize: 16, color: AppColors.nearDarkText, height: 1.1, ), ), ), ), ], ), ), ), ], ), ), ], ), ), ), ), SliverToBoxAdapter( child: Container( decoration: BoxDecoration( color: AppColors.backgroundRegular, borderRadius: BorderRadius.only( topLeft: Radius.circular(30), topRight: Radius.circular(30), ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 10, offset: Offset(0, -5), ), ], ), child: ClipRRect( borderRadius: BorderRadius.only( topLeft: Radius.circular(30), topRight: Radius.circular(30), ), child: Container( padding: const EdgeInsets.symmetric(horizontal: 0), decoration: const BoxDecoration( color: AppColors.backgroundRegular, ), child: Padding( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 10, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 20), /// Build dynamic branch list for (var item in data) Padding( padding: const EdgeInsets.only(bottom: 8.0), child: _buildItemRow( id: item.id ?? "", branchName: item.branchName ?? "Unknown Branch", address: item.address ?? "No address available", phoneNo: item.telephoneNo ?? "", mapLink: item.googleMapLink ?? "", lat: item.latitude ?? "", long: item.longitude ?? "", loc: item.loc ?? "", provider: provider, ), ), const SizedBox(height: 30), ], ), ), ), ), ), ), ], ), ), ); }, ); } Widget _buildItemRow({ required String branchName, required String address, required String phoneNo, required String mapLink, required String lat, required String long, required String loc, required String id, required MapProvider provider, }) { final latLng = provider.parseLatLng(loc); return InkResponse( onTap: () { HapticFeedback.lightImpact(); // 1. Animate camera to the branch location provider.mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: latLng, zoom: 16.0, // Nice zoom level ), ), ); Future.delayed(const Duration(milliseconds: 600), () { if (id != null) { final markerId = MarkerId(id.toString()); provider.mapController?.showMarkerInfoWindow(markerId); } }); }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(22), ), child: Row( children: [ const SizedBox(width: 14), Expanded( flex: 6, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( branchName, style: const TextStyle( color: AppColors.normalText, fontWeight: FontWeight.w600, fontSize: 14, overflow: TextOverflow.ellipsis, ), ), const SizedBox(height: 4), Text( address, maxLines: 4, style: const TextStyle( color: AppColors.subtitleText, fontWeight: FontWeight.w400, fontSize: 14, overflow: TextOverflow.ellipsis, ), ), ], ), ), const SizedBox(width: 8), Row( children: [ InkResponse( onTap: () async { // map lat & long var location = "$lat, $long"; if (await canLaunch(mapLink)) { await launchUrl( Uri.parse("https://maps.google.com?q=$location"), ); } else { throw 'Could not launch $mapLink'; } }, child: Container( padding: const EdgeInsets.all(1), decoration: BoxDecoration( color: Colors.transparent, borderRadius: BorderRadius.circular(30), ), child: SvgPicture.asset( "assets/svg/route_ic.svg", height: 42, fit: BoxFit.contain, ), ), ), const SizedBox(width: 6), InkResponse( onTap: () async { final phone = phoneNo.trim(); if (phone.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( backgroundColor: Colors.redAccent, content: const Text( "Phone number not available", style: TextStyle(color: Colors.white), ), duration: Duration(seconds: 2), behavior: SnackBarBehavior.floating, ), ); return; } final Uri phoneUri = Uri(scheme: 'tel', path: phone); try { if (await canLaunchUrl(phoneUri)) { await launchUrl(phoneUri); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar( backgroundColor: Colors.redAccent, content: const Text( "Unable to start the call", style: TextStyle(color: Colors.white), ), duration: Duration(seconds: 2), behavior: SnackBarBehavior.floating, ), ); } } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( backgroundColor: Colors.redAccent, content: Text( "Error while trying to call: $e", style: const TextStyle(color: Colors.white), ), duration: const Duration(seconds: 2), behavior: SnackBarBehavior.floating, ), ); } }, child: Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: const Color(0xFF4CAF50), borderRadius: BorderRadius.circular(30), ), child: SvgPicture.asset( "assets/svg/phone_ic.svg", height: 16, color: Colors.white, fit: BoxFit.contain, ), ), ), ], ), ], ), ), ); } }