import 'dart:math'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:latlong2/latlong.dart'; import 'package:niogu_ecommerce_v1/core/constant/app_asset.dart'; import 'package:niogu_ecommerce_v1/core/constant/app_font_size.dart'; import 'package:niogu_ecommerce_v1/core/providers/app_provider.dart'; import 'package:niogu_ecommerce_v1/core/system/system_setting.dart'; import 'package:niogu_ecommerce_v1/core/widgets/custom_snackbar.dart'; import 'package:niogu_ecommerce_v1/core/widgets/triangle_painter.dart'; import 'package:niogu_ecommerce_v1/features/home/domain/entities/home.dart'; import 'package:niogu_ecommerce_v1/features/home/presentation/providers/home_provider.dart'; import 'package:sizer/sizer.dart'; import 'package:niogu_ecommerce_v1/core/constant/app_color.dart'; class OutletMapScreen extends ConsumerStatefulWidget { final LatLng userLocation; final List outlets; const OutletMapScreen({ super.key, required this.userLocation, required this.outlets, }); @override ConsumerState createState() => _OutletMapScreenState(); } class _OutletMapScreenState extends ConsumerState { final MapController _mapController = MapController(); late final LatLng _userLocation; late final List _outlets; int _selectedOutletIndex = 0; @override void initState() { super.initState(); _userLocation = widget.userLocation; _outlets = widget.outlets; WidgetsBinding.instance.addPostFrameCallback((_) { _fitBounds(); }); } double _calculateDistance( double lat1, double lon1, double lat2, double lon2, ) { final p = 0.017453292519943295; final c = cos; final a = 0.5 - c((lat2 - lat1) * p) / 2 + c(lat1 * p) * c(lat2 * p) * (1 - c((lon2 - lon1) * p)) / 2; return 12742 * asin(sqrt(a)); } void _fitBounds() { _mapController.fitCamera( CameraFit.bounds( bounds: LatLngBounds( _userLocation, _outlets[_selectedOutletIndex].coordinate!, ), padding: EdgeInsets.symmetric(vertical: 15.h, horizontal: 10.w), ), ); } @override Widget build(BuildContext context) { return LayoutBuilder( builder: (context, constraints) { return SafeArea( top: false, bottom: true, right: false, left: false, child: Scaffold( body: Stack( children: [ FlutterMap( mapController: _mapController, options: MapOptions( initialCenter: _outlets[0].coordinate!, initialZoom: 16.0, minZoom: 3.0, maxZoom: 18.0, ), children: [ TileLayer( urlTemplate: 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', subdomains: const ['a', 'b', 'c', 'd'], ), MarkerLayer( markers: [ ..._outlets.asMap().entries.map((entry) { int idx = entry.key; var data = entry.value; final isSelected = _selectedOutletIndex == idx; final distance = _calculateDistance( _userLocation.latitude, _userLocation.longitude, data.coordinate!.latitude, data.coordinate!.longitude, ); var strDistance = '${distance.toStringAsFixed(1)} Km dari lokasimu'; if (distance < 1) { strDistance = '${(distance * 1000).toStringAsFixed(0)} meter dari lokasimu'; } return Marker( point: data.coordinate!, width: 70.w, height: 25.h, alignment: Alignment.topCenter, child: Column( mainAxisSize: MainAxisSize.min, children: [ AnimatedOpacity( duration: const Duration(milliseconds: 300), opacity: isSelected ? 1.0 : 0.0, child: Container( padding: EdgeInsets.symmetric( horizontal: 3.w, vertical: 0.8.h, ), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(2.w), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 8, offset: const Offset(0, 4), ), ], border: Border.all( color: isSelected ? AppColor.primaryColor.withOpacity( 0.5, ) : Colors.grey.shade200, ), ), child: Text( strDistance, style: TextStyle( fontSize: (AppFontSize.small - 1).sp, fontWeight: FontWeight.bold, color: AppColor.primaryColor, ), ), ), ), if (isSelected) CustomPaint( size: Size(3.w, 1.5.w), painter: TrianglePainter( Colors.white, ), // Reuse painter ), Icon( Icons.location_on, color: isSelected ? AppColor.primaryColor : Colors.grey.shade400, size: 10.w, ), ], ), ); }).toList(), ], ), ], ), Positioned( top: 6.h, left: 4.w, child: CircleAvatar( backgroundColor: Colors.white, child: IconButton( icon: Icon( Icons.arrow_back, color: Colors.black, size: 7.w, ), onPressed: () => Navigator.pop(context), ), ), ), Positioned( bottom: 12.h, left: 0, right: 0, child: SizedBox( height: 20.h, child: PageView.builder( controller: PageController(viewportFraction: 0.85), itemCount: _outlets.length, onPageChanged: (index) { setState(() => _selectedOutletIndex = index); _fitBounds(); }, itemBuilder: (context, index) { final outlet = _outlets[index]; return CachedNetworkImage( imageUrl: outlet.image ?? 'error', imageBuilder: (context, imageProvider) { return Container( width: 75.w, margin: EdgeInsets.only(right: 4.w), decoration: BoxDecoration( borderRadius: BorderRadius.circular(2.5.w), image: DecorationImage( image: imageProvider, fit: BoxFit.cover, ), ), child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(2.5.w), gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.transparent, Colors.black87, ], ), ), padding: EdgeInsets.all(4.w), child: _buildBranchInformation( name: outlet.name, location: outlet.location, ), ), ); }, errorWidget: (context, url, error) { return Container( width: 75.w, margin: EdgeInsets.only(right: 4.w), decoration: BoxDecoration( border: BoxBorder.all( color: _selectedOutletIndex == index ? AppColor.primaryColor : Colors.transparent, ), borderRadius: BorderRadius.circular(2.5.w), image: DecorationImage( image: AssetImage(AppAsset.OUTLET_MOCK), fit: BoxFit.cover, ), ), child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(2.5.w), gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.transparent, Colors.black87, ], ), ), padding: EdgeInsets.all(4.w), child: _buildBranchInformation( name: outlet.name, location: outlet.location, ), ), ); }, ); }, ), ), ), Align( alignment: Alignment.bottomCenter, child: Container( padding: EdgeInsets.all(4.w), color: Colors.white, child: SafeArea( top: false, child: ElevatedButton( onPressed: () async { final outlet = _outlets[_selectedOutletIndex]; await SystemSetting.switchOutlet( outletId: outlet.id, outletName: outlet.name, outletPhone: outlet.phoneNumber, outletLocation: outlet.location, outletCoordinate: outlet.coordinate, ); ref.read(currentOutletIdProvider.notifier).state = outlet.id; ref.read(currentOutletNameProvider.notifier).state = outlet.name; ref.read(currentOutletPhoneProvider.notifier).state = outlet.phoneNumber; ref .read(currentOutletLocationProvider.notifier) .state = outlet.location; ref .read(currentOutletCoordinateProvider.notifier) .state = outlet .coordinate; await ref .read(homeControllerProvider.notifier) .refresh(); CustomSnackbar.showSuccess( context, "Berhasil mengunjungi ${outlet.name}", ); context.pop(); }, style: ElevatedButton.styleFrom( backgroundColor: AppColor.primaryColor, minimumSize: Size(double.infinity, 6.h), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(2.5.w), ), ), child: Text( "Pilih Outlet Ini", style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: AppFontSize.medium.sp, ), ), ), ), ), ), ], ), ), ); }, ); } Widget _buildBranchInformation({required String name, String? location}) { return Column( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( name, style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: AppFontSize.small.sp, ), ), if (location != null) Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Row( children: [ Icon(Icons.location_on, size: 4.w, color: Colors.white70), SizedBox(width: 0.75.w), Expanded( child: Text( location, maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( color: Colors.white70, fontSize: (AppFontSize.small - 2).sp, ), ), ), ], ), ), /** SizedBox(width: 2.5.w), Row( children: [ Icon(Icons.straighten, size: 4.w, color: Colors.white70), SizedBox(width: 0.75.w), Text( "4km", style: TextStyle( color: Colors.white70, fontSize: (AppFontSize.small - 2).sp, ), ), ], ), */ ], ), ], ); } }