184 lines
5.7 KiB
Dart
184 lines
5.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:traccar_client/bridge/location_bridge.dart';
|
|
import 'package:traccar_client/preferences.dart';
|
|
|
|
class SettingsScreen extends StatefulWidget {
|
|
const SettingsScreen({super.key});
|
|
|
|
@override
|
|
State<SettingsScreen> createState() => _SettingsScreenState();
|
|
}
|
|
|
|
class _SettingsScreenState extends State<SettingsScreen> {
|
|
late TextEditingController _serverUrlController;
|
|
late TextEditingController _deviceIdController;
|
|
late TextEditingController _distanceFilterController;
|
|
late TextEditingController _intervalController;
|
|
late TextEditingController _heartbeatController;
|
|
late int _accuracy;
|
|
late bool _offlineBuffer;
|
|
late bool _stopDetection;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_serverUrlController = TextEditingController(text: Preferences.serverUrl);
|
|
_deviceIdController = TextEditingController(text: Preferences.deviceId);
|
|
_distanceFilterController = TextEditingController(
|
|
text: Preferences.distanceFilter.toString(),
|
|
);
|
|
_intervalController = TextEditingController(
|
|
text: Preferences.interval.toString(),
|
|
);
|
|
_heartbeatController = TextEditingController(
|
|
text: Preferences.heartbeat.toString(),
|
|
);
|
|
_accuracy = Preferences.accuracy;
|
|
_offlineBuffer = Preferences.offlineBuffer;
|
|
_stopDetection = Preferences.stopDetection;
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_serverUrlController.dispose();
|
|
_deviceIdController.dispose();
|
|
_distanceFilterController.dispose();
|
|
_intervalController.dispose();
|
|
_heartbeatController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
Future<void> _saveSettings() async {
|
|
final distanceFilter = int.tryParse(_distanceFilterController.text) ?? 75;
|
|
final interval = int.tryParse(_intervalController.text) ?? 300;
|
|
final heartbeat = int.tryParse(_heartbeatController.text) ?? 60;
|
|
|
|
await Preferences.setServerUrl(_serverUrlController.text);
|
|
await Preferences.setDeviceId(_deviceIdController.text);
|
|
await Preferences.setAccuracy(_accuracy);
|
|
await Preferences.setDistanceFilter(distanceFilter);
|
|
await Preferences.setInterval(interval);
|
|
await Preferences.setHeartbeat(heartbeat);
|
|
await Preferences.setOfflineBuffer(_offlineBuffer);
|
|
await Preferences.setStopDetection(_stopDetection);
|
|
|
|
await LocationBridge.updateConfig({
|
|
'serverUrl': _serverUrlController.text,
|
|
'deviceId': _deviceIdController.text,
|
|
'accuracy': _accuracy,
|
|
'distanceFilter': distanceFilter,
|
|
'interval': interval,
|
|
'heartbeat': heartbeat,
|
|
'offlineBuffer': _offlineBuffer,
|
|
'stopDetection': _stopDetection,
|
|
});
|
|
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(
|
|
context,
|
|
).showSnackBar(const SnackBar(content: Text('Settings saved')));
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(title: const Text('Settings')),
|
|
body: ListView(
|
|
padding: const EdgeInsets.all(16),
|
|
children: [
|
|
_buildSectionHeader('Server'),
|
|
_buildTextField('Server URL', _serverUrlController),
|
|
_buildTextField('Device ID', _deviceIdController),
|
|
const SizedBox(height: 16),
|
|
_buildSectionHeader('Location'),
|
|
_buildAccuracyDropdown(),
|
|
_buildNumberField(
|
|
'Distance Filter (m)',
|
|
_distanceFilterController,
|
|
0,
|
|
1000,
|
|
),
|
|
_buildNumberField(
|
|
'Update Interval (s)',
|
|
_intervalController,
|
|
30,
|
|
3600,
|
|
),
|
|
_buildNumberField('Heartbeat (s)', _heartbeatController, 60, 3600),
|
|
const SizedBox(height: 16),
|
|
_buildSectionHeader('Advanced'),
|
|
SwitchListTile(
|
|
title: const Text('Offline Buffering'),
|
|
subtitle: const Text('Queue locations when offline'),
|
|
value: _offlineBuffer,
|
|
onChanged: (v) => setState(() => _offlineBuffer = v),
|
|
),
|
|
SwitchListTile(
|
|
title: const Text('Stop Detection'),
|
|
subtitle: const Text('Auto-stop when stationary'),
|
|
value: _stopDetection,
|
|
onChanged: (v) => setState(() => _stopDetection = v),
|
|
),
|
|
const SizedBox(height: 24),
|
|
ElevatedButton(
|
|
onPressed: _saveSettings,
|
|
child: const Text('Save Settings'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildSectionHeader(String title) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(top: 16, bottom: 8),
|
|
child: Text(
|
|
title,
|
|
style: Theme.of(
|
|
context,
|
|
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildTextField(
|
|
String label,
|
|
TextEditingController controller, {
|
|
bool obscure = false,
|
|
}) {
|
|
return TextField(
|
|
controller: controller,
|
|
decoration: InputDecoration(labelText: label),
|
|
obscureText: obscure,
|
|
);
|
|
}
|
|
|
|
Widget _buildAccuracyDropdown() {
|
|
return DropdownButtonFormField<int>(
|
|
// ignore: deprecated_member_use
|
|
value: _accuracy,
|
|
decoration: const InputDecoration(labelText: 'Accuracy'),
|
|
items: const [
|
|
DropdownMenuItem(value: 0, child: Text('High')),
|
|
DropdownMenuItem(value: 1, child: Text('High Accuracy')),
|
|
DropdownMenuItem(value: 2, child: Text('Balanced')),
|
|
DropdownMenuItem(value: 3, child: Text('Low')),
|
|
],
|
|
onChanged: (v) => setState(() => _accuracy = v!),
|
|
);
|
|
}
|
|
|
|
Widget _buildNumberField(
|
|
String label,
|
|
TextEditingController controller,
|
|
int min,
|
|
int max,
|
|
) {
|
|
return TextField(
|
|
controller: controller,
|
|
decoration: InputDecoration(labelText: label),
|
|
keyboardType: TextInputType.number,
|
|
);
|
|
}
|
|
}
|