From eebab58faa84e1fe4d664fc95c9de9f5827a8a8f Mon Sep 17 00:00:00 2001 From: Wouter Ensink <46427708+w-ensink@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:12:02 +0100 Subject: [PATCH 01/15] add visual feedback to more tab buttons --- lib/src/screens/more/more_tab.dart | 2 +- lib/src/screens/more/widgets/tiles.dart | 39 +++++++++++++------------ lib/src/widgets/irma_card.dart | 10 +++---- lib/src/widgets/irma_divider.dart | 11 +++---- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/lib/src/screens/more/more_tab.dart b/lib/src/screens/more/more_tab.dart index bfc3abd6..3cd7198b 100644 --- a/lib/src/screens/more/more_tab.dart +++ b/lib/src/screens/more/more_tab.dart @@ -59,7 +59,7 @@ class _MoreTabState extends State { String translationKey, ) => Padding( - padding: EdgeInsets.only(bottom: theme.defaultSpacing), + padding: EdgeInsets.only(bottom: theme.smallSpacing), child: TranslatedText( translationKey, isHeader: true, diff --git a/lib/src/screens/more/widgets/tiles.dart b/lib/src/screens/more/widgets/tiles.dart index 720b67de..be19ec8a 100644 --- a/lib/src/screens/more/widgets/tiles.dart +++ b/lib/src/screens/more/widgets/tiles.dart @@ -161,7 +161,7 @@ class ToggleTile extends StatelessWidget { onTap: () => onChanged(!value), trailing: CupertinoSwitch( value: value, - onChanged: null, // We use the onTap on the Tile + onChanged: null, // We use the onTap on the Tile (Why??) activeColor: theme.success, ), ); @@ -191,25 +191,28 @@ class Tile extends StatelessWidget { return Semantics( link: true, - child: ListTile( - onTap: onTap, - minLeadingWidth: theme.mediumSpacing, - leading: iconData != null - ? Icon( - iconData, - size: 32, + child: Material( + child: ListTile( + onTap: onTap, + minLeadingWidth: theme.mediumSpacing, + leading: iconData != null + ? Icon( + iconData, + size: 28, + color: iconColor, + ) + : null, + title: TranslatedText( + labelTranslationKey, + style: theme.textButtonTextStyle, + ), + trailing: trailing ?? + Icon( + Icons.chevron_right, + size: 28, color: iconColor, - ) - : null, - title: TranslatedText( - labelTranslationKey, + ), ), - trailing: trailing ?? - Icon( - Icons.chevron_right, - size: 28, - color: iconColor, - ), ), ); } diff --git a/lib/src/widgets/irma_card.dart b/lib/src/widgets/irma_card.dart index 90ecde69..acc3a82d 100644 --- a/lib/src/widgets/irma_card.dart +++ b/lib/src/widgets/irma_card.dart @@ -38,8 +38,8 @@ class IrmaCard extends StatelessWidget { switch (style) { case IrmaCardStyle.normal: boxDecoration = BoxDecoration( - borderRadius: theme.borderRadius, - border: Border.all(color: Colors.transparent), + // borderRadius: theme.borderRadius, + // border: Border.all(color: Colors.transparent), color: theme.light, ); break; @@ -92,13 +92,13 @@ class IrmaCard extends StatelessWidget { ); } - return Padding( - padding: padding ?? EdgeInsets.all(theme.tinySpacing), + return ClipRRect( + borderRadius: theme.borderRadius, child: InkWell( borderRadius: theme.borderRadius, onTap: onTap, child: Container( - //In this context the "margin" is set on the container padding. + // In this context the "margin" is set on the container padding. padding: margin ?? EdgeInsets.all(theme.defaultSpacing), decoration: boxDecoration, child: child, diff --git a/lib/src/widgets/irma_divider.dart b/lib/src/widgets/irma_divider.dart index b36295e3..b26972c2 100644 --- a/lib/src/widgets/irma_divider.dart +++ b/lib/src/widgets/irma_divider.dart @@ -13,13 +13,10 @@ class IrmaDivider extends StatelessWidget { Widget build(BuildContext context) { final theme = IrmaTheme.of(context); - return Padding( - padding: EdgeInsets.symmetric(vertical: theme.smallSpacing), - child: Container( - height: 1, - decoration: BoxDecoration( - color: color ?? theme.neutralExtraLight, - ), + return Container( + height: 1, + decoration: BoxDecoration( + color: color ?? theme.neutralExtraLight, ), ); } From 099996b59197c16a370812808edb0e21538df796 Mon Sep 17 00:00:00 2001 From: Wouter Ensink <46427708+w-ensink@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:53:35 +0100 Subject: [PATCH 02/15] add visual feedback to buttons on data page - add allways scrollable physics to the data page for more natural feel on iOS --- ios/Podfile.lock | 2 +- lib/src/screens/data/data_tab.dart | 8 ++- .../irma_credential_type_card.dart | 72 ++++++++++--------- lib/src/widgets/irma_card.dart | 4 +- 4 files changed, 48 insertions(+), 38 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 9ef8aab6..a605e5a9 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -108,4 +108,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: a5d23baa2c1e9ecb0238b15941bc5d7f9ca5c6bc -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/lib/src/screens/data/data_tab.dart b/lib/src/screens/data/data_tab.dart index 9fc775ea..c3d15620 100644 --- a/lib/src/screens/data/data_tab.dart +++ b/lib/src/screens/data/data_tab.dart @@ -27,9 +27,11 @@ class DataTab extends StatelessWidget { Widget build(BuildContext context) { final theme = IrmaTheme.of(context); - return SingleChildScrollView( - padding: EdgeInsets.all(theme.defaultSpacing), - child: SafeArea( + return SizedBox( + height: double.infinity, + child: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + padding: EdgeInsets.all(theme.defaultSpacing), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/lib/src/widgets/credential_card/irma_credential_type_card.dart b/lib/src/widgets/credential_card/irma_credential_type_card.dart index df42ab30..9c76d0f0 100644 --- a/lib/src/widgets/credential_card/irma_credential_type_card.dart +++ b/lib/src/widgets/credential_card/irma_credential_type_card.dart @@ -48,46 +48,54 @@ class IrmaCredentialTypeCard extends StatelessWidget { } return IrmaCard( - key: Key('${credType.fullId}_tile'), - onTap: onTap, - child: Row( - children: [ - avatar, - SizedBox( - width: theme.defaultSpacing - theme.tinySpacing, - ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + margin: EdgeInsets.zero, + child: Material( + child: InkWell( + key: Key('${credType.fullId}_tile'), + onTap: onTap, + child: Padding( + padding: EdgeInsets.all(theme.defaultSpacing), + child: Row( children: [ - Text( - getTranslation(context, credType.name), - style: theme.textTheme.headlineMedium!.copyWith( - color: theme.dark, - ), - ), + avatar, SizedBox( - height: theme.tinySpacing, + width: theme.defaultSpacing - theme.tinySpacing, ), - Text( - getTranslation( - context, - repo.irmaConfiguration.issuers[credType.fullIssuerId]!.name, - ), - style: theme.textTheme.bodyMedium!.copyWith( - fontSize: 14, + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + getTranslation(context, credType.name), + style: theme.textTheme.headlineMedium!.copyWith( + color: theme.dark, + ), + ), + SizedBox( + height: theme.tinySpacing, + ), + Text( + getTranslation( + context, + repo.irmaConfiguration.issuers[credType.fullIssuerId]!.name, + ), + style: theme.textTheme.bodyMedium!.copyWith( + fontSize: 14, + ), + ), + ], ), ), + SizedBox(width: theme.smallSpacing), + Icon( + trailingIcon ?? Icons.chevron_right, + size: 24, + color: theme.neutralExtraDark, + ), ], ), ), - SizedBox(width: theme.smallSpacing), - Icon( - trailingIcon ?? Icons.chevron_right, - size: 24, - color: theme.neutralExtraDark, - ), - ], + ), ), ); } diff --git a/lib/src/widgets/irma_card.dart b/lib/src/widgets/irma_card.dart index acc3a82d..c8dd4127 100644 --- a/lib/src/widgets/irma_card.dart +++ b/lib/src/widgets/irma_card.dart @@ -38,8 +38,8 @@ class IrmaCard extends StatelessWidget { switch (style) { case IrmaCardStyle.normal: boxDecoration = BoxDecoration( - // borderRadius: theme.borderRadius, - // border: Border.all(color: Colors.transparent), + borderRadius: theme.borderRadius, + border: Border.all(width: 0, color: Colors.transparent), color: theme.light, ); break; From f75cfe92b3ba98047262c4d487a9638b24b0cc2a Mon Sep 17 00:00:00 2001 From: Wouter Ensink <46427708+w-ensink@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:06:43 +0100 Subject: [PATCH 03/15] improve activity page - add always scrollable physics - add splash on buttons presses --- lib/src/screens/activity/activity_tab.dart | 10 ++- .../activity/widgets/activity_card.dart | 90 ++++++++++--------- 2 files changed, 56 insertions(+), 44 deletions(-) diff --git a/lib/src/screens/activity/activity_tab.dart b/lib/src/screens/activity/activity_tab.dart index 73deb2b5..1f9311a2 100644 --- a/lib/src/screens/activity/activity_tab.dart +++ b/lib/src/screens/activity/activity_tab.dart @@ -107,15 +107,19 @@ class _ActivityTabState extends State { ), ), ), - ActivityCard( - logEntry: logEntry, - irmaConfiguration: irmaConfiguration, + Padding( + padding: EdgeInsets.only(bottom: theme.smallSpacing), + child: ActivityCard( + logEntry: logEntry, + irmaConfiguration: irmaConfiguration, + ), ), ]; }, ).flattened.toList(); return ListView( + physics: const AlwaysScrollableScrollPhysics(), controller: _scrollController, padding: EdgeInsets.symmetric( vertical: theme.smallSpacing, diff --git a/lib/src/screens/activity/widgets/activity_card.dart b/lib/src/screens/activity/widgets/activity_card.dart index 478e03b9..6eb0d314 100644 --- a/lib/src/screens/activity/widgets/activity_card.dart +++ b/lib/src/screens/activity/widgets/activity_card.dart @@ -105,60 +105,68 @@ class ActivityCard extends StatelessWidget { label: semanticLabel, button: true, child: IrmaCard( - onTap: () => Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => ActivityDetailScreen( - logEntry: logEntry, - irmaConfiguration: irmaConfiguration, + margin: EdgeInsets.zero, + child: Material( + child: InkWell( + onTap: () => Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => ActivityDetailScreen( + logEntry: logEntry, + irmaConfiguration: irmaConfiguration, + ), + ), ), - ), - ), - child: Semantics( - excludeSemantics: true, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( + child: Semantics( + excludeSemantics: true, + child: Padding( + padding: EdgeInsets.all(theme.defaultSpacing), child: Row( - crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - IrmaAvatar( - size: 52, - logoPath: logo, - initials: title != '' ? title[0] : null, - ), - SizedBox( - width: theme.smallSpacing, - ), Flexible( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, children: [ - TranslatedText(localizedTimeStamp), - Text( - title, - style: theme.themeData.textTheme.headlineMedium!.copyWith( - color: theme.dark, - ), + IrmaAvatar( + size: 52, + logoPath: logo, + initials: title != '' ? title[0] : null, + ), + SizedBox( + width: theme.smallSpacing, ), - TranslatedText( - subtitleTranslationKey, - style: theme.themeData.textTheme.bodyMedium!.copyWith( - fontSize: 14, - color: theme.dark, + Flexible( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TranslatedText(localizedTimeStamp), + Text( + title, + style: theme.themeData.textTheme.headlineMedium!.copyWith( + color: theme.dark, + ), + ), + TranslatedText( + subtitleTranslationKey, + style: theme.themeData.textTheme.bodyMedium!.copyWith( + fontSize: 14, + color: theme.dark, + ), + ) + ], ), ) ], ), - ) + ), + Icon( + Icons.chevron_right, + color: Colors.grey.shade700, + ), ], ), ), - Icon( - Icons.chevron_right, - color: Colors.grey.shade700, - ), - ], + ), ), ), ), From 106a03d9a705ee2bf44b243fa9ad68dd42091b2e Mon Sep 17 00:00:00 2001 From: Wouter Ensink <46427708+w-ensink@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:24:21 +0100 Subject: [PATCH 04/15] add always scrollable physics to home page --- .../widgets/credential_category_list.dart | 15 +++++--- lib/src/screens/home/home_tab.dart | 37 +++++++++++-------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/lib/src/screens/data/widgets/credential_category_list.dart b/lib/src/screens/data/widgets/credential_category_list.dart index 8bc9b9fc..2b5befbc 100644 --- a/lib/src/screens/data/widgets/credential_category_list.dart +++ b/lib/src/screens/data/widgets/credential_category_list.dart @@ -38,12 +38,15 @@ class CredentialCategoryList extends StatelessWidget { ...credentialTypes.map( (credType) => Semantics( button: true, - child: IrmaCredentialTypeCard( - credType: credType, - checked: obtainedCredentialTypes?.contains(credType) ?? false, - trailingIcon: credentialTypeTrailingIcon, - onTap: () => onCredentialTypeTap?.call( - credType, + child: Padding( + padding: EdgeInsets.only(bottom: theme.smallSpacing), + child: IrmaCredentialTypeCard( + credType: credType, + checked: obtainedCredentialTypes?.contains(credType) ?? false, + trailingIcon: credentialTypeTrailingIcon, + onTap: () => onCredentialTypeTap?.call( + credType, + ), ), ), ), diff --git a/lib/src/screens/home/home_tab.dart b/lib/src/screens/home/home_tab.dart index 2bdb7cd8..9523ff56 100644 --- a/lib/src/screens/home/home_tab.dart +++ b/lib/src/screens/home/home_tab.dart @@ -17,27 +17,32 @@ class HomeTab extends StatelessWidget { Widget build(BuildContext context) { final theme = IrmaTheme.of(context); - return SingleChildScrollView( - padding: EdgeInsets.all( - theme.defaultSpacing, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - IrmaActionCard( + return SizedBox( + height: double.infinity, + child: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + padding: EdgeInsets.all( + theme.defaultSpacing, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + IrmaActionCard( key: const Key('home_action_fetch'), titleKey: 'home_tab.action_card.fetch.title', subtitleKey: 'home_tab.action_card.fetch.subtitle', onTap: () => Navigator.of(context).pushNamed(AddDataScreen.routeName), - icon: Icons.add_circle_sharp), - SizedBox(height: theme.largeSpacing), + icon: Icons.add_circle_sharp, + ), + SizedBox(height: theme.largeSpacing), - //Recent activity - RecentActivity( - onTap: () => onChangeTab(IrmaNavBarTab.activity), - ), - SizedBox(height: theme.defaultSpacing) - ], + //Recent activity + RecentActivity( + onTap: () => onChangeTab(IrmaNavBarTab.activity), + ), + SizedBox(height: theme.defaultSpacing) + ], + ), ), ); } From 0e9702bc7d61924761c34df347a7debad92e17e6 Mon Sep 17 00:00:00 2001 From: Wouter Ensink <46427708+w-ensink@users.noreply.github.com> Date: Thu, 21 Nov 2024 16:17:27 +0100 Subject: [PATCH 05/15] improved background color on many pages - used the same blue color as the main screens for a more consistent look - add always scrollable physics to more pages for more natural feel --- .../activity/activity_detail_screen.dart | 86 +++++++------- .../add_data/add_data_details_screen.dart | 78 +++++++------ .../change_language_screen.dart | 30 ++--- .../data/credentials_detail_screen.dart | 106 +++++++++--------- lib/src/screens/help/help_screen.dart | 2 +- .../notifications/notifications_screen.dart | 2 +- lib/src/screens/settings/settings_screen.dart | 16 +-- 7 files changed, 169 insertions(+), 151 deletions(-) diff --git a/lib/src/screens/activity/activity_detail_screen.dart b/lib/src/screens/activity/activity_detail_screen.dart index 30d9f5b9..bb2577cb 100644 --- a/lib/src/screens/activity/activity_detail_screen.dart +++ b/lib/src/screens/activity/activity_detail_screen.dart @@ -28,52 +28,56 @@ class ActivityDetailScreen extends StatelessWidget { final lang = FlutterI18n.currentLocale(context)!.languageCode; return Scaffold( - backgroundColor: theme.backgroundSecondary, + backgroundColor: theme.backgroundTertiary, appBar: const IrmaAppBar( titleTranslationKey: 'home.nav_bar.activity', ), - body: SingleChildScrollView( - padding: EdgeInsets.all(theme.defaultSpacing), - child: SafeArea( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Builder( - builder: (context) { - switch (logEntry.type) { - case LogEntryType.signing: - case LogEntryType.disclosing: - return ActivityDetailDisclosure( - logEntry: logEntry, - irmaConfiguration: irmaConfiguration, - ); - case LogEntryType.issuing: - return ActivityDetailIssuance( - logEntry: logEntry, - irmaConfiguration: irmaConfiguration, - ); - case LogEntryType.removal: - return ActivityDetailRemoval( - logEntry: logEntry, - irmaConfiguration: irmaConfiguration, - ); - } - }, - ), - //Always add the timestamp of the activity on the bottom - SizedBox(height: theme.smallSpacing), - Center( - child: TranslatedText( - 'credential.date_at_time', - key: const Key('activity_timestamp'), - translationParams: { - 'date': DateFormat.yMMMMd(lang).format(logEntry.time), - 'time': DateFormat.jm(lang).format(logEntry.time), + body: SizedBox( + height: double.infinity, + child: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + padding: EdgeInsets.all(theme.defaultSpacing), + child: SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Builder( + builder: (context) { + switch (logEntry.type) { + case LogEntryType.signing: + case LogEntryType.disclosing: + return ActivityDetailDisclosure( + logEntry: logEntry, + irmaConfiguration: irmaConfiguration, + ); + case LogEntryType.issuing: + return ActivityDetailIssuance( + logEntry: logEntry, + irmaConfiguration: irmaConfiguration, + ); + case LogEntryType.removal: + return ActivityDetailRemoval( + logEntry: logEntry, + irmaConfiguration: irmaConfiguration, + ); + } }, - style: theme.themeData.textTheme.bodyMedium, ), - ), - ], + //Always add the timestamp of the activity on the bottom + SizedBox(height: theme.smallSpacing), + Center( + child: TranslatedText( + 'credential.date_at_time', + key: const Key('activity_timestamp'), + translationParams: { + 'date': DateFormat.yMMMMd(lang).format(logEntry.time), + 'time': DateFormat.jm(lang).format(logEntry.time), + }, + style: theme.themeData.textTheme.bodyMedium, + ), + ), + ], + ), ), ), ), diff --git a/lib/src/screens/add_data/add_data_details_screen.dart b/lib/src/screens/add_data/add_data_details_screen.dart index 44ff38be..c6276646 100644 --- a/lib/src/screens/add_data/add_data_details_screen.dart +++ b/lib/src/screens/add_data/add_data_details_screen.dart @@ -50,7 +50,7 @@ class _AddDataDetailsScreenState extends State { ); return Scaffold( - backgroundColor: theme.backgroundSecondary, + backgroundColor: theme.backgroundTertiary, appBar: IrmaAppBar( titleTranslationKey: 'data.add.details.title', leadingAction: widget.inDisclosure ? widget.onCancel : null, @@ -66,43 +66,47 @@ class _AddDataDetailsScreenState extends State { ), ], ), - body: SingleChildScrollView( - controller: _controller, - padding: EdgeInsets.symmetric( - vertical: theme.defaultSpacing, - horizontal: theme.smallSpacing, - ), - child: SafeArea( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: paddingText, - child: Text( - widget.credentialType.faqIntro.isEmpty - ? - // Fallback generic add credential text - FlutterI18n.translate( - context, - 'data.add.details.obtain', - translationParams: { - 'credential': widget.credentialType.name.translate(lang), - }, - ) - : getTranslation(context, widget.credentialType.faqIntro).replaceAll('\\n', '\n'), - style: theme.textTheme.bodyMedium, - ), - ), - Padding( - padding: paddingQuestions, - child: AddDataQuestions( - inDisclosure: widget.inDisclosure, - credentialType: widget.credentialType, - parentScrollController: _controller, + body: SizedBox( + height: double.infinity, + child: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + controller: _controller, + padding: EdgeInsets.symmetric( + vertical: theme.defaultSpacing, + horizontal: theme.smallSpacing, + ), + child: SafeArea( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: paddingText, + child: Text( + widget.credentialType.faqIntro.isEmpty + ? + // Fallback generic add credential text + FlutterI18n.translate( + context, + 'data.add.details.obtain', + translationParams: { + 'credential': widget.credentialType.name.translate(lang), + }, + ) + : getTranslation(context, widget.credentialType.faqIntro).replaceAll('\\n', '\n'), + style: theme.textTheme.bodyMedium, + ), ), - ) - ], + Padding( + padding: paddingQuestions, + child: AddDataQuestions( + inDisclosure: widget.inDisclosure, + credentialType: widget.credentialType, + parentScrollController: _controller, + ), + ) + ], + ), ), ), ), diff --git a/lib/src/screens/change_language/change_language_screen.dart b/lib/src/screens/change_language/change_language_screen.dart index 3e959374..09ca56d7 100644 --- a/lib/src/screens/change_language/change_language_screen.dart +++ b/lib/src/screens/change_language/change_language_screen.dart @@ -14,22 +14,26 @@ class ChangeLanguageScreen extends StatelessWidget { final theme = IrmaTheme.of(context); return Scaffold( - backgroundColor: theme.backgroundSecondary, + backgroundColor: theme.backgroundTertiary, appBar: const IrmaAppBar( titleTranslationKey: 'settings.language', ), - body: SingleChildScrollView( - padding: EdgeInsets.all( - theme.screenPadding, - ), - child: SafeArea( - child: PreferredLocaleBuilder( - builder: (context, preferredLocale) => Column( - children: [ - UseSystemLanguageToggle(), - SizedBox(height: theme.defaultSpacing), - if (preferredLocale != null) ChangeLanguageRadio(), - ], + body: SizedBox( + height: double.infinity, + child: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + padding: EdgeInsets.all( + theme.screenPadding, + ), + child: SafeArea( + child: PreferredLocaleBuilder( + builder: (context, preferredLocale) => Column( + children: [ + UseSystemLanguageToggle(), + SizedBox(height: theme.defaultSpacing), + if (preferredLocale != null) ChangeLanguageRadio(), + ], + ), ), ), ), diff --git a/lib/src/screens/data/credentials_detail_screen.dart b/lib/src/screens/data/credentials_detail_screen.dart index e75506a8..a68e4150 100644 --- a/lib/src/screens/data/credentials_detail_screen.dart +++ b/lib/src/screens/data/credentials_detail_screen.dart @@ -95,61 +95,65 @@ class _CredentialsDetailScreenState extends State { return Scaffold( key: _scaffoldKey, - backgroundColor: theme.backgroundSecondary, + backgroundColor: theme.backgroundTertiary, appBar: IrmaAppBar( titleTranslationKey: widget.categoryName, ), - body: SingleChildScrollView( - padding: EdgeInsets.symmetric( - horizontal: theme.defaultSpacing, - ), - child: SafeArea( - child: StreamBuilder( - stream: repo.getCredentials(), - builder: (context, AsyncSnapshot snapshot) { - if (!snapshot.hasData) return Container(); - - final filteredCredentials = snapshot.data!.values - .where((cred) => cred.info.credentialType.fullId == widget.credentialTypeId) - .toList(); - - if (filteredCredentials.isEmpty) { - WidgetsBinding.instance.addPostFrameCallback((_) => Navigator.pop(context)); - return Container(); - } - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - height: theme.mediumSpacing, - ), - ...filteredCredentials - .map( - (cred) => IrmaCredentialCard.fromCredential( - cred, - headerTrailing: - // Credential must either be reobtainable or deletable - // for the options bottom sheet to be accessible - cred.info.credentialType.disallowDelete && cred.info.credentialType.issueUrl.isEmpty - ? null - : IconButton( - alignment: Alignment.topRight, - padding: EdgeInsets.zero, - onPressed: () => _showCredentialOptionsBottomSheet(context, cred), - icon: const Icon( - Icons.more_horiz_sharp, + body: SizedBox( + height: double.infinity, + child: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + padding: EdgeInsets.symmetric( + horizontal: theme.defaultSpacing, + ), + child: SafeArea( + child: StreamBuilder( + stream: repo.getCredentials(), + builder: (context, AsyncSnapshot snapshot) { + if (!snapshot.hasData) return Container(); + + final filteredCredentials = snapshot.data!.values + .where((cred) => cred.info.credentialType.fullId == widget.credentialTypeId) + .toList(); + + if (filteredCredentials.isEmpty) { + WidgetsBinding.instance.addPostFrameCallback((_) => Navigator.pop(context)); + return Container(); + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: theme.mediumSpacing, + ), + ...filteredCredentials + .map( + (cred) => IrmaCredentialCard.fromCredential( + cred, + headerTrailing: + // Credential must either be reobtainable or deletable + // for the options bottom sheet to be accessible + cred.info.credentialType.disallowDelete && cred.info.credentialType.issueUrl.isEmpty + ? null + : IconButton( + alignment: Alignment.topRight, + padding: EdgeInsets.zero, + onPressed: () => _showCredentialOptionsBottomSheet(context, cred), + icon: const Icon( + Icons.more_horiz_sharp, + ), ), - ), - ), - ) - .toList(), - SizedBox( - height: theme.mediumSpacing, - ), - ], - ); - }, + ), + ) + .toList(), + SizedBox( + height: theme.mediumSpacing, + ), + ], + ); + }, + ), ), ), ), diff --git a/lib/src/screens/help/help_screen.dart b/lib/src/screens/help/help_screen.dart index 8533df58..c30e21e1 100644 --- a/lib/src/screens/help/help_screen.dart +++ b/lib/src/screens/help/help_screen.dart @@ -36,7 +36,7 @@ class _HelpScreenState extends State { final theme = IrmaTheme.of(context); return Scaffold( - backgroundColor: theme.backgroundSecondary, + backgroundColor: theme.backgroundTertiary, appBar: const IrmaAppBar( titleTranslationKey: 'help.faq', ), diff --git a/lib/src/screens/notifications/notifications_screen.dart b/lib/src/screens/notifications/notifications_screen.dart index 591a8030..b95463e5 100644 --- a/lib/src/screens/notifications/notifications_screen.dart +++ b/lib/src/screens/notifications/notifications_screen.dart @@ -93,7 +93,7 @@ class _NotificationsScreenState extends State { final theme = IrmaTheme.of(context); return Scaffold( - backgroundColor: theme.backgroundSecondary, + backgroundColor: theme.backgroundTertiary, appBar: const IrmaAppBar( titleTranslationKey: 'notifications.title', ), diff --git a/lib/src/screens/settings/settings_screen.dart b/lib/src/screens/settings/settings_screen.dart index d817b213..51f38d23 100644 --- a/lib/src/screens/settings/settings_screen.dart +++ b/lib/src/screens/settings/settings_screen.dart @@ -58,7 +58,7 @@ class _SettingsScreenState extends State { final repo = IrmaRepositoryProvider.of(context); Widget buildHeaderText(String translationKey) => Padding( - padding: EdgeInsets.only(bottom: theme.defaultSpacing), + padding: EdgeInsets.only(bottom: theme.smallSpacing), child: Semantics( header: true, child: TranslatedText( @@ -87,15 +87,17 @@ class _SettingsScreenState extends State { ); return Scaffold( - backgroundColor: theme.backgroundSecondary, + backgroundColor: theme.backgroundTertiary, appBar: const IrmaAppBar( titleTranslationKey: 'settings.title', ), - body: SingleChildScrollView( - padding: EdgeInsets.all( - theme.defaultSpacing, - ), - child: SafeArea( + body: SizedBox( + height: double.infinity, + child: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + padding: EdgeInsets.all( + theme.defaultSpacing, + ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ From 8dc7dab4ae56cd63197393c354e2b8d9b8931b07 Mon Sep 17 00:00:00 2001 From: Wouter Ensink <46427708+w-ensink@users.noreply.github.com> Date: Thu, 21 Nov 2024 16:21:56 +0100 Subject: [PATCH 06/15] bring back box shadow on cards --- lib/src/widgets/irma_card.dart | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/src/widgets/irma_card.dart b/lib/src/widgets/irma_card.dart index c8dd4127..190581a2 100644 --- a/lib/src/widgets/irma_card.dart +++ b/lib/src/widgets/irma_card.dart @@ -92,16 +92,18 @@ class IrmaCard extends StatelessWidget { ); } - return ClipRRect( - borderRadius: theme.borderRadius, - child: InkWell( + return Container( + decoration: boxDecoration, + child: ClipRRect( borderRadius: theme.borderRadius, - onTap: onTap, - child: Container( - // In this context the "margin" is set on the container padding. - padding: margin ?? EdgeInsets.all(theme.defaultSpacing), - decoration: boxDecoration, - child: child, + child: InkWell( + borderRadius: theme.borderRadius, + onTap: onTap, + child: Container( + // In this context the "margin" is set on the container padding. + padding: margin ?? EdgeInsets.all(theme.defaultSpacing), + child: child, + ), ), ), ); From 3246dadd2c29a9788dd2e67edc4546a38a25c9ac Mon Sep 17 00:00:00 2001 From: Wouter Ensink <46427708+w-ensink@users.noreply.github.com> Date: Thu, 21 Nov 2024 17:14:33 +0100 Subject: [PATCH 07/15] fix some padding issues --- lib/src/screens/activity/widgets/recent_activity.dart | 9 ++++++--- .../disclosure_permission_issue_wizard_screen.dart | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/src/screens/activity/widgets/recent_activity.dart b/lib/src/screens/activity/widgets/recent_activity.dart index b7918c4e..921a2fd0 100644 --- a/lib/src/screens/activity/widgets/recent_activity.dart +++ b/lib/src/screens/activity/widgets/recent_activity.dart @@ -105,9 +105,12 @@ class _RecentActivityState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: logEntries .map( - (logEntry) => ActivityCard( - logEntry: logEntry, - irmaConfiguration: irmaConfiguration, + (logEntry) => Padding( + padding: EdgeInsets.only(bottom: theme.smallSpacing), + child: ActivityCard( + logEntry: logEntry, + irmaConfiguration: irmaConfiguration, + ), ), ) .toList(), diff --git a/lib/src/screens/session/disclosure/widgets/disclosure_permission_issue_wizard_screen.dart b/lib/src/screens/session/disclosure/widgets/disclosure_permission_issue_wizard_screen.dart index c4c1b3f3..7caf2caa 100644 --- a/lib/src/screens/session/disclosure/widgets/disclosure_permission_issue_wizard_screen.dart +++ b/lib/src/screens/session/disclosure/widgets/disclosure_permission_issue_wizard_screen.dart @@ -44,6 +44,7 @@ class DisclosurePermissionIssueWizardScreen extends StatelessWidget { title: requestor.name.translate(lang), imagePath: requestor.logoPath, ), + SizedBox(height: theme.defaultSpacing), SessionProgressIndicator( step: state.currentStepIndex + 1, stepCount: state.plannedSteps.length, From c8de79641f92c604948592f2dad3aae5866da7bc Mon Sep 17 00:00:00 2001 From: Wouter Ensink <46427708+w-ensink@users.noreply.github.com> Date: Tue, 26 Nov 2024 10:07:56 +0100 Subject: [PATCH 08/15] add adaptive icons to navbar - now shows outline when tab is not selected for home and data tabs --- lib/src/screens/home/widgets/irma_nav_bar.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/screens/home/widgets/irma_nav_bar.dart b/lib/src/screens/home/widgets/irma_nav_bar.dart index 0655ca66..f16903c9 100644 --- a/lib/src/screens/home/widgets/irma_nav_bar.dart +++ b/lib/src/screens/home/widgets/irma_nav_bar.dart @@ -49,14 +49,14 @@ class IrmaNavBar extends StatelessWidget { children: [ IrmaNavButton( key: const Key('nav_button_home'), - iconData: Icons.home_filled, + iconData: selectedTab == IrmaNavBarTab.home ? Icons.home : Icons.home_outlined, tab: IrmaNavBarTab.home, changeTab: onChangeTab, isSelected: IrmaNavBarTab.home == selectedTab, ), IrmaNavButton( key: const Key('nav_button_data'), - iconData: Icons.person_outline_rounded, + iconData: selectedTab == IrmaNavBarTab.data ? Icons.person : Icons.person_outline, tab: IrmaNavBarTab.data, changeTab: onChangeTab, isSelected: IrmaNavBarTab.data == selectedTab, From be383cf2950d241b3e46cab5337ab75ac134b430 Mon Sep 17 00:00:00 2001 From: Wouter Ensink <46427708+w-ensink@users.noreply.github.com> Date: Tue, 26 Nov 2024 10:57:30 +0100 Subject: [PATCH 09/15] add margin at bottom of navbar on newer iOS devices - check if the app is running on a newer iOS device by looking at the aspect ratio --- lib/src/screens/home/home_screen.dart | 9 +++++-- .../screens/home/widgets/irma_nav_bar.dart | 7 ++++- .../screens/home/widgets/irma_nav_button.dart | 2 +- lib/src/util/rounded_display.dart | 26 +++++++++++++++++++ 4 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 lib/src/util/rounded_display.dart diff --git a/lib/src/screens/home/home_screen.dart b/lib/src/screens/home/home_screen.dart index 41b80b82..92922477 100644 --- a/lib/src/screens/home/home_screen.dart +++ b/lib/src/screens/home/home_screen.dart @@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import '../../models/native_events.dart'; import '../../theme/theme.dart'; +import '../../util/rounded_display.dart'; import '../../widgets/irma_app_bar.dart'; import '../../widgets/irma_repository_provider.dart'; import '../activity/activity_tab.dart'; @@ -58,6 +59,7 @@ class _HomeScreenState extends State { // home tab first. If the home tab is already selected, then we cannot go back further. The HomeScreen is the // root route in the navigator. In that case, we background the app on Android. // On iOS, there is no back button so we don't have to handle this case. + final theme = IrmaTheme.of(context); return WillPopScope( onWillPop: () async { if (selectedTab == IrmaNavBarTab.home) { @@ -104,8 +106,11 @@ class _HomeScreenState extends State { }), ), floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, - floatingActionButton: const IrmaQrScanButton( - key: Key('nav_button_scanner'), + floatingActionButton: Padding( + padding: EdgeInsets.only(bottom: hasRoundedDisplay(context) ? theme.defaultSpacing : 0), + child: const IrmaQrScanButton( + key: Key('nav_button_scanner'), + ), ), bottomNavigationBar: IrmaNavBar( selectedTab: selectedTab, diff --git a/lib/src/screens/home/widgets/irma_nav_bar.dart b/lib/src/screens/home/widgets/irma_nav_bar.dart index f16903c9..47b6c59b 100644 --- a/lib/src/screens/home/widgets/irma_nav_bar.dart +++ b/lib/src/screens/home/widgets/irma_nav_bar.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import '../../../theme/theme.dart'; +import '../../../util/rounded_display.dart'; import 'irma_nav_button.dart'; enum IrmaNavBarTab { @@ -25,7 +26,11 @@ class IrmaNavBar extends StatelessWidget { final theme = IrmaTheme.of(context); return Container( - padding: EdgeInsets.symmetric(horizontal: theme.tinySpacing), + padding: EdgeInsets.only( + left: theme.tinySpacing, + right: theme.tinySpacing, + bottom: hasRoundedDisplay(context) ? theme.defaultSpacing : 0, + ), // Reduce vertical padding for screens with limited height (i.e. landscape mode). height: MediaQuery.of(context).size.height > 450 ? 95 : 85, decoration: BoxDecoration( diff --git a/lib/src/screens/home/widgets/irma_nav_button.dart b/lib/src/screens/home/widgets/irma_nav_button.dart index 3cb2b1df..9d0fc5e6 100644 --- a/lib/src/screens/home/widgets/irma_nav_button.dart +++ b/lib/src/screens/home/widgets/irma_nav_button.dart @@ -53,7 +53,7 @@ class IrmaNavButton extends StatelessWidget { style: theme.themeData.textTheme.titleLarge!.copyWith( color: isSelected ? activeColor : inactiveColor, ), - ) + ), ], ), ), diff --git a/lib/src/util/rounded_display.dart b/lib/src/util/rounded_display.dart new file mode 100644 index 00000000..9bf5904c --- /dev/null +++ b/lib/src/util/rounded_display.dart @@ -0,0 +1,26 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; + +/// Returns whether or not the device has a rounded display. +/// Useful to make layout exceptions for newer iPhones for example. +bool hasRoundedDisplay(BuildContext context) { + if (!Platform.isIOS) { + return false; // Not an iOS device + } + + // Get screen dimensions + final size = MediaQuery.of(context).size; + final aspectRatio = size.height / size.width; + + // Get safe area insets + final padding = MediaQuery.of(context).padding; + + // Check if the device has rounded corners or a notch + final isRounded = padding.top > 20.0 || padding.bottom > 0.0; + + // List of known aspect ratios for rounded iOS devices (adjust as necessary) + final roundedAspectRatios = [2.16, 2.17, 2.34]; + + return isRounded && roundedAspectRatios.any((ratio) => (aspectRatio - ratio).abs() < 0.01); +} From 7e04c1b0f89491993d1b70fcba9e434e0dc177f7 Mon Sep 17 00:00:00 2001 From: Wouter Ensink <46427708+w-ensink@users.noreply.github.com> Date: Tue, 26 Nov 2024 11:18:24 +0100 Subject: [PATCH 10/15] fix Scan QR text bottom offset - it was just ever so slightly to high compared to the other nav bar text --- lib/src/screens/home/widgets/irma_qr_scan_button.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/screens/home/widgets/irma_qr_scan_button.dart b/lib/src/screens/home/widgets/irma_qr_scan_button.dart index 0322e72d..cfebff32 100644 --- a/lib/src/screens/home/widgets/irma_qr_scan_button.dart +++ b/lib/src/screens/home/widgets/irma_qr_scan_button.dart @@ -64,7 +64,7 @@ class IrmaQrScanButton extends StatelessWidget { ), ), SizedBox( - height: theme.tinySpacing, + height: theme.tinySpacing + 0.5, ), ExcludeSemantics( child: TranslatedText( From 6f0db331bd273332d990b1a571ae7ec34088fa07 Mon Sep 17 00:00:00 2001 From: Wouter Ensink <46427708+w-ensink@users.noreply.github.com> Date: Tue, 26 Nov 2024 13:24:00 +0100 Subject: [PATCH 11/15] fix swipe to go back - update deprecated WillPopScope to PopScope --- lib/routing.dart | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/lib/routing.dart b/lib/routing.dart index 0fb8bb7c..5c8925cc 100644 --- a/lib/routing.dart +++ b/lib/routing.dart @@ -1,7 +1,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:irmamobile/src/models/native_events.dart'; +import 'package:irmamobile/src/widgets/irma_repository_provider.dart'; -import 'src/models/native_events.dart'; import 'src/screens/add_data/add_data_screen.dart'; import 'src/screens/change_language/change_language_screen.dart'; import 'src/screens/change_pin/change_pin_screen.dart'; @@ -17,7 +18,6 @@ import 'src/screens/session/session.dart'; import 'src/screens/session/session_screen.dart'; import 'src/screens/session/unknown_session_screen.dart'; import 'src/screens/settings/settings_screen.dart'; -import 'src/widgets/irma_repository_provider.dart'; class Routing { static Map simpleRoutes = { @@ -61,6 +61,27 @@ class Routing { return settings.name == EnrollmentScreen.routeName || settings.name == ChangePinScreen.routeName; } + static _canPop(RouteSettings settings, BuildContext context) { + // If the current route has a subnavigator and is on the root, defer to that component's `WillPopScope` + if (_isSubnavigatorRoute(settings) && _isRootRoute(settings)) { + return true; + } + + // Otherwise if it is a root route, background the app on backpress + if (_isRootRoute(settings)) { + if (settings.name == HomeScreen.routeName) { + // Check if we are in the drawn state. + // We don't want the app to background in this case. + // Defer to home_screen.dart + return true; + } + IrmaRepositoryProvider.of(context).bridgedDispatch(AndroidSendToBackgroundEvent()); + return false; + } + + return true; + } + static Route generateRoute(RouteSettings settings) { // Try to find the appropriate screen, but keep `RouteNotFoundScreen` as default WidgetBuilder screenBuilder = (context) => const RouteNotFoundScreen(); @@ -74,27 +95,8 @@ class Routing { // if the route is an initial route return MaterialPageRoute( builder: (BuildContext context) { - return WillPopScope( - onWillPop: () async { - // If the current route has a subnavigator and is on the root, defer to that component's `WillPopScope` - if (_isSubnavigatorRoute(settings) && _isRootRoute(settings)) { - return true; - } - - // Otherwise if it is a root route, background the app on backpress - if (_isRootRoute(settings)) { - if (settings.name == HomeScreen.routeName) { - // Check if we are in the drawn state. - // We don't want the app to background in this case. - // Defer to home_screen.dart - return true; - } - IrmaRepositoryProvider.of(context).bridgedDispatch(AndroidSendToBackgroundEvent()); - return false; - } - - return true; - }, + return PopScope( + canPop: _canPop(settings, context), child: screenBuilder(context), ); }, From cbc8b4b1a763adc82998bbd36b9d4d0f56f32e1f Mon Sep 17 00:00:00 2001 From: Wouter Ensink <46427708+w-ensink@users.noreply.github.com> Date: Thu, 28 Nov 2024 08:45:16 +0100 Subject: [PATCH 12/15] couple of cleanups --- lib/src/screens/home/widgets/irma_qr_scan_button.dart | 2 +- lib/src/screens/more/widgets/tiles.dart | 2 +- lib/src/widgets/irma_divider.dart | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/src/screens/home/widgets/irma_qr_scan_button.dart b/lib/src/screens/home/widgets/irma_qr_scan_button.dart index cfebff32..0322e72d 100644 --- a/lib/src/screens/home/widgets/irma_qr_scan_button.dart +++ b/lib/src/screens/home/widgets/irma_qr_scan_button.dart @@ -64,7 +64,7 @@ class IrmaQrScanButton extends StatelessWidget { ), ), SizedBox( - height: theme.tinySpacing + 0.5, + height: theme.tinySpacing, ), ExcludeSemantics( child: TranslatedText( diff --git a/lib/src/screens/more/widgets/tiles.dart b/lib/src/screens/more/widgets/tiles.dart index be19ec8a..42502502 100644 --- a/lib/src/screens/more/widgets/tiles.dart +++ b/lib/src/screens/more/widgets/tiles.dart @@ -161,7 +161,7 @@ class ToggleTile extends StatelessWidget { onTap: () => onChanged(!value), trailing: CupertinoSwitch( value: value, - onChanged: null, // We use the onTap on the Tile (Why??) + onChanged: null, // We use the onTap on the Tile activeColor: theme.success, ), ); diff --git a/lib/src/widgets/irma_divider.dart b/lib/src/widgets/irma_divider.dart index b26972c2..9ac3850a 100644 --- a/lib/src/widgets/irma_divider.dart +++ b/lib/src/widgets/irma_divider.dart @@ -15,9 +15,7 @@ class IrmaDivider extends StatelessWidget { return Container( height: 1, - decoration: BoxDecoration( - color: color ?? theme.neutralExtraLight, - ), + color: color ?? theme.neutralExtraLight, ); } } From 2cf524dcdf4a4049474427cd847368df1251f261 Mon Sep 17 00:00:00 2001 From: Wouter Ensink <46427708+w-ensink@users.noreply.github.com> Date: Thu, 28 Nov 2024 13:08:29 +0100 Subject: [PATCH 13/15] use relative file paths in imports - is required by flutter analyze --- lib/routing.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/routing.dart b/lib/routing.dart index 5c8925cc..d548c0c4 100644 --- a/lib/routing.dart +++ b/lib/routing.dart @@ -1,8 +1,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:irmamobile/src/models/native_events.dart'; -import 'package:irmamobile/src/widgets/irma_repository_provider.dart'; +import 'src/models/native_events.dart'; import 'src/screens/add_data/add_data_screen.dart'; import 'src/screens/change_language/change_language_screen.dart'; import 'src/screens/change_pin/change_pin_screen.dart'; @@ -18,6 +17,7 @@ import 'src/screens/session/session.dart'; import 'src/screens/session/session_screen.dart'; import 'src/screens/session/unknown_session_screen.dart'; import 'src/screens/settings/settings_screen.dart'; +import 'src/widgets/irma_repository_provider.dart'; class Routing { static Map simpleRoutes = { From 7251220ee458230489a4cdbb72c79b95b2fd73ed Mon Sep 17 00:00:00 2001 From: Wouter Ensink <46427708+w-ensink@users.noreply.github.com> Date: Thu, 28 Nov 2024 13:55:40 +0100 Subject: [PATCH 14/15] Fix hit target position for 'more' button - on data card --- .../data/credentials_detail_screen.dart | 49 ++++++++++--------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/lib/src/screens/data/credentials_detail_screen.dart b/lib/src/screens/data/credentials_detail_screen.dart index a68e4150..ebbc590d 100644 --- a/lib/src/screens/data/credentials_detail_screen.dart +++ b/lib/src/screens/data/credentials_detail_screen.dart @@ -28,23 +28,25 @@ class CredentialsDetailScreen extends StatefulWidget { class _CredentialsDetailScreenState extends State { final GlobalKey _scaffoldKey = GlobalKey(); - _showCredentialOptionsBottomSheet(BuildContext context, Credential cred) async => showModalBottomSheet( - context: context, - builder: (context) => IrmaCredentialCardOptionsBottomSheet( - onDelete: cred.info.credentialType.disallowDelete - ? null - : () async { - Navigator.of(context).pop(); - await _showConfirmDeleteDialog(_scaffoldKey.currentContext!, cred); - }, - onReobtain: cred.info.credentialType.issueUrl.isEmpty - ? null - : () { - Navigator.of(context).pop(); - _reobtainCredential(context, cred); - }, - ), - ); + _showCredentialOptionsBottomSheet(BuildContext context, Credential cred) async { + showModalBottomSheet( + context: context, + builder: (context) => IrmaCredentialCardOptionsBottomSheet( + onDelete: cred.info.credentialType.disallowDelete + ? null + : () async { + Navigator.of(context).pop(); + await _showConfirmDeleteDialog(_scaffoldKey.currentContext!, cred); + }, + onReobtain: cred.info.credentialType.issueUrl.isEmpty + ? null + : () { + Navigator.of(context).pop(); + _reobtainCredential(context, cred); + }, + ), + ); + } Future _showConfirmDeleteDialog(BuildContext context, Credential credential) async { final confirmed = await showDialog( @@ -136,12 +138,13 @@ class _CredentialsDetailScreenState extends State { // for the options bottom sheet to be accessible cred.info.credentialType.disallowDelete && cred.info.credentialType.issueUrl.isEmpty ? null - : IconButton( - alignment: Alignment.topRight, - padding: EdgeInsets.zero, - onPressed: () => _showCredentialOptionsBottomSheet(context, cred), - icon: const Icon( - Icons.more_horiz_sharp, + : Transform.translate( + offset: Offset(theme.smallSpacing, -10), + child: IconButton( + onPressed: () => _showCredentialOptionsBottomSheet(context, cred), + icon: const Icon( + Icons.more_horiz_sharp, + ), ), ), ), From b034ea6ae4574df2f07d5811120d6e08aff55d94 Mon Sep 17 00:00:00 2001 From: Wouter Ensink <46427708+w-ensink@users.noreply.github.com> Date: Thu, 28 Nov 2024 13:56:13 +0100 Subject: [PATCH 15/15] fix PopScope _canPop function - was calling a bloc function on every render, when it should only be called when the user tries to pop a page and it fails --- lib/routing.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/routing.dart b/lib/routing.dart index d548c0c4..eec09b0a 100644 --- a/lib/routing.dart +++ b/lib/routing.dart @@ -75,7 +75,6 @@ class Routing { // Defer to home_screen.dart return true; } - IrmaRepositoryProvider.of(context).bridgedDispatch(AndroidSendToBackgroundEvent()); return false; } @@ -97,6 +96,11 @@ class Routing { builder: (BuildContext context) { return PopScope( canPop: _canPop(settings, context), + onPopInvokedWithResult: (didPop, popResult) { + if (!didPop) { + IrmaRepositoryProvider.of(context).bridgedDispatch(AndroidSendToBackgroundEvent()); + } + }, child: screenBuilder(context), ); },