refactor(ui): polish extension update affordance per review

- Move the update dot from the icon corner to beside the card title
- Restyle the card update button to match the row's ghost buttons
- Drop the detail-view dot; the existing update status chip covers it
- Rename the detail update button label to just Update
This commit is contained in:
Levi committed 2026-09-19 19:04:58 +08:00
1 parent d94b59b165
commit d8758267c4
2 files changed
+33 -55

No files matched your search

@@ -976,37 +976,32 @@ class _ExtensionCard extends ConsumerWidget {
children: [
Row(
children: [
Stack(
clipBehavior: Clip.none,
children: [
ExtensionIcon(item: item, size: 40, borderRadius: BorderRadius.circular(6), fallbackFit: BoxFit.cover),
if (canUpdate)
Positioned(
top: -3,
right: -3,
child: Container(
key: ValueKey('extension-card-update-dot-${item.id}'),
width: 11,
height: 11,
decoration: BoxDecoration(
color: palette.error,
shape: BoxShape.circle,
border: Border.all(color: palette.cardBg, width: 2),
),
),
),
],
),
ExtensionIcon(item: item, size: 40, borderRadius: BorderRadius.circular(6), fallbackFit: BoxFit.cover),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: palette.textPrimary, fontSize: 14, fontWeight: FontWeight.w700),
Row(
children: [
Flexible(
child: Text(
item.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: palette.textPrimary, fontSize: 14, fontWeight: FontWeight.w700),
),
),
if (canUpdate) ...[
const SizedBox(width: 6),
Container(
key: ValueKey('extension-card-update-dot-${item.id}'),
width: 8,
height: 8,
decoration: BoxDecoration(color: palette.error, shape: BoxShape.circle),
),
],
],
),
const SizedBox(height: 2),
Text(
@@ -1076,11 +1071,14 @@ class _ExtensionCard extends ConsumerWidget {
mainAxisSize: MainAxisSize.min,
children: [
if (canUpdate && installed != null)
shad.PrimaryButton(
shad.GhostButton(
key: ValueKey('update-extension-${installed.identity}'),
density: shad.ButtonDensity.dense,
onPressed: busy ? null : () => showExtensionUpdateDialog(context, installed),
child: Text(context.l10n.extensionUpdateAction),
child: Text(
context.l10n.extensionUpdateAction,
style: TextStyle(color: palette.error, fontWeight: FontWeight.w600),
),
),
if (installed == null && item.store != null)
shad.GhostButton(
@@ -65,32 +65,12 @@ class ExtensionDetailView extends ConsumerWidget {
key: const ValueKey('extension-details-hero'),
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(
clipBehavior: Clip.none,
children: [
ExtensionIcon(
item: current,
size: mobile ? 60 : 68,
borderRadius: BorderRadius.circular(14),
fallbackPadding: EdgeInsets.all((mobile ? 60 : 68) * 0.16),
fallbackBackgroundColor: palette.surfaceSoft,
),
if (canUpdate)
Positioned(
top: -2,
right: -2,
child: Container(
key: const ValueKey('extension-details-update-dot'),
width: 13,
height: 13,
decoration: BoxDecoration(
color: palette.error,
shape: BoxShape.circle,
border: Border.all(color: palette.bg, width: 2.5),
),
),
),
],
ExtensionIcon(
item: current,
size: mobile ? 60 : 68,
borderRadius: BorderRadius.circular(14),
fallbackPadding: EdgeInsets.all((mobile ? 60 : 68) * 0.16),
fallbackBackgroundColor: palette.surfaceSoft,
),
SizedBox(width: mobile ? 16 : 20),
Expanded(
@@ -157,7 +137,7 @@ class ExtensionDetailView extends ConsumerWidget {
variant: AppLoadingButtonVariant.primary,
icon: const Icon(Icons.upgrade, size: 16),
onPressed: busy ? null : () => showExtensionUpdateDialog(context, installed),
child: Text(context.l10n.newVersionUpdate),
child: Text(context.l10n.extensionUpdateAction),
),
if (installed != null && !canUpdate)
AppLoadingButton(