11import 'package:flutter/material.dart' ;
2+ import 'package:geolocator/geolocator.dart' ;
23import 'package:provider/provider.dart' ;
34
45import '../models/repeater.dart' ;
56import '../providers/app_state_provider.dart' ;
7+ import '../services/gps_service.dart' ;
8+ import '../utils/distance_formatter.dart' ;
69
710/// A styled repeater ID text with a dotted underline hint that it's tappable.
811///
@@ -95,9 +98,26 @@ class RepeaterIdChip extends StatelessWidget {
9598 ),
9699 );
97100 } else {
101+ final position = appState.currentPosition;
102+
103+ // Sort by distance (closest first) when GPS is available
104+ if (position != null ) {
105+ matches.sort ((a, b) {
106+ final distA = GpsService .distanceBetween (
107+ position.latitude, position.longitude, a.lat, a.lon,
108+ );
109+ final distB = GpsService .distanceBetween (
110+ position.latitude, position.longitude, b.lat, b.lon,
111+ );
112+ return distA.compareTo (distB);
113+ });
114+ }
115+
98116 content = Column (
99117 mainAxisSize: MainAxisSize .min,
100- children: matches.map ((r) => _buildRepeaterRow (context, r)).toList (),
118+ children: matches
119+ .map ((r) => _buildRepeaterRow (context, r, position: position))
120+ .toList (),
101121 );
102122 }
103123 }
@@ -153,11 +173,32 @@ class RepeaterIdChip extends StatelessWidget {
153173 );
154174 }
155175
156- static Widget _buildRepeaterRow (BuildContext context, Repeater repeater) {
176+ static Widget _buildRepeaterRow (
177+ BuildContext context,
178+ Repeater repeater, {
179+ Position ? position,
180+ }) {
157181 final isActive = repeater.isActive;
158182 final badgeColor = isActive ? Colors .green : Colors .grey;
159183 final statusText = isActive ? 'Active' : 'Stale' ;
160184
185+ // Calculate distance string if GPS is available
186+ String ? distanceText;
187+ if (position != null ) {
188+ final meters = GpsService .distanceBetween (
189+ position.latitude, position.longitude, repeater.lat, repeater.lon,
190+ );
191+ final isImperial = Provider .of <AppStateProvider >(context, listen: false )
192+ .preferences
193+ .isImperial;
194+ if (meters < 1000 ) {
195+ distanceText = formatMeters (meters, isImperial: isImperial);
196+ } else {
197+ distanceText =
198+ formatKilometers (meters / 1000 , isImperial: isImperial);
199+ }
200+ }
201+
161202 return Padding (
162203 padding: const EdgeInsets .symmetric (vertical: 6 ),
163204 child: Row (
@@ -184,16 +225,44 @@ class RepeaterIdChip extends StatelessWidget {
184225 ),
185226 ),
186227 const SizedBox (width: 12 ),
187- // Repeater name
228+ // Repeater name + distance subtitle
188229 Expanded (
189- child: Text (
190- repeater.name,
191- style: TextStyle (
192- fontSize: 14 ,
193- fontWeight: FontWeight .w500,
194- color: Theme .of (context).colorScheme.onSurface,
195- ),
196- overflow: TextOverflow .ellipsis,
230+ child: Column (
231+ crossAxisAlignment: CrossAxisAlignment .start,
232+ mainAxisSize: MainAxisSize .min,
233+ children: [
234+ Text (
235+ repeater.name,
236+ style: TextStyle (
237+ fontSize: 14 ,
238+ fontWeight: FontWeight .w500,
239+ color: Theme .of (context).colorScheme.onSurface,
240+ ),
241+ overflow: TextOverflow .ellipsis,
242+ ),
243+ if (distanceText != null )
244+ Padding (
245+ padding: const EdgeInsets .only (top: 2 ),
246+ child: Row (
247+ mainAxisSize: MainAxisSize .min,
248+ children: [
249+ Icon (
250+ Icons .near_me,
251+ size: 10 ,
252+ color: Theme .of (context).colorScheme.onSurfaceVariant,
253+ ),
254+ const SizedBox (width: 3 ),
255+ Text (
256+ distanceText,
257+ style: TextStyle (
258+ fontSize: 11 ,
259+ color: Theme .of (context).colorScheme.onSurfaceVariant,
260+ ),
261+ ),
262+ ],
263+ ),
264+ ),
265+ ],
197266 ),
198267 ),
199268 const SizedBox (width: 8 ),
0 commit comments