|
| 1 | +import 'package:choice/choice.dart'; |
| 2 | +import 'package:country_flags/country_flags.dart'; |
1 | 3 | import 'package:flex_color_picker/flex_color_picker.dart'; |
2 | 4 | import 'package:flutter/material.dart'; |
| 5 | +import 'package:flutter_localized_locales/flutter_localized_locales.dart'; |
3 | 6 | import 'package:flutter_riverpod/flutter_riverpod.dart'; |
4 | 7 | import 'package:wakelock_plus/wakelock_plus.dart'; |
5 | 8 |
|
6 | 9 | import '../../Backend/Bluetooth/bluetooth_manager.dart'; |
7 | 10 | import '../../Backend/Definitions/Device/device_definition.dart'; |
8 | 11 | import '../../Backend/logging_wrappers.dart'; |
9 | 12 | import '../../constants.dart'; |
| 13 | +import '../../l10n/app_localizations.dart'; |
10 | 14 | import '../go_router_config.dart'; |
11 | 15 | import '../translation_string_definitions.dart'; |
| 16 | +import '../utils.dart'; |
12 | 17 |
|
13 | 18 | class Settings extends ConsumerStatefulWidget { |
14 | 19 | const Settings({super.key}); |
@@ -36,6 +41,7 @@ class _SettingsState extends ConsumerState<Settings> { |
36 | 41 | body: ListView( |
37 | 42 | controller: _controller, |
38 | 43 | children: [ |
| 44 | + LanguagePicker(), |
39 | 45 | ListTile( |
40 | 46 | leading: const Icon(Icons.color_lens), |
41 | 47 | title: Text( |
@@ -253,3 +259,114 @@ class _SettingsState extends ConsumerState<Settings> { |
253 | 259 | _controller.dispose(); |
254 | 260 | } |
255 | 261 | } |
| 262 | + |
| 263 | +class LanguagePicker extends StatelessWidget { |
| 264 | + final ChoicePromptBuilder<Locale>? anchorBuilder; |
| 265 | + |
| 266 | + const LanguagePicker({ |
| 267 | + super.key, |
| 268 | + this.anchorBuilder, |
| 269 | + }); |
| 270 | + |
| 271 | + @override |
| 272 | + Widget build(BuildContext context) { |
| 273 | + return PromptedChoice<Locale>.single( |
| 274 | + title: appLanguageSelectorTitle(), |
| 275 | + promptDelegate: ChoicePrompt.delegateBottomSheet(useRootNavigator: true, enableDrag: true, maxHeightFactor: 0.8), |
| 276 | + itemCount: AppLocalizations.supportedLocales.length, |
| 277 | + modalHeaderBuilder: ChoiceModal.createHeader( |
| 278 | + automaticallyImplyLeading: true, |
| 279 | + actionsBuilder: [], |
| 280 | + ), |
| 281 | + anchorBuilder: anchorBuilder, |
| 282 | + modalFooterBuilder: ChoiceModal.createFooter( |
| 283 | + mainAxisAlignment: MainAxisAlignment.center, |
| 284 | + children: [ |
| 285 | + (choiceController) { |
| 286 | + return FilledButton( |
| 287 | + onPressed: choiceController.value.isNotEmpty ? () => choiceController.closeModal(confirmed: true) : null, |
| 288 | + child: Row( |
| 289 | + mainAxisAlignment: MainAxisAlignment.center, |
| 290 | + children: [ |
| 291 | + const Icon(Icons.check), |
| 292 | + const Padding( |
| 293 | + padding: EdgeInsets.symmetric(horizontal: 4), |
| 294 | + ), |
| 295 | + Text( |
| 296 | + triggersDefSelectSaveLabel(), |
| 297 | + style: Theme.of(context).textTheme.labelLarge!.copyWith( |
| 298 | + color: getTextColor( |
| 299 | + Theme.of(context).colorScheme.primary, |
| 300 | + ), |
| 301 | + ), |
| 302 | + ), |
| 303 | + ], |
| 304 | + ), |
| 305 | + ); |
| 306 | + }, |
| 307 | + ], |
| 308 | + ), |
| 309 | + onChanged: (value) async { |
| 310 | + if (value != null) { |
| 311 | + HiveProxy.put(settings, selectedLocale, value.toLanguageTag()); |
| 312 | + initLocale(); |
| 313 | + } |
| 314 | + }, |
| 315 | + confirmation: true, |
| 316 | + value: AppLocalizations.supportedLocales |
| 317 | + .where( |
| 318 | + (element) => element.toLanguageTag() == HiveProxy.getOrDefault(settings, selectedLocale, defaultValue: ""), |
| 319 | + ) |
| 320 | + .firstOrNull, |
| 321 | + itemBuilder: (ChoiceController<Locale> state, int index) { |
| 322 | + Locale locale = AppLocalizations.supportedLocales[index]; |
| 323 | + return RadioListTile( |
| 324 | + value: locale, |
| 325 | + onChanged: (Locale? value) { |
| 326 | + state.select(locale); |
| 327 | + }, |
| 328 | + groupValue: state.single, |
| 329 | + title: Text(LocaleNames.of(context)!.nameOf(locale.toLanguageTag().replaceAll("-", "_")) ?? locale.toLanguageTag()), |
| 330 | + secondary: Builder(builder: (context) { |
| 331 | + if (locale.countryCode != null) { |
| 332 | + return CountryFlag.fromCountryCode(locale.countryCode!.replaceAll("zh", "zh-cn")); |
| 333 | + } else { |
| 334 | + return CountryFlag.fromLanguageCode(locale.languageCode.replaceAll("zh", "zh-cn")); |
| 335 | + } |
| 336 | + }), |
| 337 | + ); |
| 338 | + }, |
| 339 | + ); |
| 340 | + return ListTile( |
| 341 | + title: Text("Language"), |
| 342 | + trailing: DropdownMenu<Locale>( |
| 343 | + width: MediaQuery.of(context).size.width / 3, |
| 344 | + onSelected: (value) { |
| 345 | + if (value != null) { |
| 346 | + HiveProxy.put(settings, selectedLocale, value.toLanguageTag()); |
| 347 | + } |
| 348 | + }, |
| 349 | + initialSelection: AppLocalizations.supportedLocales |
| 350 | + .where( |
| 351 | + (element) => element.toLanguageTag() == HiveProxy.getOrDefault(settings, selectedLocale, defaultValue: ""), |
| 352 | + ) |
| 353 | + .firstOrNull, |
| 354 | + dropdownMenuEntries: AppLocalizations.supportedLocales |
| 355 | + .map( |
| 356 | + (e) => DropdownMenuEntry( |
| 357 | + label: LocaleNames.of(context)!.nameOf(e.toLanguageTag().replaceAll("-", "_")) ?? e.toLanguageTag(), |
| 358 | + value: e, |
| 359 | + leadingIcon: Builder(builder: (context) { |
| 360 | + if (e.countryCode != null) { |
| 361 | + return CountryFlag.fromCountryCode(e.countryCode!.replaceAll("zh", "zh-cn")); |
| 362 | + } else { |
| 363 | + return CountryFlag.fromLanguageCode(e.languageCode.replaceAll("zh", "zh-cn")); |
| 364 | + } |
| 365 | + }), |
| 366 | + ), |
| 367 | + ) |
| 368 | + .toList(), |
| 369 | + ), |
| 370 | + ); |
| 371 | + } |
| 372 | +} |
0 commit comments