tracpulse/lib/settings_screen.dart

179 lines
No EOL
5.8 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 _passwordController;
late int _accuracy;
late int _distanceFilter;
late int _interval;
late int _heartbeat;
late bool _offlineBuffer;
late bool _stopDetection;
@override
void initState() {
super.initState();
_serverUrlController = TextEditingController(text: Preferences.serverUrl);
_deviceIdController = TextEditingController(text: Preferences.deviceId);
_passwordController = TextEditingController(text: Preferences.password);
_accuracy = Preferences.accuracy;
_distanceFilter = Preferences.distanceFilter;
_interval = Preferences.interval;
_heartbeat = Preferences.heartbeat;
_offlineBuffer = Preferences.offlineBuffer;
_stopDetection = Preferences.stopDetection;
}
@override
void dispose() {
_serverUrlController.dispose();
_deviceIdController.dispose();
_passwordController.dispose();
super.dispose();
}
Future<void> _saveSettings() async {
await Preferences.setServerUrl(_serverUrlController.text);
await Preferences.setDeviceId(_deviceIdController.text);
await Preferences.setPassword(_passwordController.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,
'password': _passwordController.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),
_buildTextField('Password', _passwordController, obscure: true),
const SizedBox(height: 16),
_buildSectionHeader('Location'),
_buildAccuracyDropdown(),
_buildNumberField('Distance Filter (m)', _distanceFilter, 0, 1000),
_buildNumberField('Update Interval (s)', _interval, 30, 3600),
_buildNumberField('Heartbeat (s)', _heartbeat, 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>(
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, int value, int min, int max) {
return TextFormField(
initialValue: value.toString(),
decoration: InputDecoration(labelText: label),
keyboardType: TextInputType.number,
validator: (v) {
final parsed = int.tryParse(v ?? '');
if (parsed == null || parsed < min || parsed > max) {
return 'Must be between $min and $max';
}
return null;
},
onSaved: (v) {
final parsed = int.tryParse(v ?? '');
if (parsed != null) {
if (label.contains('Distance')) {
_distanceFilter = parsed;
} else if (label.contains('Interval')) {
_interval = parsed;
} else if (label.contains('Heartbeat')) {
_heartbeat = parsed;
}
}
},
);
}
}