From 97bc2ba141e6ad39471140f69b1eb4200976eed2 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Fri, 8 Sep 2023 17:48:26 +0530 Subject: [PATCH 1/6] Accept icon-width as a param --- lib/ui/utils/icon_utils.dart | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/ui/utils/icon_utils.dart b/lib/ui/utils/icon_utils.dart index 4ee3f8592..1a0ec3edc 100644 --- a/lib/ui/utils/icon_utils.dart +++ b/lib/ui/utils/icon_utils.dart @@ -18,29 +18,39 @@ class IconUtils { await _loadJson(); } - Widget getIcon(String provider) { + Widget getIcon( + String provider, { + double width = 24, + }) { final title = _getProviderTitle(provider); if (_customIcons.containsKey(title)) { return _getSVGIcon( "assets/custom-icons/icons/$title.svg", title, _customIcons[title]!, + width, ); } else if (_simpleIcons.containsKey(title)) { return _getSVGIcon( "assets/simple-icons/icons/$title.svg", title, _simpleIcons[title]!, + width, ); } else { return const SizedBox.shrink(); } } - Widget _getSVGIcon(String path, String title, String color) { + Widget _getSVGIcon( + String path, + String title, + String color, + double width, + ) { return SvgPicture.asset( path, - width: 24, + width: width, semanticsLabel: title, colorFilter: ColorFilter.mode( Color(int.parse("0xFF" + color)), From c02a16a321cbe14be4c661f1ab1a360e6ac6363e Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Fri, 8 Sep 2023 17:49:16 +0530 Subject: [PATCH 2/6] Refactor code widget --- lib/ui/code_widget.dart | 260 +++++++++++++++++++++------------------- 1 file changed, 134 insertions(+), 126 deletions(-) diff --git a/lib/ui/code_widget.dart b/lib/ui/code_widget.dart index 393670f4a..7984052f7 100644 --- a/lib/ui/code_widget.dart +++ b/lib/ui/code_widget.dart @@ -136,132 +136,7 @@ class _CodeWidgetState extends State { onLongPress: () { _copyToClipboard(); }, - child: SizedBox( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (widget.code.type == Type.totp) - CodeTimerProgress( - period: widget.code.period, - ), - const SizedBox( - height: 16, - ), - Padding( - padding: const EdgeInsets.only(left: 16, right: 16), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - safeDecode(widget.code.issuer).trim(), - style: Theme.of(context).textTheme.titleLarge, - ), - const SizedBox(height: 2), - Text( - safeDecode(widget.code.account).trim(), - style: Theme.of(context) - .textTheme - .bodySmall - ?.copyWith( - fontSize: 12, - color: Colors.grey, - ), - ), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - (widget.code.hasSynced != null && - widget.code.hasSynced!) || !hasConfiguredAccount - ? const SizedBox.shrink() - : const Icon( - Icons.sync_disabled, - size: 20, - color: Colors.amber, - ), - const SizedBox(width: 12), - IconUtils.instance.getIcon( - safeDecode(widget.code.issuer).trim(), - ), - ], - ), - ], - ), - ), - const SizedBox(height: 4), - Container( - padding: const EdgeInsets.only(left: 16, right: 16), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Expanded( - child: ValueListenableBuilder( - valueListenable: _currentCode, - builder: (context, value, child) { - return Text( - value, - style: const TextStyle(fontSize: 24), - ); - }, - ), - ), - widget.code.type == Type.totp - ? Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - l10n.nextTotpTitle, - style: - Theme.of(context).textTheme.bodySmall, - ), - ValueListenableBuilder( - valueListenable: _nextCode, - builder: (context, value, child) { - return Text( - value, - style: const TextStyle( - fontSize: 18, - color: Colors.grey, - ), - ); - }, - ), - ], - ) - : Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - l10n.nextTotpTitle, - style: - Theme.of(context).textTheme.bodySmall, - ), - InkWell( - onTap: _onNextHotpTapped, - child: const Icon( - Icons.forward_outlined, - size: 32, - color: Colors.grey, - ), - ), - ], - ), - ], - ), - ), - const SizedBox( - height: 20, - ), - ], - ), - ), + child: _getCardContents(l10n), ), ), ), @@ -270,6 +145,138 @@ class _CodeWidgetState extends State { ); } + SizedBox _getCardContents(AppLocalizations l10n) { + return SizedBox( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (widget.code.type == Type.totp) + CodeTimerProgress( + period: widget.code.period, + ), + const SizedBox( + height: 16, + ), + _getTopRow(), + const SizedBox(height: 4), + _getBottomRow(l10n), + const SizedBox( + height: 20, + ), + ], + ), + ); + } + + Container _getBottomRow(AppLocalizations l10n) { + return Container( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Expanded( + child: ValueListenableBuilder( + valueListenable: _currentCode, + builder: (context, value, child) { + return Text( + value, + style: const TextStyle(fontSize: 24), + ); + }, + ), + ), + widget.code.type == Type.totp + ? Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + l10n.nextTotpTitle, + style: Theme.of(context).textTheme.bodySmall, + ), + ValueListenableBuilder( + valueListenable: _nextCode, + builder: (context, value, child) { + return Text( + value, + style: const TextStyle( + fontSize: 18, + color: Colors.grey, + ), + ); + }, + ), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + l10n.nextTotpTitle, + style: Theme.of(context).textTheme.bodySmall, + ), + InkWell( + onTap: _onNextHotpTapped, + child: const Icon( + Icons.forward_outlined, + size: 32, + color: Colors.grey, + ), + ), + ], + ), + ], + ), + ); + } + + Padding _getTopRow() { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + safeDecode(widget.code.issuer).trim(), + style: Theme.of(context).textTheme.titleLarge, + ), + const SizedBox(height: 2), + Text( + safeDecode(widget.code.account).trim(), + style: Theme.of(context).textTheme.bodySmall?.copyWith( + fontSize: 12, + color: Colors.grey, + ), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + (widget.code.hasSynced != null && widget.code.hasSynced!) || + !hasConfiguredAccount + ? const SizedBox.shrink() + : const Icon( + Icons.sync_disabled, + size: 20, + color: Colors.amber, + ), + const SizedBox(width: 12), + IconUtils.instance.getIcon( + safeDecode(widget.code.issuer).trim(), + ), + ], + ), + ], + ), + ); + } + void _copyToClipboard() { FlutterClipboard.copy(_getCurrentOTP()) .then((value) => showToast(context, context.l10n.copiedToClipboard)); @@ -300,6 +307,7 @@ class _CodeWidgetState extends State { } Future _onShowQrPressed(_) async { + // ignore: unused_local_variable final Code? code = await Navigator.of(context).push( MaterialPageRoute( builder: (BuildContext context) { From d855559bab2c1f2be45bb6d6ad5bd02ed63efe1f Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Fri, 8 Sep 2023 17:51:58 +0530 Subject: [PATCH 3/6] Add option to save the preference for viewing large icons --- lib/services/preference_service.dart | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/services/preference_service.dart b/lib/services/preference_service.dart index 06ab10309..76acd0dd8 100644 --- a/lib/services/preference_service.dart +++ b/lib/services/preference_service.dart @@ -8,6 +8,7 @@ class PreferenceService { late final SharedPreferences _prefs; static const kHasShownCoachMarkKey = "has_shown_coach_mark"; + static const kShouldShowLargeIconsKey = "should_show_large_icons"; Future init() async { _prefs = await SharedPreferences.getInstance(); @@ -24,4 +25,16 @@ class PreferenceService { Future setHasShownCoachMark(bool value) { return _prefs.setBool(kHasShownCoachMarkKey, value); } + + bool shouldShowLargeIcons() { + if (_prefs.containsKey(kShouldShowLargeIconsKey)) { + return _prefs.getBool(kShouldShowLargeIconsKey)!; + } else { + return false; + } + } + + Future setShowLargeIcons(bool value) { + return _prefs.setBool(kShouldShowLargeIconsKey, value); + } } From 7fb3ab02f903e0cbe8ec45707d31898e1cc3c277 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Fri, 8 Sep 2023 18:01:45 +0530 Subject: [PATCH 4/6] Publish an event when icons are changed --- lib/events/icons_changed_event.dart | 3 +++ lib/services/preference_service.dart | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 lib/events/icons_changed_event.dart diff --git a/lib/events/icons_changed_event.dart b/lib/events/icons_changed_event.dart new file mode 100644 index 000000000..5cc3d8b41 --- /dev/null +++ b/lib/events/icons_changed_event.dart @@ -0,0 +1,3 @@ +import 'package:ente_auth/events/event.dart'; + +class IconsChangedEvent extends Event {} diff --git a/lib/services/preference_service.dart b/lib/services/preference_service.dart index 76acd0dd8..f1c6c1c9a 100644 --- a/lib/services/preference_service.dart +++ b/lib/services/preference_service.dart @@ -1,3 +1,5 @@ +import 'package:ente_auth/core/event_bus.dart'; +import 'package:ente_auth/events/icons_changed_event.dart'; import 'package:shared_preferences/shared_preferences.dart'; class PreferenceService { @@ -34,7 +36,8 @@ class PreferenceService { } } - Future setShowLargeIcons(bool value) { - return _prefs.setBool(kShouldShowLargeIconsKey, value); + Future setShowLargeIcons(bool value) async { + await _prefs.setBool(kShouldShowLargeIconsKey, value); + Bus.instance.fire(IconsChangedEvent()); } } From 16975470916a1dcc90ca6e4d0df1ae37a9df20bf Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Fri, 8 Sep 2023 18:02:10 +0530 Subject: [PATCH 5/6] Refresh UI when icon settings are updated --- lib/ui/code_widget.dart | 41 +++++++++++++++++++++++++++++++++++------ lib/ui/home_page.dart | 8 +++++++- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/lib/ui/code_widget.dart b/lib/ui/code_widget.dart index 7984052f7..8aa62a949 100644 --- a/lib/ui/code_widget.dart +++ b/lib/ui/code_widget.dart @@ -7,6 +7,7 @@ import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/models/code.dart'; import 'package:ente_auth/onboarding/view/setup_enter_secret_key_page.dart'; import 'package:ente_auth/onboarding/view/view_qr_page.dart'; +import 'package:ente_auth/services/preference_service.dart'; import 'package:ente_auth/store/code_store.dart'; import 'package:ente_auth/ui/code_timer_progress.dart'; import 'package:ente_auth/ui/utils/icon_utils.dart'; @@ -33,6 +34,7 @@ class _CodeWidgetState extends State { final Logger logger = Logger("_CodeWidgetState"); bool _isInitialized = false; late bool hasConfiguredAccount; + late bool _shouldShowLargeIcon; @override void initState() { @@ -61,6 +63,7 @@ class _CodeWidgetState extends State { @override Widget build(BuildContext context) { + _shouldShowLargeIcon = PreferenceService.instance.shouldShowLargeIcons(); if (!_isInitialized) { _currentCode.value = _getCurrentOTP(); if (widget.code.type == Type.totp) { @@ -158,9 +161,20 @@ class _CodeWidgetState extends State { const SizedBox( height: 16, ), - _getTopRow(), - const SizedBox(height: 4), - _getBottomRow(l10n), + Row( + children: [ + _shouldShowLargeIcon ? _getIcon() : const SizedBox.shrink(), + Expanded( + child: Column( + children: [ + _getTopRow(), + const SizedBox(height: 4), + _getBottomRow(l10n), + ], + ), + ), + ], + ), const SizedBox( height: 20, ), @@ -267,9 +281,7 @@ class _CodeWidgetState extends State { color: Colors.amber, ), const SizedBox(width: 12), - IconUtils.instance.getIcon( - safeDecode(widget.code.issuer).trim(), - ), + _shouldShowLargeIcon ? const SizedBox.shrink() : _getIcon(), ], ), ], @@ -277,6 +289,23 @@ class _CodeWidgetState extends State { ); } + Widget _getIcon() { + return Padding( + padding: _shouldShowLargeIcon + ? const EdgeInsets.only(left: 16) + : const EdgeInsets.all(0), + child: GestureDetector( + onTap: () { + PreferenceService.instance.setShowLargeIcons(!_shouldShowLargeIcon); + }, + child: IconUtils.instance.getIcon( + safeDecode(widget.code.issuer).trim(), + width: _shouldShowLargeIcon ? 42 : 24, + ), + ), + ); + } + void _copyToClipboard() { FlutterClipboard.copy(_getCurrentOTP()) .then((value) => showToast(context, context.l10n.copiedToClipboard)); diff --git a/lib/ui/home_page.dart b/lib/ui/home_page.dart index e8f9305d0..f7f07c21e 100644 --- a/lib/ui/home_page.dart +++ b/lib/ui/home_page.dart @@ -5,6 +5,7 @@ import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/core/event_bus.dart'; import 'package:ente_auth/ente_theme_data.dart'; import 'package:ente_auth/events/codes_updated_event.dart'; +import 'package:ente_auth/events/icons_changed_event.dart'; import 'package:ente_auth/events/trigger_logout_event.dart'; import "package:ente_auth/l10n/l10n.dart"; import 'package:ente_auth/models/code.dart'; @@ -51,6 +52,7 @@ class _HomePageState extends State { List _filteredCodes = []; StreamSubscription? _streamSubscription; StreamSubscription? _triggerLogoutEvent; + StreamSubscription? _iconsChangedEvent; @override void initState() { @@ -69,6 +71,9 @@ class _HomePageState extends State { const Duration(seconds: 1), () async => await CodeStore.instance.importOfflineCodes(), ); + _iconsChangedEvent = Bus.instance.on().listen((event) { + setState(() {}); + }); } void _loadCodes() { @@ -100,6 +105,7 @@ class _HomePageState extends State { void dispose() { _streamSubscription?.cancel(); _triggerLogoutEvent?.cancel(); + _iconsChangedEvent?.cancel(); _textController.removeListener(_applyFilteringAndRefresh); super.dispose(); } @@ -228,7 +234,7 @@ class _HomePageState extends State { itemBuilder: ((context, index) { try { return CodeWidget(_filteredCodes[index]); - } catch(e) { + } catch (e) { return const Text("Failed"); } }), From 91cd77ad8d6a2b6c777734f327c0811d616cb19c Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Fri, 8 Sep 2023 18:26:51 +0530 Subject: [PATCH 6/6] Animate while switching between small and large icons --- lib/ui/code_widget.dart | 258 ++++++++++++++++++++++------------------ pubspec.lock | 8 ++ pubspec.yaml | 1 + 3 files changed, 149 insertions(+), 118 deletions(-) diff --git a/lib/ui/code_widget.dart b/lib/ui/code_widget.dart index 8aa62a949..900f644fb 100644 --- a/lib/ui/code_widget.dart +++ b/lib/ui/code_widget.dart @@ -16,7 +16,9 @@ import 'package:ente_auth/utils/toast_util.dart'; import 'package:ente_auth/utils/totp_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; +import 'package:local_hero/local_hero.dart'; import 'package:logging/logging.dart'; +import 'package:uuid/uuid.dart'; class CodeWidget extends StatefulWidget { final Code code; @@ -35,6 +37,7 @@ class _CodeWidgetState extends State { bool _isInitialized = false; late bool hasConfiguredAccount; late bool _shouldShowLargeIcon; + final String _key = const Uuid().v4(); @override void initState() { @@ -148,126 +151,142 @@ class _CodeWidgetState extends State { ); } - SizedBox _getCardContents(AppLocalizations l10n) { - return SizedBox( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (widget.code.type == Type.totp) - CodeTimerProgress( - period: widget.code.period, - ), - const SizedBox( - height: 16, - ), - Row( - children: [ - _shouldShowLargeIcon ? _getIcon() : const SizedBox.shrink(), - Expanded( - child: Column( - children: [ - _getTopRow(), - const SizedBox(height: 4), - _getBottomRow(l10n), - ], - ), + Widget _getCardContents(AppLocalizations l10n) { + return LocalHeroScope( + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOut, + child: SizedBox( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (widget.code.type == Type.totp) + CodeTimerProgress( + period: widget.code.period, ), - ], - ), - const SizedBox( - height: 20, - ), - ], - ), - ); - } - - Container _getBottomRow(AppLocalizations l10n) { - return Container( - padding: const EdgeInsets.only(left: 16, right: 16), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Expanded( - child: ValueListenableBuilder( - valueListenable: _currentCode, - builder: (context, value, child) { - return Text( - value, - style: const TextStyle(fontSize: 24), - ); - }, + const SizedBox( + height: 16, ), - ), - widget.code.type == Type.totp - ? Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - l10n.nextTotpTitle, - style: Theme.of(context).textTheme.bodySmall, - ), - ValueListenableBuilder( - valueListenable: _nextCode, - builder: (context, value, child) { - return Text( - value, - style: const TextStyle( - fontSize: 18, - color: Colors.grey, - ), - ); - }, - ), - ], - ) - : Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - l10n.nextTotpTitle, - style: Theme.of(context).textTheme.bodySmall, - ), - InkWell( - onTap: _onNextHotpTapped, - child: const Icon( - Icons.forward_outlined, - size: 32, - color: Colors.grey, - ), - ), - ], + Row( + children: [ + _shouldShowLargeIcon ? _getIcon() : const SizedBox.shrink(), + Expanded( + child: Column( + children: [ + _getTopRow(), + const SizedBox(height: 4), + _getBottomRow(l10n), + ], + ), ), - ], + ], + ), + const SizedBox( + height: 20, + ), + ], + ), ), ); } - Padding _getTopRow() { + Widget _getBottomRow(AppLocalizations l10n) { + return LocalHero( + tag: _key + "_bottom_row", + child: Container( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Expanded( + child: ValueListenableBuilder( + valueListenable: _currentCode, + builder: (context, value, child) { + return Material( + type: MaterialType.transparency, + child: Text( + value, + style: const TextStyle(fontSize: 24), + ), + ); + }, + ), + ), + widget.code.type == Type.totp + ? Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + l10n.nextTotpTitle, + style: Theme.of(context).textTheme.bodySmall, + ), + ValueListenableBuilder( + valueListenable: _nextCode, + builder: (context, value, child) { + return Material( + type: MaterialType.transparency, + child: Text( + value, + style: const TextStyle( + fontSize: 18, + color: Colors.grey, + ), + ), + ); + }, + ), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + l10n.nextTotpTitle, + style: Theme.of(context).textTheme.bodySmall, + ), + InkWell( + onTap: _onNextHotpTapped, + child: const Icon( + Icons.forward_outlined, + size: 32, + color: Colors.grey, + ), + ), + ], + ), + ], + ), + ), + ); + } + + Widget _getTopRow() { return Padding( padding: const EdgeInsets.only(left: 16, right: 16), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - safeDecode(widget.code.issuer).trim(), - style: Theme.of(context).textTheme.titleLarge, - ), - const SizedBox(height: 2), - Text( - safeDecode(widget.code.account).trim(), - style: Theme.of(context).textTheme.bodySmall?.copyWith( - fontSize: 12, - color: Colors.grey, - ), - ), - ], + LocalHero( + tag: _key + "_top_row", + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + safeDecode(widget.code.issuer).trim(), + style: Theme.of(context).textTheme.titleLarge, + ), + const SizedBox(height: 2), + Text( + safeDecode(widget.code.account).trim(), + style: Theme.of(context).textTheme.bodySmall?.copyWith( + fontSize: 12, + color: Colors.grey, + ), + ), + ], + ), ), Row( mainAxisAlignment: MainAxisAlignment.end, @@ -290,17 +309,20 @@ class _CodeWidgetState extends State { } Widget _getIcon() { - return Padding( - padding: _shouldShowLargeIcon - ? const EdgeInsets.only(left: 16) - : const EdgeInsets.all(0), - child: GestureDetector( - onTap: () { - PreferenceService.instance.setShowLargeIcons(!_shouldShowLargeIcon); - }, - child: IconUtils.instance.getIcon( - safeDecode(widget.code.issuer).trim(), - width: _shouldShowLargeIcon ? 42 : 24, + return LocalHero( + tag: _key, + child: Padding( + padding: _shouldShowLargeIcon + ? const EdgeInsets.only(left: 16) + : const EdgeInsets.all(0), + child: GestureDetector( + onTap: () { + PreferenceService.instance.setShowLargeIcons(!_shouldShowLargeIcon); + }, + child: IconUtils.instance.getIcon( + safeDecode(widget.code.issuer).trim(), + width: _shouldShowLargeIcon ? 42 : 24, + ), ), ), ); diff --git a/pubspec.lock b/pubspec.lock index 79b0ae47f..ef62acde9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -783,6 +783,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" + local_hero: + dependency: "direct main" + description: + name: local_hero + sha256: "2dd2904c46d786dbc6f7179ba863e04f2be1fd603c530501a336a07744b60c7b" + url: "https://pub.dev" + source: hosted + version: "0.2.0" logging: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index b8669655f..4f5eb5309 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -51,6 +51,7 @@ dependencies: intl: ^0.18.0 json_annotation: ^4.5.0 local_auth: ^2.1.3 + local_hero: ^0.2.0 logging: ^1.0.1 modal_bottom_sheet: ^3.0.0-pre move_to_background: ^1.0.2