From e7099855e01653fbdaa1379ca354db98387683fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Harrault?= <benoit@harrault.fr> Date: Thu, 23 Nov 2023 16:36:38 +0100 Subject: [PATCH] Add "interface type" setting --- assets/translations/en.json | 3 +++ assets/translations/fr.json | 3 +++ lib/cubit/settings_cubit.dart | 32 +++++++++++++++++++++++++- lib/cubit/settings_state.dart | 4 ++++ lib/models/interface_type.dart | 14 ++++++++++++ lib/ui/screens/demo_page.dart | 7 +++--- lib/ui/skeleton.dart | 2 ++ lib/ui/widgets/app_bar.dart | 10 ++++----- lib/ui/widgets/header_app.dart | 26 ++++++++++++++++++---- lib/ui/widgets/settings_form.dart | 37 +++++++++++++++++++++++++++++-- pubspec.lock | 14 ++++++------ 11 files changed, 129 insertions(+), 23 deletions(-) create mode 100644 lib/models/interface_type.dart diff --git a/assets/translations/en.json b/assets/translations/en.json index 2347221..d90a462 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 332b150..67575f6 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 8d13db0..01d3b94 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 f435ed3..3f95d16 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 0000000..05dc342 --- /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 b488381..03afe0a 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 01f8cca..cf3414e 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 ea07b61..3afc4bb 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 77b015b..a64053e 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 e0a6e85..fa8677a 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: 16), ElevatedButton( child: Row( mainAxisAlignment: MainAxisAlignment.center, diff --git a/pubspec.lock b/pubspec.lock index 287e966..ced1c87 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: @@ -401,10 +401,10 @@ 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: @@ -422,5 +422,5 @@ packages: source: hosted version: "1.0.3" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=3.7.0" -- GitLab