map gemeni
Mon Sep 22 2025 18:21:57 GMT+0000 (Coordinated Universal Time)
Saved by @mehran
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:provider/provider.dart';
/// Data model for the user's mission and simulated location.
class MissionData extends ChangeNotifier {
final double _totalDistanceKm;
double _currentDistanceKm;
final double _userLatitude;
final double _userLongitude;
/// Initializes the mission with a total distance and a simulated user location.
///
/// The initial current distance walked is 0.0.
MissionData({
required double totalDistanceKm,
required double userLatitude,
required double userLongitude,
double initialDistanceKm = 0.0,
}) : _totalDistanceKm = totalDistanceKm,
_userLatitude = userLatitude,
_userLongitude = userLongitude,
_currentDistanceKm = initialDistanceKm {
if (_totalDistanceKm <= 0) {
throw ArgumentError('Total distance must be positive.');
}
if (_currentDistanceKm < 0) {
throw ArgumentError('Initial distance cannot be negative.');
}
if (_currentDistanceKm > _totalDistanceKm) {
_currentDistanceKm = _totalDistanceKm; // Cap initial distance at total
}
}
/// The total distance required for the mission in kilometers.
double get totalDistanceKm => _totalDistanceKm;
/// The current distance walked by the user in kilometers.
double get currentDistanceKm => _currentDistanceKm;
/// The simulated geographical latitude of the user.
double get userLatitude => _userLatitude;
/// The simulated geographical longitude of the user.
double get userLongitude => _userLongitude;
/// The remaining distance to complete the mission in kilometers.
double get remainingDistanceKm =>
(_totalDistanceKm - _currentDistanceKm).clamp(0.0, _totalDistanceKm);
/// The progress of the mission as a percentage (0.0 to 1.0).
double get progressPercentage =>
_currentDistanceKm / _totalDistanceKm.clamp(1.0, double.infinity);
/// Adds a specified [distance] in kilometers to the current distance walked.
///
/// The distance added must be positive. The current distance will not exceed
/// the total mission distance.
void addDistance(double distance) {
if (distance <= 0) {
throw ArgumentError('Distance to add must be positive.');
}
_currentDistanceKm = (_currentDistanceKm + distance).clamp(0.0, _totalDistanceKm);
notifyListeners();
}
}
/// The main application widget for displaying location and mission.
class LocationMissionApp extends StatelessWidget {
const LocationMissionApp({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<MissionData>(
create: (context) => MissionData(
totalDistanceKm: 100.0, // Mission: walk 100 kilometers
userLatitude: 51.5, // Simulated London latitude
userLongitude: -0.09, // Simulated London longitude
),
builder: (context, child) {
return MaterialApp(
title: 'Mission Tracker',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: const MissionScreen(),
);
},
);
}
}
/// Displays the map with simulated user location and mission progress.
class MissionScreen extends StatefulWidget {
const MissionScreen({super.key});
@override
State<MissionScreen> createState() => _MissionScreenState();
}
class _MissionScreenState extends State<MissionScreen> {
final TextEditingController _distanceInputController = TextEditingController();
final _formKey = GlobalKey<FormState>();
@override
void dispose() {
_distanceInputController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Mission Progress'),
),
body: Consumer<MissionData>(
builder: (context, missionData, child) {
return Column(
children: <Widget>[
Expanded(
flex: 2,
child: Card(
margin: const EdgeInsets.all(8.0),
clipBehavior: Clip.antiAlias,
child: Stack(
children: [
FlutterMap(
options: MapOptions(
initialCenter: LatLng(missionData.userLatitude, missionData.userLongitude),
initialZoom: 13.0,
interactionOptions: const InteractionOptions(
flags: InteractiveFlag.all & ~InteractiveFlag.rotate,
),
),
children: [
TileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
userAgentPackageName: 'com.example.app',
),
MarkerLayer(
markers: [
Marker(
point: LatLng(missionData.userLatitude, missionData.userLongitude),
width: 80,
height: 80,
child: const Icon(
Icons.location_on,
color: Colors.red,
size: 40.0,
),
),
],
),
],
),
Positioned(
top: 8,
left: 8,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.black54,
borderRadius: BorderRadius.circular(4),
),
child: const Text(
'Simulated Location',
style: TextStyle(color: Colors.white, fontSize: 12),
),
),
),
],
),
),
),
Expanded(
flex: 1,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
'Mission: Walk ${missionData.totalDistanceKm.toStringAsFixed(0)} km',
style: Theme.of(context).textTheme.headlineSmall,
textAlign: TextAlign.center,
),
const SizedBox(height: 8.0),
Text(
'Progress: ${missionData.currentDistanceKm.toStringAsFixed(1)} km of ${missionData.totalDistanceKm.toStringAsFixed(0)} km',
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.center,
),
const SizedBox(height: 4.0),
LinearProgressIndicator(
value: missionData.progressPercentage,
minHeight: 10,
backgroundColor: Colors.grey[300],
valueColor: const AlwaysStoppedAnimation<Color>(Colors.green),
),
const SizedBox(height: 8.0),
Text(
'Remaining: ${missionData.remainingDistanceKm.toStringAsFixed(1)} km',
style: Theme.of(context).textTheme.titleSmall,
textAlign: TextAlign.center,
),
const Spacer(),
Form(
key: _formKey,
child: Row(
children: [
Expanded(
child: TextFormField(
controller: _distanceInputController,
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
decoration: InputDecoration(
labelText: 'Add distance (km)',
border: const OutlineInputBorder(),
suffixText: 'km',
errorStyle: TextStyle(
color: Theme.of(context).colorScheme.error,
fontSize: 10),
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return 'Please enter a distance';
}
final double? distance = double.tryParse(value);
if (distance == null || distance <= 0) {
return 'Enter a positive number';
}
return null;
},
),
),
const SizedBox(width: 8.0),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
final double distanceToAdd =
double.parse(_distanceInputController.text);
missionData.addDistance(distanceToAdd);
_distanceInputController.clear();
FocusScope.of(context).unfocus(); // Dismiss keyboard
}
},
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 24, vertical: 16),
),
child: const Text('Add Walk'),
),
],
),
),
],
),
),
),
],
);
},
),
);
}
}
void main() {
runApp(const LocationMissionApp());
}



Comments