Преглед изворни кода

Animate while switching between small and large icons

vishnukvmd пре 1 година
родитељ
комит
91cd77ad8d
3 измењених фајлова са 144 додато и 113 уклоњено
  1. 135 113
      lib/ui/code_widget.dart
  2. 8 0
      pubspec.lock
  3. 1 0
      pubspec.yaml

+ 135 - 113
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<CodeWidget> {
   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<CodeWidget> {
     );
   }
 
-  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,
+  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: 16,
             ),
-          const SizedBox(
-            height: 16,
-          ),
-          Row(
-            children: [
-              _shouldShowLargeIcon ? _getIcon() : const SizedBox.shrink(),
-              Expanded(
-                child: Column(
-                  children: [
-                    _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,
-          ),
-        ],
+              ],
+            ),
+            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<String>(
-              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<String>(
-                      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,
+  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<String>(
+                valueListenable: _currentCode,
+                builder: (context, value, child) {
+                  return Material(
+                    type: MaterialType.transparency,
+                    child: Text(
+                      value,
+                      style: const TextStyle(fontSize: 24),
                     ),
-                    InkWell(
-                      onTap: _onNextHotpTapped,
-                      child: const Icon(
-                        Icons.forward_outlined,
-                        size: 32,
-                        color: Colors.grey,
+                  );
+                },
+              ),
+            ),
+            widget.code.type == Type.totp
+                ? Column(
+                    crossAxisAlignment: CrossAxisAlignment.end,
+                    children: [
+                      Text(
+                        l10n.nextTotpTitle,
+                        style: Theme.of(context).textTheme.bodySmall,
                       ),
-                    ),
-                  ],
-                ),
-        ],
+                      ValueListenableBuilder<String>(
+                        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,
+                        ),
+                      ),
+                    ],
+                  ),
+          ],
+        ),
       ),
     );
   }
 
-  Padding _getTopRow() {
+  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<CodeWidget> {
   }
 
   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,
+          ),
         ),
       ),
     );

+ 8 - 0
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:

+ 1 - 0
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