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