mirror of
https://github.com/FoggedLens/deflock-app.git
synced 2026-08-17 16:37:31 +02:00
localizations, dark mode touchups
This commit is contained in:
@@ -7,6 +7,7 @@ import 'package:provider/provider.dart';
|
||||
import '../app_state.dart';
|
||||
import '../dev_config.dart';
|
||||
import '../widgets/map_view.dart';
|
||||
import '../services/localization_service.dart';
|
||||
|
||||
import '../widgets/add_node_sheet.dart';
|
||||
import '../widgets/edit_node_sheet.dart';
|
||||
@@ -44,13 +45,14 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
|
||||
}
|
||||
|
||||
String _getFollowMeTooltip(FollowMeMode mode) {
|
||||
final locService = LocalizationService.instance;
|
||||
switch (mode) {
|
||||
case FollowMeMode.off:
|
||||
return 'Enable follow-me (north up)';
|
||||
return locService.t('followMe.off');
|
||||
case FollowMeMode.northUp:
|
||||
return 'Enable follow-me (rotating)';
|
||||
return locService.t('followMe.northUp');
|
||||
case FollowMeMode.rotating:
|
||||
return 'Disable follow-me';
|
||||
return locService.t('followMe.rotating');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,9 +174,13 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
|
||||
}
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.settings),
|
||||
onPressed: () => Navigator.pushNamed(context, '/settings'),
|
||||
AnimatedBuilder(
|
||||
animation: LocalizationService.instance,
|
||||
builder: (context, child) => IconButton(
|
||||
tooltip: LocalizationService.instance.settings,
|
||||
icon: const Icon(Icons.settings),
|
||||
onPressed: () => Navigator.pushNamed(context, '/settings'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -216,28 +222,34 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
icon: Icon(Icons.add_location_alt),
|
||||
label: Text('Tag Node'),
|
||||
onPressed: _openAddNodeSheet,
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: Size(0, 48),
|
||||
textStyle: TextStyle(fontSize: 16),
|
||||
child: AnimatedBuilder(
|
||||
animation: LocalizationService.instance,
|
||||
builder: (context, child) => ElevatedButton.icon(
|
||||
icon: Icon(Icons.add_location_alt),
|
||||
label: Text(LocalizationService.instance.tagNode),
|
||||
onPressed: _openAddNodeSheet,
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: Size(0, 48),
|
||||
textStyle: TextStyle(fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
icon: Icon(Icons.download_for_offline),
|
||||
label: Text('Download'),
|
||||
onPressed: () => showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => DownloadAreaDialog(controller: _mapController.mapController),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: Size(0, 48),
|
||||
textStyle: TextStyle(fontSize: 16),
|
||||
child: AnimatedBuilder(
|
||||
animation: LocalizationService.instance,
|
||||
builder: (context, child) => ElevatedButton.icon(
|
||||
icon: Icon(Icons.download_for_offline),
|
||||
label: Text(LocalizationService.instance.download),
|
||||
onPressed: () => showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => DownloadAreaDialog(controller: _mapController.mapController),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: Size(0, 48),
|
||||
textStyle: TextStyle(fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -9,37 +9,44 @@ import 'settings_screen_sections/offline_mode_section.dart';
|
||||
import 'settings_screen_sections/about_section.dart';
|
||||
import 'settings_screen_sections/max_nodes_section.dart';
|
||||
import 'settings_screen_sections/tile_provider_section.dart';
|
||||
import 'settings_screen_sections/language_section.dart';
|
||||
import '../services/localization_service.dart';
|
||||
|
||||
class SettingsScreen extends StatelessWidget {
|
||||
const SettingsScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Settings')),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: const [
|
||||
UploadModeSection(),
|
||||
Divider(),
|
||||
AuthSection(),
|
||||
Divider(),
|
||||
QueueSection(),
|
||||
Divider(),
|
||||
ProfileListSection(),
|
||||
Divider(),
|
||||
OperatorProfileListSection(),
|
||||
Divider(),
|
||||
MaxNodesSection(),
|
||||
Divider(),
|
||||
TileProviderSection(),
|
||||
Divider(),
|
||||
OfflineModeSection(),
|
||||
Divider(),
|
||||
OfflineAreasSection(),
|
||||
Divider(),
|
||||
AboutSection(),
|
||||
],
|
||||
return AnimatedBuilder(
|
||||
animation: LocalizationService.instance,
|
||||
builder: (context, child) => Scaffold(
|
||||
appBar: AppBar(title: Text(LocalizationService.instance.t('settings.title'))),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: const [
|
||||
UploadModeSection(),
|
||||
Divider(),
|
||||
AuthSection(),
|
||||
Divider(),
|
||||
QueueSection(),
|
||||
Divider(),
|
||||
ProfileListSection(),
|
||||
Divider(),
|
||||
OperatorProfileListSection(),
|
||||
Divider(),
|
||||
MaxNodesSection(),
|
||||
Divider(),
|
||||
TileProviderSection(),
|
||||
Divider(),
|
||||
OfflineModeSection(),
|
||||
Divider(),
|
||||
OfflineAreasSection(),
|
||||
Divider(),
|
||||
LanguageSection(),
|
||||
Divider(),
|
||||
AboutSection(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,35 +1,42 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../services/localization_service.dart';
|
||||
|
||||
class AboutSection extends StatelessWidget {
|
||||
const AboutSection({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
leading: const Icon(Icons.info_outline),
|
||||
title: const Text('About / Info'),
|
||||
onTap: () async {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => FutureBuilder<String>(
|
||||
future: DefaultAssetBundle.of(context).loadString('assets/info.txt'),
|
||||
builder: (context, snapshot) => AlertDialog(
|
||||
title: const Text('About This App'),
|
||||
content: SingleChildScrollView(
|
||||
child: Text(
|
||||
snapshot.connectionState == ConnectionState.done
|
||||
? (snapshot.data ?? 'No info available.')
|
||||
: 'Loading...',
|
||||
return AnimatedBuilder(
|
||||
animation: LocalizationService.instance,
|
||||
builder: (context, child) {
|
||||
final locService = LocalizationService.instance;
|
||||
return ListTile(
|
||||
leading: const Icon(Icons.info_outline),
|
||||
title: Text(locService.t('settings.aboutInfo')),
|
||||
onTap: () async {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => FutureBuilder<String>(
|
||||
future: DefaultAssetBundle.of(context).loadString('assets/info.txt'),
|
||||
builder: (context, snapshot) => AlertDialog(
|
||||
title: Text(locService.t('settings.aboutThisApp')),
|
||||
content: SingleChildScrollView(
|
||||
child: Text(
|
||||
snapshot.connectionState == ConnectionState.done
|
||||
? (snapshot.data ?? 'No info available.')
|
||||
: 'Loading...',
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(locService.ok),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('OK'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../../services/localization_service.dart';
|
||||
|
||||
class LanguageSection extends StatefulWidget {
|
||||
const LanguageSection({super.key});
|
||||
|
||||
@override
|
||||
State<LanguageSection> createState() => _LanguageSectionState();
|
||||
}
|
||||
|
||||
class _LanguageSectionState extends State<LanguageSection> {
|
||||
String? _selectedLanguage;
|
||||
Map<String, String> _languageNames = {};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadSelectedLanguage();
|
||||
_loadLanguageNames();
|
||||
}
|
||||
|
||||
_loadSelectedLanguage() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
setState(() {
|
||||
_selectedLanguage = prefs.getString('language_code');
|
||||
});
|
||||
}
|
||||
|
||||
_loadLanguageNames() async {
|
||||
final locService = LocalizationService.instance;
|
||||
final Map<String, String> names = {};
|
||||
|
||||
for (String langCode in locService.availableLanguages) {
|
||||
names[langCode] = await locService.getLanguageDisplayName(langCode);
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_languageNames = names;
|
||||
});
|
||||
}
|
||||
|
||||
_setLanguage(String? languageCode) async {
|
||||
await LocalizationService.instance.setLanguage(languageCode);
|
||||
setState(() {
|
||||
_selectedLanguage = languageCode;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedBuilder(
|
||||
animation: LocalizationService.instance,
|
||||
builder: (context, child) {
|
||||
final locService = LocalizationService.instance;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
|
||||
child: Text(
|
||||
locService.t('settings.language'),
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
// System Default option
|
||||
RadioListTile<String?>(
|
||||
title: Text(locService.t('settings.systemDefault')),
|
||||
value: null,
|
||||
groupValue: _selectedLanguage,
|
||||
onChanged: _setLanguage,
|
||||
),
|
||||
// Dynamic language options
|
||||
...locService.availableLanguages.map((langCode) =>
|
||||
RadioListTile<String>(
|
||||
title: Text(_languageNames[langCode] ?? langCode.toUpperCase()),
|
||||
value: langCode,
|
||||
groupValue: _selectedLanguage,
|
||||
onChanged: _setLanguage,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ class UploadModeSection extends StatelessWidget {
|
||||
builder: (context) {
|
||||
switch (appState.uploadMode) {
|
||||
case UploadMode.production:
|
||||
return const Text('Upload to the live OSM database (visible to all users)', style: TextStyle(fontSize: 12, color: Colors.black87));
|
||||
return Text('Upload to the live OSM database (visible to all users)', style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7)));
|
||||
case UploadMode.sandbox:
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
||||
Reference in New Issue
Block a user