Added: settings entry to switch the locale dynamically.

This commit is contained in:
Yannick Mauray 2021-12-10 00:37:17 +01:00
parent 118d39659c
commit c00526a5be
No known key found for this signature in database
GPG Key ID: 67C4AAC5E99CB909
8 changed files with 256 additions and 94 deletions

View File

@ -2,12 +2,13 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:provider/provider.dart';
import 'package:quickgui/src/globals.dart';
import 'package:tuple/tuple.dart';
import 'package:window_size/window_size.dart';
import 'src/app.dart';
import 'src/mixins/app_version.dart';
import 'src/model/app_theme.dart';
import 'src/model/app_settings.dart';
import 'src/model/operating_system.dart';
import 'src/model/option.dart';
import 'src/model/version.dart';
@ -20,7 +21,12 @@ Future<List<OperatingSystem>> loadOperatingSystems(bool showUbuntus) async {
OperatingSystem? currentOperatingSystem;
Version? currentVersion;
stdout.split('\n').skip(1).where((element) => element.isNotEmpty).map((e) => e.trim()).forEach((element) {
stdout
.split('\n')
.skip(1)
.where((element) => element.isNotEmpty)
.map((e) => e.trim())
.forEach((element) {
var chunks = element.split(",");
Tuple5 supportedVersion;
if (chunks.length == 4) // Legacy version of quickget
@ -32,7 +38,8 @@ Future<List<OperatingSystem>> loadOperatingSystems(bool showUbuntus) async {
}
if (currentOperatingSystem?.code != supportedVersion.item2) {
currentOperatingSystem = OperatingSystem(supportedVersion.item1, supportedVersion.item2);
currentOperatingSystem =
OperatingSystem(supportedVersion.item1, supportedVersion.item2);
output.add(currentOperatingSystem!);
currentVersion = null;
}
@ -40,7 +47,8 @@ Future<List<OperatingSystem>> loadOperatingSystems(bool showUbuntus) async {
currentVersion = Version(supportedVersion.item3);
currentOperatingSystem!.versions.add(currentVersion!);
}
currentVersion!.options.add(Option(supportedVersion.item4, supportedVersion.item5));
currentVersion!.options
.add(Option(supportedVersion.item4, supportedVersion.item5));
});
return output;
@ -48,6 +56,7 @@ Future<List<OperatingSystem>> loadOperatingSystems(bool showUbuntus) async {
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Don't forget to also change the size in linux/my_application.cc:50
setWindowMinSize(const Size(692, 580));
setWindowMaxSize(const Size(692, 580));
gOperatingSystems = await loadOperatingSystems(false);
@ -55,7 +64,7 @@ void main() async {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => AppTheme()),
ChangeNotifierProvider(create: (_) => AppSettings()),
],
builder: (context, _) => const App(),
),

View File

@ -1,12 +1,16 @@
import 'dart:ffi';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:provider/provider.dart';
import 'package:gettext_i18n/gettext_i18n.dart';
import 'package:window_size/window_size.dart';
import 'package:quickgui/src/supported_locales.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'globals.dart';
import 'mixins/preferences_mixin.dart';
import 'model/app_theme.dart';
import 'model/app_settings.dart';
import 'pages/main_page.dart';
class App extends StatefulWidget {
@ -19,56 +23,33 @@ class App extends StatefulWidget {
class _AppState extends State<App> with PreferencesMixin {
@override
Widget build(BuildContext context) {
return FutureBuilder<bool?>(
future: getPreference<bool>(prefThemeMode),
builder: (context, AsyncSnapshot<bool?> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data != null) {
context.read<AppTheme>().useDarkModeSilently = snapshot.data!;
}
return Consumer<AppTheme>(
builder: (context, appTheme, _) => MaterialApp(
return FutureBuilder<SharedPreferences>(
future: SharedPreferences.getInstance(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.data != null) {
var appSettings = context.read<AppSettings>();
appSettings.setActiveLocaleSilently(
snapshot.data?.getString(prefCurrentLocale) ??
Platform.localeName);
appSettings.useDarkModeSilently =
snapshot.data!.getBool(prefThemeMode) as bool;
return Consumer<AppSettings>(
builder: (context, appSettings, _) => MaterialApp(
theme: ThemeData(primarySwatch: Colors.pink),
darkTheme: ThemeData.dark(),
themeMode: appTheme.themeMode,
themeMode: appSettings.themeMode,
home: const MainPage(),
supportedLocales: const [
/*
* List of locales (language + country) we have translations for.
*
* If there is a file for the tuple (langue, country) in assets/lib/i18n, then this
* will be used for translation.
*
* If there is not, then we'll look for a file for the language only.
*
* If there is no file for the language code, we'll fallback to the english file.
*
* Example : let's say the locale is fr_CH. We will look for "assets/lib/i18n/fr_CH.po",
* "assets/lib/i18n/fr.po", and "assets/lib/i18n/en.po", stopping at the first file we
* find.
*
* Translation files are not merged, meaning if some translations are missing in fr_CH.po
* but are present in fr.po, the missing translations will not be picked up from fr.po,
* and thus will show up in english.
*/
Locale('cs_CZ'),
Locale('cy'),
Locale('de'),
Locale('en'),
Locale('fr'),
Locale('fr', 'CH'),
Locale('gd'),
Locale('it'),
Locale('nl'),
Locale('no'),
Locale('oc'),
Locale('ru'),
],
supportedLocales: supportedLocales.map((s) => s.contains("_")
? Locale(s.split("_")[0], s.split("_")[1])
: Locale(s)),
localizationsDelegates: [
GettextLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
locale:
Locale(appSettings.languageCode!, appSettings.countryCode),
localeListResolutionCallback: (locales, supportedLocales) {
if (locales != null) {
for (var locale in locales) {
@ -96,5 +77,56 @@ class _AppState extends State<App> with PreferencesMixin {
}
},
);
/*
return FutureBuilder<bool?>(
future: getPreference<bool>(prefThemeMode),
builder: (context, AsyncSnapshot<bool?> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data != null) {
context.read<AppSettings>().useDarkModeSilently = snapshot.data!;
}
return Consumer<AppSettings>(
builder: (context, appTheme, _) => MaterialApp(
theme: ThemeData(primarySwatch: Colors.pink),
darkTheme: ThemeData.dark(),
themeMode: appTheme.themeMode,
home: const MainPage(),
supportedLocales: supportedLocales.map((s) => s.contains("_")
? Locale(s.split("_")[0], s.split("_")[1])
: Locale(s)),
localizationsDelegates: [
GettextLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
locale: const Locale('ru'),
localeListResolutionCallback: (locales, supportedLocales) {
if (locales != null) {
for (var locale in locales) {
var supportedLocale = supportedLocales.where((element) =>
element.languageCode == locale.languageCode &&
element.countryCode == locale.countryCode);
if (supportedLocale.isNotEmpty) {
return supportedLocale.first;
}
supportedLocale = supportedLocales.where((element) =>
element.languageCode == locale.languageCode);
if (supportedLocale.isNotEmpty) {
return supportedLocale.first;
}
}
}
return null;
},
),
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
);
*/
}
}

View File

@ -3,3 +3,4 @@ import 'dart:io';
var gIsSnap = Platform.environment['SNAP']?.isNotEmpty ?? false;
const String prefWorkingDirectory = 'workingDirectory';
const String prefThemeMode = 'themeMode';
const String prefCurrentLocale = 'currentLocale';

View File

@ -0,0 +1,48 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:quickgui/src/supported_locales.dart';
class AppSettings extends ChangeNotifier {
ThemeMode? _themeMode;
String? _activeLocale;
ThemeMode get themeMode => _themeMode ?? ThemeMode.system;
String get activeLocale => _activeLocale ?? Platform.localeName;
String? get languageCode => _activeLocale?.split("_")[0];
String? get countryCode =>
((_activeLocale != null) && (_activeLocale!.contains("_")))
? _activeLocale!.split("_")[1]
: null;
set activeLocale(String locale) {
_activeLocale = locale;
notifyListeners();
}
setActiveLocaleSilently(String locale) {
_activeLocale = locale;
if (_activeLocale!.contains(".")) {
_activeLocale = _activeLocale!.split(".")[0];
}
if (!supportedLocales.contains(_activeLocale)) {
if (activeLocale.contains("_")) {
_activeLocale = _activeLocale!.split("_")[0];
}
if (!supportedLocales.contains(_activeLocale)) {
_activeLocale = "en";
}
}
}
set useDarkModeSilently(bool useDarkMode) {
_themeMode = useDarkMode ? ThemeMode.dark : ThemeMode.light;
}
set useDarkMode(bool useDarkMode) {
useDarkModeSilently = useDarkMode;
notifyListeners();
}
}

View File

@ -1,16 +0,0 @@
import 'package:flutter/material.dart';
class AppTheme extends ChangeNotifier {
ThemeMode? _themeMode;
ThemeMode get themeMode => _themeMode ?? ThemeMode.system;
set useDarkModeSilently(bool useDarkMode) {
_themeMode = useDarkMode ? ThemeMode.dark : ThemeMode.light;
}
set useDarkMode(bool useDarkMode) {
useDarkModeSilently = useDarkMode;
notifyListeners();
}
}

View File

@ -0,0 +1,31 @@
/*
* List of locales (language + country) we have translations for.
*
* If there is a file for the tuple (langue, country) in assets/lib/i18n, then this
* will be used for translation.
*
* If there is not, then we'll look for a file for the language only.
*
* If there is no file for the language code, we'll fallback to the english file.
*
* Example : let's say the locale is fr_CH. We will look for "assets/lib/i18n/fr_CH.po",
* "assets/lib/i18n/fr.po", and "assets/lib/i18n/en.po", stopping at the first file we
* find.
*
* Translation files are not merged, meaning if some translations are missing in fr_CH.po
* but are present in fr.po, the missing translations will not be picked up from fr.po,
* and thus will show up in english.
*/
final supportedLocales = [
'cs_CZ',
'cy',
'de',
'en',
'fr',
'gd',
'it',
'nl',
'no',
'oc',
'ru',
];

View File

@ -1,45 +1,102 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:gettext_i18n/gettext_i18n.dart';
import 'package:quickgui/src/supported_locales.dart';
import '../globals.dart';
import '../mixins/app_version.dart';
import '../mixins/preferences_mixin.dart';
import '../model/app_theme.dart';
import '../model/app_settings.dart';
class LeftMenu extends StatelessWidget with PreferencesMixin {
class LeftMenu extends StatefulWidget {
const LeftMenu({Key? key}) : super(key: key);
@override
State<LeftMenu> createState() => _LeftMenuState();
}
class _LeftMenuState extends State<LeftMenu> with PreferencesMixin {
late String currentLocale;
@override
void initState() {
super.initState();
}
@override
void didChangeDependencies() {
var appSettings = context.read<AppSettings>();
currentLocale = appSettings.activeLocale;
if (!supportedLocales.contains(currentLocale)) {
currentLocale = currentLocale.split("_")[0];
if (!supportedLocales.contains(currentLocale)) {
currentLocale = "en";
}
}
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
var _version = AppVersion.packageInfo!.version;
return Consumer<AppTheme>(
builder: (context, appTheme, _) => Drawer(
child: ListView(
children: [
ListTile(
title: Text("quickgui $_version", style: Theme.of(context).textTheme.headline6),
),
const Divider(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
Text(context.t('Use dark mode')),
Switch(
value: Theme.of(context).brightness == Brightness.dark,
onChanged: (value) {
appTheme.useDarkMode = value;
savePreference(prefThemeMode, value);
},
),
],
return Consumer<AppSettings>(
builder: (context, appSettings, _) {
return Drawer(
child: ListView(
children: [
ListTile(
title: Text("quickgui $_version",
style: Theme.of(context).textTheme.headline6),
),
)
],
),
),
const Divider(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
Text(context.t('Use dark mode')),
Expanded(
child: Container(),
),
Switch(
value: Theme.of(context).brightness == Brightness.dark,
onChanged: (value) {
appSettings.useDarkMode = value;
savePreference(prefThemeMode, value);
},
),
],
),
),
const Divider(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
Text(context.t('Language')),
Expanded(
child: Container(),
),
DropdownButton<String>(
value: currentLocale,
items: supportedLocales
.map(
(e) => DropdownMenuItem(child: Text(e), value: e))
.toList(),
onChanged: (value) {
setState(() {
currentLocale = value!;
appSettings.activeLocale = currentLocale;
savePreference(prefCurrentLocale, currentLocale);
});
},
),
],
),
),
],
),
);
},
);
}
}

View File

@ -47,7 +47,7 @@ static void my_application_activate(GApplication* application) {
gtk_window_set_title(window, "quickgui");
}
gtk_window_set_default_size(window, 640, 480);
gtk_window_set_default_size(window, 692, 580);
gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlDartProject) project = fl_dart_project_new();