From fa990cd5205160f2f650847ddecdab62516fd97a Mon Sep 17 00:00:00 2001 From: wanghongenpin Date: Tue, 12 May 2026 05:24:05 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20improve=20app=20icon=20loading=20and=20c?= =?UTF-8?q?aching=20in=20InstalledAppsWidget=20=EF=BC=88#739=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/ui/mobile/setting/app_filter.dart | 49 ++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/lib/ui/mobile/setting/app_filter.dart b/lib/ui/mobile/setting/app_filter.dart index ec78f57..8161991 100644 --- a/lib/ui/mobile/setting/app_filter.dart +++ b/lib/ui/mobile/setting/app_filter.dart @@ -13,8 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import 'dart:typed_data'; - import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:proxypin/l10n/app_localizations.dart'; @@ -321,6 +319,7 @@ class InstalledAppsWidget extends StatefulWidget { class _InstalledAppsWidgetState extends State { static List? apps; static bool includeSystemApps = false; + static final Map> _iconFutureCache = {}; RxBool loading = false.obs; @@ -338,9 +337,10 @@ class _InstalledAppsWidgetState extends State { @override void dispose() { - DelayedTask().debounce("InstalledAppsWidget_release", const Duration(seconds: 10), () { + DelayedTask().debounce("InstalledAppsWidget_release", const Duration(seconds: 60), () { apps = null; includeSystemApps = false; + _iconFutureCache.clear(); }); super.dispose(); } @@ -348,7 +348,7 @@ class _InstalledAppsWidgetState extends State { void refreshApps() async { try { loading.value = true; - apps = await InstalledApps.getInstalledApps(true, includeSystemApps: includeSystemApps); + apps = await InstalledApps.getInstalledApps(false, includeSystemApps: includeSystemApps); } finally { loading.value = false; } @@ -414,7 +414,7 @@ class _InstalledAppsWidgetState extends State { itemBuilder: (BuildContext context, int index) { AppInfo appInfo = appInfoList[index]; return ListTile( - leading: Image.memory(appInfo.icon ?? Uint8List(0)), + leading: _buildAppIcon(appInfo), title: Text(appInfo.name ?? ""), subtitle: Text(appInfo.packageName ?? ""), onTap: () async { @@ -423,4 +423,43 @@ class _InstalledAppsWidgetState extends State { ); }); } + + Widget _buildAppIcon(AppInfo appInfo) { + final icon = appInfo.icon; + if (icon != null && icon.isNotEmpty) { + return Image.memory(icon); + } + + final packageName = appInfo.packageName; + if (packageName == null || packageName.isEmpty) { + return const Icon(Icons.question_mark); + } + + final future = _iconFutureCache.putIfAbsent( + packageName, + () => InstalledApps.getAppInfo(packageName), + ); + + return FutureBuilder( + future: future, + builder: (BuildContext context, AsyncSnapshot snapshot) { + final loadedIcon = snapshot.data?.icon; + if (loadedIcon != null && loadedIcon.isNotEmpty) { + return Image.memory(loadedIcon); + } + + if (snapshot.hasError) { + return const Icon(Icons.question_mark); + } + + return const SizedBox( + width: 24, + height: 24, + child: Center( + child: CircularProgressIndicator(strokeWidth: 2), + ), + ); + }, + ); + } }