trcr/lib/main_screen.dart

224 lines
7.7 KiB
Dart
Raw Normal View History

import 'package:app_settings/app_settings.dart';
2025-05-05 07:47:09 -07:00
import 'package:flutter/material.dart';
2025-06-30 23:00:08 -07:00
import 'package:flutter/services.dart';
import 'package:traccar_client/main.dart';
2025-06-28 14:57:56 -07:00
import 'package:traccar_client/password_service.dart';
2025-05-07 22:06:29 -07:00
import 'package:traccar_client/preferences.dart';
2026-02-02 20:20:58 -08:00
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
2025-05-07 22:06:29 -07:00
import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg;
2025-05-05 08:02:02 -07:00
2025-06-05 23:15:05 -07:00
import 'l10n/app_localizations.dart';
2025-05-05 08:02:02 -07:00
import 'status_screen.dart';
import 'settings_screen.dart';
2025-05-05 07:47:09 -07:00
2025-05-07 22:06:29 -07:00
class MainScreen extends StatefulWidget {
2025-05-05 07:47:09 -07:00
const MainScreen({super.key});
2025-05-07 22:06:29 -07:00
@override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
bool trackingEnabled = false;
2025-06-13 18:29:48 -07:00
bool? isMoving;
2025-05-07 22:06:29 -07:00
@override
void initState() {
super.initState();
_initState();
}
void _initState() async {
2025-05-10 09:51:51 -07:00
final state = await bg.BackgroundGeolocation.state;
2025-05-07 22:06:29 -07:00
setState(() {
trackingEnabled = state.enabled;
2025-06-13 18:29:48 -07:00
isMoving = state.isMoving;
2025-05-07 22:06:29 -07:00
});
bg.BackgroundGeolocation.onEnabledChange((bool enabled) {
setState(() {
trackingEnabled = enabled;
});
});
2025-06-13 18:29:48 -07:00
bg.BackgroundGeolocation.onMotionChange((bg.Location location) {
setState(() {
isMoving = location.isMoving;
});
});
2025-05-07 22:06:29 -07:00
}
2025-06-14 13:26:13 -07:00
Future<void> _checkBatteryOptimizations(BuildContext context) async {
try {
if (!await bg.DeviceSettings.isIgnoringBatteryOptimizations) {
final request = await bg.DeviceSettings.showIgnoreBatteryOptimizations();
if (!request.seen && context.mounted) {
showDialog(
context: context,
builder: (_) => AlertDialog(
2025-07-10 20:16:21 -07:00
scrollable: true,
2025-06-14 13:26:13 -07:00
content: Text(AppLocalizations.of(context)!.optimizationMessage),
actions: [
TextButton(
2025-06-18 17:43:07 -07:00
onPressed: () {
Navigator.of(context).pop();
bg.DeviceSettings.show(request);
},
2025-06-14 13:26:13 -07:00
child: Text(AppLocalizations.of(context)!.okButton),
),
],
),
);
}
}
} catch (error) {
debugPrint(error.toString());
}
}
Future<bool> _isPermissionDenied() async {
final providerState = await bg.BackgroundGeolocation.providerState;
return providerState.status == bg.ProviderChangeEvent.AUTHORIZATION_STATUS_DENIED ||
providerState.status == bg.ProviderChangeEvent.AUTHORIZATION_STATUS_RESTRICTED;
}
2025-05-07 22:06:29 -07:00
Widget _buildTrackingCard() {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListTile(
contentPadding: EdgeInsets.zero,
title: Text(AppLocalizations.of(context)!.trackingTitle),
titleTextStyle: Theme.of(context).textTheme.headlineMedium,
),
ListTile(
contentPadding: EdgeInsets.zero,
title: Text(AppLocalizations.of(context)!.idLabel),
2025-05-13 07:40:09 -07:00
subtitle: Text(Preferences.instance.getString(Preferences.id) ?? ''),
2025-05-07 22:06:29 -07:00
),
SwitchListTile(
contentPadding: EdgeInsets.zero,
title: Text(AppLocalizations.of(context)!.trackingLabel),
value: trackingEnabled,
2026-01-18 07:33:23 -08:00
activeTrackColor: isMoving == false ? Theme.of(context).colorScheme.secondary : null,
2025-06-28 14:57:56 -07:00
onChanged: (bool value) async {
if (await PasswordService.authenticate(context) && mounted) {
if (value) {
2025-06-30 23:00:08 -07:00
try {
2026-02-02 20:20:58 -08:00
FirebaseCrashlytics.instance.log('tracking_toggle_start');
2025-06-30 23:00:08 -07:00
await bg.BackgroundGeolocation.start();
if (mounted) {
_checkBatteryOptimizations(context);
}
} on PlatformException catch (error) {
final isPermissionError = await _isPermissionDenied();
if (!mounted) return;
messengerKey.currentState?.showSnackBar(
SnackBar(
content: Text(error.message ?? error.code),
duration: const Duration(seconds: 4),
action: isPermissionError
? SnackBarAction(
label: AppLocalizations.of(context)!.settingsTitle,
onPressed: () => AppSettings.openAppSettings(
type: AppSettingsType.settings,
),
)
: null,
),
);
2025-06-30 23:00:08 -07:00
}
2025-06-28 14:57:56 -07:00
} else {
2026-02-02 20:20:58 -08:00
FirebaseCrashlytics.instance.log('tracking_toggle_stop');
2025-06-28 14:57:56 -07:00
bg.BackgroundGeolocation.stop();
}
2025-05-07 22:06:29 -07:00
}
},
),
const SizedBox(height: 8),
2025-05-08 22:10:56 -07:00
OverflowBar(
spacing: 8,
children: [
FilledButton.tonal(
onPressed: () async {
try {
2025-06-16 22:43:21 -07:00
await bg.BackgroundGeolocation.getCurrentPosition(samples: 1, persist: true, extras: {'manual': true});
2025-06-30 23:00:08 -07:00
} on PlatformException catch (error) {
messengerKey.currentState?.showSnackBar(SnackBar(content: Text(error.message ?? error.code)));
2025-05-08 22:10:56 -07:00
}
},
child: Text(AppLocalizations.of(context)!.locationButton),
),
FilledButton.tonal(
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (_) => const StatusScreen()));
},
child: Text(AppLocalizations.of(context)!.statusButton),
),
],
2025-05-07 22:06:29 -07:00
),
],
),
),
);
}
Widget _buildSettingsCard() {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListTile(
contentPadding: EdgeInsets.zero,
title: Text(AppLocalizations.of(context)!.settingsTitle),
titleTextStyle: Theme.of(context).textTheme.headlineMedium,
),
ListTile(
contentPadding: EdgeInsets.zero,
title: Text(AppLocalizations.of(context)!.urlLabel),
2025-05-13 07:40:09 -07:00
subtitle: Text(Preferences.instance.getString(Preferences.url) ?? ''),
2025-05-07 22:06:29 -07:00
),
const SizedBox(height: 8),
2025-05-08 22:10:56 -07:00
OverflowBar(
spacing: 8,
children: [
FilledButton.tonal(
2025-05-13 07:40:09 -07:00
onPressed: () async {
2025-06-28 14:57:56 -07:00
if (await PasswordService.authenticate(context) && mounted) {
await Navigator.push(context, MaterialPageRoute(builder: (_) => const SettingsScreen()));
2025-08-30 05:58:24 -07:00
setState(() {});
2025-06-28 14:57:56 -07:00
}
2025-05-08 22:10:56 -07:00
},
child: Text(AppLocalizations.of(context)!.settingsButton),
),
],
2025-05-07 22:06:29 -07:00
),
]
),
),
);
}
2025-05-05 07:47:09 -07:00
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
2025-06-05 20:16:27 -07:00
title: Text('Traccar Client'),
2025-05-05 07:47:09 -07:00
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
2025-05-07 22:06:29 -07:00
_buildTrackingCard(),
const SizedBox(height: 16),
_buildSettingsCard(),
2025-05-05 07:47:09 -07:00
],
),
),
);
}
}