diff --git a/android/gradle.properties b/android/gradle.properties index 6c74aa0cfdc459c0709940ec7fcf37bff56804d0..a080b10f3c36d8834cb51893b1a3ff3deb40644b 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true -app.versionName=1.0.32 -app.versionCode=33 +app.versionName=1.0.33 +app.versionCode=34 diff --git a/assets/translations/en.json b/assets/translations/en.json index 2347221fb3e9e2ceb24bed2076e933740103fcba..d90a462d962ae53f8ef610e33ab4bc347b0a5909 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -9,6 +9,9 @@ "settings_title": "Settings", "settings_label_api_url": "API URL:", "settings_label_security_token": "Security token:", + "settings_label_interface_type": "Interface type:", + "interface_type_basic": "basic", + "interface_type_expert": "expert", "settings_button_save": "Save", "about_title": "About", diff --git a/assets/translations/fr.json b/assets/translations/fr.json index 332b150839dcc5b662baddceaba6e2203af94ae1..67575f68dbb45583075ae61204c3a66c79a1fba2 100644 --- a/assets/translations/fr.json +++ b/assets/translations/fr.json @@ -9,6 +9,9 @@ "settings_title": "Paramètres", "settings_label_api_url": "URL de l'API :", "settings_label_security_token": "Jeton de sécurité :", + "settings_label_interface_type": "Type d'interface :", + "interface_type_basic": "simple", + "interface_type_expert": "expert", "settings_button_save": "Enregistrer", "about_title": "À propos", diff --git a/lib/cubit/settings_cubit.dart b/lib/cubit/settings_cubit.dart index 8d13db05cfcb2ee127aa6cf6188f15982a8d7c0d..01d3b943b6542e57c2ba42f346dc587285173158 100644 --- a/lib/cubit/settings_cubit.dart +++ b/lib/cubit/settings_cubit.dart @@ -2,12 +2,14 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; +import 'package:random/models/interface_type.dart'; + part 'settings_state.dart'; class SettingsCubit extends HydratedCubit<SettingsState> { SettingsCubit() : super(const SettingsState()); - String getSetting(String key, [String? defaultValue]) { + Object getSetting(String key, [String? defaultValue]) { if (state.values.keys.contains(key)) { return state.values[key] ?? defaultValue ?? ''; } @@ -15,13 +17,27 @@ class SettingsCubit extends HydratedCubit<SettingsState> { return defaultValue ?? ''; } + String getApiUrl() { + return state.apiUrl ?? ''; + } + + String getSecurityToken() { + return state.securityToken ?? ''; + } + + InterfaceType getInterfaceType() { + return state.interfaceType ?? InterfaceType.basic; + } + void setValues({ String? apiUrl, String? securityToken, + InterfaceType? interfaceType, }) { emit(SettingsState( apiUrl: apiUrl != null ? apiUrl : state.apiUrl, securityToken: securityToken != null ? securityToken : state.securityToken, + interfaceType: interfaceType != null ? interfaceType : state.interfaceType, )); } @@ -29,10 +45,23 @@ class SettingsCubit extends HydratedCubit<SettingsState> { SettingsState? fromJson(Map<String, dynamic> json) { String apiUrl = json['apiUrl'] as String; String securityToken = json['securityToken'] as String; + InterfaceType interfaceType; + + switch (json['interfaceType'] as String) { + case 'InterfaceType.basic': + interfaceType = InterfaceType.basic; + break; + case 'InterfaceType.expert': + interfaceType = InterfaceType.expert; + break; + default: + interfaceType = InterfaceType.basic; + } return SettingsState( apiUrl: apiUrl, securityToken: securityToken, + interfaceType: interfaceType, ); } @@ -41,6 +70,7 @@ class SettingsCubit extends HydratedCubit<SettingsState> { return <String, String>{ 'apiUrl': state.apiUrl ?? '', 'securityToken': state.securityToken ?? '', + 'interfaceType': state.interfaceType.toString(), }; } } diff --git a/lib/cubit/settings_state.dart b/lib/cubit/settings_state.dart index f435ed3db87719dc5d3fe3452fb440c27da62fc0..3f95d16207157b01c1426c00d82ce9b5a2d47783 100644 --- a/lib/cubit/settings_state.dart +++ b/lib/cubit/settings_state.dart @@ -5,19 +5,23 @@ class SettingsState extends Equatable { const SettingsState({ this.apiUrl, this.securityToken, + this.interfaceType, }); final String? apiUrl; final String? securityToken; + final InterfaceType? interfaceType; @override List<String?> get props => <String?>[ apiUrl, securityToken, + interfaceType.toString(), ]; Map<String, String?> get values => <String, String?>{ 'apiUrl': apiUrl, 'securityToken': securityToken, + 'interfaceType': interfaceType.toString(), }; } diff --git a/lib/models/interface_type.dart b/lib/models/interface_type.dart new file mode 100644 index 0000000000000000000000000000000000000000..05dc342a74312efd54825091623fab48122e9a27 --- /dev/null +++ b/lib/models/interface_type.dart @@ -0,0 +1,14 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; + +enum InterfaceType { + basic, + expert, +} + +class InterfaceTypes { + static List<Widget> selectWidgets = <Widget>[ + Text('interface_type_basic').tr(), + Text('interface_type_expert').tr(), + ]; +} diff --git a/lib/ui/screens/demo_page.dart b/lib/ui/screens/demo_page.dart index b488381d2fc803f05caaae135277695b6422c8d0..03afe0a3f5d6d70445e6a03c2146dc35e2591652 100644 --- a/lib/ui/screens/demo_page.dart +++ b/lib/ui/screens/demo_page.dart @@ -78,9 +78,10 @@ class DemoPage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Text('apiUrl: ' + settings.getSetting('apiUrl')), - Text('securityToken: ' + settings.getSetting('securityToken')), - Text('unknown: ' + settings.getSetting('unknown', 'undefined')), + Text('apiUrl: ' + settings.getApiUrl()), + Text('securityToken: ' + settings.getSecurityToken()), + Text('interfaceType: ' + settings.getInterfaceType().toString()), + Text('unknown: ' + settings.getSetting('unknown', 'undefined').toString()), ], ); }, diff --git a/lib/ui/skeleton.dart b/lib/ui/skeleton.dart index 01f8cca5fd7dc6e9cfbc164a0a41cd28b8dac4a0..cf3414ee1e453f3f69a97f53eea9989af5a7732e 100644 --- a/lib/ui/skeleton.dart +++ b/lib/ui/skeleton.dart @@ -21,6 +21,8 @@ class SkeletonScreen extends StatefulWidget { class _SkeletonScreenState extends State<SkeletonScreen> { @override Widget build(BuildContext context) { + print('SkeletonScreen - build'); + const List<Widget> pageNavigation = <Widget>[ DemoPage(), GraphPage(), diff --git a/lib/ui/widgets/app_bar.dart b/lib/ui/widgets/app_bar.dart index ea07b61c4ef693143a24c328273ede8e83dbabde..3afc4bbd82ba83a7bdac0389bb09df1b38d21189 100644 --- a/lib/ui/widgets/app_bar.dart +++ b/lib/ui/widgets/app_bar.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:unicons/unicons.dart'; import 'package:random/ui/widgets/header_app.dart'; @@ -8,13 +7,12 @@ class StandardAppBar extends StatelessWidget implements PreferredSizeWidget { @override Widget build(BuildContext context) { + print('StandardAppBar - build'); + return AppBar( - title: const AppHeader(text: 'app_name'), + title: AppHeader(text: 'app_name'), actions: [ - IconButton( - onPressed: () {}, - icon: const Icon(UniconsSolid.refresh), - ), + // ], ); } diff --git a/lib/ui/widgets/header_app.dart b/lib/ui/widgets/header_app.dart index 77b015bc6cb8712734f5383ca58cd818e0a51f0f..a64053e888c4d94319343a8b9192619dfbf13d5f 100644 --- a/lib/ui/widgets/header_app.dart +++ b/lib/ui/widgets/header_app.dart @@ -1,5 +1,9 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:random/cubit/settings_cubit.dart'; +import 'package:random/models/interface_type.dart'; class AppHeader extends StatelessWidget { const AppHeader({super.key, required this.text}); @@ -8,10 +12,24 @@ class AppHeader extends StatelessWidget { @override Widget build(BuildContext context) { - return Text( - tr(text), - textAlign: TextAlign.start, - style: Theme.of(context).textTheme.headlineMedium!.apply(fontWeightDelta: 2), + print('AppHeader - build (' + this.text + ')'); + + return BlocProvider<SettingsCubit>( + create: (BuildContext context) => SettingsCubit(), + child: BlocBuilder<SettingsCubit, SettingsState>( + builder: (BuildContext context, SettingsState state) { + SettingsCubit settings = BlocProvider.of<SettingsCubit>(context); + + bool isExpert = settings.getInterfaceType() == InterfaceType.expert; + String titleSuffix = isExpert ? ' ⭐' : ''; + + return Text( + tr(text) + titleSuffix, + textAlign: TextAlign.start, + style: Theme.of(context).textTheme.headlineMedium!.apply(fontWeightDelta: 2), + ); + }, + ), ); } } diff --git a/lib/ui/widgets/settings_form.dart b/lib/ui/widgets/settings_form.dart index e0a6e853c0968c178fa14e797e59809ff91d81fc..f9d821cc549efb2ca6675b47071988746785a9c9 100644 --- a/lib/ui/widgets/settings_form.dart +++ b/lib/ui/widgets/settings_form.dart @@ -5,6 +5,8 @@ import 'package:unicons/unicons.dart'; import 'package:random/cubit/bottom_nav_cubit.dart'; import 'package:random/cubit/settings_cubit.dart'; +import 'package:random/config/theme.dart'; +import 'package:random/models/interface_type.dart'; class SettingsForm extends StatefulWidget { const SettingsForm({super.key}); @@ -16,6 +18,7 @@ class SettingsForm extends StatefulWidget { class _SettingsFormState extends State<SettingsForm> { final apiUrlController = TextEditingController(); final securityTokenController = TextEditingController(); + final List<Widget> interfaceTypesWidgets = InterfaceTypes.selectWidgets; @override void dispose() { @@ -24,17 +27,26 @@ class _SettingsFormState extends State<SettingsForm> { super.dispose(); } + List<bool> _selectedInterfaceType = <bool>[]; + @override Widget build(BuildContext context) { SettingsCubit settings = BlocProvider.of<SettingsCubit>(context); - apiUrlController.text = settings.getSetting('apiUrl'); - securityTokenController.text = settings.getSetting('securityToken'); + apiUrlController.text = settings.getApiUrl(); + securityTokenController.text = settings.getSecurityToken(); + if (_selectedInterfaceType.length != 2) { + _selectedInterfaceType = <bool>[ + settings.getInterfaceType() == InterfaceType.basic, + settings.getInterfaceType() == InterfaceType.expert, + ]; + } void saveSettings() { BlocProvider.of<SettingsCubit>(context).setValues( apiUrl: apiUrlController.text, securityToken: securityTokenController.text, + interfaceType: _selectedInterfaceType[0] ? InterfaceType.basic : InterfaceType.expert, ); BlocProvider.of<BottomNavCubit>(context).getDemoPage(); @@ -61,6 +73,27 @@ class _SettingsFormState extends State<SettingsForm> { ), ), SizedBox(height: 16), + Text('settings_label_interface_type').tr(), + ToggleButtons( + direction: Axis.horizontal, + onPressed: (int index) { + setState(() { + _selectedInterfaceType = index == 0 ? [true, false] : [false, true]; + }); + }, + borderRadius: const BorderRadius.all(Radius.circular(8)), + selectedBorderColor: appTheme.primaryColor, + selectedColor: appTheme.colorScheme.onSecondary, + fillColor: appTheme.colorScheme.secondary, + color: appTheme.primaryColor, + constraints: const BoxConstraints( + minHeight: 40.0, + minWidth: 80.0, + ), + isSelected: _selectedInterfaceType, + children: interfaceTypesWidgets, + ), + SizedBox(height: 20), ElevatedButton( child: Row( mainAxisAlignment: MainAxisAlignment.center, diff --git a/pubspec.lock b/pubspec.lock index 287e9663b92807989e74a57109ba14f0bdbbd11c..426a507a4f752b326e35c1ca97797cfa3ff952f4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" crypto: dependency: transitive description: @@ -172,10 +172,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" nested: dependency: transitive description: @@ -188,10 +188,10 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017" + sha256: "88bc797f44a94814f2213db1c9bd5badebafdfb8290ca9f78d4b9ee2a3db4d79" url: "https://pub.dev" source: hosted - version: "4.2.0" + version: "5.0.1" package_info_plus_platform_interface: dependency: transitive description: @@ -268,18 +268,18 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.7" provider: dependency: transitive description: name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" url: "https://pub.dev" source: hosted - version: "6.0.5" + version: "6.1.1" shared_preferences: dependency: transitive description: @@ -324,10 +324,10 @@ packages: dependency: transitive description: name: shared_preferences_web - sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf + sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.2" shared_preferences_windows: dependency: transitive description: @@ -401,18 +401,18 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" win32: dependency: transitive description: name: win32 - sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" + sha256: "7c99c0e1e2fa190b48d25c81ca5e42036d5cac81430ef249027d97b0935c553f" url: "https://pub.dev" source: hosted - version: "5.0.9" + version: "5.1.0" xdg_directories: dependency: transitive description: @@ -422,5 +422,5 @@ packages: source: hosted version: "1.0.3" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" - flutter: ">=3.7.0" + dart: ">=3.2.0 <4.0.0" + flutter: ">=3.16.0" diff --git a/pubspec.yaml b/pubspec.yaml index fcec92610e135213f88202653d2d174bed2654b6..da0ce025f3df924405a6c916aa250adafe063019 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: A random application, for testing purpose only. publish_to: 'none' -version: 1.0.32+33 +version: 1.0.33+34 environment: sdk: '^3.0.0' @@ -18,7 +18,7 @@ dependencies: path_provider: ^2.0.11 hydrated_bloc: ^9.0.0 unicons: ^2.1.1 - package_info_plus: ^4.2.0 + package_info_plus: ^5.0.1 flutter: uses-material-design: false