From 10f4e60cbb0425e02f8c6d09211e7aeb824541cc Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Tue, 19 Dec 2023 17:15:01 +0100 Subject: [PATCH 01/24] chore(#636): update dependencies [wip] * Update packages and Android build tools * Updates and refactors routing * Uses Flutter's adaptive dialog * Need to test building for both platforms and functionality --- app/CONTRIBUTING.md | 6 +- app/android/app/build.gradle | 13 +- app/android/app/src/debug/AndroidManifest.xml | 3 +- app/android/app/src/main/AndroidManifest.xml | 3 +- .../app/src/profile/AndroidManifest.xml | 3 +- app/android/build.gradle | 6 +- app/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 4 +- app/generate_screenshots/app_test.dart | 1 + app/generate_screenshots/test_driver.dart | 2 +- app/integration_test/drugs_test.dart | 4 +- app/integration_test/faq_test.dart | 2 +- app/integration_test/login_test.dart | 12 +- app/integration_test/main_page_test.dart | 2 +- app/integration_test/onboarding_test.dart | 2 +- app/integration_test/settings_test.dart | 6 +- app/ios/Podfile.lock | 40 +- app/ios/Runner.xcodeproj/project.pbxproj | 3 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- app/lib/{common/widgets => }/app.dart | 23 +- .../pages/drug/widgets/adaptive_dialog.dart | 87 --- app/lib/common/routing/router.dart | 43 +- app/lib/common/utilities/drug_utils.dart | 11 +- app/lib/common/utilities/genome_data.dart | 4 +- app/lib/common/utilities/module.dart | 1 + app/lib/common/utilities/pdf_utils.dart | 3 +- app/lib/common/widgets/context_menu.dart | 4 +- .../drug_list/drug_items/drug_cards.dart | 4 +- app/lib/common/widgets/drug_search.dart | 16 +- app/lib/common/widgets/full_width_button.dart | 4 +- app/lib/common/widgets/headings.dart | 4 +- app/lib/common/widgets/module.dart | 1 - app/lib/common/widgets/subheader_divider.dart | 4 +- app/lib/{common/pages => }/drug/cubit.dart | 3 +- app/lib/drug/module.dart | 8 + .../pages/drug => drug/pages}/drug.dart | 9 +- .../drug/widgets/annotation_cards/drug.dart | 26 +- .../widgets/annotation_cards/guideline.dart | 2 +- .../pages => }/drug/widgets/disclaimer.dart | 5 +- .../pages => }/drug/widgets/module.dart | 1 + .../pages => }/drug/widgets/source_card.dart | 4 +- .../pages => }/drug/widgets/sub_header.dart | 5 +- .../pages => }/drug/widgets/tooltip_icon.dart | 0 app/lib/drug_selection/cubit.dart | 26 + app/lib/drug_selection/module.dart | 14 +- app/lib/drug_selection/pages/cubit.dart | 26 - .../drug_selection/pages/drug_selection.dart | 21 +- app/lib/faq/module.dart | 9 +- app/lib/faq/pages/faq.dart | 9 +- app/lib/login/{pages => }/cubit.dart | 28 +- app/lib/login/module.dart | 11 +- app/lib/login/pages/login.dart | 39 +- app/lib/main.dart | 1 + app/lib/main/module.dart | 10 + .../pages/main => main/pages}/main.dart | 17 +- app/lib/onboarding/module.dart | 11 +- app/lib/onboarding/pages/onboarding.dart | 7 +- app/lib/report/module.dart | 15 +- app/lib/report/pages/gene.dart | 4 +- app/lib/report/pages/report.dart | 5 +- app/lib/search/module.dart | 22 +- app/lib/search/pages/search.dart | 10 +- app/lib/settings/module.dart | 23 +- app/lib/settings/pages/about_us.dart | 3 +- app/lib/settings/pages/privacy_policy.dart | 3 +- app/lib/settings/pages/settings.dart | 30 +- .../settings/pages/terms_and_conditions.dart | 3 +- app/pubspec.lock | 728 ++++++++---------- app/pubspec.yaml | 36 +- 69 files changed, 682 insertions(+), 818 deletions(-) rename app/lib/{common/widgets => }/app.dart (74%) delete mode 100644 app/lib/common/pages/drug/widgets/adaptive_dialog.dart rename app/lib/{common/pages => }/drug/cubit.dart (93%) create mode 100644 app/lib/drug/module.dart rename app/lib/{common/pages/drug => drug/pages}/drug.dart (93%) rename app/lib/{common/pages => }/drug/widgets/annotation_cards/drug.dart (80%) rename app/lib/{common/pages => }/drug/widgets/annotation_cards/guideline.dart (99%) rename app/lib/{common/pages => }/drug/widgets/disclaimer.dart (89%) rename app/lib/{common/pages => }/drug/widgets/module.dart (82%) rename app/lib/{common/pages => }/drug/widgets/source_card.dart (91%) rename app/lib/{common/pages => }/drug/widgets/sub_header.dart (84%) rename app/lib/{common/pages => }/drug/widgets/tooltip_icon.dart (100%) create mode 100644 app/lib/drug_selection/cubit.dart delete mode 100644 app/lib/drug_selection/pages/cubit.dart rename app/lib/login/{pages => }/cubit.dart (76%) create mode 100644 app/lib/main/module.dart rename app/lib/{common/pages/main => main/pages}/main.dart (83%) diff --git a/app/CONTRIBUTING.md b/app/CONTRIBUTING.md index 395707cfa..417eb4d2b 100644 --- a/app/CONTRIBUTING.md +++ b/app/CONTRIBUTING.md @@ -11,8 +11,8 @@ Please also see the [contribution guide in the root folder](../CONTRIBUTING.md). - Run `dart pub get` to fetch all dart dependencies - Run `flutter pub get` to fetch all flutter dependencies and setup all generated code - - Run `flutter pub run build_runner build --delete-conflicting-outputs` or - `flutter pub run build_runner watch --delete-conflicting-outputs` to + - Run `dart run build_runner build --delete-conflicting-outputs` or + `dart run build_runner watch --delete-conflicting-outputs` to re-generate code upon file changes while developing You should now be able to run the app by opening the debug panel on the left and @@ -24,7 +24,7 @@ For (cleaning) generated code, you might want to add the following aliases to your shell configuration: ```bash -alias flutter-generate='flutter pub run build_runner build --delete-conflicting-outputs' +alias flutter-generate='dart run build_runner build --delete-conflicting-outputs' alias flutter-clean='find . -maxdepth 20 -type f \( -name "*.inject.summary" -o -name "*.inject.dart" -o -name "*.g.dart" \) -delete' ``` diff --git a/app/android/app/build.gradle b/app/android/app/build.gradle index e9a640652..f35701529 100644 --- a/app/android/app/build.gradle +++ b/app/android/app/build.gradle @@ -26,15 +26,17 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 33 + namespace 'de.hpi.pharme' + + compileSdk 33 compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '17' } sourceSets { @@ -42,8 +44,7 @@ android { } defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "de.hpi.pharme" + applicationId 'de.hpi.pharme' minSdkVersion 19 targetSdkVersion 33 multiDexEnabled true diff --git a/app/android/app/src/debug/AndroidManifest.xml b/app/android/app/src/debug/AndroidManifest.xml index d1df7c5b4..f880684a6 100644 --- a/app/android/app/src/debug/AndroidManifest.xml +++ b/app/android/app/src/debug/AndroidManifest.xml @@ -1,5 +1,4 @@ - + diff --git a/app/android/app/src/main/AndroidManifest.xml b/app/android/app/src/main/AndroidManifest.xml index d07f3150a..beab9a926 100644 --- a/app/android/app/src/main/AndroidManifest.xml +++ b/app/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,4 @@ - + diff --git a/app/android/app/src/profile/AndroidManifest.xml b/app/android/app/src/profile/AndroidManifest.xml index d1df7c5b4..f880684a6 100644 --- a/app/android/app/src/profile/AndroidManifest.xml +++ b/app/android/app/src/profile/AndroidManifest.xml @@ -1,5 +1,4 @@ - + diff --git a/app/android/build.gradle b/app/android/build.gradle index 76374445e..aca8b4a20 100644 --- a/app/android/build.gradle +++ b/app/android/build.gradle @@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.6.21' + ext.kotlin_version = '1.8.0' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:7.4.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -24,6 +24,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/app/android/gradle.properties b/app/android/gradle.properties index 94adc3a3f..b9a9a2464 100644 --- a/app/android/gradle.properties +++ b/app/android/gradle.properties @@ -1,3 +1,6 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true +android.defaults.buildfeatures.buildconfig=true +android.nonTransitiveRClass=false +android.nonFinalResIds=false diff --git a/app/android/gradle/wrapper/gradle-wrapper.properties b/app/android/gradle/wrapper/gradle-wrapper.properties index bc6a58afd..2d1906879 100644 --- a/app/android/gradle/wrapper/gradle-wrapper.properties +++ b/app/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Jun 23 08:50:38 CEST 2017 +#Tue Dec 19 14:27:03 CET 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/app/generate_screenshots/app_test.dart b/app/generate_screenshots/app_test.dart index c33e5014b..05964864c 100644 --- a/app/generate_screenshots/app_test.dart +++ b/app/generate_screenshots/app_test.dart @@ -3,6 +3,7 @@ import 'dart:io'; +import 'package:app/app.dart'; import 'package:app/common/module.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; diff --git a/app/generate_screenshots/test_driver.dart b/app/generate_screenshots/test_driver.dart index 5dee08a09..d7bd66f77 100644 --- a/app/generate_screenshots/test_driver.dart +++ b/app/generate_screenshots/test_driver.dart @@ -7,7 +7,7 @@ import 'package:integration_test/integration_test_driver_extended.dart'; Future main() async { try { await integrationDriver( - onScreenshot: (screenshotName, screenshotBytes) async { + onScreenshot: (screenshotName, screenshotBytes, [_]) async { final image = await File( '../docs/screenshots/$screenshotName.png' diff --git a/app/integration_test/drugs_test.dart b/app/integration_test/drugs_test.dart index bc87a03bd..f3e9ff85b 100644 --- a/app/integration_test/drugs_test.dart +++ b/app/integration_test/drugs_test.dart @@ -1,8 +1,8 @@ // ignore_for_file: cast_nullable_to_non_nullable import 'package:app/common/module.dart'; -import 'package:app/common/pages/drug/widgets/module.dart'; -import 'package:app/search/module.dart'; +import 'package:app/drug/module.dart'; +import 'package:app/drug/widgets/disclaimer.dart'; import 'package:bloc_test/bloc_test.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/app/integration_test/faq_test.dart b/app/integration_test/faq_test.dart index 57f91406c..d031ece8f 100644 --- a/app/integration_test/faq_test.dart +++ b/app/integration_test/faq_test.dart @@ -14,7 +14,7 @@ void main() { final faqWidget = MaterialApp.router( routeInformationParser: appRouter.defaultRouteParser(), routerDelegate: appRouter.delegate( - initialDeepLink: 'main/faq', + deepLinkBuilder: (_) => DeepLink.path('main/faq'), ), localizationsDelegates: [ AppLocalizations.delegate, diff --git a/app/integration_test/login_test.dart b/app/integration_test/login_test.dart index ed077c69b..76f9dbbfc 100644 --- a/app/integration_test/login_test.dart +++ b/app/integration_test/login_test.dart @@ -8,8 +8,8 @@ import 'package:integration_test/integration_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:provider/provider.dart'; -class MockLoginCubit extends MockCubit - implements LoginPageCubit {} +class MockLoginCubit extends MockCubit + implements LoginCubit {} void main() { final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -20,7 +20,7 @@ void main() { group('integration tests for the login page', () { testWidgets('test loading state', (tester) async { when(() => mockLoginCubit.state).thenReturn( - LoginPageState.loadingUserData(), + LoginState.loadingUserData(), ); await tester.pumpWidget( @@ -44,7 +44,7 @@ void main() { testWidgets('test error state', (tester) async { when(() => mockLoginCubit.state).thenReturn( - LoginPageState.error('Some error'), + LoginState.error('Some error'), ); await tester.pumpWidget( @@ -70,7 +70,7 @@ void main() { testWidgets('test loaded state', (tester) async { when(() => mockLoginCubit.state).thenReturn( - LoginPageState.loadedUserData(), + LoginState.loadedUserData(), ); await tester.pumpWidget( @@ -97,7 +97,7 @@ void main() { testWidgets('test initial state', (tester) async { when(() => mockLoginCubit.state).thenReturn( - LoginPageState.initial(), + LoginState.initial(), ); await tester.pumpWidget( diff --git a/app/integration_test/main_page_test.dart b/app/integration_test/main_page_test.dart index 992acb46f..d7d0c61d6 100644 --- a/app/integration_test/main_page_test.dart +++ b/app/integration_test/main_page_test.dart @@ -25,7 +25,7 @@ void main() { child: MaterialApp.router( routeInformationParser: appRouter.defaultRouteParser(), routerDelegate: appRouter.delegate( - initialDeepLink: 'main', + deepLinkBuilder: (_) => DeepLink.path('main'), ), localizationsDelegates: [ AppLocalizations.delegate, diff --git a/app/integration_test/onboarding_test.dart b/app/integration_test/onboarding_test.dart index 6fe261d95..10f1f4345 100644 --- a/app/integration_test/onboarding_test.dart +++ b/app/integration_test/onboarding_test.dart @@ -1,5 +1,5 @@ import 'package:app/common/module.dart'; -import 'package:app/onboarding/pages/onboarding.dart'; +import 'package:app/onboarding/module.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; diff --git a/app/integration_test/settings_test.dart b/app/integration_test/settings_test.dart index 8954b95e7..a8ee94e62 100644 --- a/app/integration_test/settings_test.dart +++ b/app/integration_test/settings_test.dart @@ -15,7 +15,7 @@ void main() { debugShowCheckedModeBanner: false, routeInformationParser: appRouter.defaultRouteParser(), routerDelegate: appRouter.delegate( - initialDeepLink: 'main/settings', + deepLinkBuilder: (_) => DeepLink.path('main/settings'), ), localizationsDelegates: [ AppLocalizations.delegate, @@ -65,7 +65,7 @@ void main() { findsOneWidget, ); - context.router.navigateBack(); + context.router.back(); await tester.pumpAndSettle(); // test privacy policy @@ -77,7 +77,7 @@ void main() { findsOneWidget, ); - context.router.navigateBack(); + context.router.back(); await tester.pumpAndSettle(); // test terms and conditions diff --git a/app/ios/Podfile.lock b/app/ios/Podfile.lock index 20a82c12e..df08f0612 100644 --- a/app/ios/Podfile.lock +++ b/app/ios/Podfile.lock @@ -2,24 +2,26 @@ PODS: - device_info_plus (0.0.1): - Flutter - Flutter (1.0.0) - - flutter_secure_storage (3.3.1): + - flutter_secure_storage (6.0.0): - Flutter - flutter_share (0.0.1): - Flutter - - flutter_web_auth (0.4.1): + - flutter_web_auth (0.5.0): - Flutter - integration_test (0.0.1): - Flutter - package_info_plus (0.4.5): - Flutter - - path_provider_ios (0.0.1): + - path_provider_foundation (0.0.1): - Flutter + - FlutterMacOS - printing (1.0.0): - Flutter - sensors_plus (0.0.1): - Flutter - - shared_preferences_ios (0.0.1): + - shared_preferences_foundation (0.0.1): - Flutter + - FlutterMacOS - url_launcher_ios (0.0.1): - Flutter @@ -31,10 +33,10 @@ DEPENDENCIES: - flutter_web_auth (from `.symlinks/plugins/flutter_web_auth/ios`) - integration_test (from `.symlinks/plugins/integration_test/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - printing (from `.symlinks/plugins/printing/ios`) - sensors_plus (from `.symlinks/plugins/sensors_plus/ios`) - - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`) + - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) EXTERNAL SOURCES: @@ -52,30 +54,30 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/integration_test/ios" package_info_plus: :path: ".symlinks/plugins/package_info_plus/ios" - path_provider_ios: - :path: ".symlinks/plugins/path_provider_ios/ios" + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/darwin" printing: :path: ".symlinks/plugins/printing/ios" sensors_plus: :path: ".symlinks/plugins/sensors_plus/ios" - shared_preferences_ios: - :path: ".symlinks/plugins/shared_preferences_ios/ios" + shared_preferences_foundation: + :path: ".symlinks/plugins/shared_preferences_foundation/darwin" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" SPEC CHECKSUMS: - device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed + device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 - flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec + flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be flutter_share: 4be0208963c60b537e6255ed2ce1faae61cd9ac2 - flutter_web_auth: 09a0abd245f1a07a3ff4dcf1247a048d89ee12a9 - integration_test: a1e7d09bd98eca2fc37aefd79d4f41ad37bdbbe5 - package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e - path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 - printing: eafa00acb682c0ca029d4d98d0798f55a1e27102 + flutter_web_auth: c25208760459cec375a3c39f6a8759165ca0fa4d + integration_test: 13825b8a9334a850581300559b8839134b124670 + package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 + path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 + printing: 233e1b73bd1f4a05615548e9b5a324c98588640b sensors_plus: 5717760720f7e6acd96fdbd75b7428f5ad755ec2 - shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad - url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de + shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 + url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 diff --git a/app/ios/Runner.xcodeproj/project.pbxproj b/app/ios/Runner.xcodeproj/project.pbxproj index 51341095d..c599db8d7 100644 --- a/app/ios/Runner.xcodeproj/project.pbxproj +++ b/app/ios/Runner.xcodeproj/project.pbxproj @@ -155,7 +155,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -226,6 +226,7 @@ files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( diff --git a/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 3db53b6e1..b52b2e698 100644 --- a/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ _instance; - PharMeApp._({Key? key}) : super(key: key); + PharMeApp._(); static final _instance = PharMeApp._(); static GlobalKey get navigatorKey => @@ -24,13 +23,17 @@ class PharMeApp extends StatelessWidget { debugShowCheckedModeBanner: false, routeInformationParser: _appRouter.defaultRouteParser(), routerDelegate: _appRouter.delegate( - initialDeepLink: !_isLoggedIn - ? 'login' - : !_onboardingDone - ? 'onboarding' - : !_initialDrugSelectionDone - ? 'drugselection' - : 'main', + deepLinkBuilder: (deepLink) { + late String path; + path = !_isLoggedIn + ? 'login' + : !_onboardingDone + ? 'onboarding' + : !_initialDrugSelectionDone + ? 'drugselection' + : 'main'; + return DeepLink.path(path); + }, ), theme: PharMeTheme.light, localizationsDelegates: [ diff --git a/app/lib/common/pages/drug/widgets/adaptive_dialog.dart b/app/lib/common/pages/drug/widgets/adaptive_dialog.dart deleted file mode 100644 index 48a7374a3..000000000 --- a/app/lib/common/pages/drug/widgets/adaptive_dialog.dart +++ /dev/null @@ -1,87 +0,0 @@ -import 'package:flutter/cupertino.dart'; - -import '../../../module.dart'; - -class AdaptiveAlertDialog extends StatelessWidget { - const AdaptiveAlertDialog({ - Key? key, - required this.title, - required this.content, - required this.actions, - }) : super(key: key); - - final String title; - final Widget? content; - final List actions; - - @override - Widget build(BuildContext context) { - switch (getPlatform()) { - case SupportedPlatform.android: - return AlertDialog( - title: Text(title), - content: content, - actions: actions, - ); - case SupportedPlatform.ios: - return CupertinoAlertDialog( - title: Text(title), - content: Card( - color: Colors.transparent, - elevation: 0, - child: content, - ), - actions: actions, - ); - } - } -} - -class AdaptiveDialogAction extends StatelessWidget { - const AdaptiveDialogAction({ - Key? key, - this.isDefault = false, - this.isDestructive = false, - this.onPressed, - required this.text, - }) : super(key: key); - - final bool isDefault; - final bool isDestructive; - final void Function()? onPressed; - final String text; - - @override - Widget build(BuildContext context) { - switch (getPlatform()) { - case SupportedPlatform.android: - return TextButton( - onPressed: onPressed, - child: Text(text, style: onPressed != null - ? isDestructive - ? TextStyle(color: PharMeTheme.errorColor) - : TextStyle(color: PharMeTheme.primaryColor) - : TextStyle(color: PharMeTheme.onSurfaceColor)), - ); - case SupportedPlatform.ios: - return CupertinoDialogAction( - isDefaultAction: isDefault, - isDestructiveAction: isDestructive, - onPressed: onPressed, - child: Text(text), - ); - } - } -} - -Future showAdaptiveDialog({ - required BuildContext context, - required Widget Function(BuildContext) builder, -}) { - switch (getPlatform()) { - case SupportedPlatform.android: - return showDialog(context: context, builder: builder); - case SupportedPlatform.ios: - return showCupertinoDialog(context: context, builder: builder); - } -} \ No newline at end of file diff --git a/app/lib/common/routing/router.dart b/app/lib/common/routing/router.dart index 2545fa7f3..a5c4eec40 100644 --- a/app/lib/common/routing/router.dart +++ b/app/lib/common/routing/router.dart @@ -1,31 +1,32 @@ -import '../../common/module.dart'; +// Imports for generated routes +import '../../drug/module.dart'; import '../../drug_selection/module.dart'; import '../../faq/module.dart'; import '../../login/module.dart'; +import '../../main/module.dart'; import '../../onboarding/module.dart'; + import '../../report/module.dart'; import '../../search/module.dart'; import '../../settings/module.dart'; -import '../pages/main/main.dart'; +import '../module.dart'; part 'router.gr.dart'; -@MaterialAutoRouter( - replaceInRouteName: 'Page,Route', - routes: [ - drugSelectionRoutes, - loginRoutes, - onboardingRoutes, - AutoRoute( - path: 'main', - page: MainPage, - children: [ - reportRoutes, - searchRoutes, - settingsRoutes, - faqRoutes, - ], - ), - ], -) -class AppRouter extends _$AppRouter {} +@AutoRouterConfig() +class AppRouter extends _$AppRouter { + @override + RouteType get defaultRouteType => RouteType.adaptive(); + @override + List get routes => [ + drugSelectionRoute, + loginRoute, + onboardingRoute, + mainRoute(children: [ + reportRoute, + searchRoute, + settingsRoute, + faqRoute, + ]), + ]; +} diff --git a/app/lib/common/utilities/drug_utils.dart b/app/lib/common/utilities/drug_utils.dart index 9cd910351..ee638dc38 100644 --- a/app/lib/common/utilities/drug_utils.dart +++ b/app/lib/common/utilities/drug_utils.dart @@ -1,9 +1,9 @@ import 'dart:convert'; import 'package:http/http.dart'; +import '../../app.dart'; import '../models/drug/cached_drugs.dart'; import '../module.dart'; -import '../pages/drug/widgets/adaptive_dialog.dart'; Future updateCachedDrugs() async { if (UserData.instance.lookups == null) throw Exception(); @@ -33,14 +33,13 @@ Future updateCachedDrugs() async { // ignore: use_build_context_synchronously await showAdaptiveDialog( context: context, - builder: (context) => AdaptiveAlertDialog( - title: context.l10n.update_warning_title, + builder: (context) => AlertDialog.adaptive( + title: Text(context.l10n.update_warning_title), content: Text(context.l10n.update_warning_body), actions: [ - AdaptiveDialogAction( - isDefault: true, + TextButton( onPressed: () => Navigator.pop(context), - text: context.l10n.action_continue, + child: Text(context.l10n.action_continue), ), ], ), diff --git a/app/lib/common/utilities/genome_data.dart b/app/lib/common/utilities/genome_data.dart index 1f8d5f58e..b0478c701 100644 --- a/app/lib/common/utilities/genome_data.dart +++ b/app/lib/common/utilities/genome_data.dart @@ -31,7 +31,9 @@ Future _saveDiplotypeAndActiveDrugsResponse( diplotypesFromHTTPResponse(response).filterValidDiplotypes(); final activeDrugList = activeDrugsFromHTTPResponse(response); - UserData.instance.diplotypes = {for (var d in diplotypes) d.gene: d}; + UserData.instance.diplotypes = { + for (final diplotype in diplotypes) diplotype.gene: diplotype + }; await UserData.save(); await activeDrugs.setList(activeDrugList); // invalidate cached drugs because lookups may have changed and we need to diff --git a/app/lib/common/utilities/module.dart b/app/lib/common/utilities/module.dart index b7529502e..118fa982f 100644 --- a/app/lib/common/utilities/module.dart +++ b/app/lib/common/utilities/module.dart @@ -2,6 +2,7 @@ export 'drug_utils.dart'; export 'genome_data.dart'; export 'material_colors.dart'; export 'networking_utils.dart'; +export 'pdf_utils.dart'; export 'platform_utils.dart'; export 'routing_utils.dart'; export 'string_utils.dart'; diff --git a/app/lib/common/utilities/pdf_utils.dart b/app/lib/common/utilities/pdf_utils.dart index 4d47b4598..750a84494 100644 --- a/app/lib/common/utilities/pdf_utils.dart +++ b/app/lib/common/utilities/pdf_utils.dart @@ -332,8 +332,7 @@ List _buildGuidelinePart( implication.key ), text: implication.value, - ))) - .toList(), + ))), _buildTextSpacer(), _PdfSegment( child: _PdfDescription( diff --git a/app/lib/common/widgets/context_menu.dart b/app/lib/common/widgets/context_menu.dart index 70ee1a910..bfab2118c 100644 --- a/app/lib/common/widgets/context_menu.dart +++ b/app/lib/common/widgets/context_menu.dart @@ -102,7 +102,7 @@ class ContextMenuCheckmark extends StatelessWidget { this.initialState = false}); final String label; - final void Function(bool) setState; + final void Function({ required bool value }) setState; final bool initialState; @override @@ -113,7 +113,7 @@ class ContextMenuCheckmark extends StatelessWidget { onTap: () { rebuild(() { state = !state; - setState(state); + setState(value: state); }); }, child: Row( diff --git a/app/lib/common/widgets/drug_list/drug_items/drug_cards.dart b/app/lib/common/widgets/drug_list/drug_items/drug_cards.dart index f44e850ab..a53cad8df 100644 --- a/app/lib/common/widgets/drug_list/drug_items/drug_cards.dart +++ b/app/lib/common/widgets/drug_list/drug_items/drug_cards.dart @@ -33,8 +33,8 @@ class DrugCard extends StatelessWidget { required this.onTap, required this.drug, required this.showDrugInteractionIndicator, - Key? key, - }) : super(key: key); + super.key, + }); final VoidCallback onTap; final Drug drug; diff --git a/app/lib/common/widgets/drug_search.dart b/app/lib/common/widgets/drug_search.dart index fdff3a40c..35c006464 100644 --- a/app/lib/common/widgets/drug_search.dart +++ b/app/lib/common/widgets/drug_search.dart @@ -3,11 +3,11 @@ import 'package:flutter/cupertino.dart'; import 'package:provider/provider.dart'; import '../../../common/module.dart'; -import '../../common/pages/drug/widgets/tooltip_icon.dart'; +import '../../drug/widgets/tooltip_icon.dart'; class DrugSearch extends HookWidget { DrugSearch({ - Key? key, + super.key, required this.showFilter, required this.buildDrugItems, required this.showDrugInteractionIndicator, @@ -15,8 +15,7 @@ class DrugSearch extends HookWidget { this.keepPosition = false, this.drugItemsBuildParams, DrugListCubit? cubit, - }) : cubit = cubit ?? DrugListCubit(), - super(key: key); + }) : cubit = cubit ?? DrugListCubit(); final bool showFilter; final bool useDrugClass; @@ -111,7 +110,7 @@ class DrugSearch extends HookWidget { label: context.l10n.search_page_filter_only_active, // Invert state as filter has opposite meaning ('only show active' vs. // 'show inactive') - setState: (state) => cubit.search(showInactive: !state), + setState: ({ required value }) => cubit.search(showInactive: !value), initialState: filter != null && !filter.showInactive), ...WarningLevel.values.filter((level) => level != WarningLevel.none) .map((level) => ContextMenuCheckmark( @@ -120,7 +119,8 @@ class DrugSearch extends HookWidget { WarningLevel.yellow: context.l10n.search_page_filter_yellow, WarningLevel.red: context.l10n.search_page_filter_red, }[level]!, - setState: (state) => cubit.search(showWarningLevel: {level: state}), + setState: ({ required value }) => + cubit.search(showWarningLevel: { level: value }), initialState: filter?.showWarningLevel[level] ?? false ) ), @@ -128,8 +128,8 @@ class DrugSearch extends HookWidget { label: context.l10n.search_page_filter_only_with_guidelines, // Invert state as filter has opposite meaning ('show only with // guidelines' vs. 'show with unknown warning level') - setState: (state) => cubit.search( - showWarningLevel: {WarningLevel.none: !state} + setState: ({ required value }) => cubit.search( + showWarningLevel: { WarningLevel.none: !value } ), initialState: filter != null && !filter.showWarningLevel[WarningLevel.none]!,) diff --git a/app/lib/common/widgets/full_width_button.dart b/app/lib/common/widgets/full_width_button.dart index 79022d9d2..8d5d0c0b8 100644 --- a/app/lib/common/widgets/full_width_button.dart +++ b/app/lib/common/widgets/full_width_button.dart @@ -4,9 +4,9 @@ class FullWidthButton extends StatelessWidget { const FullWidthButton( this.text, this.action, { - Key? key, + super.key, this.enabled = true, - }) : super(key: key); + }); final bool enabled; final String text; diff --git a/app/lib/common/widgets/headings.dart b/app/lib/common/widgets/headings.dart index ec83f8a33..8160c8623 100644 --- a/app/lib/common/widgets/headings.dart +++ b/app/lib/common/widgets/headings.dart @@ -5,8 +5,8 @@ import '../theme.dart'; class Heading extends StatelessWidget { const Heading( this.text, { - Key? key, - }) : super(key: key); + super.key, + }); final String text; diff --git a/app/lib/common/widgets/module.dart b/app/lib/common/widgets/module.dart index 70359f7f3..6f4f4c1ac 100644 --- a/app/lib/common/widgets/module.dart +++ b/app/lib/common/widgets/module.dart @@ -1,4 +1,3 @@ -export 'app.dart'; export 'context_menu.dart'; export 'drug_list/builder.dart'; export 'drug_list/cubit.dart'; diff --git a/app/lib/common/widgets/subheader_divider.dart b/app/lib/common/widgets/subheader_divider.dart index bddbaee93..c1791c0a5 100644 --- a/app/lib/common/widgets/subheader_divider.dart +++ b/app/lib/common/widgets/subheader_divider.dart @@ -5,8 +5,8 @@ class SubheaderDivider extends StatelessWidget { this.text, { this.indent = 20.0, this.color, - Key? key, - }) : super(key: key); + super.key, + }); final String text; final double indent; diff --git a/app/lib/common/pages/drug/cubit.dart b/app/lib/drug/cubit.dart similarity index 93% rename from app/lib/common/pages/drug/cubit.dart rename to app/lib/drug/cubit.dart index 1a92b593a..a2a39cd3b 100644 --- a/app/lib/common/pages/drug/cubit.dart +++ b/app/lib/drug/cubit.dart @@ -3,8 +3,7 @@ import 'dart:async'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:overlay_dialog/overlay_dialog.dart'; -import '../../module.dart'; -import '../../utilities/pdf_utils.dart'; +import '../common/module.dart'; part 'cubit.freezed.dart'; diff --git a/app/lib/drug/module.dart b/app/lib/drug/module.dart new file mode 100644 index 000000000..ea729c361 --- /dev/null +++ b/app/lib/drug/module.dart @@ -0,0 +1,8 @@ +import '../common/module.dart'; + +// For generated routes +export 'cubit.dart'; +export 'pages/drug.dart'; + +// Used by multiple parent routes, therefore returning function for creation +AutoRoute drugRoute() => AutoRoute(page: DrugRoute.page); \ No newline at end of file diff --git a/app/lib/common/pages/drug/drug.dart b/app/lib/drug/pages/drug.dart similarity index 93% rename from app/lib/common/pages/drug/drug.dart rename to app/lib/drug/pages/drug.dart index 1ffed21d2..bf042e2c0 100644 --- a/app/lib/common/pages/drug/drug.dart +++ b/app/lib/drug/pages/drug.dart @@ -1,9 +1,10 @@ import 'package:provider/provider.dart'; -import '../../module.dart'; -import 'cubit.dart'; -import 'widgets/module.dart'; +import '../../common/module.dart'; +import '../cubit.dart'; +import '../widgets/module.dart'; +@RoutePage() class DrugPage extends StatelessWidget { const DrugPage( this.drug, { @@ -54,7 +55,7 @@ class DrugPage extends StatelessWidget { DrugAnnotationCard( drug, isActive: drug.isActive, - setActivity: (value) => + setActivity: ({ value }) => context.read().setActivity(drug, value), disabled: loading, ), diff --git a/app/lib/common/pages/drug/widgets/annotation_cards/drug.dart b/app/lib/drug/widgets/annotation_cards/drug.dart similarity index 80% rename from app/lib/common/pages/drug/widgets/annotation_cards/drug.dart rename to app/lib/drug/widgets/annotation_cards/drug.dart index 06289ca16..c75e74a29 100644 --- a/app/lib/common/pages/drug/widgets/annotation_cards/drug.dart +++ b/app/lib/drug/widgets/annotation_cards/drug.dart @@ -1,5 +1,4 @@ -import '../../../../module.dart'; -import '../adaptive_dialog.dart'; +import '../../../common/module.dart'; import '../sub_header.dart'; class DrugAnnotationCard extends StatelessWidget { @@ -12,7 +11,7 @@ class DrugAnnotationCard extends StatelessWidget { final Drug drug; final bool isActive; - final void Function(bool?) setActivity; + final void Function({ bool? value }) setActivity; final bool disabled; @override @@ -53,28 +52,29 @@ class DrugAnnotationCard extends StatelessWidget { if (isInhibitor(drug.name)) { showAdaptiveDialog( context: context, - builder: (context) => AdaptiveAlertDialog( - title: context.l10n.drugs_page_active_warn_header, + builder: (context) => AlertDialog.adaptive( + title: Text(context.l10n.drugs_page_active_warn_header), content: Text(context.l10n.drugs_page_active_warn), actions: [ - AdaptiveDialogAction( - isDefault: true, + TextButton( onPressed: () => Navigator.pop(context, 'Cancel'), - text: context.l10n.action_cancel, + child: Text(context.l10n.action_cancel), ), - AdaptiveDialogAction( - isDestructive: true, + TextButton( onPressed: () { Navigator.pop(context, 'OK'); - setActivity(newValue); + setActivity(value: newValue); }, - text: context.l10n.action_continue, + child: Text( + context.l10n.action_continue, + style: TextStyle(color: PharMeTheme.errorColor), + ), ), ], ), ) } else { - setActivity(newValue) + setActivity(value: newValue) } }, controlAffinity: ListTileControlAffinity.leading, diff --git a/app/lib/common/pages/drug/widgets/annotation_cards/guideline.dart b/app/lib/drug/widgets/annotation_cards/guideline.dart similarity index 99% rename from app/lib/common/pages/drug/widgets/annotation_cards/guideline.dart rename to app/lib/drug/widgets/annotation_cards/guideline.dart index 9eb3b6c31..8891cde53 100644 --- a/app/lib/common/pages/drug/widgets/annotation_cards/guideline.dart +++ b/app/lib/drug/widgets/annotation_cards/guideline.dart @@ -1,6 +1,6 @@ import 'package:url_launcher/url_launcher.dart'; -import '../../../../module.dart'; +import '../../../common/module.dart'; import '../sub_header.dart'; class GuidelineAnnotationCard extends StatelessWidget { diff --git a/app/lib/common/pages/drug/widgets/disclaimer.dart b/app/lib/drug/widgets/disclaimer.dart similarity index 89% rename from app/lib/common/pages/drug/widgets/disclaimer.dart rename to app/lib/drug/widgets/disclaimer.dart index bff430eab..383b18179 100644 --- a/app/lib/common/pages/drug/widgets/disclaimer.dart +++ b/app/lib/drug/widgets/disclaimer.dart @@ -1,7 +1,4 @@ -import 'package:flutter/material.dart'; - -import '../../../l10n.dart'; -import '../../../theme.dart'; +import '../../common/module.dart'; class Disclaimer extends StatelessWidget { const Disclaimer({this.text}); diff --git a/app/lib/common/pages/drug/widgets/module.dart b/app/lib/drug/widgets/module.dart similarity index 82% rename from app/lib/common/pages/drug/widgets/module.dart rename to app/lib/drug/widgets/module.dart index a95ff23a8..e01e7d6ec 100644 --- a/app/lib/common/pages/drug/widgets/module.dart +++ b/app/lib/drug/widgets/module.dart @@ -2,3 +2,4 @@ export 'annotation_cards/drug.dart'; export 'annotation_cards/guideline.dart'; export 'disclaimer.dart'; export 'sub_header.dart'; +export 'tooltip_icon.dart'; diff --git a/app/lib/common/pages/drug/widgets/source_card.dart b/app/lib/drug/widgets/source_card.dart similarity index 91% rename from app/lib/common/pages/drug/widgets/source_card.dart rename to app/lib/drug/widgets/source_card.dart index e2d284756..cf82036a5 100644 --- a/app/lib/common/pages/drug/widgets/source_card.dart +++ b/app/lib/drug/widgets/source_card.dart @@ -1,7 +1,5 @@ -import 'package:auto_size_text/auto_size_text.dart'; -import 'package:flutter/material.dart'; +import '../../common/module.dart'; -import '../../../theme.dart'; class SourceCard extends StatelessWidget { const SourceCard({ diff --git a/app/lib/common/pages/drug/widgets/sub_header.dart b/app/lib/drug/widgets/sub_header.dart similarity index 84% rename from app/lib/common/pages/drug/widgets/sub_header.dart rename to app/lib/drug/widgets/sub_header.dart index 5f589f960..ea3913bb4 100644 --- a/app/lib/common/pages/drug/widgets/sub_header.dart +++ b/app/lib/drug/widgets/sub_header.dart @@ -1,7 +1,4 @@ -import 'package:dartx/dartx.dart'; -import 'package:flutter/material.dart'; - -import '../../../theme.dart'; +import '../../common/module.dart'; import 'tooltip_icon.dart'; class SubHeader extends StatelessWidget { diff --git a/app/lib/common/pages/drug/widgets/tooltip_icon.dart b/app/lib/drug/widgets/tooltip_icon.dart similarity index 100% rename from app/lib/common/pages/drug/widgets/tooltip_icon.dart rename to app/lib/drug/widgets/tooltip_icon.dart diff --git a/app/lib/drug_selection/cubit.dart b/app/lib/drug_selection/cubit.dart new file mode 100644 index 000000000..4d0af2c30 --- /dev/null +++ b/app/lib/drug_selection/cubit.dart @@ -0,0 +1,26 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +import '../../common/module.dart'; + +part 'cubit.freezed.dart'; + +class DrugSelectionCubit extends Cubit { + DrugSelectionCubit(this.activeDrugs) : + super(DrugSelectionState.stable()); + + final ActiveDrugs activeDrugs; + + // ignore: avoid_positional_boolean_parameters + Future updateDrugActivity(Drug drug, bool? value) async { + if (value == null) return; + emit(DrugSelectionState.updating()); + await activeDrugs.changeActivity(drug.name, value); + emit(DrugSelectionState.stable()); + } +} + +@freezed +class DrugSelectionState with _$DrugSelectionState { + const factory DrugSelectionState.stable() = _StableState; + const factory DrugSelectionState.updating() = _UpdatingState; +} diff --git a/app/lib/drug_selection/module.dart b/app/lib/drug_selection/module.dart index 2a63e7106..fd7d514c5 100644 --- a/app/lib/drug_selection/module.dart +++ b/app/lib/drug_selection/module.dart @@ -1,12 +1,10 @@ import '../common/module.dart'; -import 'pages/drug_selection.dart'; -// We need to expose all pages for AutoRouter -export 'pages/cubit.dart'; +// For generated route +export 'cubit.dart'; export 'pages/drug_selection.dart'; -const drugSelectionRoutes = AutoRoute( - path: 'drugselection', - name: 'DrugSelectionRouter', - page: DrugSelectionPage, -); +final drugSelectionRoute = AutoRoute( + path: '/drugselection', + page: DrugSelectionRoute.page, +); \ No newline at end of file diff --git a/app/lib/drug_selection/pages/cubit.dart b/app/lib/drug_selection/pages/cubit.dart deleted file mode 100644 index 2ae4f0e2d..000000000 --- a/app/lib/drug_selection/pages/cubit.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -import '../../../common/module.dart'; - -part 'cubit.freezed.dart'; - -class DrugSelectionPageCubit extends Cubit { - DrugSelectionPageCubit(this.activeDrugs) : - super(DrugSelectionPageState.stable()); - - final ActiveDrugs activeDrugs; - - // ignore: avoid_positional_boolean_parameters - Future updateDrugActivity(Drug drug, bool? value) async { - if (value == null) return; - emit(DrugSelectionPageState.updating()); - await activeDrugs.changeActivity(drug.name, value); - emit(DrugSelectionPageState.stable()); - } -} - -@freezed -class DrugSelectionPageState with _$DrugSelectionPageState { - const factory DrugSelectionPageState.stable() = _StableState; - const factory DrugSelectionPageState.updating() = _UpdatingState; -} diff --git a/app/lib/drug_selection/pages/drug_selection.dart b/app/lib/drug_selection/pages/drug_selection.dart index d4d07a6fe..f2ee0a14d 100644 --- a/app/lib/drug_selection/pages/drug_selection.dart +++ b/app/lib/drug_selection/pages/drug_selection.dart @@ -6,24 +6,25 @@ import '../../common/module.dart' hide MetaData; import '../../common/widgets/drug_list/drug_items/drug_checkbox_list.dart'; import '../../common/widgets/drug_search.dart'; import '../../common/widgets/full_width_button.dart'; -import 'cubit.dart'; +import '../cubit.dart'; +@RoutePage() class DrugSelectionPage extends HookWidget { const DrugSelectionPage({ - Key? key, + super.key, this.concludesOnboarding = true, @visibleForTesting this.cubit, - }) : super(key: key); + }); - final DrugSelectionPageCubit? cubit; + final DrugSelectionCubit? cubit; final bool concludesOnboarding; @override Widget build(BuildContext context) { return Consumer( builder: (context, activeDrugs, child) => BlocProvider( - create: (context) => cubit ?? DrugSelectionPageCubit(activeDrugs), - child: BlocBuilder( + create: (context) => cubit ?? DrugSelectionCubit(activeDrugs), + child: BlocBuilder( builder: (context, state) { return unscrollablePageScaffold( title: context.l10n.drug_selection_header, @@ -44,14 +45,14 @@ class DrugSelectionPage extends HookWidget { ); } - bool _isEditable(DrugSelectionPageState state) { + bool _isEditable(DrugSelectionState state) { return state.when( stable: () => true, updating: () => false ); } - Widget _buildButton(BuildContext context, DrugSelectionPageState state) { + Widget _buildButton(BuildContext context, DrugSelectionState state) { return Padding( padding: EdgeInsets.only(top: PharMeTheme.mediumSpace), child: FullWidthButton( @@ -67,7 +68,7 @@ class DrugSelectionPage extends HookWidget { ); } - Widget _buildDrugList(BuildContext context, DrugSelectionPageState state) { + Widget _buildDrugList(BuildContext context, DrugSelectionState state) { if (CachedDrugs.instance.drugs!.isEmpty) { return Column( children: [ @@ -88,7 +89,7 @@ class DrugSelectionPage extends HookWidget { drugItemsBuildParams: { 'checkboxesEnabled': _isEditable(state), 'onCheckboxChange': (drug, value) => context - .read() + .read() .updateDrugActivity(drug, value), }, showDrugInteractionIndicator: false, diff --git a/app/lib/faq/module.dart b/app/lib/faq/module.dart index 804e253c6..58fe08c8b 100644 --- a/app/lib/faq/module.dart +++ b/app/lib/faq/module.dart @@ -1,11 +1,6 @@ import '../common/module.dart'; -import 'pages/faq.dart'; -// We need to expose all pages for AutoRouter +// For generated routes export 'pages/faq.dart'; -const faqRoutes = AutoRoute( - path: 'faq', - name: 'FaqRouter', - page: FaqPage, -); +final faqRoute = AutoRoute(page: FaqRoute.page); \ No newline at end of file diff --git a/app/lib/faq/pages/faq.dart b/app/lib/faq/pages/faq.dart index cdc7f668b..dc8124445 100644 --- a/app/lib/faq/pages/faq.dart +++ b/app/lib/faq/pages/faq.dart @@ -1,12 +1,14 @@ import '../../common/module.dart'; import '../constants.dart'; +@RoutePage() class FaqPage extends StatelessWidget { - const FaqPage({Key? key}) : super(key: key); + const FaqPage({super.key}); @override Widget build(BuildContext context) { - return WillPopScope( + return PopScope( + canPop: false, child: pageScaffold(title: context.l10n.tab_faq, body: [ Padding( padding: const EdgeInsets.all(8), @@ -27,7 +29,6 @@ class FaqPage extends StatelessWidget { ), ), ]), - onWillPop: () async => false, ); } @@ -44,7 +45,7 @@ class FaqPage extends StatelessWidget { ), dense: true, ), - ...questions.map((question) => _buildQuestion(context, question)).toList() + ...questions.map((question) => _buildQuestion(context, question)) ]; } diff --git a/app/lib/login/pages/cubit.dart b/app/lib/login/cubit.dart similarity index 76% rename from app/lib/login/pages/cubit.dart rename to app/lib/login/cubit.dart index b360f7732..b7f806c8f 100644 --- a/app/lib/login/pages/cubit.dart +++ b/app/lib/login/cubit.dart @@ -5,22 +5,22 @@ import 'package:flutter_web_auth/flutter_web_auth.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:http/http.dart' as http; -import '../../../common/module.dart'; -import '../models/lab.dart'; +import '../../common/module.dart'; +import 'models/lab.dart'; part 'cubit.freezed.dart'; -class LoginPageCubit extends Cubit { - LoginPageCubit(this.activeDrugs): super(LoginPageState.initial()); +class LoginCubit extends Cubit { + LoginCubit(this.activeDrugs): super(LoginState.initial()); ActiveDrugs activeDrugs; - void revertToInitialState() => emit(LoginPageState.initial()); + void revertToInitialState() => emit(LoginState.initial()); // signInAndLoadUserData authenticates a user with a Lab and fetches their // genomic data from it's endpoint. Future signInAndLoadUserData(BuildContext context, Lab lab) async { - emit(LoginPageState.loadingUserData()); + emit(LoginState.loadingUserData()); // authenticate String? token; @@ -38,7 +38,7 @@ class LoginPageCubit extends Cubit { } if (token == null) { - emit(LoginPageState.error( + emit(LoginState.error( // ignore: use_build_context_synchronously context.l10n.err_could_not_retrieve_access_token, )); @@ -56,10 +56,10 @@ class LoginPageCubit extends Cubit { // login + fetching of data successful MetaData.instance.isLoggedIn = true; await MetaData.save(); - emit(LoginPageState.loadedUserData()); + emit(LoginState.loadedUserData()); } catch (e) { // ignore: use_build_context_synchronously - emit(LoginPageState.error(context.l10n.err_fetch_user_data_failed)); + emit(LoginState.error(context.l10n.err_fetch_user_data_failed)); } } @@ -102,9 +102,9 @@ class LoginPageCubit extends Cubit { } @freezed -class LoginPageState with _$LoginPageState { - const factory LoginPageState.initial() = _InitialState; - const factory LoginPageState.loadingUserData() = _LoadingUserDataState; - const factory LoginPageState.loadedUserData() = _LoadedUserDataState; - const factory LoginPageState.error(String string) = _ErrorState; +class LoginState with _$LoginState { + const factory LoginState.initial() = _InitialState; + const factory LoginState.loadingUserData() = _LoadingUserDataState; + const factory LoginState.loadedUserData() = _LoadedUserDataState; + const factory LoginState.error(String string) = _ErrorState; } diff --git a/app/lib/login/module.dart b/app/lib/login/module.dart index 5fbae01e8..b9e187a15 100644 --- a/app/lib/login/module.dart +++ b/app/lib/login/module.dart @@ -1,12 +1,7 @@ import '../common/module.dart'; -import 'pages/login.dart'; -// We need to expose all pages for AutoRouter -export 'pages/cubit.dart'; +// For generated route +export 'cubit.dart'; export 'pages/login.dart'; -const loginRoutes = AutoRoute( - path: 'login', - name: 'LoginRouter', - page: LoginPage, -); +final loginRoute = AutoRoute(path: '/login', page: LoginRoute.page); \ No newline at end of file diff --git a/app/lib/login/pages/login.dart b/app/lib/login/pages/login.dart index 90f5f5deb..30b8ba6d6 100644 --- a/app/lib/login/pages/login.dart +++ b/app/lib/login/pages/login.dart @@ -3,16 +3,17 @@ import 'package:provider/provider.dart'; import '../../../common/module.dart'; import '../../common/widgets/full_width_button.dart'; +import '../cubit.dart'; import '../models/lab.dart'; -import 'cubit.dart'; +@RoutePage() class LoginPage extends HookWidget { const LoginPage({ - Key? key, + super.key, @visibleForTesting this.cubit, - }) : super(key: key); + }); - final LoginPageCubit? cubit; + final LoginCubit? cubit; @override Widget build(BuildContext context) { @@ -20,8 +21,8 @@ class LoginPage extends HookWidget { return Consumer( builder: (context, activeDrugs, child) => BlocProvider( - create: (context) => cubit ?? LoginPageCubit(activeDrugs), - child: BlocBuilder( + create: (context) => cubit ?? LoginCubit(activeDrugs), + child: BlocBuilder( builder: (context, state) { return unscrollablePageScaffold( padding: PharMeTheme.largeSpace, @@ -66,7 +67,7 @@ class LoginPage extends HookWidget { (el) => el.name == dropdownValue.value, ); await context - .read() + .read() .signInAndLoadUserData(context, selectedLab); } @@ -87,7 +88,12 @@ class LoginPage extends HookWidget { DropdownButtonHideUnderline( child: DropdownButton2( isExpanded: true, - dropdownOverButton: true, + dropdownStyleData: DropdownStyleData( + isOverButton: true, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + ), + ), hint: Text(context.l10n.auth_choose_lab), value: dropdownValue.value, onChanged: (value) { @@ -99,13 +105,12 @@ class LoginPage extends HookWidget { child: Text(lab.name), )) .toList(), - buttonPadding: const EdgeInsets.only(left: 16, right: 16), - buttonDecoration: BoxDecoration( - borderRadius: BorderRadius.circular(32), - border: Border.all(color: PharMeTheme.borderColor), - ), - dropdownDecoration: BoxDecoration( - borderRadius: BorderRadius.circular(16), + buttonStyleData: ButtonStyleData( + padding: const EdgeInsets.only(left: 16, right: 16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(32), + border: Border.all(color: PharMeTheme.borderColor), + ), ), ), ), @@ -117,7 +122,7 @@ class LoginPage extends HookWidget { return _buildColumnWrapper( action: () => overwriteRoutes( context, - nextPage: OnboardingRouter(), + nextPage: OnboardingRoute(), ), actionText: context.l10n.general_continue, children: [ @@ -138,7 +143,7 @@ class LoginPage extends HookWidget { Widget _buildErrorScreen(BuildContext context, String message) { return _buildColumnWrapper( - action: () => context.read().revertToInitialState(), + action: () => context.read().revertToInitialState(), actionText: context.l10n.general_retry, children: [ Icon( diff --git a/app/lib/main.dart b/app/lib/main.dart index 9a4ad8695..bfb574653 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -1,5 +1,6 @@ import 'package:provider/provider.dart'; +import 'app.dart'; import 'common/module.dart'; Future main() async { diff --git a/app/lib/main/module.dart b/app/lib/main/module.dart new file mode 100644 index 000000000..d3d6986ac --- /dev/null +++ b/app/lib/main/module.dart @@ -0,0 +1,10 @@ +import '../common/module.dart'; + +// For generated route +export 'pages/main.dart'; + +AutoRoute mainRoute({ required List children }) => AutoRoute( + path: '/main', + page: MainRoute.page, + children: children, +); \ No newline at end of file diff --git a/app/lib/common/pages/main/main.dart b/app/lib/main/pages/main.dart similarity index 83% rename from app/lib/common/pages/main/main.dart rename to app/lib/main/pages/main.dart index 904a16851..1ffad4014 100644 --- a/app/lib/common/pages/main/main.dart +++ b/app/lib/main/pages/main.dart @@ -1,8 +1,4 @@ -import 'package:auto_route/auto_route.dart'; -import 'package:flutter/material.dart'; - -import '../../l10n.dart'; -import '../../routing/router.dart'; +import '../../common/module.dart'; class TabRouteDefinition { TabRouteDefinition({ @@ -18,30 +14,31 @@ class TabRouteDefinition { List getTabRoutesDefinition(BuildContext context) { return [ TabRouteDefinition( - pageRouteInfo: ReportRouter(), + pageRouteInfo: ReportRoute(), label: context.l10n.nav_report, icon: Icon(Icons.summarize_rounded), ), TabRouteDefinition( - pageRouteInfo: SearchRouter(), + pageRouteInfo: SearchRoute(), label: context.l10n.nav_drugs, icon: Icon(Icons.medication_rounded), ), TabRouteDefinition( - pageRouteInfo: FaqRouter(), + pageRouteInfo: FaqRoute(), label: context.l10n.nav_faq, icon: Icon(Icons.lightbulb_rounded), ), TabRouteDefinition( - pageRouteInfo: SettingsRouter(), + pageRouteInfo: SettingsRoute(), label: context.l10n.nav_more, icon: Icon(Icons.more_horiz_rounded), ), ]; } +@RoutePage() class MainPage extends StatelessWidget { - const MainPage({Key? key}) : super(key: key); + const MainPage({super.key}); @override Widget build(BuildContext context) { diff --git a/app/lib/onboarding/module.dart b/app/lib/onboarding/module.dart index e01f2332a..506f8d391 100644 --- a/app/lib/onboarding/module.dart +++ b/app/lib/onboarding/module.dart @@ -1,10 +1,9 @@ import '../common/module.dart'; -import 'pages/onboarding.dart'; +// For generated route export 'pages/onboarding.dart'; -const onboardingRoutes = AutoRoute( - path: 'onboarding', - name: 'OnboardingRouter', - page: OnboardingPage, -); +final onboardingRoute = AutoRoute( + path: '/onboarding', + page: OnboardingRoute.page, +); \ No newline at end of file diff --git a/app/lib/onboarding/pages/onboarding.dart b/app/lib/onboarding/pages/onboarding.dart index 158cdffa4..5763ae8ff 100644 --- a/app/lib/onboarding/pages/onboarding.dart +++ b/app/lib/onboarding/pages/onboarding.dart @@ -1,6 +1,7 @@ import '../../../common/module.dart' hide MetaData; import '../../common/models/metadata.dart'; +@RoutePage() class OnboardingPage extends HookWidget { OnboardingPage({ this.isRevisiting = false }); @@ -95,7 +96,7 @@ class OnboardingPage extends HookWidget { right: PharMeTheme.mediumToLargeSpace, child: IconButton( icon: Icon(Icons.close, size: 32, color: Colors.white,), - onPressed: () => context.navigateBack(), + onPressed: () => context.router.back(), ) ), Positioned( @@ -167,13 +168,13 @@ class OnboardingPage extends HookWidget { onPressed: () async { if (isLastPage) { if (isRevisiting) { - context.router.navigateBack(); + context.router.back(); } else { MetaData.instance.onboardingDone = true; await MetaData.save(); // ignore: use_build_context_synchronously await context.router.push( - DrugSelectionRouter(concludesOnboarding: true) + DrugSelectionRoute(concludesOnboarding: true) ); } } else { diff --git a/app/lib/report/module.dart b/app/lib/report/module.dart index 25a69296c..afb997b0a 100644 --- a/app/lib/report/module.dart +++ b/app/lib/report/module.dart @@ -1,18 +1,15 @@ import '../common/module.dart'; -import '../search/module.dart'; -import 'pages/gene.dart'; -import 'pages/report.dart'; +import '../drug/module.dart'; +// For generated routes export 'pages/gene.dart'; export 'pages/report.dart'; -const reportRoutes = AutoRoute( +final reportRoute = AutoRoute( path: 'report', - name: 'ReportRouter', - page: EmptyRouterPage, + page: ReportRoute.page, children: [ - AutoRoute(path: '', page: ReportPage), - AutoRoute(page: GenePage), - AutoRoute(page: DrugPage) + AutoRoute(page: GeneRoute.page), + drugRoute(), ], ); diff --git a/app/lib/report/pages/gene.dart b/app/lib/report/pages/gene.dart index fae05a03d..e4605f002 100644 --- a/app/lib/report/pages/gene.dart +++ b/app/lib/report/pages/gene.dart @@ -1,9 +1,9 @@ import 'package:provider/provider.dart'; import '../../common/module.dart'; -import '../../common/pages/drug/widgets/sub_header.dart'; -import '../../common/pages/drug/widgets/tooltip_icon.dart'; +import '../../drug/widgets/module.dart'; +@RoutePage() class GenePage extends HookWidget { GenePage(this.phenotype) : cubit = DrugListCubit( diff --git a/app/lib/report/pages/report.dart b/app/lib/report/pages/report.dart index 84524a03a..7f02aaf8a 100644 --- a/app/lib/report/pages/report.dart +++ b/app/lib/report/pages/report.dart @@ -4,6 +4,7 @@ import '../../common/models/drug/cached_drugs.dart'; import '../../common/module.dart'; import '../../common/utilities/color_utils.dart'; +@RoutePage() class ReportPage extends StatelessWidget { @override Widget build(BuildContext context) { @@ -25,7 +26,8 @@ class ReportPage extends StatelessWidget { lookupkey: notTestedString ) ); - return WillPopScope( + return PopScope( + canPop: false, child: unscrollablePageScaffold( title: context.l10n.tab_report, barBottom: context.l10n.report_content_explanation, @@ -45,7 +47,6 @@ class ReportPage extends StatelessWidget { ] ) ), - onWillPop: () async => false, ); } diff --git a/app/lib/search/module.dart b/app/lib/search/module.dart index 09f1e070c..d8964ae24 100644 --- a/app/lib/search/module.dart +++ b/app/lib/search/module.dart @@ -1,19 +1,11 @@ -import 'package:auto_route/auto_route.dart'; +import '../common/module.dart'; +import '../drug/module.dart'; -import '../common/pages/drug/drug.dart'; -import 'pages/search.dart'; - -export '../common/models/module.dart'; -export '../common/pages/drug/cubit.dart'; -export '../common/pages/drug/drug.dart'; +// For generated routes export 'pages/search.dart'; -const searchRoutes = AutoRoute( +final searchRoute = AutoRoute( path: 'search', - name: 'SearchRouter', - page: EmptyRouterPage, - children: [ - AutoRoute(path: '', page: SearchPage), - AutoRoute(page: DrugPage), - ], -); + page: SearchRoute.page, + children: [ drugRoute() ], +); \ No newline at end of file diff --git a/app/lib/search/pages/search.dart b/app/lib/search/pages/search.dart index e7aeb1c97..7ba0bed0a 100644 --- a/app/lib/search/pages/search.dart +++ b/app/lib/search/pages/search.dart @@ -2,12 +2,12 @@ import '../../../common/module.dart'; import '../../common/widgets/drug_list/drug_items/drug_cards.dart'; import '../../common/widgets/drug_search.dart'; +@RoutePage() class SearchPage extends HookWidget { SearchPage({ - Key? key, + super.key, @visibleForTesting DrugListCubit? cubit, - }) : cubit = cubit ?? DrugListCubit(), - super(key: key); + }) : cubit = cubit ?? DrugListCubit(); final DrugListCubit cubit; @@ -18,7 +18,8 @@ class SearchPage extends HookWidget { await cubit.loadDrugs(useCache: false); } }); - return WillPopScope( + return PopScope( + canPop: false, child: unscrollablePageScaffold( title: context.l10n.tab_drugs, body: DrugSearch( @@ -28,7 +29,6 @@ class SearchPage extends HookWidget { showDrugInteractionIndicator: true, ), ), - onWillPop: () async => false, ); } } diff --git a/app/lib/settings/module.dart b/app/lib/settings/module.dart index 8fad25f3b..c87094de1 100644 --- a/app/lib/settings/module.dart +++ b/app/lib/settings/module.dart @@ -1,26 +1,17 @@ -import 'package:auto_route/auto_route.dart'; - import '../common/module.dart'; -import 'pages/about_us.dart'; -import 'pages/privacy_policy.dart'; -import 'pages/settings.dart'; -import 'pages/terms_and_conditions.dart'; - -// We need to expose all pages for AutoRouter +// For generated routes export 'pages/about_us.dart'; export 'pages/privacy_policy.dart'; export 'pages/settings.dart'; export 'pages/terms_and_conditions.dart'; -const settingsRoutes = AutoRoute( +final settingsRoute = AutoRoute( path: 'settings', - name: 'SettingsRouter', - page: EmptyRouterPage, + page: SettingsRoute.page, children: [ - AutoRoute(path: '', page: SettingsPage), - AutoRoute(path: 'about_us', page: AboutUsPage), - AutoRoute(path: 'privacy_policy', page: PrivacyPolicyPage), - AutoRoute(path: 'terms_and_conditions', page: TermsAndConditionsPage), + AutoRoute(path: 'about', page: AboutUsRoute.page), + AutoRoute(path: 'privacy', page: PrivacyPolicyRoute.page), + AutoRoute(path: 'terms', page: TermsAndConditionsRoute.page), ], -); +); \ No newline at end of file diff --git a/app/lib/settings/pages/about_us.dart b/app/lib/settings/pages/about_us.dart index e5030e59d..be60fb479 100644 --- a/app/lib/settings/pages/about_us.dart +++ b/app/lib/settings/pages/about_us.dart @@ -1,7 +1,8 @@ import '../../common/module.dart'; +@RoutePage() class AboutUsPage extends StatelessWidget { - const AboutUsPage({Key? key}) : super(key: key); + const AboutUsPage({super.key}); @override Widget build(BuildContext context) { diff --git a/app/lib/settings/pages/privacy_policy.dart b/app/lib/settings/pages/privacy_policy.dart index 82ff95f57..df2069088 100644 --- a/app/lib/settings/pages/privacy_policy.dart +++ b/app/lib/settings/pages/privacy_policy.dart @@ -1,7 +1,8 @@ import '../../common/module.dart'; +@RoutePage() class PrivacyPolicyPage extends StatelessWidget { - const PrivacyPolicyPage({Key? key}) : super(key: key); + const PrivacyPolicyPage({super.key}); @override Widget build(BuildContext context) { diff --git a/app/lib/settings/pages/settings.dart b/app/lib/settings/pages/settings.dart index 3c522d87a..10397dfe2 100644 --- a/app/lib/settings/pages/settings.dart +++ b/app/lib/settings/pages/settings.dart @@ -1,13 +1,14 @@ import '../../common/module.dart'; -import '../../common/pages/drug/widgets/adaptive_dialog.dart'; import '../utils.dart'; +@RoutePage() class SettingsPage extends StatelessWidget { - const SettingsPage({Key? key}) : super(key: key); + const SettingsPage({super.key}); @override Widget build(BuildContext context) { - return WillPopScope( + return PopScope( + canPop: false, child: pageScaffold(title: context.l10n.tab_more, body: [ ListTile( title: Text( @@ -20,7 +21,7 @@ class SettingsPage extends StatelessWidget { title: Text(context.l10n.drug_selection_header), trailing: Icon(Icons.chevron_right_rounded), onTap: () => context.router.push( - DrugSelectionRouter(concludesOnboarding: false) + DrugSelectionRoute(concludesOnboarding: false) ), ), ListTile( @@ -42,7 +43,7 @@ class SettingsPage extends StatelessWidget { ListTile( title: Text(context.l10n.settings_page_onboarding), trailing: Icon(Icons.chevron_right_rounded), - onTap: () => context.router.push(OnboardingRouter(isRevisiting: true)), + onTap: () => context.router.push(OnboardingRoute(isRevisiting: true)), ), ListTile( title: Text(context.l10n.settings_page_about_us), @@ -65,7 +66,6 @@ class SettingsPage extends StatelessWidget { trailing: Icon(Icons.chevron_right_rounded), onTap: sendEmail) ]), - onWillPop: () async => false, ); } } @@ -75,8 +75,8 @@ class DeleteDataDialog extends HookWidget { Widget build(BuildContext context) { final agreedToDeletion = useState(false); - return AdaptiveAlertDialog( - title: context.l10n.settings_page_delete_data, + return AlertDialog.adaptive( + title: Text(context.l10n.settings_page_delete_data), content: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -93,20 +93,22 @@ class DeleteDataDialog extends HookWidget { ), ]), actions: [ - AdaptiveDialogAction( + TextButton( onPressed: context.router.root.pop, - text: context.l10n.action_cancel, + child: Text(context.l10n.action_cancel), ), - AdaptiveDialogAction( - isDestructive: true, + TextButton( onPressed: agreedToDeletion.value ? () async { await deleteAllAppData(); // ignore: use_build_context_synchronously - await overwriteRoutes(context, nextPage: LoginRouter()); + await overwriteRoutes(context, nextPage: LoginRoute()); } : null, - text: context.l10n.action_continue, + child: Text( + context.l10n.action_continue, + style: TextStyle(color: PharMeTheme.errorColor), + ), ), ], ); diff --git a/app/lib/settings/pages/terms_and_conditions.dart b/app/lib/settings/pages/terms_and_conditions.dart index 9c1f29d0d..efcceb92d 100644 --- a/app/lib/settings/pages/terms_and_conditions.dart +++ b/app/lib/settings/pages/terms_and_conditions.dart @@ -1,7 +1,8 @@ import '../../common/module.dart'; +@RoutePage() class TermsAndConditionsPage extends StatelessWidget { - const TermsAndConditionsPage({Key? key}) : super(key: key); + const TermsAndConditionsPage({super.key}); @override Widget build(BuildContext context) { diff --git a/app/pubspec.lock b/app/pubspec.lock index 7638491e3..9fe9be8f2 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -5,58 +5,58 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: d37dfd404e9bb23adb23ee61fad5b8e14e0ae018fb6948eda6ca44b197ff1158 + sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 url: "https://pub.dev" source: hosted - version: "43.0.0" + version: "64.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "40cbac251e4fffed9c85afca62e1d8236f4778d647934220f38007e2bd9009d8" + sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" url: "https://pub.dev" source: hosted - version: "4.3.1" + version: "6.2.0" archive: dependency: transitive description: name: archive - sha256: "80e5141fafcb3361653ce308776cfd7d45e6e9fbb429e14eec571382c0c5fecb" + sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b" url: "https://pub.dev" source: hosted - version: "3.3.2" + version: "3.4.9" args: dependency: transitive description: name: args - sha256: b003c3098049a51720352d219b0bb5f219b60fbfb68e7a4748139a06a5676515 + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.4.2" async: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" auto_route: dependency: "direct main" description: name: auto_route - sha256: "931f7c93fdfff8a0344b885dd2f3001746c01fb05820208fe51ddccc6f081a13" + sha256: "82f8df1d177416bc6b7a449127d0270ff1f0f633a91f2ceb7a85d4f07c3affa1" url: "https://pub.dev" source: hosted - version: "4.2.1" + version: "7.8.4" auto_route_generator: dependency: "direct dev" description: name: auto_route_generator - sha256: "3d0239a0d6f4a0a5b9f511b4a46f43812a6f2261887de8f5a0bdfe2a44465073" + sha256: "11067a3bcd643812518fe26c0c9ec073990286cabfd9d74b6da9ef9b913c4d22" url: "https://pub.dev" source: hosted - version: "4.2.1" + version: "7.3.2" auto_size_text: dependency: "direct main" description: @@ -69,34 +69,42 @@ packages: dependency: transitive description: name: barcode - sha256: "52570564684bbb0240a9f1fdb6bad12adc5e0540103c1c96d6dd550bd928b1c9" + sha256: "2a8b2ee065f419c2aeda141436cc556d91ae772d220fd80679f4d431d6c2ab43" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.2.5" + bidi: + dependency: transitive + description: + name: bidi + sha256: "1a7d0c696324b2089f72e7671fd1f1f64fef44c980f3cebc84e803967c597b63" + url: "https://pub.dev" + source: hosted + version: "2.0.10" black_hole_flutter: dependency: "direct main" description: name: black_hole_flutter - sha256: ad957dd07106932f07b040dcc4d08c7532a284821183a21ca5ad993b545c2707 + sha256: d1646e3638c15cd56a0f075128fa50317d26e7c824bde6b981d70118be34bd2f url: "https://pub.dev" source: hosted - version: "0.3.5" + version: "1.1.0" bloc: dependency: "direct main" description: name: bloc - sha256: bd4f8027bfa60d96c8046dec5ce74c463b2c918dce1b0d36593575995344534a + sha256: "3820f15f502372d979121de1f6b97bfcf1630ebff8fe1d52fb2b0bfa49be5b49" url: "https://pub.dev" source: hosted - version: "8.1.0" + version: "8.1.2" bloc_test: dependency: "direct dev" description: name: bloc_test - sha256: "622b97678bf8c06a94f4c26a89ee9ebf7319bf775383dee2233e86e1f94ee28d" + sha256: "02f04270be5abae8df171143e61a0058a7acbce5dcac887612e89bb40cca4c33" url: "https://pub.dev" source: hosted - version: "9.1.0" + version: "9.1.5" boolean_selector: dependency: transitive description: @@ -109,50 +117,50 @@ packages: dependency: transitive description: name: build - sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777" + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.4.1" build_config: dependency: transitive description: name: build_config - sha256: "5b7355c14258f5e7df24bad1566f7b991de3e54aeacfb94e1a65e5233d9739c1" + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" build_daemon: dependency: transitive description: name: build_daemon - sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf" + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "4.0.1" build_resolvers: dependency: transitive description: name: build_resolvers - sha256: "9aae031a54ab0beebc30a888c93e900d15ae2fd8883d031dbfbd5ebdb57f5a4c" + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" url: "https://pub.dev" source: hosted - version: "2.0.9" + version: "2.4.2" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "56942f8114731d1e79942cd981cfef29501937ff1bccf4dbdce0273f31f13640" + sha256: "67d591d602906ef9201caf93452495ad1812bea2074f04e25dbd7c133785821b" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.4.7" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: "409c20ff6b6a9c9f4152fc9fcbf16440fedf02fcacc0fb26ea3b8eab9a860a40" + sha256: c9e32d21dd6626b5c163d48b037ce906bbe428bc23ab77bcd77bb21e593b6185 url: "https://pub.dev" source: hosted - version: "7.2.4" + version: "7.2.11" built_collection: dependency: transitive description: @@ -165,34 +173,34 @@ packages: dependency: transitive description: name: built_value - sha256: d7a9cd57c215bdf8d502772447aa6b52a8ab3f956d25d5fdea6ef1df2d2dad60 + sha256: c9aabae0718ec394e5bc3c7272e6bb0dc0b32201a08fe185ec1d8401d3e39309 url: "https://pub.dev" source: hosted - version: "8.4.1" + version: "8.8.1" characters: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" - charcode: + version: "1.3.0" + checked_yaml: dependency: transitive description: - name: charcode - sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff url: "https://pub.dev" source: hosted - version: "1.3.1" - checked_yaml: + version: "2.0.3" + cli_util: dependency: transitive description: - name: checked_yaml - sha256: dd007e4fb8270916820a0d66e24f619266b60773cddd082c6439341645af2659 + name: cli_util + sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "0.4.1" clock: dependency: transitive description: @@ -205,74 +213,66 @@ packages: dependency: transitive description: name: code_builder - sha256: "02ce3596b459c666530f045ad6f96209474e8fee6e4855940a3cee65fb872ec5" + sha256: feee43a5c05e7b3199bb375a86430b8ada1b04104f2923d0e03cc01ca87b6d84 url: "https://pub.dev" source: hosted - version: "4.3.0" + version: "4.9.0" collection: dependency: "direct main" description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.18.0" convert: dependency: transitive description: name: convert - sha256: "196284f26f69444b7f5c50692b55ec25da86d9e500451dc09333bf2e3ad69259" + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.1.1" coverage: dependency: transitive description: name: coverage - sha256: "525ac94733f9ce82507a050bfd62ad89eb1dcbc56308e1e2e17ab11abeee4a75" + sha256: "595a29b55ce82d53398e1bcc2cba525d7bd7c59faeb2d2540e9d42c390cfeeeb" url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.6.4" crypto: dependency: transitive description: name: crypto - sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab url: "https://pub.dev" source: hosted - version: "3.0.2" - csslib: - dependency: transitive - description: - name: csslib - sha256: b36c7f7e24c0bdf1bf9a3da461c837d1de64b9f8beb190c9011d8c72a3dfd745 - url: "https://pub.dev" - source: hosted - version: "0.17.2" + version: "3.0.3" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be + sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "1.0.6" dart_style: dependency: transitive description: name: dart_style - sha256: "8aff82f9b26fd868992e5430335a9d773bfef01e1d852d7ba71bf4c5d9349351" + sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.3.4" dartx: dependency: "direct main" description: name: dartx - sha256: "45d7176701f16c5a5e00a4798791c1964bc231491b879369c818dd9a9c764871" + sha256: "8b25435617027257d43e6508b5fe061012880ddfdaa75a71d607c3de2a13d244" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" data_size: dependency: transitive description: @@ -285,58 +285,26 @@ packages: dependency: "direct main" description: name: debug_overlay - sha256: "33fd675a18cef743454fc15a4f84c3d9e3a6ca3be1b06466746f28b6d791f9fc" + sha256: "3c630f1c4e59dca72e9da9caac8602645c252b78fd9f6802ef5b2b260e40a6b2" url: "https://pub.dev" source: hosted - version: "0.1.5" + version: "0.2.11" device_info_plus: dependency: transitive description: name: device_info_plus - sha256: c2386729379f04cd39ee0d5d4c48d8c8a0e70f7622dac626cbf5e396392602fd - url: "https://pub.dev" - source: hosted - version: "3.2.4" - device_info_plus_linux: - dependency: transitive - description: - name: device_info_plus_linux - sha256: e4eb5db4704f5534e872148a21cfcd39581022b63df556da6720d88f7c9f91a9 - url: "https://pub.dev" - source: hosted - version: "2.1.1" - device_info_plus_macos: - dependency: transitive - description: - name: device_info_plus_macos - sha256: "38871fd2ad31871399d8307630c9f4eb5941dd2c643ee221c44d58de95d367a1" + sha256: "0042cb3b2a76413ea5f8a2b40cec2a33e01d0c937e91f0f7c211fde4f7739ba6" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "9.1.1" device_info_plus_platform_interface: dependency: transitive description: name: device_info_plus_platform_interface - sha256: b2743934f0efc3e291880d76fb341ea114b7e8417d77ee0f93bd21f5dfd3e8d2 - url: "https://pub.dev" - source: hosted - version: "2.6.1" - device_info_plus_web: - dependency: transitive - description: - name: device_info_plus_web - sha256: "38715ad1ef3bee8915dd7bee08a9ac9ab54472a8df425c887062a3046209f663" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - device_info_plus_windows: - dependency: transitive - description: - name: device_info_plus_windows - sha256: "8fb1403fc94636d6ab48aeebb5f9379f2ca51cde3b337167ec6f39db09234492" + sha256: d3b01d5868b50ae571cd1dc6e502fc94d956b665756180f7b16ead09e836fd64 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "7.0.0" diff_match_patch: dependency: transitive description: @@ -349,10 +317,10 @@ packages: dependency: "direct main" description: name: dropdown_button2 - sha256: b036a3006b6cdc964488f4eae366ee83b3b33eaa2677f0292538a82fa85201e1 + sha256: b0fe8d49a030315e9eef6c7ac84ca964250155a6224d491c1365061bc974a9e1 url: "https://pub.dev" source: hosted - version: "1.8.5" + version: "2.3.9" fake_async: dependency: transitive description: @@ -365,10 +333,10 @@ packages: dependency: transitive description: name: ffi - sha256: "13a6ccf6a459a125b3fcdb6ec73bd5ff90822e071207c663bfd1f70062d51d18" + sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "2.1.0" file: dependency: transitive description: @@ -381,10 +349,10 @@ packages: dependency: transitive description: name: fixnum - sha256: "04be3e934c52e082558cc9ee21f42f5c1cd7a1262f4c63cd0357c08d5bba81ec" + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.1.0" flutter: dependency: "direct main" description: flutter @@ -394,10 +362,10 @@ packages: dependency: "direct main" description: name: flutter_bloc - sha256: "890c51c8007f0182360e523518a0c732efb89876cb4669307af7efada5b55557" + sha256: e74efb89ee6945bcbce74a5b3a5a3376b088e5f21f55c263fc38cbdc6237faae url: "https://pub.dev" source: hosted - version: "8.1.1" + version: "8.1.3" flutter_driver: dependency: transitive description: flutter @@ -407,26 +375,26 @@ packages: dependency: "direct main" description: name: flutter_hooks - sha256: "2b202559a4ed3656bbb7aae9d8b335fb0037b23acc7ae3f377d1ba0b95c21aec" + sha256: "7c8db779c2d1010aa7f9ea3fbefe8f86524fcb87b69e8b0af31e1a4b55422dec" url: "https://pub.dev" source: hosted - version: "0.18.5+1" + version: "0.20.3" flutter_launcher_icons: dependency: "direct dev" description: name: flutter_launcher_icons - sha256: "559c600f056e7c704bd843723c21e01b5fba47e8824bd02422165bcc02a5de1d" + sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea" url: "https://pub.dev" source: hosted - version: "0.9.3" + version: "0.13.1" flutter_lints: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" flutter_list_view: dependency: "direct main" description: @@ -444,50 +412,50 @@ packages: dependency: "direct main" description: name: flutter_secure_storage - sha256: "5abe3d5c25ab435e48c47fb61bac25606062a305fac637c2f020e25abd30014a" + sha256: ffdbb60130e4665d2af814a0267c481bcf522c41ae2e43caf69fa0146876d685 url: "https://pub.dev" source: hosted - version: "5.1.2" + version: "9.0.0" flutter_secure_storage_linux: dependency: transitive description: name: flutter_secure_storage_linux - sha256: "7b36defaed0a994cc58d6a169a7c89c695ea660ef86903df13c2a2708fb1e1bb" + sha256: "3d5032e314774ee0e1a7d0a9f5e2793486f0dff2dd9ef5a23f4e3fb2a0ae6a9e" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.2.0" flutter_secure_storage_macos: dependency: transitive description: name: flutter_secure_storage_macos - sha256: cd8eff6ebb4d9083aa3a57f29da00df837a869e8d5c65d9ad1ce549037c55d06 + sha256: bd33935b4b628abd0b86c8ca20655c5b36275c3a3f5194769a7b3f37c905369c url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "3.0.1" flutter_secure_storage_platform_interface: dependency: transitive description: name: flutter_secure_storage_platform_interface - sha256: "9af003dec5ba9f959300fd76ac8adf5608fbfbc957cdd7a987af206a8e073b32" + sha256: "0d4d3a5dd4db28c96ae414d7ba3b8422fd735a8255642774803b2532c9a61d7e" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.2" flutter_secure_storage_web: dependency: transitive description: name: flutter_secure_storage_web - sha256: "926716d84eb45220cb1783af629093301613f5e5b51da7c1813e430d69bd716f" + sha256: "30f84f102df9dcdaa2241866a958c2ec976902ebdaa8883fbfe525f1f2f3cf20" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.1.2" flutter_secure_storage_windows: dependency: transitive description: name: flutter_secure_storage_windows - sha256: cf9fd49c2807f80dc01f449592e1e7971a3697d1e248b653efb8323536dc2961 + sha256: "5809c66f9dd3b4b93b0a6e2e8561539405322ee767ac2f64d084e2ab5429d108" url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "3.0.0" flutter_share: dependency: "direct main" description: @@ -500,18 +468,18 @@ packages: dependency: "direct main" description: name: flutter_sliding_up_panel - sha256: "60fdb281d2a81047dadecb931f9b53b6f7189680cd71f898441073207ff90e6f" + sha256: "94f928973d83e146bbc52051e2d9f2a7ed7a5c9e7f04b54d835fff2e41d6cb99" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.1" flutter_svg: dependency: "direct main" description: name: flutter_svg - sha256: f2aa23d9d1721cd5c2c5097558368d2a1f85be35a85a6cf26a626e80c369a47c + sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c url: "https://pub.dev" source: hosted - version: "1.1.5" + version: "2.0.9" flutter_test: dependency: "direct dev" description: flutter @@ -521,10 +489,10 @@ packages: dependency: "direct main" description: name: flutter_web_auth - sha256: "1becc47ee73009e9ea19402e2194c35dda2b2fbbe2023894df97c8caa4781335" + sha256: a69fa8f43b9e4d86ac72176bf747b735e7b977dd7cf215076d95b87cb05affdd url: "https://pub.dev" source: hosted - version: "0.4.1" + version: "0.5.0" flutter_web_plugins: dependency: transitive description: flutter @@ -534,26 +502,26 @@ packages: dependency: "direct dev" description: name: freezed - sha256: b8f1017d491344ef41045d3fe85950404c49e74eeaf84a84d7b8b67ac24dfb91 + sha256: "6c5031daae12c7072b3a87eff98983076434b4889ef2a44384d0cae3f82372ba" url: "https://pub.dev" source: hosted - version: "2.1.0+1" + version: "2.4.6" freezed_annotation: dependency: "direct main" description: name: freezed_annotation - sha256: "625eb228fd9f00f952b7cd245be34791434fad48375f74e46f97dea4b4e11678" + sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.4.1" frontend_server_client: dependency: transitive description: name: frontend_server_client - sha256: "4f4a162323c86ffc1245765cfe138872b8f069deb42f7dbb36115fa27f31469b" + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "3.2.0" fuchsia_remote_debug_protocol: dependency: transitive description: flutter @@ -563,26 +531,26 @@ packages: dependency: transitive description: name: glob - sha256: c51b4fdfee4d281f49b8c957f1add91b815473597f76bcf07377987f66a55729 + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.2" google_fonts: dependency: "direct main" description: name: google_fonts - sha256: "8f099045e2f2a30e4d4d0a35f40c6bc941a8f2ca0e10ad9d214ee9edd3f37483" + sha256: f0b8d115a13ecf827013ec9fc883390ccc0e87a96ed5347a3114cac177ef18e8 url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "6.1.0" graphs: dependency: transitive description: name: graphs - sha256: ae0b3d956ff324c6f8671f08dcb2dbd71c99cdbf2aa3ca63a14190c47aa6679c + sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.3.1" hive: dependency: "direct main" description: @@ -603,26 +571,18 @@ packages: dependency: "direct dev" description: name: hive_generator - sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938" - url: "https://pub.dev" - source: hosted - version: "1.1.3" - html: - dependency: transitive - description: - name: html - sha256: bfef906cbd4e78ef49ae511d9074aebd1d2251482ef601a280973e8b58b51bbf + sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4" url: "https://pub.dev" source: hosted - version: "0.15.0" + version: "2.0.1" http: dependency: "direct main" description: name: http - sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" + sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 url: "https://pub.dev" source: hosted - version: "0.13.5" + version: "1.1.2" http_multi_server: dependency: transitive description: @@ -635,26 +595,26 @@ packages: dependency: transitive description: name: http_parser - sha256: db3060f22889f3d9d55f6a217565486737037eec3609f7f3eca4d0c67ee0d8a0 + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" url: "https://pub.dev" source: hosted - version: "4.0.1" + version: "4.0.2" image: dependency: transitive description: name: image - sha256: "9d2c5f73435a70a936d317769ee8e7ef480e37674b9f2bce95ea98969a307855" + sha256: "028f61960d56f26414eb616b48b04eb37d700cbe477b7fb09bf1d7ce57fd9271" url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "4.1.3" implicitly_animated_list: dependency: transitive description: name: implicitly_animated_list - sha256: eb87227f9d55ac2d927ea8c2ef59b6cd24cada87f869a7a584c1c9e07cbf35c6 + sha256: "327074913ed350fbd5089bf433d6268fb98022210e03668035523da7ff64dfdb" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.2.0" integration_test: dependency: "direct dev" description: flutter @@ -664,50 +624,58 @@ packages: dependency: "direct main" description: name: intl - sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.dev" source: hosted - version: "0.17.0" + version: "0.18.1" io: dependency: transitive description: name: io - sha256: "0d4c73c3653ab85bf696d51a9657604c900a370549196a91f33e4c39af760852" + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" js: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" json_annotation: dependency: "direct main" description: name: json_annotation - sha256: cb314f00b2488de7bc575207e54402cd2f92363f333a7933fd1b0631af226baa + sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 url: "https://pub.dev" source: hosted - version: "4.6.0" + version: "4.8.1" json_serializable: dependency: "direct dev" description: name: json_serializable - sha256: "0cec7060459254cf1ff980c08dedca6fa50917724a3c3ec8c5026cb88dee8238" + sha256: aa1f5a8912615733e0fdc7a02af03308933c93235bdc8d50d0b0c8a8ccb0b969 url: "https://pub.dev" source: hosted - version: "6.3.1" + version: "6.7.1" + json_view: + dependency: transitive + description: + name: json_view + sha256: "905c69f9e69d1eab5406b87ab6c10c3706c04c70c6a4959621bd2b43c2d27374" + url: "https://pub.dev" + source: hosted + version: "0.4.2" lints: dependency: transitive description: name: lints - sha256: "5cfd6509652ff5e7fe149b6df4859e687fca9048437857cb2e65c8d780f396e3" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "3.0.0" list_diff: dependency: transitive description: @@ -720,50 +688,50 @@ packages: dependency: transitive description: name: logging - sha256: c0bbfe94d46aedf9b8b3e695cf3bd48c8e14b35e3b2c639e0aa7755d589ba946 + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" matcher: dependency: transitive description: name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.13" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.10.0" mime: dependency: transitive description: name: mime - sha256: dab22e92b41aa1255ea90ddc4bc2feaf35544fd0728e209638cad041a6e3928a + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.0.4" mocktail: dependency: "direct dev" description: name: mocktail - sha256: "80a996cd9a69284b3dc521ce185ffe9150cde69767c2d3a0720147d93c0cef53" + sha256: f603ebd85a576e5914870b02e5839fc5d0243b867bf710651cf239a28ebb365e url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "1.0.2" nested: dependency: transitive description: @@ -776,10 +744,10 @@ packages: dependency: transitive description: name: node_preamble - sha256: "8ebdbaa3b96d5285d068f80772390d27c21e1fa10fb2df6627b1b9415043608d" + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" overlay_dialog: dependency: "direct main" description: @@ -800,66 +768,26 @@ packages: dependency: transitive description: name: package_info_plus - sha256: "7a6114becdf042be2b0777d77ace954d4a205644171a1cbd8712976b9bbb837c" - url: "https://pub.dev" - source: hosted - version: "1.4.2" - package_info_plus_linux: - dependency: transitive - description: - name: package_info_plus_linux - sha256: "04b575f44233d30edbb80a94e57cad9107aada334fc02aabb42b6becd13c43fc" - url: "https://pub.dev" - source: hosted - version: "1.0.5" - package_info_plus_macos: - dependency: transitive - description: - name: package_info_plus_macos - sha256: a2ad8b4acf4cd479d4a0afa5a74ea3f5b1c7563b77e52cc32b3ee6956d5482a6 + sha256: "88bc797f44a94814f2213db1c9bd5badebafdfb8290ca9f78d4b9ee2a3db4d79" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "5.0.1" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - sha256: f7a0c8f1e7e981bc65f8b64137a53fd3c195b18d429fba960babc59a5a1c7ae8 - url: "https://pub.dev" - source: hosted - version: "1.0.2" - package_info_plus_web: - dependency: transitive - description: - name: package_info_plus_web - sha256: f0829327eb534789e0a16ccac8936a80beed4e2401c4d3a74f3f39094a822d3b + sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" url: "https://pub.dev" source: hosted - version: "1.0.6" - package_info_plus_windows: - dependency: transitive - description: - name: package_info_plus_windows - sha256: a6040b8695c82f8dd3c3d4dfab7ef96fbe9c67aac21b9bcf5db272589ef84441 - url: "https://pub.dev" - source: hosted - version: "1.0.5" + version: "2.0.1" path: dependency: transitive description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.8.2" - path_drawing: - dependency: transitive - description: - name: path_drawing - sha256: bbb1934c0cbb03091af082a6389ca2080345291ef07a5fa6d6e078ba8682f977 - url: "https://pub.dev" - source: hosted - version: "1.0.1" + version: "1.8.3" path_parsing: dependency: transitive description: @@ -872,90 +800,90 @@ packages: dependency: "direct main" description: name: path_provider - sha256: "050e8e85e4b7fecdf2bb3682c1c64c4887a183720c802d323de8a5fd76d372dd" + sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa url: "https://pub.dev" source: hosted - version: "2.0.11" + version: "2.1.1" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "4d5542667150f5b779ba411dd5dc0b674a85d1355e45bda2877e0e82f4ad08d8" + sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 url: "https://pub.dev" source: hosted - version: "2.0.20" - path_provider_ios: + version: "2.2.1" + path_provider_foundation: dependency: transitive description: - name: path_provider_ios - sha256: "03d639406f5343478352433f00d3c4394d52dac8df3d847869c5e2333e0bbce8" + name: path_provider_foundation + sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" url: "https://pub.dev" source: hosted - version: "2.0.11" + version: "2.3.1" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379 - url: "https://pub.dev" - source: hosted - version: "2.1.7" - path_provider_macos: - dependency: transitive - description: - name: path_provider_macos - sha256: "2a97e7fbb7ae9dcd0dfc1220a78e9ec3e71da691912e617e8715ff2a13086ae8" + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.2.1" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76 + sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.1.1" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: a34ecd7fb548f8e57321fd8e50d865d266941b54e6c3b7758cf8f37c24116905 + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" url: "https://pub.dev" source: hosted - version: "2.0.7" + version: "2.2.1" pdf: dependency: "direct main" description: name: pdf - sha256: "47bcb9818427d099ccc8bea7608eef8fc0e3a6bfd9c2370565361ea78eabf6bf" + sha256: "93cbb2c06de9bab91844550f19896b2373e7a5ce25173995e7e5ec5e1741429d" url: "https://pub.dev" source: hosted - version: "3.8.3" + version: "3.10.7" petitparser: dependency: transitive description: name: petitparser - sha256: "2ebb289dc4764ec397f5cd3ca9881c6d17196130a7d646ed022a0dd9c2e25a71" + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "6.0.2" platform: dependency: transitive description: name: platform - sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.2" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.7" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" + url: "https://pub.dev" + source: hosted + version: "3.7.3" pool: dependency: transitive description: @@ -968,18 +896,18 @@ packages: dependency: "direct main" description: name: popover - sha256: "4255a09e3bb64cada6aebdeeeb15453a2790802d4eecb9256ff5c895863582ea" + sha256: "6a0928ccdcf12d46b407372b644a0d94400b316d0ee072a19dcef03c2bb88c3f" url: "https://pub.dev" source: hosted - version: "0.2.8+1" + version: "0.2.9" printing: dependency: "direct main" description: name: printing - sha256: "4df9e22bd4cb2ecea67183e5b328711b10276127b8878d9d5cb60ccd4c24a77c" + sha256: ad39a42a5f83125952457dfd94f395c8cf0eb1f7759583dadb769be5c7f99d24 url: "https://pub.dev" source: hosted - version: "5.9.3" + version: "5.11.1" process: dependency: transitive description: @@ -1000,18 +928,18 @@ packages: dependency: transitive description: name: pub_semver - sha256: "816c1a640e952d213ddd223b3e7aafae08cd9f8e1f6864eed304cc13b0272b07" + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.4" pubspec_parse: dependency: transitive description: name: pubspec_parse - sha256: "75f6614d6dde2dc68948dffbaa4fe5dae32cd700eb9fb763fe11dfb45a3c4d0a" + sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.3" qr: dependency: transitive description: @@ -1032,10 +960,10 @@ packages: dependency: transitive description: name: sensors_plus_platform_interface - sha256: dde5184f72b56b25a19d5d88a27154bedf5c3fb9584def621d0d320b613c8d09 + sha256: bc472d6cfd622acb4f020e726433ee31788b038056691ba433fec80e448a094f url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.2.0" sensors_plus_web: dependency: transitive description: @@ -1048,106 +976,98 @@ packages: dependency: transitive description: name: shake - sha256: "4c3b9477d72b88e5108a3f13c8cadfa83355bed50b26087166628602f4e261af" + sha256: "107546951c6b8f5e4c2dca66dfb3aa27dd1a853b4e9a26c9aea224b167045023" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.2.0" shared_preferences: dependency: "direct main" description: name: shared_preferences - sha256: "76917b7d4b9526b2ba416808a7eb9fb2863c1a09cf63ec85f1453da240fa818a" + sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.2.2" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "3e128864b9cff21bdd5b3ad569953070a851d62901bee880bb052b1110b38007" + sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" url: "https://pub.dev" source: hosted - version: "2.0.13" - shared_preferences_ios: + version: "2.2.1" + shared_preferences_foundation: dependency: transitive description: - name: shared_preferences_ios - sha256: "585a14cefec7da8c9c2fb8cd283a3bb726b4155c0952afe6a0caaa7b2272de34" + name: shared_preferences_foundation + sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.3.4" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - sha256: "28aefc1261746e7bad3d09799496054beb84e8c4ffcdfed7734e17b4ada459a5" + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" url: "https://pub.dev" source: hosted - version: "2.1.1" - shared_preferences_macos: - dependency: transitive - description: - name: shared_preferences_macos - sha256: fbb94bf296576f49be37a1496d5951796211a8db0aa22cc0d68c46440dad808c - url: "https://pub.dev" - source: hosted - version: "2.0.4" + version: "2.3.2" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - sha256: da9431745ede5ece47bc26d5d73a9d3c6936ef6945c101a5aca46f62e52c1cf3 + sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.3.1" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - sha256: a4b5bc37fe1b368bbc81f953197d55e12f49d0296e7e412dfe2d2d77d6929958 + sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21" url: "https://pub.dev" source: hosted - version: "2.0.4" + version: "2.2.2" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - sha256: "97f7ab9a7da96d9cf19581f5de520ceb529548498bd6b5e0ccd02d68a0d15eba" + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.3.2" shelf: dependency: transitive description: name: shelf - sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.4.1" shelf_packages_handler: dependency: transitive description: name: shelf_packages_handler - sha256: aef74dc9195746a384843102142ab65b6a4735bb3beea791e63527b88cc83306 + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" shelf_static: dependency: transitive description: name: shelf_static - sha256: e792b76b96a36d4a41b819da593aff4bdd413576b3ba6150df5d8d9996d2e74c + sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: "6db16374bc3497d21aa0eebe674d3db9fdf82082aac0f04dc7b44e4af5b08afc" + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.0.4" sky_engine: dependency: transitive description: flutter @@ -1157,66 +1077,66 @@ packages: dependency: transitive description: name: source_gen - sha256: "00f8b6b586f724a8c769c96f1d517511a41661c0aede644544d8d86a1ab11142" + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.5.0" source_helper: dependency: transitive description: name: source_helper - sha256: "522d9b05c40ec14f479ce4428337d106c0465fedab42f514582c198460a784fe" + sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.3.4" source_map_stack_trace: dependency: transitive description: name: source_map_stack_trace - sha256: "8c463326277f68a628abab20580047b419c2ff66756fd0affd451f73f9508c11" + sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" source_maps: dependency: transitive description: name: source_maps - sha256: "52de2200bb098de739794c82d09c41ac27b2e42fd7e23cce7b9c74bf653c7296" + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" url: "https://pub.dev" source: hosted - version: "0.10.10" + version: "0.10.12" source_span: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" stream_transform: dependency: transitive description: name: stream_transform - sha256: f1d172e22a5432c042b5adfa9aff621372e4292231d9d73ad00f486ad01c2395 + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" string_scanner: dependency: transitive description: @@ -1245,130 +1165,146 @@ packages: dependency: transitive description: name: test - sha256: a5fcd2d25eeadbb6589e80198a47d6a464ba3e2049da473943b8af9797900c2d + sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f url: "https://pub.dev" source: hosted - version: "1.22.0" + version: "1.24.9" test_api: dependency: transitive description: name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.4.16" + version: "0.6.1" test_core: dependency: transitive description: name: test_core - sha256: "0ef9755ec6d746951ba0aabe62f874b707690b5ede0fecc818b138fcc9b14888" + sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a url: "https://pub.dev" source: hosted - version: "0.4.20" + version: "0.5.9" time: dependency: transitive description: name: time - sha256: "267028bb7b3e87bbfd66876c6389d7101e4b14eb94fe863d3e008e497ca07844" + sha256: "83427e11d9072e038364a5e4da559e85869b227cf699a541be0da74f14140124" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" timing: dependency: transitive description: name: timing - sha256: c386d07d7f5efc613479a7c4d9d64b03710b03cfaa7e8ad5f2bfb295a1f0dfad + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.1" typed_data: dependency: transitive description: name: typed_data - sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - universal_html: - dependency: transitive - description: - name: universal_html - sha256: "5ff50b7c14d201421cf5230ec389a0591c4deb5c817c9d7ccca3b26fe5f31e34" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "2.0.8" + version: "1.3.2" universal_io: dependency: transitive description: name: universal_io - sha256: "79f78ddad839ee3aae3ec7c01eb4575faf0d5c860f8e5223bc9f9c17f7f03cef" + sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" url: "https://pub.dev" source: hosted - version: "2.0.4" + version: "2.2.2" url_launcher: dependency: "direct main" description: name: url_launcher - sha256: "568176fc8ab5ac1d88ff0db8ff28659d103851670dda55e83b485664c2309299" + sha256: e9aa5ea75c84cf46b3db4eea212523591211c3cf2e13099ee4ec147f54201c86 url: "https://pub.dev" source: hosted - version: "6.1.6" + version: "6.2.2" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "9e262cbec69233717d5198f4d0b0c4961fa027e3685ba425442c43c64f38bb9b" + sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def" url: "https://pub.dev" source: hosted - version: "6.0.19" + version: "6.2.0" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "6ba7dddee26c9fae27c9203c424631109d73c8fa26cfa7bc3e35e751cb87f62e" + sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3 url: "https://pub.dev" source: hosted - version: "6.0.17" + version: "6.2.1" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: "360fa359ab06bcb4f7c5cd3123a2a9a4d3364d4575d27c4b33468bd4497dd094" + sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.1.1" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: a9b3ea9043eabfaadfa3fb89de67a11210d85569086d22b3854484beab8b3978 + sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.1.0" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: "4eae912628763eb48fc214522e58e942fd16ce195407dbf45638239523c759a6" + sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.2.0" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "5669882643b96bb6d5786637cac727c6e918a790053b09245fd4513b8a07df2a" + sha256: "7286aec002c8feecc338cc33269e96b73955ab227456e9fb2a91f7fab8a358e9" url: "https://pub.dev" source: hosted - version: "2.0.13" + version: "2.2.2" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: e3c3b16d3104260c10eea3b0e34272aaa57921f83148b0619f74c2eced9b7ef1 + sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.1.1" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: "0f0c746dd2d6254a0057218ff980fc7f5670fd0fcf5e4db38a490d31eed4ad43" + url: "https://pub.dev" + source: hosted + version: "1.1.9+1" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: "0edf6d630d1bfd5589114138ed8fada3234deacc37966bec033d3047c29248b7" + url: "https://pub.dev" + source: hosted + version: "1.1.9+1" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: d24333727332d9bd20990f1483af4e09abdb9b1fc7c3db940b56ab5c42790c26 + url: "https://pub.dev" + source: hosted + version: "1.1.9+1" vector_math: dependency: transitive description: @@ -1381,66 +1317,82 @@ packages: dependency: transitive description: name: vm_service - sha256: e7fb6c2282f7631712b69c19d1bff82f3767eea33a2321c14fa59ad67ea391c7 + sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 url: "https://pub.dev" source: hosted - version: "9.4.0" + version: "11.10.0" watcher: dependency: transitive description: name: watcher - sha256: e42dfcc48f67618344da967b10f62de57e04bae01d9d3af4c2596f3712a88c99 + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + url: "https://pub.dev" + source: hosted + version: "0.3.0" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "3a969ddcc204a3e34e863d204b29c0752716f78b6f9cc8235083208d268a4ccd" + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.4.0" webdriver: dependency: transitive description: name: webdriver - sha256: ef67178f0cc7e32c1494645b11639dd1335f1d18814aa8435113a92e9ef9d841 + sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" webkit_inspection_protocol: dependency: transitive description: name: webkit_inspection_protocol - sha256: "67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d" + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.1" win32: dependency: transitive description: name: win32 - sha256: c0e3a4f7be7dae51d8f152230b86627e3397c1ba8c3fa58e63d44a9f3edc9cef + sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574 + url: "https://pub.dev" + source: hosted + version: "5.1.1" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a" url: "https://pub.dev" source: hosted - version: "2.6.1" + version: "1.1.2" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: "11541eedefbcaec9de35aa82650b695297ce668662bbd6e3911a7fabdbde589f" + sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" url: "https://pub.dev" source: hosted - version: "0.2.0+2" + version: "1.0.3" xml: dependency: transitive description: name: xml - sha256: ac0e3f4bf00ba2708c33fbabbbe766300e509f8c82dbd4ab6525039813f7e2fb + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "6.5.0" yaml: dependency: transitive description: @@ -1450,5 +1402,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=2.19.0 <3.0.0" - flutter: ">=3.3.0" + dart: ">=3.2.0 <3.3.0" + flutter: ">=3.16.0" diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 6a2f63f3c..c2d15a2b6 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -4,37 +4,37 @@ publish_to: 'none' version: 1.0.0+1 environment: - sdk: '>=2.17.0 <3.0.0' - flutter: '>=3.0.0 <3.1.0' + sdk: '>=2.17.0 <3.3.0' + flutter: '>=3.3.0' dependencies: - auto_route: ^4.2.0 + auto_route: ^7.8.4 auto_size_text: ^3.0.0 - black_hole_flutter: ^0.3.5 + black_hole_flutter: ^1.1.0 bloc: ^8.0.3 collection: ^1.16.0 cupertino_icons: ^1.0.5 dartx: ^1.1.0 - debug_overlay: ^0.1.4 - dropdown_button2: ^1.6.2 + debug_overlay: ^0.2.11 + dropdown_button2: ^2.3.9 flutter: sdk: flutter flutter_bloc: ^8.0.1 - flutter_hooks: ^0.18.5+1 + flutter_hooks: ^0.20.3 flutter_list_view: ^1.1.22 flutter_localizations: sdk: flutter - flutter_secure_storage: ^5.0.2 + flutter_secure_storage: ^9.0.0 flutter_share: ^2.0.0 flutter_sliding_up_panel: ^2.0.1 - flutter_svg: ^1.1.1 - flutter_web_auth: ^0.4.1 + flutter_svg: ^2.0.9 + flutter_web_auth: ^0.5.0 freezed_annotation: ^2.0.3 - google_fonts: ^3.0.1 + google_fonts: ^6.1.0 hive: ^2.2.2 hive_flutter: ^1.1.0 - http: ^0.13.4 - intl: ^0.17.0 + http: ^1.1.2 + intl: ^0.18.1 json_annotation: ^4.6.0 overlay_dialog: ^0.2.2 path_provider: ^2.0.11 @@ -46,19 +46,19 @@ dependencies: url_launcher: ^6.1.4 dev_dependencies: - auto_route_generator: ^4.2.0 + auto_route_generator: ^7.3.2 bloc_test: ^9.0.3 build_runner: ^2.1.11 - flutter_launcher_icons: ^0.9.3 - flutter_lints: ^2.0.1 + flutter_launcher_icons: ^0.13.1 + flutter_lints: ^3.0.1 flutter_test: sdk: flutter freezed: ^2.0.3+1 - hive_generator: ^1.1.3 + hive_generator: ^2.0.1 integration_test: sdk: flutter json_serializable: ^6.2.0 - mocktail: ^0.3.0 + mocktail: ^1.0.2 flutter: generate: true From cf0f7a7fdcc7b6a9b9f14e80f9f3a9b33d9fb3d0 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Wed, 20 Dec 2023 09:11:30 +0100 Subject: [PATCH 02/24] feat(#636): update versions in app workflow --- .github/workflows/app.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/app.yml b/.github/workflows/app.yml index 9edffd0c3..25128bfb9 100644 --- a/.github/workflows/app.yml +++ b/.github/workflows/app.yml @@ -11,9 +11,9 @@ defaults: working-directory: ./app env: - JAVA_VERSION: 12.x + JAVA_VERSION: 17.x FLUTTER_CHANNEL: stable - FLUTTER_VERSION: 3.7.6 + FLUTTER_VERSION: 3.16.4 jobs: lint: From c440a373b321e7ae93ba3b345e2df1eec2707dd3 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Wed, 20 Dec 2023 10:52:25 +0100 Subject: [PATCH 03/24] fix(#636): downgrade Gradle version --- app/android/gradle/wrapper/gradle-wrapper.properties | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/android/gradle/wrapper/gradle-wrapper.properties b/app/android/gradle/wrapper/gradle-wrapper.properties index 2d1906879..5c6f89dba 100644 --- a/app/android/gradle/wrapper/gradle-wrapper.properties +++ b/app/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ -#Tue Dec 19 14:27:03 CET 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From b4c95b5c48003cdb1f128f92f9b6981fe5341fe5 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Wed, 20 Dec 2023 12:32:13 +0100 Subject: [PATCH 04/24] feat(#636): update iOS build Impeller settings --- app/ios/Runner/Info.plist | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/ios/Runner/Info.plist b/app/ios/Runner/Info.plist index d5c9bdd0d..3bffd80b8 100644 --- a/app/ios/Runner/Info.plist +++ b/app/ios/Runner/Info.plist @@ -51,8 +51,10 @@ LSSupportsOpeningDocumentsInPlace UIFileSharingEnabled - + UIApplicationSupportsIndirectInputEvents + FLTEnableImpeller + From 54288a840b9a456228dcdd3d4f0667de3da61068 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Wed, 20 Dec 2023 12:47:40 +0100 Subject: [PATCH 05/24] fix(#636): update routing --- app/lib/app.dart | 18 +----------------- app/lib/common/utilities/routing_utils.dart | 16 ++++++++++++++++ app/lib/report/module.dart | 6 +++++- app/lib/search/module.dart | 10 ++++++++-- 4 files changed, 30 insertions(+), 20 deletions(-) diff --git a/app/lib/app.dart b/app/lib/app.dart index 2a443f72c..35709ac32 100644 --- a/app/lib/app.dart +++ b/app/lib/app.dart @@ -12,29 +12,13 @@ class PharMeApp extends StatelessWidget { _instance._appRouter.navigatorKey; final _appRouter = AppRouter(); - final _isLoggedIn = MetaData.instance.isLoggedIn ?? false; - final _onboardingDone = MetaData.instance.onboardingDone ?? false; - final _initialDrugSelectionDone = - MetaData.instance.initialDrugSelectionDone ?? false; @override Widget build(BuildContext context) { return MaterialApp.router( debugShowCheckedModeBanner: false, routeInformationParser: _appRouter.defaultRouteParser(), - routerDelegate: _appRouter.delegate( - deepLinkBuilder: (deepLink) { - late String path; - path = !_isLoggedIn - ? 'login' - : !_onboardingDone - ? 'onboarding' - : !_initialDrugSelectionDone - ? 'drugselection' - : 'main'; - return DeepLink.path(path); - }, - ), + routerDelegate: _appRouter.delegate(deepLinkBuilder: getInitialRoute), theme: PharMeTheme.light, localizationsDelegates: [ AppLocalizations.delegate, diff --git a/app/lib/common/utilities/routing_utils.dart b/app/lib/common/utilities/routing_utils.dart index 2fe94e68a..2bd7d9241 100644 --- a/app/lib/common/utilities/routing_utils.dart +++ b/app/lib/common/utilities/routing_utils.dart @@ -1,5 +1,21 @@ import '../module.dart'; +DeepLink getInitialRoute(_) { + final isLoggedIn = MetaData.instance.isLoggedIn ?? false; + final onboardingDone = MetaData.instance.onboardingDone ?? false; + final initialDrugSelectionDone = + MetaData.instance.initialDrugSelectionDone ?? false; + late String path; + path = !isLoggedIn + ? '/login' + : !onboardingDone + ? '/onboarding' + : !initialDrugSelectionDone + ? '/drugselection' + : '/main'; + return DeepLink.path(path); +} + // Replace whole stack, see https://stackoverflow.com/a/73784156 Future overwriteRoutes( BuildContext context, diff --git a/app/lib/report/module.dart b/app/lib/report/module.dart index afb997b0a..80145bc7d 100644 --- a/app/lib/report/module.dart +++ b/app/lib/report/module.dart @@ -5,10 +5,14 @@ import '../drug/module.dart'; export 'pages/gene.dart'; export 'pages/report.dart'; +@RoutePage() +class ReportRootPage extends AutoRouter {} + final reportRoute = AutoRoute( path: 'report', - page: ReportRoute.page, + page: ReportRootRoute.page, children: [ + AutoRoute(path: '', page: ReportRoute.page), AutoRoute(page: GeneRoute.page), drugRoute(), ], diff --git a/app/lib/search/module.dart b/app/lib/search/module.dart index d8964ae24..19ebbb3b1 100644 --- a/app/lib/search/module.dart +++ b/app/lib/search/module.dart @@ -4,8 +4,14 @@ import '../drug/module.dart'; // For generated routes export 'pages/search.dart'; +@RoutePage() +class SearchRootPage extends AutoRouter {} + final searchRoute = AutoRoute( path: 'search', - page: SearchRoute.page, - children: [ drugRoute() ], + page: SearchRootRoute.page, + children: [ + AutoRoute(path: '', page: SearchRoute.page), + drugRoute() + ], ); \ No newline at end of file From 773703ad7848d35ed8fe27384aa50b5d1c3cee00 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Wed, 20 Dec 2023 12:48:48 +0100 Subject: [PATCH 06/24] chore(#636): ignore devtools options file --- app/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/app/.gitignore b/app/.gitignore index a80a374fb..4ff0d8334 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -30,6 +30,7 @@ .pub-cache/ .pub/ /build/ +devtools_options.yaml # Web related lib/generated_plugin_registrant.dart From 87fc0760ee3c85701decffe74c97ca1dea3f9620 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Wed, 20 Dec 2023 13:07:03 +0100 Subject: [PATCH 07/24] refactor(#646): use adaptive dialog wrapper --- app/lib/common/utilities/drug_utils.dart | 8 +-- app/lib/common/widgets/adaptive_dialog.dart | 66 +++++++++++++++++++ app/lib/common/widgets/module.dart | 1 + .../drug/widgets/annotation_cards/drug.dart | 16 ++--- app/lib/settings/pages/settings.dart | 19 +++--- 5 files changed, 87 insertions(+), 23 deletions(-) create mode 100644 app/lib/common/widgets/adaptive_dialog.dart diff --git a/app/lib/common/utilities/drug_utils.dart b/app/lib/common/utilities/drug_utils.dart index ee638dc38..76eaa3dc1 100644 --- a/app/lib/common/utilities/drug_utils.dart +++ b/app/lib/common/utilities/drug_utils.dart @@ -33,13 +33,13 @@ Future updateCachedDrugs() async { // ignore: use_build_context_synchronously await showAdaptiveDialog( context: context, - builder: (context) => AlertDialog.adaptive( - title: Text(context.l10n.update_warning_title), + builder: (context) => AdaptiveDialogWrapper( + title: context.l10n.update_warning_title, content: Text(context.l10n.update_warning_body), actions: [ - TextButton( + DialogAction( onPressed: () => Navigator.pop(context), - child: Text(context.l10n.action_continue), + text: context.l10n.action_continue, ), ], ), diff --git a/app/lib/common/widgets/adaptive_dialog.dart b/app/lib/common/widgets/adaptive_dialog.dart new file mode 100644 index 000000000..f179a1213 --- /dev/null +++ b/app/lib/common/widgets/adaptive_dialog.dart @@ -0,0 +1,66 @@ +import 'package:flutter/cupertino.dart'; + +import '../module.dart'; + +class AdaptiveDialogWrapper extends StatelessWidget { + const AdaptiveDialogWrapper({ + super.key, + required this.title, + required this.content, + required this.actions, + }); + + final String title; + final Widget? content; + final List actions; + + @override + Widget build(BuildContext context) { + final materialContent = getPlatform() == SupportedPlatform.ios + ? Card( + color: Colors.transparent, + elevation: 0, + child: content, + ) + : content; + return AlertDialog.adaptive( + title: Text(title), + content: materialContent, + actions: actions, + ); + } +} + +class DialogAction extends StatelessWidget { + const DialogAction({ + super.key, + this.isDestructive = false, + this.onPressed, + required this.text, + }); + + final bool isDestructive; + final void Function()? onPressed; + final String text; + + @override + Widget build(BuildContext context) { + switch (getPlatform()) { + case SupportedPlatform.ios: + return CupertinoDialogAction( + isDestructiveAction: isDestructive, + onPressed: onPressed, + child: Text(text), + ); + default: + return TextButton( + onPressed: onPressed, + child: Text(text, style: onPressed != null + ? isDestructive + ? TextStyle(color: PharMeTheme.errorColor) + : TextStyle(color: PharMeTheme.primaryColor) + : TextStyle(color: PharMeTheme.onSurfaceColor)), + ); + } + } +} diff --git a/app/lib/common/widgets/module.dart b/app/lib/common/widgets/module.dart index 6f4f4c1ac..2ffd2c647 100644 --- a/app/lib/common/widgets/module.dart +++ b/app/lib/common/widgets/module.dart @@ -1,3 +1,4 @@ +export 'adaptive_dialog.dart'; export 'context_menu.dart'; export 'drug_list/builder.dart'; export 'drug_list/cubit.dart'; diff --git a/app/lib/drug/widgets/annotation_cards/drug.dart b/app/lib/drug/widgets/annotation_cards/drug.dart index c75e74a29..45c447a02 100644 --- a/app/lib/drug/widgets/annotation_cards/drug.dart +++ b/app/lib/drug/widgets/annotation_cards/drug.dart @@ -52,23 +52,21 @@ class DrugAnnotationCard extends StatelessWidget { if (isInhibitor(drug.name)) { showAdaptiveDialog( context: context, - builder: (context) => AlertDialog.adaptive( - title: Text(context.l10n.drugs_page_active_warn_header), + builder: (context) => AdaptiveDialogWrapper( + title: context.l10n.drugs_page_active_warn_header, content: Text(context.l10n.drugs_page_active_warn), actions: [ - TextButton( + DialogAction( onPressed: () => Navigator.pop(context, 'Cancel'), - child: Text(context.l10n.action_cancel), + text: context.l10n.action_cancel, ), - TextButton( + DialogAction( onPressed: () { Navigator.pop(context, 'OK'); setActivity(value: newValue); }, - child: Text( - context.l10n.action_continue, - style: TextStyle(color: PharMeTheme.errorColor), - ), + text: context.l10n.action_continue, + isDestructive: true, ), ], ), diff --git a/app/lib/settings/pages/settings.dart b/app/lib/settings/pages/settings.dart index 10397dfe2..b26f5a1ab 100644 --- a/app/lib/settings/pages/settings.dart +++ b/app/lib/settings/pages/settings.dart @@ -75,8 +75,8 @@ class DeleteDataDialog extends HookWidget { Widget build(BuildContext context) { final agreedToDeletion = useState(false); - return AlertDialog.adaptive( - title: Text(context.l10n.settings_page_delete_data), + return AdaptiveDialogWrapper( + title: context.l10n.settings_page_delete_data, content: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -91,13 +91,14 @@ class DeleteDataDialog extends HookWidget { contentPadding: EdgeInsets.zero, activeColor: PharMeTheme.primaryColor, ), - ]), + ], + ), actions: [ - TextButton( + DialogAction( onPressed: context.router.root.pop, - child: Text(context.l10n.action_cancel), + text: context.l10n.action_cancel, ), - TextButton( + DialogAction( onPressed: agreedToDeletion.value ? () async { await deleteAllAppData(); @@ -105,10 +106,8 @@ class DeleteDataDialog extends HookWidget { await overwriteRoutes(context, nextPage: LoginRoute()); } : null, - child: Text( - context.l10n.action_continue, - style: TextStyle(color: PharMeTheme.errorColor), - ), + text: context.l10n.action_continue, + isDestructive: true, ), ], ); From 136fce86fe64a02ec0c3b9c4ed5e3f9cec5ef14d Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Wed, 20 Dec 2023 13:45:03 +0100 Subject: [PATCH 08/24] refactor(#636): routing modules return functions --- app/CONTRIBUTING.md | 64 ++++++++++++++----- app/lib/common/routing/router.dart | 24 +++---- app/lib/drug_selection/module.dart | 2 +- app/lib/faq/module.dart | 2 +- app/lib/login/module.dart | 2 +- app/lib/main/pages/main.dart | 2 +- app/lib/onboarding/module.dart | 2 +- app/lib/report/module.dart | 8 +-- app/lib/search/module.dart | 5 +- app/lib/settings/module.dart | 26 +++++--- .../pages/{about_us.dart => about.dart} | 4 +- .../pages/{settings.dart => more.dart} | 10 +-- .../{privacy_policy.dart => privacy.dart} | 4 +- .../{terms_and_conditions.dart => terms.dart} | 4 +- 14 files changed, 99 insertions(+), 60 deletions(-) rename app/lib/settings/pages/{about_us.dart => about.dart} (85%) rename app/lib/settings/pages/{settings.dart => more.dart} (92%) rename app/lib/settings/pages/{privacy_policy.dart => privacy.dart} (83%) rename app/lib/settings/pages/{terms_and_conditions.dart => terms.dart} (84%) diff --git a/app/CONTRIBUTING.md b/app/CONTRIBUTING.md index 417eb4d2b..4ffd002dd 100644 --- a/app/CONTRIBUTING.md +++ b/app/CONTRIBUTING.md @@ -30,36 +30,68 @@ alias flutter-clean='find . -maxdepth 20 -type f \( -name "*.inject.summary" -o ## Architecture -The app consists of multiple so-called modules. Our main modules correspond to -the direct subfolders of `lib/`. +The app consists of multiple so-called modules. Our main modules (usually app +screens) correspond to the direct subfolders of `lib/`. -### Example Module +Common functions used by modules such as `models`, `widgets`, and `utilities` +are living in `common`. All such functions are exported from +`common/module.dart`. -Structure of `lib/my_module`: +The structure of an example module `lib/my_module` should look as follows: - `my_module` - `module.dart`: - - exports everything that is required by other modules - - declares all routes as a const variable (`myModuleRoutes`) + - exports everything that is required by other modules, i.e., page(s) and + possibly the cubit + - declares all routes as functions `myModuleRoute` (see example below) - may contain initialization code (`initMyModule()`) - - `cubit.dart`: contains `MyModuleCubit` and `MyModuleState`s - `widgets`: - `my_widget.dart`: contains `MyWidget` and helpers - `pages`: - - `my_first.dart`: contains `MyFirstPage` and helpers - - `my_complex`: create a folder for complex pages (e.g., tabbed ones) + - `my_module.dart`: contains `MyModulePage` and helpers + - `my_child_page.dart`: contains + - `my_complex_page`: create a folder for complex pages (e.g., tabbed ones) - `page.dart`: contains `MyComplexPage` - `tab_first.dart`: contains `FirstTab` and helpers - `tab_second.dart`: contains `SecondTab` and helpers - `utils.dart`: contains utilities used by multiple files in this page - `utils.dart`: contains utilities used throughout this module - - `submodule_one` - - `submodule_two` - -If a single file gets too complex for routes, the `Cubit`, a widget, a page, -etc., you can create a folder with the same name and split the original file -into different files. An example of that is `MyComplexPage` in the file tree -above. + - `cubit.dart`: contains `MyModuleCubit` and `MyModuleState`s (if needed) + +Example for `my_module/module.dart`; the page is used as a root page in the tab +router, which is why the empty router `MyModuleRootPage` and adding +`AutoRoute(path: '', page: MyModuleRoute.page)` to children is needed. + +```dart +import '../common/module.dart'; + +// For generated routes +export 'cubit.dart'; +export 'pages/my_module.dart'; +export 'pages/my_child_page.dart'; +export 'pages/my_complex_page/page.dart'; + +@RoutePage() +class MyModuleRootPage extends AutoRouter {} + +AutoRoute myChildRoute() => AutoRoute( + path: 'my_child', + page: MyChildRoute.page, +); +AutoRoute myComplexRoute() => AutoRoute( + path: 'my_complex', + page: MyComplexRoute.page, +); + +AutoRoute myModuleRoute({ required List children }) => AutoRoute( + path: 'my_module', + page: MyModuleRootRoute.page, + children: [ + AutoRoute(path: '', page: MyModuleRoute.page), + ...children, // includes myChildRoute() and priva + ], +); +``` ## Making app icons diff --git a/app/lib/common/routing/router.dart b/app/lib/common/routing/router.dart index a5c4eec40..4d55f02a7 100644 --- a/app/lib/common/routing/router.dart +++ b/app/lib/common/routing/router.dart @@ -1,11 +1,9 @@ -// Imports for generated routes import '../../drug/module.dart'; import '../../drug_selection/module.dart'; import '../../faq/module.dart'; import '../../login/module.dart'; import '../../main/module.dart'; import '../../onboarding/module.dart'; - import '../../report/module.dart'; import '../../search/module.dart'; import '../../settings/module.dart'; @@ -19,14 +17,18 @@ class AppRouter extends _$AppRouter { RouteType get defaultRouteType => RouteType.adaptive(); @override List get routes => [ - drugSelectionRoute, - loginRoute, - onboardingRoute, - mainRoute(children: [ - reportRoute, - searchRoute, - settingsRoute, - faqRoute, - ]), + drugSelectionRoute(), + loginRoute(), + onboardingRoute(), + mainRoute( + children: [ + reportRoute(children: [ geneRoute(), drugRoute() ]), + searchRoute(children: [ drugRoute() ]), + faqRoute(), + moreRoute( + children: [ aboutRoute(), termsRoute(), privacyRoute() ], + ), + ], + ), ]; } diff --git a/app/lib/drug_selection/module.dart b/app/lib/drug_selection/module.dart index fd7d514c5..1b00a4161 100644 --- a/app/lib/drug_selection/module.dart +++ b/app/lib/drug_selection/module.dart @@ -4,7 +4,7 @@ import '../common/module.dart'; export 'cubit.dart'; export 'pages/drug_selection.dart'; -final drugSelectionRoute = AutoRoute( +AutoRoute drugSelectionRoute() => AutoRoute( path: '/drugselection', page: DrugSelectionRoute.page, ); \ No newline at end of file diff --git a/app/lib/faq/module.dart b/app/lib/faq/module.dart index 58fe08c8b..492767c0d 100644 --- a/app/lib/faq/module.dart +++ b/app/lib/faq/module.dart @@ -3,4 +3,4 @@ import '../common/module.dart'; // For generated routes export 'pages/faq.dart'; -final faqRoute = AutoRoute(page: FaqRoute.page); \ No newline at end of file +AutoRoute faqRoute() => AutoRoute(page: FaqRoute.page); \ No newline at end of file diff --git a/app/lib/login/module.dart b/app/lib/login/module.dart index b9e187a15..ad844be59 100644 --- a/app/lib/login/module.dart +++ b/app/lib/login/module.dart @@ -4,4 +4,4 @@ import '../common/module.dart'; export 'cubit.dart'; export 'pages/login.dart'; -final loginRoute = AutoRoute(path: '/login', page: LoginRoute.page); \ No newline at end of file +AutoRoute loginRoute() => AutoRoute(path: '/login', page: LoginRoute.page); \ No newline at end of file diff --git a/app/lib/main/pages/main.dart b/app/lib/main/pages/main.dart index 1ffad4014..98db08039 100644 --- a/app/lib/main/pages/main.dart +++ b/app/lib/main/pages/main.dart @@ -29,7 +29,7 @@ List getTabRoutesDefinition(BuildContext context) { icon: Icon(Icons.lightbulb_rounded), ), TabRouteDefinition( - pageRouteInfo: SettingsRoute(), + pageRouteInfo: MoreRoute(), label: context.l10n.nav_more, icon: Icon(Icons.more_horiz_rounded), ), diff --git a/app/lib/onboarding/module.dart b/app/lib/onboarding/module.dart index 506f8d391..1b99465f7 100644 --- a/app/lib/onboarding/module.dart +++ b/app/lib/onboarding/module.dart @@ -3,7 +3,7 @@ import '../common/module.dart'; // For generated route export 'pages/onboarding.dart'; -final onboardingRoute = AutoRoute( +AutoRoute onboardingRoute() => AutoRoute( path: '/onboarding', page: OnboardingRoute.page, ); \ No newline at end of file diff --git a/app/lib/report/module.dart b/app/lib/report/module.dart index 80145bc7d..a2fc0a7bb 100644 --- a/app/lib/report/module.dart +++ b/app/lib/report/module.dart @@ -1,5 +1,4 @@ import '../common/module.dart'; -import '../drug/module.dart'; // For generated routes export 'pages/gene.dart'; @@ -8,12 +7,13 @@ export 'pages/report.dart'; @RoutePage() class ReportRootPage extends AutoRouter {} -final reportRoute = AutoRoute( +AutoRoute geneRoute() => AutoRoute(page: GeneRoute.page); + +AutoRoute reportRoute({ required List children }) => AutoRoute( path: 'report', page: ReportRootRoute.page, children: [ AutoRoute(path: '', page: ReportRoute.page), - AutoRoute(page: GeneRoute.page), - drugRoute(), + ...children, ], ); diff --git a/app/lib/search/module.dart b/app/lib/search/module.dart index 19ebbb3b1..c98075765 100644 --- a/app/lib/search/module.dart +++ b/app/lib/search/module.dart @@ -1,5 +1,4 @@ import '../common/module.dart'; -import '../drug/module.dart'; // For generated routes export 'pages/search.dart'; @@ -7,11 +6,11 @@ export 'pages/search.dart'; @RoutePage() class SearchRootPage extends AutoRouter {} -final searchRoute = AutoRoute( +AutoRoute searchRoute({ required List children }) => AutoRoute( path: 'search', page: SearchRootRoute.page, children: [ AutoRoute(path: '', page: SearchRoute.page), - drugRoute() + ...children, ], ); \ No newline at end of file diff --git a/app/lib/settings/module.dart b/app/lib/settings/module.dart index c87094de1..a18bcc3de 100644 --- a/app/lib/settings/module.dart +++ b/app/lib/settings/module.dart @@ -1,17 +1,23 @@ import '../common/module.dart'; // For generated routes -export 'pages/about_us.dart'; -export 'pages/privacy_policy.dart'; -export 'pages/settings.dart'; -export 'pages/terms_and_conditions.dart'; +export 'pages/about.dart'; +export 'pages/more.dart'; +export 'pages/privacy.dart'; +export 'pages/terms.dart'; -final settingsRoute = AutoRoute( - path: 'settings', - page: SettingsRoute.page, +@RoutePage() +class MoreRootPage extends AutoRouter {} + +AutoRoute aboutRoute() => AutoRoute(path: 'about', page: AboutRoute.page); +AutoRoute privacyRoute() => AutoRoute(path: 'privacy', page: PrivacyRoute.page); +AutoRoute termsRoute() => AutoRoute(path: 'terms', page: TermsRoute.page); + +AutoRoute moreRoute({ required List children }) => AutoRoute( + path: 'more', + page: MoreRootRoute.page, children: [ - AutoRoute(path: 'about', page: AboutUsRoute.page), - AutoRoute(path: 'privacy', page: PrivacyPolicyRoute.page), - AutoRoute(path: 'terms', page: TermsAndConditionsRoute.page), + AutoRoute(path: '', page: MoreRoute.page), + ...children, ], ); \ No newline at end of file diff --git a/app/lib/settings/pages/about_us.dart b/app/lib/settings/pages/about.dart similarity index 85% rename from app/lib/settings/pages/about_us.dart rename to app/lib/settings/pages/about.dart index be60fb479..852bac33d 100644 --- a/app/lib/settings/pages/about_us.dart +++ b/app/lib/settings/pages/about.dart @@ -1,8 +1,8 @@ import '../../common/module.dart'; @RoutePage() -class AboutUsPage extends StatelessWidget { - const AboutUsPage({super.key}); +class AboutPage extends StatelessWidget { + const AboutPage({super.key}); @override Widget build(BuildContext context) { diff --git a/app/lib/settings/pages/settings.dart b/app/lib/settings/pages/more.dart similarity index 92% rename from app/lib/settings/pages/settings.dart rename to app/lib/settings/pages/more.dart index b26f5a1ab..772993f30 100644 --- a/app/lib/settings/pages/settings.dart +++ b/app/lib/settings/pages/more.dart @@ -2,8 +2,8 @@ import '../../common/module.dart'; import '../utils.dart'; @RoutePage() -class SettingsPage extends StatelessWidget { - const SettingsPage({super.key}); +class MorePage extends StatelessWidget { + const MorePage({super.key}); @override Widget build(BuildContext context) { @@ -48,17 +48,17 @@ class SettingsPage extends StatelessWidget { ListTile( title: Text(context.l10n.settings_page_about_us), trailing: Icon(Icons.chevron_right_rounded), - onTap: () => context.router.push(AboutUsRoute()), + onTap: () => context.router.push(AboutRoute()), ), ListTile( title: Text(context.l10n.settings_page_privacy_policy), trailing: Icon(Icons.chevron_right_rounded), - onTap: () => context.router.push(PrivacyPolicyRoute()), + onTap: () => context.router.push(PrivacyRoute()), ), ListTile( title: Text(context.l10n.settings_page_terms_and_conditions), trailing: Icon(Icons.chevron_right_rounded), - onTap: () => context.router.push(TermsAndConditionsRoute()), + onTap: () => context.router.push(TermsRoute()), ), Divider(), ListTile( diff --git a/app/lib/settings/pages/privacy_policy.dart b/app/lib/settings/pages/privacy.dart similarity index 83% rename from app/lib/settings/pages/privacy_policy.dart rename to app/lib/settings/pages/privacy.dart index df2069088..f5125ec85 100644 --- a/app/lib/settings/pages/privacy_policy.dart +++ b/app/lib/settings/pages/privacy.dart @@ -1,8 +1,8 @@ import '../../common/module.dart'; @RoutePage() -class PrivacyPolicyPage extends StatelessWidget { - const PrivacyPolicyPage({super.key}); +class PrivacyPage extends StatelessWidget { + const PrivacyPage({super.key}); @override Widget build(BuildContext context) { diff --git a/app/lib/settings/pages/terms_and_conditions.dart b/app/lib/settings/pages/terms.dart similarity index 84% rename from app/lib/settings/pages/terms_and_conditions.dart rename to app/lib/settings/pages/terms.dart index efcceb92d..8844f17ea 100644 --- a/app/lib/settings/pages/terms_and_conditions.dart +++ b/app/lib/settings/pages/terms.dart @@ -1,8 +1,8 @@ import '../../common/module.dart'; @RoutePage() -class TermsAndConditionsPage extends StatelessWidget { - const TermsAndConditionsPage({super.key}); +class TermsPage extends StatelessWidget { + const TermsPage({super.key}); @override Widget build(BuildContext context) { From bc28f2e371f9e7fe1a9800607de8160195fc4bfe Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Wed, 20 Dec 2023 14:13:23 +0100 Subject: [PATCH 09/24] chore(#636): update routing in tests --- app/CONTRIBUTING.md | 11 ++++------- app/integration_test/faq_test.dart | 2 +- app/integration_test/main_page_test.dart | 2 +- .../{settings_test.dart => more_test.dart} | 2 +- app/lib/common/routing/router.dart | 2 +- app/lib/faq/module.dart | 2 +- app/lib/{settings => more}/module.dart | 0 app/lib/{settings => more}/pages/about.dart | 0 app/lib/{settings => more}/pages/more.dart | 0 app/lib/{settings => more}/pages/privacy.dart | 0 app/lib/{settings => more}/pages/terms.dart | 0 app/lib/{settings => more}/utils.dart | 0 12 files changed, 9 insertions(+), 12 deletions(-) rename app/integration_test/{settings_test.dart => more_test.dart} (97%) rename app/lib/{settings => more}/module.dart (100%) rename app/lib/{settings => more}/pages/about.dart (100%) rename app/lib/{settings => more}/pages/more.dart (100%) rename app/lib/{settings => more}/pages/privacy.dart (100%) rename app/lib/{settings => more}/pages/terms.dart (100%) rename app/lib/{settings => more}/utils.dart (100%) diff --git a/app/CONTRIBUTING.md b/app/CONTRIBUTING.md index 4ffd002dd..1c47088e6 100644 --- a/app/CONTRIBUTING.md +++ b/app/CONTRIBUTING.md @@ -40,21 +40,18 @@ are living in `common`. All such functions are exported from The structure of an example module `lib/my_module` should look as follows: - `my_module` - - `module.dart`: + - `module.dart` (see example below): - exports everything that is required by other modules, i.e., page(s) and possibly the cubit - - declares all routes as functions `myModuleRoute` (see example below) + - declares all routes as functions reeturning `AutoRoute` - may contain initialization code (`initMyModule()`) - `widgets`: - `my_widget.dart`: contains `MyWidget` and helpers - `pages`: - `my_module.dart`: contains `MyModulePage` and helpers - `my_child_page.dart`: contains - - `my_complex_page`: create a folder for complex pages (e.g., tabbed ones) - - `page.dart`: contains `MyComplexPage` - - `tab_first.dart`: contains `FirstTab` and helpers - - `tab_second.dart`: contains `SecondTab` and helpers - - `utils.dart`: contains utilities used by multiple files in this page + - `my_complex_page`: create a folder for complex pages (e.g., tabbed ones); + might want to create an own module if getting too complex - `utils.dart`: contains utilities used throughout this module - `cubit.dart`: contains `MyModuleCubit` and `MyModuleState`s (if needed) diff --git a/app/integration_test/faq_test.dart b/app/integration_test/faq_test.dart index d031ece8f..f54190a66 100644 --- a/app/integration_test/faq_test.dart +++ b/app/integration_test/faq_test.dart @@ -14,7 +14,7 @@ void main() { final faqWidget = MaterialApp.router( routeInformationParser: appRouter.defaultRouteParser(), routerDelegate: appRouter.delegate( - deepLinkBuilder: (_) => DeepLink.path('main/faq'), + deepLinkBuilder: (_) => DeepLink.path('/main/faq'), ), localizationsDelegates: [ AppLocalizations.delegate, diff --git a/app/integration_test/main_page_test.dart b/app/integration_test/main_page_test.dart index d7d0c61d6..3e678c74e 100644 --- a/app/integration_test/main_page_test.dart +++ b/app/integration_test/main_page_test.dart @@ -25,7 +25,7 @@ void main() { child: MaterialApp.router( routeInformationParser: appRouter.defaultRouteParser(), routerDelegate: appRouter.delegate( - deepLinkBuilder: (_) => DeepLink.path('main'), + deepLinkBuilder: (_) => DeepLink.path('/main'), ), localizationsDelegates: [ AppLocalizations.delegate, diff --git a/app/integration_test/settings_test.dart b/app/integration_test/more_test.dart similarity index 97% rename from app/integration_test/settings_test.dart rename to app/integration_test/more_test.dart index a8ee94e62..02b7b2633 100644 --- a/app/integration_test/settings_test.dart +++ b/app/integration_test/more_test.dart @@ -15,7 +15,7 @@ void main() { debugShowCheckedModeBanner: false, routeInformationParser: appRouter.defaultRouteParser(), routerDelegate: appRouter.delegate( - deepLinkBuilder: (_) => DeepLink.path('main/settings'), + deepLinkBuilder: (_) => DeepLink.path('/main/more'), ), localizationsDelegates: [ AppLocalizations.delegate, diff --git a/app/lib/common/routing/router.dart b/app/lib/common/routing/router.dart index 4d55f02a7..95d0bffee 100644 --- a/app/lib/common/routing/router.dart +++ b/app/lib/common/routing/router.dart @@ -3,10 +3,10 @@ import '../../drug_selection/module.dart'; import '../../faq/module.dart'; import '../../login/module.dart'; import '../../main/module.dart'; +import '../../more/module.dart'; import '../../onboarding/module.dart'; import '../../report/module.dart'; import '../../search/module.dart'; -import '../../settings/module.dart'; import '../module.dart'; part 'router.gr.dart'; diff --git a/app/lib/faq/module.dart b/app/lib/faq/module.dart index 492767c0d..38c656133 100644 --- a/app/lib/faq/module.dart +++ b/app/lib/faq/module.dart @@ -3,4 +3,4 @@ import '../common/module.dart'; // For generated routes export 'pages/faq.dart'; -AutoRoute faqRoute() => AutoRoute(page: FaqRoute.page); \ No newline at end of file +AutoRoute faqRoute() => AutoRoute(path: 'faq', page: FaqRoute.page); \ No newline at end of file diff --git a/app/lib/settings/module.dart b/app/lib/more/module.dart similarity index 100% rename from app/lib/settings/module.dart rename to app/lib/more/module.dart diff --git a/app/lib/settings/pages/about.dart b/app/lib/more/pages/about.dart similarity index 100% rename from app/lib/settings/pages/about.dart rename to app/lib/more/pages/about.dart diff --git a/app/lib/settings/pages/more.dart b/app/lib/more/pages/more.dart similarity index 100% rename from app/lib/settings/pages/more.dart rename to app/lib/more/pages/more.dart diff --git a/app/lib/settings/pages/privacy.dart b/app/lib/more/pages/privacy.dart similarity index 100% rename from app/lib/settings/pages/privacy.dart rename to app/lib/more/pages/privacy.dart diff --git a/app/lib/settings/pages/terms.dart b/app/lib/more/pages/terms.dart similarity index 100% rename from app/lib/settings/pages/terms.dart rename to app/lib/more/pages/terms.dart diff --git a/app/lib/settings/utils.dart b/app/lib/more/utils.dart similarity index 100% rename from app/lib/settings/utils.dart rename to app/lib/more/utils.dart From 436d215b15639da1884309644ed6665158f3a0ae Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Wed, 20 Dec 2023 14:42:38 +0100 Subject: [PATCH 10/24] chore(docs): add idea how to enable screenshot generation --- app/generate_screenshots/app_test.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/generate_screenshots/app_test.dart b/app/generate_screenshots/app_test.dart index 05964864c..cf1cddd59 100644 --- a/app/generate_screenshots/app_test.dart +++ b/app/generate_screenshots/app_test.dart @@ -52,6 +52,8 @@ void main() { await takeScreenshot(tester, binding, 'login'); // login-redirect (not working; only taking screenshot of loading screen) + // could try to use cubit function to directly sign in which will only + // open the webview and close it again // await tester.tap(find.byType(FullWidthButton).first); // await Future.delayed(Duration(seconds: 3)); // wait for dialog // await takeScreenshot(tester, binding, 'login-redirect'); From 6a7743e3c30d62ade0a2113a18b33f5d684ba643 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Wed, 20 Dec 2023 16:21:58 +0100 Subject: [PATCH 11/24] feat(#636, #681): adapt checkboxes --- app/integration_test/drugs_test.dart | 8 ++-- .../widgets/checkbox_list_tile_wrapper.dart | 41 +++++++++++++++++++ .../drug_items/drug_checkbox_list.dart | 12 +++--- app/lib/common/widgets/module.dart | 1 + .../drug/widgets/annotation_cards/drug.dart | 7 ++-- app/lib/more/pages/more.dart | 7 ++-- 6 files changed, 58 insertions(+), 18 deletions(-) create mode 100644 app/lib/common/widgets/checkbox_list_tile_wrapper.dart diff --git a/app/integration_test/drugs_test.dart b/app/integration_test/drugs_test.dart index f3e9ff85b..40c18c6ea 100644 --- a/app/integration_test/drugs_test.dart +++ b/app/integration_test/drugs_test.dart @@ -139,8 +139,8 @@ void main() { context = tester.element(find.byType(Tooltip).first); // test that drug activity can be set - final checkbox = tester.widget(find.byType(CheckboxListTile)) - as CheckboxListTile; + final checkbox = tester.widget(find.byType(CheckboxListTileWrapper)) + as CheckboxListTileWrapper; expect(checkbox.onChanged, isNotNull); // test tooltips @@ -202,8 +202,8 @@ void main() { ), ); - final checkbox = tester.widget(find.byType(CheckboxListTile)) - as CheckboxListTile; + final checkbox = tester.widget(find.byType(CheckboxListTileWrapper)) + as CheckboxListTileWrapper; expect(checkbox.onChanged, isNull); }); }); diff --git a/app/lib/common/widgets/checkbox_list_tile_wrapper.dart b/app/lib/common/widgets/checkbox_list_tile_wrapper.dart new file mode 100644 index 000000000..b8e09a4f4 --- /dev/null +++ b/app/lib/common/widgets/checkbox_list_tile_wrapper.dart @@ -0,0 +1,41 @@ +import '../module.dart'; + +class CheckboxListTileWrapper extends StatelessWidget { + const CheckboxListTileWrapper({ + super.key, + required this.title, + required this.isChecked, + required this.onChanged, + this.subtitle, + this.isEnabled = true, + this.controlAffinity = ListTileControlAffinity.platform, + this.contentPadding, + this.activeColor, + }); + + final String title; + final String? subtitle; + final bool isChecked; + // ignore: avoid_positional_boolean_parameters + final void Function(bool?)? onChanged; + final bool isEnabled; + final ListTileControlAffinity controlAffinity; + final EdgeInsetsGeometry? contentPadding; + final Color? activeColor; + + @override + Widget build(BuildContext context) { + return CheckboxListTile.adaptive( + enabled: isEnabled, + value: isChecked, + onChanged: onChanged, + title: Text(title, style: PharMeTheme.textTheme.bodyLarge), + subtitle: subtitle != null + ? Text(subtitle!, style: PharMeTheme.textTheme.bodyMedium) + : null, + controlAffinity: controlAffinity, + contentPadding: contentPadding, + activeColor: activeColor ?? PharMeTheme.primaryColor, + ); } + +} \ No newline at end of file diff --git a/app/lib/common/widgets/drug_list/drug_items/drug_checkbox_list.dart b/app/lib/common/widgets/drug_list/drug_items/drug_checkbox_list.dart index cbffffd57..fd900c0db 100644 --- a/app/lib/common/widgets/drug_list/drug_items/drug_checkbox_list.dart +++ b/app/lib/common/widgets/drug_list/drug_items/drug_checkbox_list.dart @@ -45,7 +45,7 @@ List buildDrugCheckboxList( ]; } -List _buildCheckboxList( +List _buildCheckboxList( List drugs, Map buildParams, bool showDrugInteractionIndicator, @@ -54,14 +54,14 @@ List _buildCheckboxList( final onCheckboxChange = buildParams['onCheckboxChange']; final checkboxesEnabled = buildParams['checkboxesEnabled']; return drugs.map( - (drug) => CheckboxListTile( + (drug) => CheckboxListTileWrapper( key: Key('drug-checkbox-tile-${drug.name}-$keyPrefix'), - enabled: checkboxesEnabled, - value: drug.isActive, + isEnabled: checkboxesEnabled, + isChecked: drug.isActive, onChanged: (value) => onCheckboxChange(drug, value), - title: Text(formatDrugName(drug, showDrugInteractionIndicator)), + title: formatDrugName(drug, showDrugInteractionIndicator), subtitle: (drug.annotations.brandNames.isNotEmpty) ? - Text('(${drug.annotations.brandNames.join(", ")})') : + '(${drug.annotations.brandNames.join(", ")})' : null, ) ).toList(); diff --git a/app/lib/common/widgets/module.dart b/app/lib/common/widgets/module.dart index 2ffd2c647..2860373f9 100644 --- a/app/lib/common/widgets/module.dart +++ b/app/lib/common/widgets/module.dart @@ -1,4 +1,5 @@ export 'adaptive_dialog.dart'; +export 'checkbox_list_tile_wrapper.dart'; export 'context_menu.dart'; export 'drug_list/builder.dart'; export 'drug_list/cubit.dart'; diff --git a/app/lib/drug/widgets/annotation_cards/drug.dart b/app/lib/drug/widgets/annotation_cards/drug.dart index 45c447a02..48c08deac 100644 --- a/app/lib/drug/widgets/annotation_cards/drug.dart +++ b/app/lib/drug/widgets/annotation_cards/drug.dart @@ -44,10 +44,9 @@ class DrugAnnotationCard extends StatelessWidget { Divider(color: PharMeTheme.borderColor), SizedBox(height: 4), SubHeader(context.l10n.drugs_page_header_active), - CheckboxListTile( - activeColor: PharMeTheme.primaryColor, - title: Text(context.l10n.drugs_page_active), - value: isActive, + CheckboxListTileWrapper( + title: context.l10n.drugs_page_active, + isChecked: isActive, onChanged: disabled ? null : (newValue) => { if (isInhibitor(drug.name)) { showAdaptiveDialog( diff --git a/app/lib/more/pages/more.dart b/app/lib/more/pages/more.dart index 772993f30..f21851942 100644 --- a/app/lib/more/pages/more.dart +++ b/app/lib/more/pages/more.dart @@ -82,14 +82,13 @@ class DeleteDataDialog extends HookWidget { children: [ Text(context.l10n.settings_page_delete_data_text), SizedBox(height: PharMeTheme.mediumSpace), - CheckboxListTile( - value: agreedToDeletion.value, + CheckboxListTileWrapper( + isChecked: agreedToDeletion.value, onChanged: (value) => agreedToDeletion.value = value ?? agreedToDeletion.value, - title: Text(context.l10n.settings_page_delete_data_confirmation), + title: context.l10n.settings_page_delete_data_confirmation, controlAffinity: ListTileControlAffinity.leading, contentPadding: EdgeInsets.zero, - activeColor: PharMeTheme.primaryColor, ), ], ), From 783f66359226271fc35384b09539fac5ac596178 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Wed, 20 Dec 2023 16:23:53 +0100 Subject: [PATCH 12/24] feat(#636): adapt dialog wrapper --- app/lib/common/utilities/drug_utils.dart | 4 +-- ...daptive_dialog.dart => dialog_action.dart} | 31 +------------------ .../common/widgets/dialog_content_text.dart | 14 +++++++++ app/lib/common/widgets/dialog_wrapper.dart | 30 ++++++++++++++++++ app/lib/common/widgets/full_width_button.dart | 9 ++---- app/lib/common/widgets/module.dart | 4 ++- .../drug/widgets/annotation_cards/drug.dart | 6 ++-- app/lib/more/pages/more.dart | 4 +-- 8 files changed, 59 insertions(+), 43 deletions(-) rename app/lib/common/widgets/{adaptive_dialog.dart => dialog_action.dart} (59%) create mode 100644 app/lib/common/widgets/dialog_content_text.dart create mode 100644 app/lib/common/widgets/dialog_wrapper.dart diff --git a/app/lib/common/utilities/drug_utils.dart b/app/lib/common/utilities/drug_utils.dart index 76eaa3dc1..53f1c4095 100644 --- a/app/lib/common/utilities/drug_utils.dart +++ b/app/lib/common/utilities/drug_utils.dart @@ -33,9 +33,9 @@ Future updateCachedDrugs() async { // ignore: use_build_context_synchronously await showAdaptiveDialog( context: context, - builder: (context) => AdaptiveDialogWrapper( + builder: (context) => DialogWrapper( title: context.l10n.update_warning_title, - content: Text(context.l10n.update_warning_body), + content: DialogContentText(context.l10n.update_warning_body), actions: [ DialogAction( onPressed: () => Navigator.pop(context), diff --git a/app/lib/common/widgets/adaptive_dialog.dart b/app/lib/common/widgets/dialog_action.dart similarity index 59% rename from app/lib/common/widgets/adaptive_dialog.dart rename to app/lib/common/widgets/dialog_action.dart index f179a1213..7c5155dcc 100644 --- a/app/lib/common/widgets/adaptive_dialog.dart +++ b/app/lib/common/widgets/dialog_action.dart @@ -2,35 +2,6 @@ import 'package:flutter/cupertino.dart'; import '../module.dart'; -class AdaptiveDialogWrapper extends StatelessWidget { - const AdaptiveDialogWrapper({ - super.key, - required this.title, - required this.content, - required this.actions, - }); - - final String title; - final Widget? content; - final List actions; - - @override - Widget build(BuildContext context) { - final materialContent = getPlatform() == SupportedPlatform.ios - ? Card( - color: Colors.transparent, - elevation: 0, - child: content, - ) - : content; - return AlertDialog.adaptive( - title: Text(title), - content: materialContent, - actions: actions, - ); - } -} - class DialogAction extends StatelessWidget { const DialogAction({ super.key, @@ -63,4 +34,4 @@ class DialogAction extends StatelessWidget { ); } } -} +} \ No newline at end of file diff --git a/app/lib/common/widgets/dialog_content_text.dart b/app/lib/common/widgets/dialog_content_text.dart new file mode 100644 index 000000000..f3185f360 --- /dev/null +++ b/app/lib/common/widgets/dialog_content_text.dart @@ -0,0 +1,14 @@ +import '../module.dart'; + +class DialogContentText extends StatelessWidget { + const DialogContentText(this.text, { + super.key, + }); + + final String text; + + @override + Widget build(BuildContext context) { + return Text(text, style: PharMeTheme.textTheme.bodyLarge); + } +} \ No newline at end of file diff --git a/app/lib/common/widgets/dialog_wrapper.dart b/app/lib/common/widgets/dialog_wrapper.dart new file mode 100644 index 000000000..29e9553a0 --- /dev/null +++ b/app/lib/common/widgets/dialog_wrapper.dart @@ -0,0 +1,30 @@ +import '../module.dart'; + +class DialogWrapper extends StatelessWidget { + const DialogWrapper({ + super.key, + required this.title, + required this.content, + required this.actions, + }); + + final String title; + final Widget? content; + final List actions; + + @override + Widget build(BuildContext context) { + final materialContent = getPlatform() == SupportedPlatform.ios + ? Card( + color: Colors.transparent, + elevation: 0, + child: content, + ) + : content; + return AlertDialog.adaptive( + title: Text(title), + content: materialContent, + actions: actions, + ); + } +} diff --git a/app/lib/common/widgets/full_width_button.dart b/app/lib/common/widgets/full_width_button.dart index 8d5d0c0b8..5e73b9dc7 100644 --- a/app/lib/common/widgets/full_width_button.dart +++ b/app/lib/common/widgets/full_width_button.dart @@ -19,13 +19,10 @@ class FullWidthButton extends StatelessWidget { child: ElevatedButton( onPressed: enabled ? action : null, style: ButtonStyle( - shape: MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(32), - ), - ), + backgroundColor: + MaterialStateProperty.all(PharMeTheme.primaryColor), ), - child: Text(text), + child: Text(text, style: PharMeTheme.textTheme.bodyLarge), ), ); } diff --git a/app/lib/common/widgets/module.dart b/app/lib/common/widgets/module.dart index 2860373f9..5136165a0 100644 --- a/app/lib/common/widgets/module.dart +++ b/app/lib/common/widgets/module.dart @@ -1,6 +1,8 @@ -export 'adaptive_dialog.dart'; export 'checkbox_list_tile_wrapper.dart'; export 'context_menu.dart'; +export 'dialog_action.dart'; +export 'dialog_content_text.dart'; +export 'dialog_wrapper.dart'; export 'drug_list/builder.dart'; export 'drug_list/cubit.dart'; export 'headings.dart'; diff --git a/app/lib/drug/widgets/annotation_cards/drug.dart b/app/lib/drug/widgets/annotation_cards/drug.dart index 48c08deac..2b983774e 100644 --- a/app/lib/drug/widgets/annotation_cards/drug.dart +++ b/app/lib/drug/widgets/annotation_cards/drug.dart @@ -51,9 +51,11 @@ class DrugAnnotationCard extends StatelessWidget { if (isInhibitor(drug.name)) { showAdaptiveDialog( context: context, - builder: (context) => AdaptiveDialogWrapper( + builder: (context) => DialogWrapper( title: context.l10n.drugs_page_active_warn_header, - content: Text(context.l10n.drugs_page_active_warn), + content: DialogContentText( + context.l10n.drugs_page_active_warn, + ), actions: [ DialogAction( onPressed: () => Navigator.pop(context, 'Cancel'), diff --git a/app/lib/more/pages/more.dart b/app/lib/more/pages/more.dart index f21851942..1dadcda73 100644 --- a/app/lib/more/pages/more.dart +++ b/app/lib/more/pages/more.dart @@ -75,12 +75,12 @@ class DeleteDataDialog extends HookWidget { Widget build(BuildContext context) { final agreedToDeletion = useState(false); - return AdaptiveDialogWrapper( + return DialogWrapper( title: context.l10n.settings_page_delete_data, content: Column( mainAxisSize: MainAxisSize.min, children: [ - Text(context.l10n.settings_page_delete_data_text), + DialogContentText(context.l10n.settings_page_delete_data_text), SizedBox(height: PharMeTheme.mediumSpace), CheckboxListTileWrapper( isChecked: agreedToDeletion.value, From ba595b09d180c3adcf3a3175a963a6ff01e5f642 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Wed, 20 Dec 2023 19:15:44 +0100 Subject: [PATCH 13/24] feat(#636): adapt page margins --- .../drug_list/drug_items/drug_cards.dart | 13 +++--- app/lib/common/widgets/drug_search.dart | 21 ++++------ app/lib/common/widgets/module.dart | 1 + .../widgets/page_indicator_explanation.dart | 19 +++++++++ app/lib/common/widgets/page_scaffold.dart | 6 ++- app/lib/common/widgets/rounded_card.dart | 41 +++++++++++-------- app/lib/common/widgets/scroll_list.dart | 23 +++++++++-- .../widgets/annotation_cards/guideline.dart | 7 +++- .../drug_selection/pages/drug_selection.dart | 8 +++- app/lib/report/pages/report.dart | 37 ++++++----------- 10 files changed, 107 insertions(+), 69 deletions(-) create mode 100644 app/lib/common/widgets/page_indicator_explanation.dart diff --git a/app/lib/common/widgets/drug_list/drug_items/drug_cards.dart b/app/lib/common/widgets/drug_list/drug_items/drug_cards.dart index a53cad8df..6f714dbf1 100644 --- a/app/lib/common/widgets/drug_list/drug_items/drug_cards.dart +++ b/app/lib/common/widgets/drug_list/drug_items/drug_cards.dart @@ -43,11 +43,9 @@ class DrugCard extends StatelessWidget { @override Widget build(BuildContext context) { final drugName = formatDrugName(drug, showDrugInteractionIndicator); - return Padding( - padding: EdgeInsets.symmetric(vertical: PharMeTheme.smallSpace / 2), - child: RoundedCard( + return RoundedCard( onTap: onTap, - padding: EdgeInsets.all(8), + innerPadding: EdgeInsets.all(PharMeTheme.smallSpace), radius: 16, color: drug.warningLevel.color, child: Row( @@ -66,15 +64,15 @@ class DrugCard extends StatelessWidget { .copyWith(fontWeight: FontWeight.bold), ), ]), - SizedBox(height: 4), + SizedBox(height: PharMeTheme.smallSpace / 2), if (drug.annotations.brandNames.isNotEmpty) ...[ - SizedBox(width: 4), + SizedBox(width: PharMeTheme.smallSpace / 2), Text( '(${drug.annotations.brandNames.join(', ')})', style: PharMeTheme.textTheme.titleMedium, ), ], - SizedBox(height: 8), + SizedBox(height: PharMeTheme.smallSpace * 0.75), Text( drug.annotations.drugclass, style: PharMeTheme.textTheme.titleSmall, @@ -85,7 +83,6 @@ class DrugCard extends StatelessWidget { Icon(Icons.chevron_right_rounded), ], ), - ), ); } } diff --git a/app/lib/common/widgets/drug_search.dart b/app/lib/common/widgets/drug_search.dart index 35c006464..8fa1c335c 100644 --- a/app/lib/common/widgets/drug_search.dart +++ b/app/lib/common/widgets/drug_search.dart @@ -65,6 +65,13 @@ class DrugSearch extends HookWidget { ] ), SizedBox(height: PharMeTheme.smallSpace), + if (showDrugInteractionIndicator) + PageIndicatorExplanation( + context.l10n.search_page_indicator_explanation( + drugInteractionIndicatorName, + drugInteractionIndicator + ), + ), scrollList( keepPosition: keepPosition, buildDrugList( @@ -79,7 +86,6 @@ class DrugSearch extends HookWidget { useDrugClass: useDrugClass, ) ), - ..._maybeShowDrugInteractionExplanation(context), ], ); } @@ -88,19 +94,6 @@ class DrugSearch extends HookWidget { ); } - List _maybeShowDrugInteractionExplanation(BuildContext context) { - if (!showDrugInteractionIndicator) return []; - return [ - SizedBox(height: PharMeTheme.smallSpace), - Text( - context.l10n.search_page_indicator_explanation( - drugInteractionIndicatorName, - drugInteractionIndicator - ) - ), - ]; - } - Widget buildFilter(BuildContext context) { final cubit = context.read(); final filter = cubit.filter; diff --git a/app/lib/common/widgets/module.dart b/app/lib/common/widgets/module.dart index 5136165a0..ad0863e7c 100644 --- a/app/lib/common/widgets/module.dart +++ b/app/lib/common/widgets/module.dart @@ -7,6 +7,7 @@ export 'drug_list/builder.dart'; export 'drug_list/cubit.dart'; export 'headings.dart'; export 'indicators.dart'; +export 'page_indicator_explanation.dart'; export 'page_scaffold.dart'; export 'radiant_gradient_mask.dart'; export 'rounded_card.dart'; diff --git a/app/lib/common/widgets/page_indicator_explanation.dart b/app/lib/common/widgets/page_indicator_explanation.dart new file mode 100644 index 000000000..522788879 --- /dev/null +++ b/app/lib/common/widgets/page_indicator_explanation.dart @@ -0,0 +1,19 @@ +import '../module.dart'; + +class PageIndicatorExplanation extends StatelessWidget { + const PageIndicatorExplanation(this.text); + + final String text; + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.only( + left: PharMeTheme.smallSpace, + right: PharMeTheme.smallSpace, + bottom: PharMeTheme.smallSpace, + ), + child: Text(text), + ); + } +} \ No newline at end of file diff --git a/app/lib/common/widgets/page_scaffold.dart b/app/lib/common/widgets/page_scaffold.dart index cbdf87860..ddf4ebfd0 100644 --- a/app/lib/common/widgets/page_scaffold.dart +++ b/app/lib/common/widgets/page_scaffold.dart @@ -77,7 +77,11 @@ Scaffold unscrollablePageScaffold({ appBar: appBar, body: SafeArea( child: Padding( - padding: EdgeInsets.all(padding ?? PharMeTheme.smallSpace), + padding: EdgeInsets.only( + top: padding ?? PharMeTheme.smallSpace, + left: padding ?? PharMeTheme.smallSpace, + right: padding ?? PharMeTheme.smallSpace, + ), child: body, ), ), diff --git a/app/lib/common/widgets/rounded_card.dart b/app/lib/common/widgets/rounded_card.dart index 7436bf301..ec27186d3 100644 --- a/app/lib/common/widgets/rounded_card.dart +++ b/app/lib/common/widgets/rounded_card.dart @@ -2,14 +2,16 @@ import '../module.dart'; class RoundedCard extends StatelessWidget { const RoundedCard({ - this.padding = const EdgeInsets.all(16), + this.innerPadding, + this.outerPadding, this.color = PharMeTheme.surfaceColor, this.radius = 20, this.onTap, required this.child, }); - final EdgeInsets padding; + final EdgeInsets? innerPadding; + final EdgeInsets? outerPadding; final VoidCallback? onTap; final Color color; final double radius; @@ -17,27 +19,34 @@ class RoundedCard extends StatelessWidget { @override Widget build(BuildContext context) { - Widget child = Padding(padding: padding, child: this.child); + Widget child = Padding( + padding: innerPadding ?? EdgeInsets.all(PharMeTheme.mediumSpace), + child: this.child, + ); if (onTap != null) child = InkWell(onTap: onTap, child: child); // ignore: sized_box_for_whitespace return Container( width: double.infinity, - child: DecoratedBox( - decoration: BoxDecoration( - color: color, - border: Border.all(width: 0.5, color: PharMeTheme.borderColor), - borderRadius: BorderRadius.all(Radius.circular(radius)), - boxShadow: [ - BoxShadow( - color: PharMeTheme.onSurfaceColor, - blurRadius: 16, - offset: Offset(0, 4), - ), - ], + child: Padding( + padding: outerPadding ?? EdgeInsets.symmetric( + horizontal: PharMeTheme.smallSpace, + vertical: PharMeTheme.smallSpace / 2 + ), + child: DecoratedBox( + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.all(Radius.circular(radius)), + boxShadow: [ + BoxShadow( + color: PharMeTheme.onSurfaceColor, + blurRadius: 16, + ), + ], + ), + child: child, ), - child: child, ), ); } diff --git a/app/lib/common/widgets/scroll_list.dart b/app/lib/common/widgets/scroll_list.dart index 28796bce6..d96425a0e 100644 --- a/app/lib/common/widgets/scroll_list.dart +++ b/app/lib/common/widgets/scroll_list.dart @@ -2,7 +2,10 @@ import 'package:flutter_list_view/flutter_list_view.dart'; import '../module.dart'; -Widget scrollList(List body, {bool keepPosition = false}) { +Widget scrollList(List body, { + bool keepPosition = false, + double? verticalPadding, +}) { String getItemKey(Widget widget) => widget.key.toString(); if (body.map(getItemKey).toSet().length != body.length) { throw Exception('Items passed to scrollList need unique keys'); @@ -12,10 +15,24 @@ Widget scrollList(List body, {bool keepPosition = false}) { thumbVisibility: true, thickness: PharMeTheme.smallSpace / 2, child: Padding( - padding: EdgeInsets.only(right: PharMeTheme.smallSpace * 1.5), + padding: EdgeInsets.only(right: PharMeTheme.mediumSpace), child: FlutterListView( delegate: FlutterListViewDelegate( - (context, index) => body[index], + (context, index) => (index == 0) + ? Padding( + padding: EdgeInsets.only( + top: verticalPadding ?? PharMeTheme.smallSpace, + ), + child: body[index] + ) + : (index == body.length - 1) + ? Padding( + padding: EdgeInsets.only( + bottom: verticalPadding ?? PharMeTheme.smallSpace, + ), + child: body[index] + ) + : body[index], childCount: body.length, onItemKey: (index) => getItemKey(body[index]), keepPosition: keepPosition, diff --git a/app/lib/drug/widgets/annotation_cards/guideline.dart b/app/lib/drug/widgets/annotation_cards/guideline.dart index 8891cde53..a77d4e865 100644 --- a/app/lib/drug/widgets/annotation_cards/guideline.dart +++ b/app/lib/drug/widgets/annotation_cards/guideline.dart @@ -11,7 +11,12 @@ class GuidelineAnnotationCard extends StatelessWidget { @override Widget build(BuildContext context) { return RoundedCard( - padding: const EdgeInsets.fromLTRB(16, 16, 16, 0), + innerPadding: const EdgeInsets.fromLTRB( + PharMeTheme.mediumSpace, + PharMeTheme.mediumSpace, + PharMeTheme.mediumSpace, + 0 + ), child: SingleChildScrollView( child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildHeader(context), diff --git a/app/lib/drug_selection/pages/drug_selection.dart b/app/lib/drug_selection/pages/drug_selection.dart index f2ee0a14d..543335320 100644 --- a/app/lib/drug_selection/pages/drug_selection.dart +++ b/app/lib/drug_selection/pages/drug_selection.dart @@ -31,7 +31,7 @@ class DrugSelectionPage extends HookWidget { barBottom: concludesOnboarding ? context.l10n.drug_selection_onboarding_description : null, - padding: PharMeTheme.largeSpace, + padding: PharMeTheme.mediumSpace, body: Column( children: [ Expanded(child: _buildDrugList(context, state)), @@ -54,7 +54,11 @@ class DrugSelectionPage extends HookWidget { Widget _buildButton(BuildContext context, DrugSelectionState state) { return Padding( - padding: EdgeInsets.only(top: PharMeTheme.mediumSpace), + padding: EdgeInsets.only( + left: PharMeTheme.mediumSpace, + top: PharMeTheme.mediumSpace, + right: PharMeTheme.mediumSpace, + ), child: FullWidthButton( context.l10n.action_continue, () async { diff --git a/app/lib/report/pages/report.dart b/app/lib/report/pages/report.dart index 7f02aaf8a..f67b61850 100644 --- a/app/lib/report/pages/report.dart +++ b/app/lib/report/pages/report.dart @@ -33,38 +33,27 @@ class ReportPage extends StatelessWidget { barBottom: context.l10n.report_content_explanation, body: Column( children: [ + if (hasActiveInhibitors) PageIndicatorExplanation( + context.l10n.report_page_indicator_explanation( + drugInteractionIndicatorName, + drugInteractionIndicator + ), + ), scrollList( - userPhenotypes.map((phenotype) => - Column( - key: Key('gene-card-${phenotype.geneSymbol}'), - children: [ - GeneCard(phenotype), - SizedBox(height: 8) - ] - ) - ).toList()), - if (hasActiveInhibitors) drugInteractionExplanation(context), + userPhenotypes.map((phenotype) => GeneCard( + phenotype, + key: Key('gene-card-${phenotype.geneSymbol}') + )).toList(), + ), ] ) ), ); } - - Widget drugInteractionExplanation(BuildContext context) { - return Column(children: [ - SizedBox(height: PharMeTheme.smallSpace), - Text( - context.l10n.report_page_indicator_explanation( - drugInteractionIndicatorName, - drugInteractionIndicator - ) - ), - ]); - } } class GeneCard extends StatelessWidget { - const GeneCard(this.phenotype); + const GeneCard(this.phenotype, { super.key }); final CpicPhenotype phenotype; @@ -108,7 +97,7 @@ class GeneCard extends StatelessWidget { ).toList(); return RoundedCard( onTap: () => context.router.push(GeneRoute(phenotype: phenotype)), - padding: EdgeInsets.all(8), + innerPadding: EdgeInsets.all(PharMeTheme.smallSpace), radius: 16, child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( From 68136697ec1d89ca0dff6ad446896baa24d48924 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Thu, 21 Dec 2023 11:25:24 +0100 Subject: [PATCH 14/24] feat(#636): app bar not changing color on scroll --- app/lib/common/widgets/page_scaffold.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/lib/common/widgets/page_scaffold.dart b/app/lib/common/widgets/page_scaffold.dart index ddf4ebfd0..2cadf5dd3 100644 --- a/app/lib/common/widgets/page_scaffold.dart +++ b/app/lib/common/widgets/page_scaffold.dart @@ -9,6 +9,7 @@ AppBar? buildBarBottom(String? barBottom) { ? null : AppBar( automaticallyImplyLeading: false, + scrolledUnderElevation: 0, backgroundColor: PharMeTheme.appBarTheme.backgroundColor, elevation: PharMeTheme.appBarTheme.elevation, title: RichText( @@ -35,6 +36,7 @@ Scaffold pageScaffold({ key: key, body: CustomScrollView(slivers: [ SliverAppBar( + scrolledUnderElevation: 0, backgroundColor: PharMeTheme.appBarTheme.backgroundColor, foregroundColor: PharMeTheme.appBarTheme.foregroundColor, elevation: PharMeTheme.appBarTheme.elevation, @@ -71,6 +73,7 @@ Scaffold unscrollablePageScaffold({ title: buildTitle(title), actions: actions, bottom: buildBarBottom(barBottom), + scrolledUnderElevation: 0, ); return Scaffold( key: key, From 04c8a819907f6b0ea22ea83836b5f2f612862856 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Thu, 21 Dec 2023 11:26:44 +0100 Subject: [PATCH 15/24] feat(#636): increase space between report header and indicator --- app/lib/report/pages/report.dart | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/lib/report/pages/report.dart b/app/lib/report/pages/report.dart index f67b61850..ef1efe7f1 100644 --- a/app/lib/report/pages/report.dart +++ b/app/lib/report/pages/report.dart @@ -33,10 +33,13 @@ class ReportPage extends StatelessWidget { barBottom: context.l10n.report_content_explanation, body: Column( children: [ - if (hasActiveInhibitors) PageIndicatorExplanation( - context.l10n.report_page_indicator_explanation( - drugInteractionIndicatorName, - drugInteractionIndicator + if (hasActiveInhibitors) Padding( + padding: EdgeInsets.only(top: PharMeTheme.smallSpace), + child: PageIndicatorExplanation( + context.l10n.report_page_indicator_explanation( + drugInteractionIndicatorName, + drugInteractionIndicator + ), ), ), scrollList( From ff92fb3882292d77af5480cf47b1c35a2069c445 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Thu, 21 Dec 2023 15:26:48 +0100 Subject: [PATCH 16/24] feat(#636): fix positioning of (by redoing) filter menu --- app/lib/common/widgets/context_menu.dart | 132 ----------------------- app/lib/common/widgets/drug_search.dart | 43 ++++---- app/lib/common/widgets/filter_menu.dart | 57 ++++++++++ app/lib/common/widgets/module.dart | 2 +- 4 files changed, 80 insertions(+), 154 deletions(-) delete mode 100644 app/lib/common/widgets/context_menu.dart create mode 100644 app/lib/common/widgets/filter_menu.dart diff --git a/app/lib/common/widgets/context_menu.dart b/app/lib/common/widgets/context_menu.dart deleted file mode 100644 index bfab2118c..000000000 --- a/app/lib/common/widgets/context_menu.dart +++ /dev/null @@ -1,132 +0,0 @@ -import 'package:popover/popover.dart'; - -import '../module.dart'; - -class ContextMenu extends StatelessWidget { - const ContextMenu({ - super.key, - this.headerItem, - required this.items, - required this.child, - }); - - final Widget? headerItem; - final List items; - final Widget child; - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () { - showPopover( - context: context, - bodyBuilder: (context) => Padding( - padding: EdgeInsets.symmetric( - horizontal: PharMeTheme.smallToMediumSpace - ), - child: Container( - decoration: BoxDecoration( - color: PharMeTheme.onSurfaceColor, - borderRadius: BorderRadius.circular(8), - ), - child: IntrinsicWidth( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: _buildContent(context), - ), - ), - ), - ), - direction: PopoverDirection.bottom, - arrowHeight: 0, - arrowWidth: 0, - transitionDuration: Duration(milliseconds: 100), - barrierColor: Color.fromRGBO(0, 0, 0, 0.05), - backgroundColor: Color.fromRGBO(1, 1, 1, 0), - shadow: [], - ); - }, - child: child); - } - - Widget _itemContainer( - Widget item, - { - bool showBorder = true, - double padding = PharMeTheme.smallToMediumSpace, - } - ) { - return Container( - decoration: showBorder ? BoxDecoration( - border: Border( - bottom: BorderSide( - width: 0.5, - color: PharMeTheme.borderColor - ), - ), - ) : null, - child: Padding( - padding: EdgeInsets.all(padding), - child: item, - ) - ); - } - - List _buildContent(BuildContext context) { - final body = items.mapIndexed( - (index, item) => (index == items.count() - 1) - ? _itemContainer(item, showBorder: false) - : _itemContainer(item) - ).toList(); - return headerItem != null - ? [ - _itemContainer( - Row( - children: [headerItem!] - ), - padding: PharMeTheme.mediumSpace, - showBorder: false, - ), - ...body, - ] - : body; - } -} - -class ContextMenuCheckmark extends StatelessWidget { - const ContextMenuCheckmark( - {super.key, - required this.label, - required this.setState, - this.initialState = false}); - - final String label; - final void Function({ required bool value }) setState; - final bool initialState; - - @override - Widget build(BuildContext context) { - var state = initialState; - return StatefulBuilder( - builder: (context, rebuild) => GestureDetector( - onTap: () { - rebuild(() { - state = !state; - setState(value: state); - }); - }, - child: Row( - children: [ - if (state) - Icon(Icons.check_box, size: PharMeTheme.mediumSpace) - else - Icon(Icons.check_box_outline_blank, size: PharMeTheme.mediumSpace), - SizedBox(width: PharMeTheme.smallSpace), - Expanded(child: Text(label)), - ], - ), - ), - ); - } -} diff --git a/app/lib/common/widgets/drug_search.dart b/app/lib/common/widgets/drug_search.dart index 8fa1c335c..cc14c956a 100644 --- a/app/lib/common/widgets/drug_search.dart +++ b/app/lib/common/widgets/drug_search.dart @@ -97,38 +97,39 @@ class DrugSearch extends HookWidget { Widget buildFilter(BuildContext context) { final cubit = context.read(); final filter = cubit.filter; - return ContextMenu( + return FilterMenu( items: [ - ContextMenuCheckmark( - label: context.l10n.search_page_filter_only_active, - // Invert state as filter has opposite meaning ('only show active' vs. - // 'show inactive') - setState: ({ required value }) => cubit.search(showInactive: !value), - initialState: filter != null && !filter.showInactive), - ...WarningLevel.values.filter((level) => level != WarningLevel.none) - .map((level) => ContextMenuCheckmark( - label: { + ...WarningLevel.values + .filter((level) => level != WarningLevel.none) + .map((level) => FilterMenuItem( + title: { WarningLevel.green: context.l10n.search_page_filter_green, WarningLevel.yellow: context.l10n.search_page_filter_yellow, WarningLevel.red: context.l10n.search_page_filter_red, }[level]!, - setState: ({ required value }) => - cubit.search(showWarningLevel: { level: value }), - initialState: filter?.showWarningLevel[level] ?? false + updateSearch: ({ required isChecked }) => + cubit.search(showWarningLevel: { level: isChecked }), + isChecked: filter?.showWarningLevel[level] ?? false ) ), - ContextMenuCheckmark( - label: context.l10n.search_page_filter_only_with_guidelines, + FilterMenuItem( + title: context.l10n.search_page_filter_only_active, + // Invert state as filter has opposite meaning ('only show active' vs. + // 'show inactive') + updateSearch: ({ required isChecked }) => cubit.search(showInactive: !isChecked), + isChecked: !(filter?.showInactive ?? false) + ), + FilterMenuItem( + title: context.l10n.search_page_filter_only_with_guidelines, // Invert state as filter has opposite meaning ('show only with // guidelines' vs. 'show with unknown warning level') - setState: ({ required value }) => cubit.search( - showWarningLevel: { WarningLevel.none: !value } + updateSearch: ({ required isChecked }) => cubit.search( + showWarningLevel: { WarningLevel.none: !isChecked } ), - initialState: filter != null && - !filter.showWarningLevel[WarningLevel.none]!,) + isChecked: !(filter?.showWarningLevel[WarningLevel.none] ?? false), + ) ], - child: Padding( - padding: EdgeInsets.all(8), child: Icon(Icons.filter_list_rounded)), + iconData: Icons.filter_list_rounded, ); } } diff --git a/app/lib/common/widgets/filter_menu.dart b/app/lib/common/widgets/filter_menu.dart new file mode 100644 index 000000000..3e17daefa --- /dev/null +++ b/app/lib/common/widgets/filter_menu.dart @@ -0,0 +1,57 @@ +import '../module.dart'; + +class FilterMenuItem { + FilterMenuItem({ + required this.title, + required this.updateSearch, + required bool isChecked, + }) : _isChecked = isChecked; + + final String title; + final void Function({ required bool isChecked }) updateSearch; + bool _isChecked; + + set checked(newValue) => _isChecked = newValue; + bool get checked => _isChecked; +} + +class FilterMenu extends HookWidget { + const FilterMenu({ + super.key, + this.headerItem, + required this.items, + required this.iconData, + }); + + final Widget? headerItem; + final List items; + final IconData iconData; + + @override + Widget build(BuildContext context) { + return PopupMenuButton( + icon: Icon(iconData), + color: PharMeTheme.onSurfaceColor, + elevation: 0, + itemBuilder: (context) => items.map( + (item) => PopupMenuItem( + child: StatefulBuilder(builder: (context, setState) { + void toggleCheckbox([_]) { + final newValue = !item.checked; + setState(() => item.checked = newValue); + item.updateSearch(isChecked: newValue); + } + return ListTile( + title: Text(item.title), + leading: Checkbox.adaptive( + value: item.checked, + onChanged: toggleCheckbox, + ), + onTap: toggleCheckbox, + ); + }), + ) + ).toList(), + ); + } +} \ No newline at end of file diff --git a/app/lib/common/widgets/module.dart b/app/lib/common/widgets/module.dart index ad0863e7c..b425d5197 100644 --- a/app/lib/common/widgets/module.dart +++ b/app/lib/common/widgets/module.dart @@ -1,10 +1,10 @@ export 'checkbox_list_tile_wrapper.dart'; -export 'context_menu.dart'; export 'dialog_action.dart'; export 'dialog_content_text.dart'; export 'dialog_wrapper.dart'; export 'drug_list/builder.dart'; export 'drug_list/cubit.dart'; +export 'filter_menu.dart'; export 'headings.dart'; export 'indicators.dart'; export 'page_indicator_explanation.dart'; From 700d0cb281f8f0ccbbeb32c26a138df3792634cb Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Thu, 21 Dec 2023 17:37:24 +0100 Subject: [PATCH 17/24] chore(#636): remove unused popover dependency --- app/pubspec.lock | 8 -------- app/pubspec.yaml | 1 - 2 files changed, 9 deletions(-) diff --git a/app/pubspec.lock b/app/pubspec.lock index 9fe9be8f2..40cf68ec4 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -892,14 +892,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" - popover: - dependency: "direct main" - description: - name: popover - sha256: "6a0928ccdcf12d46b407372b644a0d94400b316d0ee072a19dcef03c2bb88c3f" - url: "https://pub.dev" - source: hosted - version: "0.2.9" printing: dependency: "direct main" description: diff --git a/app/pubspec.yaml b/app/pubspec.yaml index c2d15a2b6..fd758c4de 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -39,7 +39,6 @@ dependencies: overlay_dialog: ^0.2.2 path_provider: ^2.0.11 pdf: ^3.8.1 - popover: ^0.2.8+1 printing: ^5.9.3 provider: ^6.1.1 shared_preferences: ^2.0.15 From 80615ea2e67178f74c3dd51294f4a1be47df8b00 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Thu, 21 Dec 2023 17:45:45 +0100 Subject: [PATCH 18/24] feat(#636, #681): improve delete data dialog (closes #681) --- app/lib/l10n/app_en.arb | 3 ++- app/lib/more/pages/more.dart | 31 ++++++++++++++++++++++++------- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/app/lib/l10n/app_en.arb b/app/lib/l10n/app_en.arb index 990f60140..f1274529b 100644 --- a/app/lib/l10n/app_en.arb +++ b/app/lib/l10n/app_en.arb @@ -344,7 +344,8 @@ "settings_page_account_settings": "Settings", "settings_page_delete_data": "Delete app data", "settings_page_delete_data_text": "Are you sure that you want to delete all app data? This also includes your genetic data and will reset the app.", - "settings_page_delete_data_confirmation": "I understand that my genetic data will be deleted and it might not be possible to import it again.", + "settings_page_delete_data_additional_text": "Your genetic data will be deleted and it might not be possible to import it again.", + "settings_page_delete_data_confirmation": "I understand the consequences and want to delete all app data", "settings_page_more": "More", "settings_page_onboarding": "Onboarding", "settings_page_about_us": "About us", diff --git a/app/lib/more/pages/more.dart b/app/lib/more/pages/more.dart index 1dadcda73..2617dc489 100644 --- a/app/lib/more/pages/more.dart +++ b/app/lib/more/pages/more.dart @@ -82,13 +82,30 @@ class DeleteDataDialog extends HookWidget { children: [ DialogContentText(context.l10n.settings_page_delete_data_text), SizedBox(height: PharMeTheme.mediumSpace), - CheckboxListTileWrapper( - isChecked: agreedToDeletion.value, - onChanged: (value) => agreedToDeletion.value = value - ?? agreedToDeletion.value, - title: context.l10n.settings_page_delete_data_confirmation, - controlAffinity: ListTileControlAffinity.leading, - contentPadding: EdgeInsets.zero, + DialogContentText( + context.l10n.settings_page_delete_data_additional_text, + ), + SizedBox(height: PharMeTheme.mediumSpace), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: PharMeTheme.mediumToLargeSpace, + height: PharMeTheme.mediumToLargeSpace, + child: Checkbox.adaptive( + value: agreedToDeletion.value, + onChanged: (value) => + agreedToDeletion.value = value ?? agreedToDeletion.value, + ), + ), + SizedBox(width: PharMeTheme.smallSpace), + Expanded( + child: DialogContentText( + context.l10n.settings_page_delete_data_confirmation, + ), + ), + ], ), ], ), From 0da27c302dfadfe000e20ddad67812bb796b06fd Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Thu, 21 Dec 2023 20:32:08 +0100 Subject: [PATCH 19/24] feat(#636): improve cards and list dividers --- app/lib/common/theme.dart | 2 + app/lib/common/utilities/color_utils.dart | 2 +- .../drug_list/drug_items/drug_cards.dart | 4 +- .../drug_items/drug_checkbox_list.dart | 4 +- app/lib/common/widgets/rounded_card.dart | 28 +++---- app/lib/common/widgets/subheader_divider.dart | 8 +- app/lib/faq/pages/faq.dart | 81 ++++++++++++------- app/lib/l10n/app_en.arb | 2 + app/lib/more/pages/more.dart | 69 ++++++++-------- app/lib/report/pages/report.dart | 1 - 10 files changed, 113 insertions(+), 88 deletions(-) diff --git a/app/lib/common/theme.dart b/app/lib/common/theme.dart index 9011ede43..5aee2a6f7 100644 --- a/app/lib/common/theme.dart +++ b/app/lib/common/theme.dart @@ -1,4 +1,5 @@ import 'module.dart'; +import 'utilities/color_utils.dart'; class PharMeTheme { static ThemeData get light { @@ -81,6 +82,7 @@ class PharMeTheme { static const backgroundColor = Colors.white; static const errorColor = Color(0xccf52a2a); static final borderColor = Colors.black.withOpacity(.2); + static final iconColor = darkenColor(PharMeTheme.onSurfaceText, -0.1); static const smallSpace = 8.0; static const smallToMediumSpace = 12.0; diff --git a/app/lib/common/utilities/color_utils.dart b/app/lib/common/utilities/color_utils.dart index e239cb534..f8450573f 100644 --- a/app/lib/common/utilities/color_utils.dart +++ b/app/lib/common/utilities/color_utils.dart @@ -3,7 +3,7 @@ import '../module.dart'; // From https://stackoverflow.com/a/58604669 Color darkenColor(Color color, [double amount = 0.1]) { - assert(amount >= 0 && amount <= 1); + assert(amount >= -1 && amount <= 1); final hsl = HSLColor.fromColor(color); final hslDark = hsl.withLightness((hsl.lightness - amount).clamp(0.0, 1.0)); return hslDark.toColor(); diff --git a/app/lib/common/widgets/drug_list/drug_items/drug_cards.dart b/app/lib/common/widgets/drug_list/drug_items/drug_cards.dart index 6f714dbf1..b796ac31a 100644 --- a/app/lib/common/widgets/drug_list/drug_items/drug_cards.dart +++ b/app/lib/common/widgets/drug_list/drug_items/drug_cards.dart @@ -45,8 +45,8 @@ class DrugCard extends StatelessWidget { final drugName = formatDrugName(drug, showDrugInteractionIndicator); return RoundedCard( onTap: onTap, - innerPadding: EdgeInsets.all(PharMeTheme.smallSpace), - radius: 16, + innerPadding: EdgeInsets.all(PharMeTheme.smallSpace * 1.15), + radius: 18, color: drug.warningLevel.color, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, diff --git a/app/lib/common/widgets/drug_list/drug_items/drug_checkbox_list.dart b/app/lib/common/widgets/drug_list/drug_items/drug_checkbox_list.dart index fd900c0db..af81e3452 100644 --- a/app/lib/common/widgets/drug_list/drug_items/drug_checkbox_list.dart +++ b/app/lib/common/widgets/drug_list/drug_items/drug_checkbox_list.dart @@ -33,12 +33,12 @@ List buildDrugCheckboxList( ); return [ SubheaderDivider( - context.l10n.drug_selection_subheader_active_drugs, + text: context.l10n.drug_selection_subheader_active_drugs, key: Key('header-active'), ), ...activeDrugsList, SubheaderDivider( - context.l10n.drug_selection_subheader_all_drugs, + text: context.l10n.drug_selection_subheader_all_drugs, key: Key('header-all'), ), ...allDrugsList, diff --git a/app/lib/common/widgets/rounded_card.dart b/app/lib/common/widgets/rounded_card.dart index ec27186d3..576d679dd 100644 --- a/app/lib/common/widgets/rounded_card.dart +++ b/app/lib/common/widgets/rounded_card.dart @@ -1,26 +1,30 @@ import '../module.dart'; +import '../utilities/color_utils.dart'; class RoundedCard extends StatelessWidget { const RoundedCard({ this.innerPadding, - this.outerPadding, - this.color = PharMeTheme.surfaceColor, + this.outerVerticalPadding, + this.outerHorizontalPadding, + this.color, this.radius = 20, this.onTap, required this.child, + super.key, }); final EdgeInsets? innerPadding; - final EdgeInsets? outerPadding; + final double? outerVerticalPadding; + final double? outerHorizontalPadding; final VoidCallback? onTap; - final Color color; + final Color? color; final double radius; final Widget child; @override Widget build(BuildContext context) { Widget child = Padding( - padding: innerPadding ?? EdgeInsets.all(PharMeTheme.mediumSpace), + padding: innerPadding ?? EdgeInsets.all(PharMeTheme.smallSpace * 1.25), child: this.child, ); @@ -30,20 +34,14 @@ class RoundedCard extends StatelessWidget { return Container( width: double.infinity, child: Padding( - padding: outerPadding ?? EdgeInsets.symmetric( - horizontal: PharMeTheme.smallSpace, - vertical: PharMeTheme.smallSpace / 2 + padding: EdgeInsets.symmetric( + vertical: outerVerticalPadding ?? PharMeTheme.smallSpace * 0.65, + horizontal: outerHorizontalPadding ?? PharMeTheme.smallSpace, ), child: DecoratedBox( decoration: BoxDecoration( - color: color, + color: color ?? darkenColor(PharMeTheme.onSurfaceColor, -0.05), borderRadius: BorderRadius.all(Radius.circular(radius)), - boxShadow: [ - BoxShadow( - color: PharMeTheme.onSurfaceColor, - blurRadius: 16, - ), - ], ), child: child, ), diff --git a/app/lib/common/widgets/subheader_divider.dart b/app/lib/common/widgets/subheader_divider.dart index c1791c0a5..c908bc80a 100644 --- a/app/lib/common/widgets/subheader_divider.dart +++ b/app/lib/common/widgets/subheader_divider.dart @@ -1,16 +1,18 @@ import '../module.dart'; class SubheaderDivider extends StatelessWidget { - const SubheaderDivider( - this.text, { + const SubheaderDivider({ + this.text = '', this.indent = 20.0, this.color, + this.useLine = true, super.key, }); final String text; final double indent; final Color? color; + final bool useLine; @override Widget build(BuildContext context) { @@ -20,7 +22,7 @@ class SubheaderDivider extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Divider(color: widgetColor), + if (useLine) Divider(color: widgetColor, thickness: 0.5), Text( text, style: diff --git a/app/lib/faq/pages/faq.dart b/app/lib/faq/pages/faq.dart index dc8124445..3905c7eec 100644 --- a/app/lib/faq/pages/faq.dart +++ b/app/lib/faq/pages/faq.dart @@ -11,20 +11,29 @@ class FaqPage extends StatelessWidget { canPop: false, child: pageScaffold(title: context.l10n.tab_faq, body: [ Padding( - padding: const EdgeInsets.all(8), + padding: const EdgeInsets.all(PharMeTheme.smallSpace), child: Column( key: Key('questionsColumn'), + crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 8), ...faqList.keys.fold>( [], (widgets, topic) => [...widgets, ..._buildTopic(context, topic, faqList[topic]!)] ), - Divider(), - ListTile( + ..._buildTopicHeader( + context.l10n.settings_page_contact_us, + addSpace: true, + ), + _buildQuestionCard( + child: ListTile( title: Text(context.l10n.faq_contact_us), trailing: Icon(Icons.chevron_right_rounded), - onTap: sendEmail) + iconColor: PharMeTheme.iconColor, + onTap: sendEmail + ) + ) + ], ), ), @@ -32,44 +41,57 @@ class FaqPage extends StatelessWidget { ); } + List _buildTopicHeader(String title, { required bool addSpace }) => [ + if (addSpace) SizedBox(height: PharMeTheme.mediumSpace), + SubheaderDivider(text: title, useLine: false), + ]; + + Widget _buildQuestionCard({ required Widget child, Key? key }) => RoundedCard( + key: key, + innerPadding: EdgeInsets.all(PharMeTheme.smallSpace * 0.25), + child: child, + ); + List _buildTopic( BuildContext context, String topicName, List questions ) { + final topicIndex = faqList.keys.toList().indexOf(topicName); return [ - ListTile( - title: Text( - topicName, - style: PharMeTheme.textTheme.bodyMedium, - ), - dense: true, - ), + ..._buildTopicHeader(topicName, addSpace: topicIndex != 0), ...questions.map((question) => _buildQuestion(context, question)) ]; } Widget _buildQuestion(BuildContext context, Question question) { final key = GlobalKey(); - - return Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - child: ExpansionTile( - key: key, - title: Text(question.question), - onExpansionChanged: (value) { - if (value) _scrollToSelectedContent(key: key); - }, - children: [ - ListTile( - contentPadding: EdgeInsets.only(left: 16, right: 16, bottom: 8), - title: Text(question.answer), + return _buildQuestionCard( + key: key, + child: Theme( + data: Theme.of(context).copyWith( + dividerColor: Colors.transparent, + ), + child: ExpansionTile( + title: Text(question.question), + iconColor: PharMeTheme.iconColor, + collapsedIconColor: PharMeTheme.iconColor, + onExpansionChanged: (value) { + if (value) _scrollToSelectedContent(key: key); + }, + children: [ + ListTile( + contentPadding: EdgeInsets.only( + left: PharMeTheme.mediumSpace, + right: PharMeTheme.mediumSpace, + bottom: PharMeTheme.smallSpace, + ), + title: Text(question.answer), + ), + ], + ), ), - ], - ), - ); + ); } void _scrollToSelectedContent({required GlobalKey key}) { @@ -79,6 +101,7 @@ class FaqPage extends StatelessWidget { Scrollable.ensureVisible( keyContext, duration: Duration(milliseconds: 200), + alignmentPolicy: ScrollPositionAlignmentPolicy.keepVisibleAtEnd, ); }); } diff --git a/app/lib/l10n/app_en.arb b/app/lib/l10n/app_en.arb index f1274529b..8a7d3eeb8 100644 --- a/app/lib/l10n/app_en.arb +++ b/app/lib/l10n/app_en.arb @@ -23,6 +23,7 @@ "update_warning_title": "Updated guidelines", "update_warning_body": "The guidelines for gene-drug interactions were updated. Please review your information, especially for medications you are currently taking.", + "faq_contact_us": "Do you have unanswered questions or feedback? Contact us", "general_continue": "Continue", @@ -354,6 +355,7 @@ "settings_page_privacy_policy_text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", "settings_page_terms_and_conditions": "Terms of use", "settings_page_terms_and_conditions_text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + "settings_page_help_and_feedback": "Help & Feedback", "settings_page_contact_us": "Contact us", "comprehension_intro_text": "Would you like to participate in a survey aiming to measure user comprehension of content in the app? This would help us make PharMe more understandable for everyone!", diff --git a/app/lib/more/pages/more.dart b/app/lib/more/pages/more.dart index 2617dc489..89e0c5ed8 100644 --- a/app/lib/more/pages/more.dart +++ b/app/lib/more/pages/more.dart @@ -10,64 +10,63 @@ class MorePage extends StatelessWidget { return PopScope( canPop: false, child: pageScaffold(title: context.l10n.tab_more, body: [ - ListTile( - title: Text( - context.l10n.settings_page_account_settings, - style: PharMeTheme.textTheme.bodyMedium, - ), - dense: true, + SubheaderDivider( + text: context.l10n.settings_page_account_settings, + useLine: false, ), - ListTile( - title: Text(context.l10n.drug_selection_header), - trailing: Icon(Icons.chevron_right_rounded), + _buildSettingsItem( + title: context.l10n.drug_selection_header, onTap: () => context.router.push( DrugSelectionRoute(concludesOnboarding: false) ), ), - ListTile( - title: Text(context.l10n.settings_page_delete_data), - trailing: Icon(Icons.chevron_right_rounded), + _buildSettingsItem( + title: context.l10n.settings_page_delete_data, onTap: () => showDialog( context: context, builder: (_) => DeleteDataDialog(), ), ), - Divider(), - ListTile( - title: Text( - context.l10n.settings_page_more, - style: PharMeTheme.textTheme.bodyMedium, - ), - dense: true, + SubheaderDivider( + text: context.l10n.settings_page_more, + useLine: false, ), - ListTile( - title: Text(context.l10n.settings_page_onboarding), - trailing: Icon(Icons.chevron_right_rounded), + _buildSettingsItem( + title: context.l10n.settings_page_onboarding, onTap: () => context.router.push(OnboardingRoute(isRevisiting: true)), ), - ListTile( - title: Text(context.l10n.settings_page_about_us), - trailing: Icon(Icons.chevron_right_rounded), + _buildSettingsItem( + title: context.l10n.settings_page_about_us, onTap: () => context.router.push(AboutRoute()), ), - ListTile( - title: Text(context.l10n.settings_page_privacy_policy), - trailing: Icon(Icons.chevron_right_rounded), + _buildSettingsItem( + title: context.l10n.settings_page_privacy_policy, onTap: () => context.router.push(PrivacyRoute()), ), - ListTile( - title: Text(context.l10n.settings_page_terms_and_conditions), - trailing: Icon(Icons.chevron_right_rounded), + _buildSettingsItem( + title: context.l10n.settings_page_terms_and_conditions, onTap: () => context.router.push(TermsRoute()), ), - Divider(), - ListTile( - title: Text(context.l10n.settings_page_contact_us), - trailing: Icon(Icons.chevron_right_rounded), + SubheaderDivider( + text: context.l10n.settings_page_help_and_feedback, + useLine: false, + ), + _buildSettingsItem( + title: context.l10n.settings_page_contact_us, onTap: sendEmail) ]), ); } + + Widget _buildSettingsItem({ + required String title, + required void Function() onTap, + }) => ListTile( + title: Text(title), + trailing: Icon(Icons.chevron_right_rounded), + iconColor: PharMeTheme.iconColor, + onTap: onTap, + ); } class DeleteDataDialog extends HookWidget { diff --git a/app/lib/report/pages/report.dart b/app/lib/report/pages/report.dart index ef1efe7f1..97da58402 100644 --- a/app/lib/report/pages/report.dart +++ b/app/lib/report/pages/report.dart @@ -100,7 +100,6 @@ class GeneCard extends StatelessWidget { ).toList(); return RoundedCard( onTap: () => context.router.push(GeneRoute(phenotype: phenotype)), - innerPadding: EdgeInsets.all(PharMeTheme.smallSpace), radius: 16, child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( From a8f07cd664661f3b8de2a6ca4beeb4578edaaa62 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Fri, 22 Dec 2023 16:27:38 +0100 Subject: [PATCH 20/24] feat(#636): improve drug page --- app/integration_test/drugs_test.dart | 2 +- app/lib/common/theme.dart | 3 + .../drug_list/drug_items/drug_cards.dart | 2 +- app/lib/common/widgets/rounded_card.dart | 10 +- app/lib/drug/pages/drug.dart | 10 +- .../annotation_cards/annotation_table.dart | 42 ++++++ .../{ => annotation_cards}/disclaimer.dart | 14 +- .../drug/widgets/annotation_cards/drug.dart | 114 ++++++++------ .../widgets/annotation_cards/guideline.dart | 139 ++++++++---------- app/lib/drug/widgets/module.dart | 3 +- app/lib/l10n/app_en.arb | 10 +- app/lib/report/pages/gene.dart | 1 + 12 files changed, 204 insertions(+), 146 deletions(-) create mode 100644 app/lib/drug/widgets/annotation_cards/annotation_table.dart rename app/lib/drug/widgets/{ => annotation_cards}/disclaimer.dart (66%) diff --git a/app/integration_test/drugs_test.dart b/app/integration_test/drugs_test.dart index 40c18c6ea..8d25b742a 100644 --- a/app/integration_test/drugs_test.dart +++ b/app/integration_test/drugs_test.dart @@ -2,7 +2,7 @@ import 'package:app/common/module.dart'; import 'package:app/drug/module.dart'; -import 'package:app/drug/widgets/disclaimer.dart'; +import 'package:app/drug/widgets/annotation_cards/disclaimer.dart'; import 'package:bloc_test/bloc_test.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/app/lib/common/theme.dart b/app/lib/common/theme.dart index 5aee2a6f7..23233825b 100644 --- a/app/lib/common/theme.dart +++ b/app/lib/common/theme.dart @@ -90,6 +90,9 @@ class PharMeTheme { static const mediumToLargeSpace = 24.0; static const largeSpace = 32.0; + static const outerCardRadius = mediumSpace; + static const innerCardRadius = smallToMediumSpace; + static final appBarTheme = AppBarTheme( backgroundColor: surfaceColor, foregroundColor: onSurfaceText, diff --git a/app/lib/common/widgets/drug_list/drug_items/drug_cards.dart b/app/lib/common/widgets/drug_list/drug_items/drug_cards.dart index b796ac31a..dd21c9db0 100644 --- a/app/lib/common/widgets/drug_list/drug_items/drug_cards.dart +++ b/app/lib/common/widgets/drug_list/drug_items/drug_cards.dart @@ -45,7 +45,7 @@ class DrugCard extends StatelessWidget { final drugName = formatDrugName(drug, showDrugInteractionIndicator); return RoundedCard( onTap: onTap, - innerPadding: EdgeInsets.all(PharMeTheme.smallSpace * 1.15), + innerPadding: EdgeInsets.all(PharMeTheme.smallSpace * 1.25), radius: 18, color: drug.warningLevel.color, child: Row( diff --git a/app/lib/common/widgets/rounded_card.dart b/app/lib/common/widgets/rounded_card.dart index 576d679dd..947f52a14 100644 --- a/app/lib/common/widgets/rounded_card.dart +++ b/app/lib/common/widgets/rounded_card.dart @@ -7,7 +7,7 @@ class RoundedCard extends StatelessWidget { this.outerVerticalPadding, this.outerHorizontalPadding, this.color, - this.radius = 20, + this.radius, this.onTap, required this.child, super.key, @@ -18,13 +18,13 @@ class RoundedCard extends StatelessWidget { final double? outerHorizontalPadding; final VoidCallback? onTap; final Color? color; - final double radius; + final double? radius; final Widget child; @override Widget build(BuildContext context) { Widget child = Padding( - padding: innerPadding ?? EdgeInsets.all(PharMeTheme.smallSpace * 1.25), + padding: innerPadding ?? EdgeInsets.all(PharMeTheme.smallToMediumSpace), child: this.child, ); @@ -41,7 +41,9 @@ class RoundedCard extends StatelessWidget { child: DecoratedBox( decoration: BoxDecoration( color: color ?? darkenColor(PharMeTheme.onSurfaceColor, -0.05), - borderRadius: BorderRadius.all(Radius.circular(radius)), + borderRadius: BorderRadius.all( + Radius.circular(radius ?? PharMeTheme.outerCardRadius) + ), ), child: child, ), diff --git a/app/lib/drug/pages/drug.dart b/app/lib/drug/pages/drug.dart index bf042e2c0..07cbad2fd 100644 --- a/app/lib/drug/pages/drug.dart +++ b/app/lib/drug/pages/drug.dart @@ -50,8 +50,6 @@ class DrugPage extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SubHeader(context.l10n.drugs_page_header_drug), - SizedBox(height: 12), DrugAnnotationCard( drug, isActive: drug.isActive, @@ -59,16 +57,12 @@ class DrugPage extends StatelessWidget { context.read().setActivity(drug, value), disabled: loading, ), - SizedBox(height: 20), + SizedBox(height: PharMeTheme.mediumSpace), SubHeader( context.l10n.drugs_page_header_guideline, tooltip: context.l10n.drugs_page_tooltip_guideline, ), - SizedBox(height: 12), - if (drug.userGuideline != null) ...[ - Disclaimer(), - SizedBox(height: 12), - ], + SizedBox(height: PharMeTheme.smallSpace), GuidelineAnnotationCard(drug), ], ), diff --git a/app/lib/drug/widgets/annotation_cards/annotation_table.dart b/app/lib/drug/widgets/annotation_cards/annotation_table.dart new file mode 100644 index 000000000..82835f7fc --- /dev/null +++ b/app/lib/drug/widgets/annotation_cards/annotation_table.dart @@ -0,0 +1,42 @@ +import '../../../common/module.dart'; + +class TableRowDefinition { + const TableRowDefinition(this.key, this.value); + final String key; + final String value; +} + +Table buildTable( + List rowDefinitions, + { + TextStyle? style, + } +) { + return Table( + defaultColumnWidth: IntrinsicColumnWidth(), + children: rowDefinitions.map((rowDefinition) => _buildRow( + rowDefinition.key, + rowDefinition.value, + style ?? PharMeTheme.textTheme.bodyMedium!, + )).toList(), + ); +} + +TableRow _buildRow( + String key, + String value, + TextStyle textStyle, +) { + return TableRow( + children: [ + Padding( + padding: EdgeInsets.only(right: PharMeTheme.smallSpace), + child: Text( + key, + style: textStyle.copyWith(fontWeight: FontWeight.bold), + ), + ), + Text(value, style: textStyle), + ], + ); +} \ No newline at end of file diff --git a/app/lib/drug/widgets/disclaimer.dart b/app/lib/drug/widgets/annotation_cards/disclaimer.dart similarity index 66% rename from app/lib/drug/widgets/disclaimer.dart rename to app/lib/drug/widgets/annotation_cards/disclaimer.dart index 383b18179..c463fb26e 100644 --- a/app/lib/drug/widgets/disclaimer.dart +++ b/app/lib/drug/widgets/annotation_cards/disclaimer.dart @@ -1,4 +1,4 @@ -import '../../common/module.dart'; +import '../../../common/module.dart'; class Disclaimer extends StatelessWidget { const Disclaimer({this.text}); @@ -8,24 +8,26 @@ class Disclaimer extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - padding: EdgeInsets.all(4), + padding: EdgeInsets.all(PharMeTheme.smallSpace), decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(8)), + borderRadius: BorderRadius.all( + Radius.circular(PharMeTheme.innerCardRadius * 0.75) + ), color: PharMeTheme.surfaceColor, border: Border.all(color: PharMeTheme.errorColor, width: 1.2), ), child: Row(children: [ Icon( Icons.warning_rounded, - size: 52, + size: PharMeTheme.largeSpace, color: PharMeTheme.errorColor, ), - SizedBox(width: 8), + SizedBox(width: PharMeTheme.smallSpace), Flexible( child: Text( text ?? context.l10n.drugs_page_disclaimer, style: PharMeTheme.textTheme.labelMedium!.copyWith( - fontWeight: FontWeight.w100, + fontWeight: FontWeight.w300, ), ), ), diff --git a/app/lib/drug/widgets/annotation_cards/drug.dart b/app/lib/drug/widgets/annotation_cards/drug.dart index 2b983774e..6dfbac36e 100644 --- a/app/lib/drug/widgets/annotation_cards/drug.dart +++ b/app/lib/drug/widgets/annotation_cards/drug.dart @@ -1,5 +1,6 @@ import '../../../common/module.dart'; import '../sub_header.dart'; +import 'annotation_table.dart'; class DrugAnnotationCard extends StatelessWidget { const DrugAnnotationCard( @@ -16,37 +17,51 @@ class DrugAnnotationCard extends StatelessWidget { @override Widget build(BuildContext context) { - return RoundedCard( - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SubHeader(context.l10n.drugs_page_header_druginfo), - SizedBox(height: 12), - Text(drug.annotations.indication), - SizedBox(height: 8), - Table(defaultColumnWidth: IntrinsicColumnWidth(), children: [ - _buildRow(context.l10n.drugs_page_header_drugclass, - drug.annotations.drugclass), - if (drug.annotations.brandNames.isNotEmpty) ...[ - _buildRow(context.l10n.drugs_page_header_synonyms, - drug.annotations.brandNames.join(', ')), - ] - ]), - SizedBox(height: 4), - if (isInhibitor(drug.name)) ...[ - SizedBox(height: 8), - Text(context.l10n.drugs_page_is_inhibitor( - drug.name, - inhibitedGenes(drug).join(', '), - )), - ], - Divider(color: PharMeTheme.borderColor), - SizedBox(height: 4), - SubHeader(context.l10n.drugs_page_header_active), - CheckboxListTileWrapper( - title: context.l10n.drugs_page_active, - isChecked: isActive, + return Column( + children: [ + SubHeader(context.l10n.drugs_page_header_drug), + SizedBox(height: PharMeTheme.smallSpace), + RoundedCard( + innerPadding: EdgeInsets.all(PharMeTheme.mediumSpace), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(drug.annotations.indication), + SizedBox(height: PharMeTheme.smallSpace), + buildTable([ + TableRowDefinition( + context.l10n.drugs_page_header_drugclass, + drug.annotations.drugclass, + ), + if (drug.annotations.brandNames.isNotEmpty) + TableRowDefinition( + context.l10n.drugs_page_header_synonyms, + drug.annotations.brandNames.join(', '), + ), + ]), + if (isInhibitor(drug.name)) ...[ + SizedBox(height: 8), + Text(context.l10n.drugs_page_is_inhibitor( + drug.name, + inhibitedGenes(drug).join(', '), + )), + ], + ], + ), + ), + ), + SizedBox(height: PharMeTheme.mediumSpace), + SubHeader(context.l10n.drugs_page_header_active), + SizedBox(height: PharMeTheme.smallSpace), + RoundedCard( + innerPadding: EdgeInsets.all(PharMeTheme.smallSpace), + child: Padding( + padding: EdgeInsets.symmetric(horizontal: PharMeTheme.smallSpace), + child: DropdownButton( + value: isActive, + isExpanded: true, + icon: const Icon(Icons.expand_more), onChanged: disabled ? null : (newValue) => { if (isInhibitor(drug.name)) { showAdaptiveDialog( @@ -76,20 +91,33 @@ class DrugAnnotationCard extends StatelessWidget { setActivity(value: newValue) } }, - controlAffinity: ListTileControlAffinity.leading, + items: [ + DropdownMenuItem( + value: true, + child: Row( + children: [ + Icon( + Icons.check_circle_outline, + color: PharMeTheme.iconColor, + ), + SizedBox(width: PharMeTheme.smallSpace), + Text(context.l10n.drugs_page_active), + ]), + ), + DropdownMenuItem( + value: false, + child: Row( + children: [ + Icon(Icons.cancel_outlined, color: PharMeTheme.iconColor), + SizedBox(width: PharMeTheme.smallSpace), + Text(context.l10n.drugs_page_inactive), + ]), + ), + ], ), - ], + ), ), - ), + ], ); } - - TableRow _buildRow(String key, String value) => TableRow(children: [ - Padding( - padding: EdgeInsets.fromLTRB(0, 4, 12, 4), - child: Text(key, - style: PharMeTheme.textTheme.bodyMedium! - .copyWith(fontWeight: FontWeight.bold))), - Padding(padding: EdgeInsets.fromLTRB(0, 4, 0, 4), child: Text(value)), - ]); } diff --git a/app/lib/drug/widgets/annotation_cards/guideline.dart b/app/lib/drug/widgets/annotation_cards/guideline.dart index a77d4e865..1f93794a5 100644 --- a/app/lib/drug/widgets/annotation_cards/guideline.dart +++ b/app/lib/drug/widgets/annotation_cards/guideline.dart @@ -1,7 +1,8 @@ import 'package:url_launcher/url_launcher.dart'; import '../../../common/module.dart'; -import '../sub_header.dart'; +import '../../../common/utilities/color_utils.dart'; +import '../module.dart'; class GuidelineAnnotationCard extends StatelessWidget { const GuidelineAnnotationCard(this.drug); @@ -11,27 +12,20 @@ class GuidelineAnnotationCard extends StatelessWidget { @override Widget build(BuildContext context) { return RoundedCard( - innerPadding: const EdgeInsets.fromLTRB( - PharMeTheme.mediumSpace, - PharMeTheme.mediumSpace, - PharMeTheme.mediumSpace, - 0 - ), + innerPadding: const EdgeInsets.all(PharMeTheme.mediumSpace), child: SingleChildScrollView( child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildHeader(context), - SizedBox(height: 12), - if (drug.userGuideline != null) ...[ + if (drug.guidelines.isNotEmpty) ...[ + _buildHeader(context), + SizedBox(height: PharMeTheme.mediumSpace), _buildCard(context), - SizedBox(height: 8), - Divider(color: PharMeTheme.borderColor), - SizedBox(height: 8), + SizedBox(height: PharMeTheme.mediumSpace), _buildSourcesSection(context), - SizedBox(height: 12), ] else ...[ + _buildHeader(context), + SizedBox(height: PharMeTheme.smallSpace), _buildCard(context), - SizedBox(height: 16), ], ]), ), @@ -45,43 +39,49 @@ class GuidelineAnnotationCard extends StatelessWidget { ); final lowerCardText = drug.userGuideline?.annotations.recommendation ?? context.l10n.drugs_page_no_guidelines_for_phenotype_recommendation; - return Card( - key: Key('annotationCard'), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - color: drug.warningLevel.color, - child: Padding( - padding: EdgeInsets.all(12), - child: - Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row(children: [ - Icon( - drug.warningLevel.icon, - color: PharMeTheme.onSurfaceText, - ), - SizedBox(width: 12), - Flexible( - child: Text( - upperCardText, - style: PharMeTheme.textTheme.bodyMedium, - ), - ) - ]), - SizedBox(height: 12), - Text( - lowerCardText, + return RoundedCard( + key: Key('annotationCard'), + radius: PharMeTheme.innerCardRadius, + outerHorizontalPadding: 0, + outerVerticalPadding: 0, + color: drug.warningLevel.color, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row(children: [ + Icon( + drug.warningLevel.icon, + color: PharMeTheme.onSurfaceText, + size: PharMeTheme.largeSpace, + ), + SizedBox(width: PharMeTheme.smallToMediumSpace), + Flexible( + child: Text( + upperCardText, style: PharMeTheme.textTheme.bodyMedium, ), - ]))); + ) + ]), + SizedBox(height: PharMeTheme.smallToMediumSpace), + Text( + lowerCardText, + style: PharMeTheme.textTheme.bodyMedium, + ), + if (drug.userGuideline != null) ...[ + SizedBox(height: PharMeTheme.smallToMediumSpace), + Disclaimer(), + ], + ] + ) + ); } Widget _buildHeader(BuildContext context) { - var headerContent = ''; - var headerStyle = PharMeTheme.textTheme.bodyLarge!; if (drug.userGuideline == null && drug.guidelines.isEmpty) { - headerContent = context.l10n.drugs_page_guidelines_empty(drug.name); - headerStyle = headerStyle.copyWith(fontStyle: FontStyle.italic); + return Text( + context.l10n.drugs_page_guidelines_empty(drug.name), + style: TextStyle(fontStyle: FontStyle.italic), + ); } else { final genes = drug.userGuideline?.lookupkey.keys ?? drug.guidelines.first.lookupkey.keys; @@ -91,53 +91,40 @@ class GuidelineAnnotationCard extends StatelessWidget { context, drug: drug.name, ); - var description = '$geneSymbol: ${phenotypeInformation.phenotype}'; + var description = phenotypeInformation.phenotype; if (phenotypeInformation.adaptionText.isNotNullOrBlank) { description = '$description (${phenotypeInformation.adaptionText})'; } - return description; + return TableRowDefinition(geneSymbol, description); }); - headerContent = geneDescriptions.join('\n'); + return buildTable(geneDescriptions.toList()); } - return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - SubHeader(context.l10n.drugs_page_your_genome), - SizedBox(height: 12), - Text( - headerContent, - style: headerStyle, - ), - ]); } Widget _buildSourcesSection(BuildContext context) { // pipes are illegal characters in URLs so please // - forgive the cheap hack or // - refactor by making a custom object and defining equality for it :) - final sources = drug.userGuideline!.externalData + final guideline = drug.userGuideline ?? drug.guidelines.first; + final sources = guideline.externalData .map((data) => '${data.source}|${data.guidelineUrl}') .toSet(); return Column(children: [ - SubHeader( - context.l10n.drugs_page_header_further_info, - ), - SizedBox(height: 12), ...sources.map( (source) => GestureDetector( onTap: () => _launchUrl(Uri.parse(source.split('|')[1])), - child: Card( - shape: - RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), - color: PharMeTheme.onSurfaceColor, - child: Padding( - padding: EdgeInsets.all(12), - child: Row(mainAxisSize: MainAxisSize.min, children: [ - Flexible( - child: Text(context.l10n - .drugs_page_sources_description(source.split('|')[0])), - ), - Icon(Icons.chevron_right_rounded) - ]), - ), + child: RoundedCard( + radius: PharMeTheme.innerCardRadius, + outerHorizontalPadding: 0, + outerVerticalPadding: 0, + color: darkenColor(PharMeTheme.onSurfaceColor, 0.05), + child: Row(mainAxisSize: MainAxisSize.min, children: [ + Flexible( + child: Text(context.l10n + .drugs_page_sources_description(source.split('|')[0])), + ), + Icon(Icons.chevron_right_rounded) + ]) ), ), ), diff --git a/app/lib/drug/widgets/module.dart b/app/lib/drug/widgets/module.dart index e01e7d6ec..c79aef87d 100644 --- a/app/lib/drug/widgets/module.dart +++ b/app/lib/drug/widgets/module.dart @@ -1,5 +1,6 @@ +export 'annotation_cards/annotation_table.dart'; +export 'annotation_cards/disclaimer.dart'; export 'annotation_cards/drug.dart'; export 'annotation_cards/guideline.dart'; -export 'disclaimer.dart'; export 'sub_header.dart'; export 'tooltip_icon.dart'; diff --git a/app/lib/l10n/app_en.arb b/app/lib/l10n/app_en.arb index 8a7d3eeb8..df557a230 100644 --- a/app/lib/l10n/app_en.arb +++ b/app/lib/l10n/app_en.arb @@ -151,7 +151,6 @@ } } }, - "drugs_page_your_genome": "Your genome", "drugs_page_guidelines_empty": "No guidelines are present for {drugName}", "@drugs_page_guidelines_empty": { "description": "Disclaimer when a drug does not have guidelines", @@ -162,16 +161,15 @@ } } }, - "drugs_page_header_further_info": "Further information", "drugs_page_header_synonyms": "Other names", "drugs_page_header_drugclass": "Drug class", - "drugs_page_header_drug": "Drug", - "drugs_page_header_druginfo": "Information", + "drugs_page_header_drug": "Drug information", "drugs_page_header_active": "Usage status", - "drugs_page_active": "I am currently taking this drug.", + "drugs_page_active": "I am currently taking this drug", + "drugs_page_inactive": "I am not taking this drug", "drugs_page_active_warn_header": "Are you sure you want to change the drug usage status?", "drugs_page_active_warn": "This can influence your results for other drugs.", - "drugs_page_header_guideline": "Clinical Guideline", + "drugs_page_header_guideline": "DNA-based clinical guideline", "drugs_page_no_guidelines_for_phenotype_implication": "More information is needed to comment on your DNA's influence on {drugName}.", "@drugs_page_no_guidelines_for_phenotype_implication": { "description": "Disclaimer for when no guidelines was found for the current drug based on the user's phenotype", diff --git a/app/lib/report/pages/gene.dart b/app/lib/report/pages/gene.dart index e4605f002..f42159fb1 100644 --- a/app/lib/report/pages/gene.dart +++ b/app/lib/report/pages/gene.dart @@ -37,6 +37,7 @@ class GenePage extends HookWidget { ), SizedBox(height: PharMeTheme.smallToMediumSpace), RoundedCard( + radius: PharMeTheme.mediumSpace, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ From 3bfea25f13b38aa4e73337d83071e741d0b2c627 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Fri, 22 Dec 2023 20:38:11 +0100 Subject: [PATCH 21/24] test(#636): fix drug page test --- app/integration_test/drugs_test.dart | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/app/integration_test/drugs_test.dart b/app/integration_test/drugs_test.dart index 8d25b742a..b3bbca4e9 100644 --- a/app/integration_test/drugs_test.dart +++ b/app/integration_test/drugs_test.dart @@ -126,7 +126,7 @@ void main() { // test the right color of the card // ignore: omit_local_variable_types - final Card card = tester.firstWidget( + final RoundedCard card = tester.firstWidget( find.byKey( ValueKey('annotationCard'), ), @@ -136,14 +136,14 @@ void main() { testDrug.guidelines.first.annotations.warningLevel.color, ); - context = tester.element(find.byType(Tooltip).first); - // test that drug activity can be set - final checkbox = tester.widget(find.byType(CheckboxListTileWrapper)) - as CheckboxListTileWrapper; - expect(checkbox.onChanged, isNotNull); + final activitySelection = tester.firstWidget( + find.byType(DropdownButton) + ) as DropdownButton; + expect(activitySelection.onChanged, isNotNull); // test tooltips + context = tester.element(find.byType(Tooltip).first); expect( find.byTooltip(context.l10n.drugs_page_tooltip_guideline), findsOneWidget, @@ -202,9 +202,10 @@ void main() { ), ); - final checkbox = tester.widget(find.byType(CheckboxListTileWrapper)) - as CheckboxListTileWrapper; - expect(checkbox.onChanged, isNull); + final activitySelection = tester.firstWidget( + find.byType(DropdownButton) + ) as DropdownButton; + expect(activitySelection.onChanged, isNull); }); }); } From 79279623920ca5ba7620339f66ac940bfe429b26 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Fri, 22 Dec 2023 22:03:30 +0100 Subject: [PATCH 22/24] fix(#636): fix Android styling --- app/lib/common/utilities/module.dart | 1 + .../widgets/checkbox_list_tile_wrapper.dart | 19 ++++++------- app/lib/common/widgets/checkbox_wrapper.dart | 28 +++++++++++++++++++ app/lib/common/widgets/dialog_wrapper.dart | 1 + app/lib/common/widgets/filter_menu.dart | 4 +-- app/lib/common/widgets/module.dart | 1 + app/lib/common/widgets/page_scaffold.dart | 6 +--- app/lib/more/pages/more.dart | 4 +-- 8 files changed, 44 insertions(+), 20 deletions(-) create mode 100644 app/lib/common/widgets/checkbox_wrapper.dart diff --git a/app/lib/common/utilities/module.dart b/app/lib/common/utilities/module.dart index 118fa982f..20e5ed438 100644 --- a/app/lib/common/utilities/module.dart +++ b/app/lib/common/utilities/module.dart @@ -1,3 +1,4 @@ +export 'color_utils.dart'; export 'drug_utils.dart'; export 'genome_data.dart'; export 'material_colors.dart'; diff --git a/app/lib/common/widgets/checkbox_list_tile_wrapper.dart b/app/lib/common/widgets/checkbox_list_tile_wrapper.dart index b8e09a4f4..d01fdd4ab 100644 --- a/app/lib/common/widgets/checkbox_list_tile_wrapper.dart +++ b/app/lib/common/widgets/checkbox_list_tile_wrapper.dart @@ -8,9 +8,7 @@ class CheckboxListTileWrapper extends StatelessWidget { required this.onChanged, this.subtitle, this.isEnabled = true, - this.controlAffinity = ListTileControlAffinity.platform, this.contentPadding, - this.activeColor, }); final String title; @@ -19,23 +17,22 @@ class CheckboxListTileWrapper extends StatelessWidget { // ignore: avoid_positional_boolean_parameters final void Function(bool?)? onChanged; final bool isEnabled; - final ListTileControlAffinity controlAffinity; final EdgeInsetsGeometry? contentPadding; - final Color? activeColor; @override Widget build(BuildContext context) { - return CheckboxListTile.adaptive( + return ListTile( enabled: isEnabled, - value: isChecked, - onChanged: onChanged, title: Text(title, style: PharMeTheme.textTheme.bodyLarge), subtitle: subtitle != null ? Text(subtitle!, style: PharMeTheme.textTheme.bodyMedium) : null, - controlAffinity: controlAffinity, contentPadding: contentPadding, - activeColor: activeColor ?? PharMeTheme.primaryColor, - ); } - + leading: CheckboxWrapper( + isEnabled: isEnabled, + isChecked: isChecked, + onChanged: onChanged, + ), + ); + } } \ No newline at end of file diff --git a/app/lib/common/widgets/checkbox_wrapper.dart b/app/lib/common/widgets/checkbox_wrapper.dart new file mode 100644 index 000000000..0d2f349a0 --- /dev/null +++ b/app/lib/common/widgets/checkbox_wrapper.dart @@ -0,0 +1,28 @@ +import '../module.dart'; + +class CheckboxWrapper extends StatelessWidget { + const CheckboxWrapper({ + super.key, + required this.isChecked, + required this.onChanged, + this.isEnabled = true, + }); + + final bool isChecked; + // ignore: avoid_positional_boolean_parameters + final void Function(bool?)? onChanged; + final bool isEnabled; + + @override + Widget build(BuildContext context) { + return Checkbox.adaptive( + value: isChecked, + onChanged: isEnabled ? onChanged : null, + activeColor: PharMeTheme.primaryColor, + checkColor: Colors.white, + side: isChecked || !isEnabled + ? null + : BorderSide(color: darkenColor(PharMeTheme.iconColor, -0.15)), + ); + } +} \ No newline at end of file diff --git a/app/lib/common/widgets/dialog_wrapper.dart b/app/lib/common/widgets/dialog_wrapper.dart index 29e9553a0..b710f4d65 100644 --- a/app/lib/common/widgets/dialog_wrapper.dart +++ b/app/lib/common/widgets/dialog_wrapper.dart @@ -25,6 +25,7 @@ class DialogWrapper extends StatelessWidget { title: Text(title), content: materialContent, actions: actions, + elevation: 0, ); } } diff --git a/app/lib/common/widgets/filter_menu.dart b/app/lib/common/widgets/filter_menu.dart index 3e17daefa..06fb25f23 100644 --- a/app/lib/common/widgets/filter_menu.dart +++ b/app/lib/common/widgets/filter_menu.dart @@ -43,8 +43,8 @@ class FilterMenu extends HookWidget { } return ListTile( title: Text(item.title), - leading: Checkbox.adaptive( - value: item.checked, + leading: CheckboxWrapper( + isChecked: item.checked, onChanged: toggleCheckbox, ), onTap: toggleCheckbox, diff --git a/app/lib/common/widgets/module.dart b/app/lib/common/widgets/module.dart index b425d5197..0da919361 100644 --- a/app/lib/common/widgets/module.dart +++ b/app/lib/common/widgets/module.dart @@ -1,4 +1,5 @@ export 'checkbox_list_tile_wrapper.dart'; +export 'checkbox_wrapper.dart'; export 'dialog_action.dart'; export 'dialog_content_text.dart'; export 'dialog_wrapper.dart'; diff --git a/app/lib/common/widgets/page_scaffold.dart b/app/lib/common/widgets/page_scaffold.dart index 2cadf5dd3..d01d2b01c 100644 --- a/app/lib/common/widgets/page_scaffold.dart +++ b/app/lib/common/widgets/page_scaffold.dart @@ -80,11 +80,7 @@ Scaffold unscrollablePageScaffold({ appBar: appBar, body: SafeArea( child: Padding( - padding: EdgeInsets.only( - top: padding ?? PharMeTheme.smallSpace, - left: padding ?? PharMeTheme.smallSpace, - right: padding ?? PharMeTheme.smallSpace, - ), + padding: EdgeInsets.all(padding ?? PharMeTheme.smallSpace), child: body, ), ), diff --git a/app/lib/more/pages/more.dart b/app/lib/more/pages/more.dart index 89e0c5ed8..b5962d1fc 100644 --- a/app/lib/more/pages/more.dart +++ b/app/lib/more/pages/more.dart @@ -92,8 +92,8 @@ class DeleteDataDialog extends HookWidget { SizedBox( width: PharMeTheme.mediumToLargeSpace, height: PharMeTheme.mediumToLargeSpace, - child: Checkbox.adaptive( - value: agreedToDeletion.value, + child: CheckboxWrapper( + isChecked: agreedToDeletion.value, onChanged: (value) => agreedToDeletion.value = value ?? agreedToDeletion.value, ), From fb142a5a31ecea08454d0c6711bdbc9e9971a311 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Fri, 22 Dec 2023 22:50:53 +0100 Subject: [PATCH 23/24] docs(#636): add alt texts to contributing --- CONTRIBUTING.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 31dea12d6..b5da93f09 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,18 +9,23 @@ Before you begin with setting up your local development environment, ensure that you have the following list of tools installed: - An editor, we **strongly recommend** [vscode-icon Visual Studio Code](https://code.visualstudio.com) - [git-icon Git](https://git-scm.com/downloads) - [docker-icon Docker](https://docs.docker.com/get-docker/) - [nodejs-icon NodeJS](https://nodejs.org) - [yarn-icon Yarn](https://yarnpkg.com/) From 494df7257410cd48025e4f42216bae71ff5bf6e4 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Fri, 22 Dec 2023 22:51:25 +0100 Subject: [PATCH 24/24] chore(#636): add color utils and cached drugs to common module --- app/integration_test/main_page_test.dart | 1 - app/lib/common/models/module.dart | 1 + app/lib/common/services.dart | 3 +-- app/lib/common/theme.dart | 1 - app/lib/common/utilities/drug_utils.dart | 1 - app/lib/common/utilities/genome_data.dart | 1 - app/lib/common/widgets/drug_list/cubit.dart | 1 - app/lib/common/widgets/rounded_card.dart | 1 - app/lib/drug/widgets/annotation_cards/guideline.dart | 1 - app/lib/drug_selection/pages/drug_selection.dart | 1 - app/lib/more/utils.dart | 1 - app/lib/report/pages/report.dart | 2 -- 12 files changed, 2 insertions(+), 13 deletions(-) diff --git a/app/integration_test/main_page_test.dart b/app/integration_test/main_page_test.dart index 3e678c74e..be977bd12 100644 --- a/app/integration_test/main_page_test.dart +++ b/app/integration_test/main_page_test.dart @@ -1,4 +1,3 @@ -import 'package:app/common/models/drug/cached_drugs.dart'; import 'package:app/common/module.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/app/lib/common/models/module.dart b/app/lib/common/models/module.dart index 30f7b4735..a1c47b710 100644 --- a/app/lib/common/models/module.dart +++ b/app/lib/common/models/module.dart @@ -1,4 +1,5 @@ export 'anni_response.dart'; +export 'drug/cached_drugs.dart'; export 'drug/drug.dart'; export 'drug/drug_inhibitors.dart'; export 'drug/guideline.dart'; diff --git a/app/lib/common/services.dart b/app/lib/common/services.dart index 6f3ee20b6..54d0ae633 100644 --- a/app/lib/common/services.dart +++ b/app/lib/common/services.dart @@ -1,7 +1,6 @@ import 'package:hive_flutter/hive_flutter.dart'; -import 'models/drug/cached_drugs.dart'; -import 'models/module.dart'; +import 'module.dart'; Future initServices() async { await Hive.initFlutter(); diff --git a/app/lib/common/theme.dart b/app/lib/common/theme.dart index 23233825b..b49c948bd 100644 --- a/app/lib/common/theme.dart +++ b/app/lib/common/theme.dart @@ -1,5 +1,4 @@ import 'module.dart'; -import 'utilities/color_utils.dart'; class PharMeTheme { static ThemeData get light { diff --git a/app/lib/common/utilities/drug_utils.dart b/app/lib/common/utilities/drug_utils.dart index 53f1c4095..6620203ef 100644 --- a/app/lib/common/utilities/drug_utils.dart +++ b/app/lib/common/utilities/drug_utils.dart @@ -2,7 +2,6 @@ import 'dart:convert'; import 'package:http/http.dart'; import '../../app.dart'; -import '../models/drug/cached_drugs.dart'; import '../module.dart'; Future updateCachedDrugs() async { diff --git a/app/lib/common/utilities/genome_data.dart b/app/lib/common/utilities/genome_data.dart index b0478c701..bf9c4a056 100644 --- a/app/lib/common/utilities/genome_data.dart +++ b/app/lib/common/utilities/genome_data.dart @@ -4,7 +4,6 @@ import 'dart:convert'; import 'package:http/http.dart'; import '../constants.dart'; -import '../models/drug/cached_drugs.dart'; import '../models/module.dart'; Future fetchAndSaveDiplotypesAndActiveDrugs( diff --git a/app/lib/common/widgets/drug_list/cubit.dart b/app/lib/common/widgets/drug_list/cubit.dart index e94be55e9..6d20f631f 100644 --- a/app/lib/common/widgets/drug_list/cubit.dart +++ b/app/lib/common/widgets/drug_list/cubit.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:freezed_annotation/freezed_annotation.dart'; -import '../../models/drug/cached_drugs.dart'; import '../../module.dart'; part 'cubit.freezed.dart'; diff --git a/app/lib/common/widgets/rounded_card.dart b/app/lib/common/widgets/rounded_card.dart index 947f52a14..9c89f6b92 100644 --- a/app/lib/common/widgets/rounded_card.dart +++ b/app/lib/common/widgets/rounded_card.dart @@ -1,5 +1,4 @@ import '../module.dart'; -import '../utilities/color_utils.dart'; class RoundedCard extends StatelessWidget { const RoundedCard({ diff --git a/app/lib/drug/widgets/annotation_cards/guideline.dart b/app/lib/drug/widgets/annotation_cards/guideline.dart index 1f93794a5..3f891be17 100644 --- a/app/lib/drug/widgets/annotation_cards/guideline.dart +++ b/app/lib/drug/widgets/annotation_cards/guideline.dart @@ -1,7 +1,6 @@ import 'package:url_launcher/url_launcher.dart'; import '../../../common/module.dart'; -import '../../../common/utilities/color_utils.dart'; import '../module.dart'; class GuidelineAnnotationCard extends StatelessWidget { diff --git a/app/lib/drug_selection/pages/drug_selection.dart b/app/lib/drug_selection/pages/drug_selection.dart index 543335320..ff5b692a5 100644 --- a/app/lib/drug_selection/pages/drug_selection.dart +++ b/app/lib/drug_selection/pages/drug_selection.dart @@ -1,6 +1,5 @@ import 'package:provider/provider.dart'; -import '../../common/models/drug/cached_drugs.dart'; import '../../common/models/metadata.dart'; import '../../common/module.dart' hide MetaData; import '../../common/widgets/drug_list/drug_items/drug_checkbox_list.dart'; diff --git a/app/lib/more/utils.dart b/app/lib/more/utils.dart index 8a7efae23..e049ba8f6 100644 --- a/app/lib/more/utils.dart +++ b/app/lib/more/utils.dart @@ -1,6 +1,5 @@ import 'package:path_provider/path_provider.dart'; -import '../common/models/drug/cached_drugs.dart'; import '../common/module.dart'; Future deleteAllAppData() async { diff --git a/app/lib/report/pages/report.dart b/app/lib/report/pages/report.dart index 97da58402..9579f8e26 100644 --- a/app/lib/report/pages/report.dart +++ b/app/lib/report/pages/report.dart @@ -1,8 +1,6 @@ import 'package:provider/provider.dart'; -import '../../common/models/drug/cached_drugs.dart'; import '../../common/module.dart'; -import '../../common/utilities/color_utils.dart'; @RoutePage() class ReportPage extends StatelessWidget {