From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id BF7EC1FF140 for ; Fri, 10 Apr 2026 17:09:42 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 8036F21292; Fri, 10 Apr 2026 17:10:21 +0200 (CEST) From: Shan Shaji To: pve-devel@lists.proxmox.com Subject: [PATCH pve_flutter_frontend 2/4] fix: breaking changes due to the upgrade of font_awesome_flutter to v11 Date: Fri, 10 Apr 2026 17:09:30 +0200 Message-ID: <20260410150935.25870-3-s.shaji@proxmox.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260410150935.25870-1-s.shaji@proxmox.com> References: <20260410150935.25870-1-s.shaji@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1775833719841 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.153 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: YCLCRQPBDJCA3PAN5E25FYVTZT3K57A3 X-Message-ID-Hash: YCLCRQPBDJCA3PAN5E25FYVTZT3K57A3 X-MailFrom: s.shaji@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Previously, `FaIconData` extended `IconData`, but starting from `v11.0.0`, `FaIconData` no longer implements `IconData` to prevent rendering issues and to anticipate `IconData` being marked `final` in an upcoming Flutter release, which will prevent subtyping outside of its library. Due to this change, the `Icon` widget can no longer directly accept `FontAwesomeIcons`. To fix this, replace the `Icon` widget with `FaIcon` wherever `FontAwesomeIcons` is used. Signed-off-by: Shan Shaji --- lib/pages/main_layout_slim.dart | 4 +- lib/utils/renderers.dart | 5 +-- lib/widgets/pve_action_card_widget.dart | 17 +++++++++ lib/widgets/pve_file_selector_widget.dart | 6 +-- lib/widgets/pve_guest_backup_widget.dart | 2 +- lib/widgets/pve_guest_migrate_widget.dart | 4 +- lib/widgets/pve_guest_os_selector_widget.dart | 4 +- lib/widgets/pve_guest_overview_header.dart | 4 +- lib/widgets/pve_lxc_overview.dart | 32 +++++----------- lib/widgets/pve_node_overview.dart | 6 +-- lib/widgets/pve_qemu_overview.dart | 38 +++++++------------ .../pve_qemu_power_settings_widget.dart | 4 +- pubspec.lock | 4 +- pubspec.yaml | 2 +- 14 files changed, 63 insertions(+), 69 deletions(-) diff --git a/lib/pages/main_layout_slim.dart b/lib/pages/main_layout_slim.dart index 4229ef4..c8ab607 100644 --- a/lib/pages/main_layout_slim.dart +++ b/lib/pages/main_layout_slim.dart @@ -861,11 +861,11 @@ class AppBarFilterIconButton extends StatelessWidget { builder: (context, state) { return IconButton( icon: rBloc.isFiltered - ? const Icon( + ? const FaIcon( FontAwesomeIcons.filter, color: Colors.black, ) - : const Icon( + : const FaIcon( FontAwesomeIcons.filter, color: Colors.grey, ), diff --git a/lib/utils/renderers.dart b/lib/utils/renderers.dart index b139a87..5c73539 100644 --- a/lib/utils/renderers.dart +++ b/lib/utils/renderers.dart @@ -35,13 +35,12 @@ class Renderers { return Icons.storage; case "qemu": return Icons.desktop_windows; - case "lxc": - return FontAwesomeIcons.cube; + return FontAwesomeIcons.cube.data; case "storage": return (shared ?? false) ? Icons.folder_shared - : FontAwesomeIcons.database; + : FontAwesomeIcons.database.data; case "pool": return Icons.label; default: diff --git a/lib/widgets/pve_action_card_widget.dart b/lib/widgets/pve_action_card_widget.dart index 019a584..9fca232 100644 --- a/lib/widgets/pve_action_card_widget.dart +++ b/lib/widgets/pve_action_card_widget.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; class ActionCard extends StatelessWidget { final Function? onTap; @@ -14,6 +15,22 @@ class ActionCard extends StatelessWidget { this.color, }); + factory ActionCard.withIcon(String title, IconData icon, Function onTap) { + return ActionCard( + title: title, + icon: Icon(icon, size: 55, color: Colors.white24), + onTap: onTap, + ); + } + + factory ActionCard.withFaIcon(String title, FaIconData icon, Function onTap) { + return ActionCard( + title: title, + icon: FaIcon(icon, size: 55, color: Colors.white24), + onTap: onTap, + ); + } + @override Widget build(BuildContext context) { return Card( diff --git a/lib/widgets/pve_file_selector_widget.dart b/lib/widgets/pve_file_selector_widget.dart index 59964dd..1722e50 100644 --- a/lib/widgets/pve_file_selector_widget.dart +++ b/lib/widgets/pve_file_selector_widget.dart @@ -373,7 +373,7 @@ class FileSelectorContentView extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon( + FaIcon( getContentIcon(content![index].content), color: Theme.of(context).colorScheme.secondary, ), @@ -401,7 +401,7 @@ class FileSelectorContentView extends StatelessWidget { itemCount: content!.length, itemBuilder: (context, index) => Card( child: ListTile( - leading: Icon( + leading: FaIcon( getContentIcon(content![index].content), color: Theme.of(context).colorScheme.secondary, ), @@ -418,7 +418,7 @@ class FileSelectorContentView extends StatelessWidget { ); } - IconData getContentIcon(PveStorageContentType? content) { + FaIconData getContentIcon(PveStorageContentType? content) { switch (content) { case PveStorageContentType.iso: return FontAwesomeIcons.compactDisc; diff --git a/lib/widgets/pve_guest_backup_widget.dart b/lib/widgets/pve_guest_backup_widget.dart index 4966e9d..119348b 100644 --- a/lib/widgets/pve_guest_backup_widget.dart +++ b/lib/widgets/pve_guest_backup_widget.dart @@ -205,7 +205,7 @@ class PveGuestBackupContent extends StatelessWidget { itemCount: content!.length, itemBuilder: (context, index) => Card( child: ListTile( - leading: Icon( + leading: FaIcon( FontAwesomeIcons.floppyDisk, color: Theme.of(context).colorScheme.onSurface, ), diff --git a/lib/widgets/pve_guest_migrate_widget.dart b/lib/widgets/pve_guest_migrate_widget.dart index cb6cd38..c65f6fe 100644 --- a/lib/widgets/pve_guest_migrate_widget.dart +++ b/lib/widgets/pve_guest_migrate_widget.dart @@ -55,7 +55,7 @@ class PveGuestMigrate extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ ListTile( - leading: const Icon(FontAwesomeIcons.globe), + leading: const FaIcon(FontAwesomeIcons.globe), title: Text( 'Mode', style: TextStyle(color: colorScheme.onPrimary), @@ -68,7 +68,7 @@ class PveGuestMigrate extends StatelessWidget { ), ), ListTile( - leading: const Icon(FontAwesomeIcons.mapPin), + leading: const FaIcon(FontAwesomeIcons.mapPin), title: Text( 'Source', style: TextStyle(color: colorScheme.onPrimary), diff --git a/lib/widgets/pve_guest_os_selector_widget.dart b/lib/widgets/pve_guest_os_selector_widget.dart index 2b3bd82..3a89143 100644 --- a/lib/widgets/pve_guest_os_selector_widget.dart +++ b/lib/widgets/pve_guest_os_selector_widget.dart @@ -40,11 +40,11 @@ class PveGuestOsSelector extends StatelessWidget { Widget getIcon(String? osGroup) { if (osGroup == "Microsoft Windows") { - return const Icon(FontAwesomeIcons.windows); + return const FaIcon(FontAwesomeIcons.windows); } if (osGroup == "Linux") { - return const Icon(FontAwesomeIcons.linux); + return const FaIcon(FontAwesomeIcons.linux); } return Text(osGroup!); diff --git a/lib/widgets/pve_guest_overview_header.dart b/lib/widgets/pve_guest_overview_header.dart index e4b5756..addb888 100644 --- a/lib/widgets/pve_guest_overview_header.dart +++ b/lib/widgets/pve_guest_overview_header.dart @@ -122,7 +122,7 @@ class PveGuestOverviewHeader extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 20.0), child: Row( children: [ - Icon( + FaIcon( FontAwesomeIcons.heartPulse, color: haError ? Colors.red : Colors.green[400], ), @@ -219,7 +219,7 @@ class _PveGuestHeaderRRDPageViewState extends State { subtitle: Renderers.formatSize(rrdData.last.mem ?? 0), data: rrdData.where((e) => e.mem != null).map( (e) => Point(e.time!.millisecondsSinceEpoch, e.mem!)), - icon: Icon(FontAwesomeIcons.memory, color: fgColor), + icon: FaIcon(FontAwesomeIcons.memory, color: fgColor), bottomRight: pageIndicator, dataRenderer: (data) => Renderers.formatSize(data), ), diff --git a/lib/widgets/pve_lxc_overview.dart b/lib/widgets/pve_lxc_overview.dart index 78ad853..7caa5cf 100644 --- a/lib/widgets/pve_lxc_overview.dart +++ b/lib/widgets/pve_lxc_overview.dart @@ -38,18 +38,6 @@ class PveLxcOverview extends StatelessWidget { static final routeName = RegExp(r"\/nodes\/(\S+)\/lxc\/(\d+)"); final String guestID; - ActionCard createActionCard(String title, IconData icon, Function onTap) { - return ActionCard( - icon: Icon( - icon, - size: 55, - color: Colors.white24, - ), - title: title, - onTap: onTap, - ); - } - const PveLxcOverview({super.key, required this.guestID}); @override Widget build(BuildContext context) { @@ -139,13 +127,13 @@ class PveLxcOverview extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ if (!(status?.template ?? false)) - createActionCard( + ActionCard.withIcon( 'Power Settings', Icons.power_settings_new, () => showPowerMenuBottomSheet( context, lxcBloc)), if (!(status?.template ?? false)) - createActionCard( + ActionCard.withIcon( 'Console', Icons.queue_play_next, () => showConsoleMenuBottomSheet( @@ -154,7 +142,7 @@ class PveLxcOverview extends StatelessWidget { guestID, state.nodeID, 'lxc')), - createActionCard( + ActionCard.withIcon( 'Options', Icons.settings, () => Navigator.of(context) @@ -164,7 +152,7 @@ class PveLxcOverview extends StatelessWidget { ), fullscreenDialog: true))), if (!resourceBloc.latestState.isStandalone) - createActionCard( + ActionCard.withFaIcon( 'Migrate', FontAwesomeIcons.paperPlane, () => Navigator.of(context).push( @@ -172,7 +160,7 @@ class PveLxcOverview extends StatelessWidget { guestID, state.nodeID, resourceBloc.apiClient!))), - createActionCard( + ActionCard.withFaIcon( 'Backup', FontAwesomeIcons.floppyDisk, () => Navigator.of(context).push( @@ -193,7 +181,7 @@ class PveLxcOverview extends StatelessWidget { ), children: [ ListTile( - leading: const Icon(FontAwesomeIcons.memory), + leading: const FaIcon(FontAwesomeIcons.memory), title: Text('${config.memory}'), subtitle: const Text('Memory'), dense: true, @@ -211,7 +199,7 @@ class PveLxcOverview extends StatelessWidget { dense: true, ), ListTile( - leading: const Icon(FontAwesomeIcons.hardDrive), + leading: const FaIcon(FontAwesomeIcons.hardDrive), dense: true, title: Text('${config.rootfs}'), ), @@ -229,7 +217,7 @@ class PveLxcOverview extends StatelessWidget { for (var net in config.net!) ListTile( leading: - const Icon(FontAwesomeIcons.ethernet), + const FaIcon(FontAwesomeIcons.ethernet), dense: true, title: Text(net), ), @@ -244,14 +232,14 @@ class PveLxcOverview extends StatelessWidget { ), children: [ ListTile( - leading: const Icon(FontAwesomeIcons.globe), + leading: const FaIcon(FontAwesomeIcons.globe), dense: true, title: Text( config.searchdomain ?? 'Use host settings'), subtitle: const Text('DNS Domain'), ), ListTile( - leading: const Icon( + leading: const FaIcon( FontAwesomeIcons.magnifyingGlass), dense: true, title: Text( diff --git a/lib/widgets/pve_node_overview.dart b/lib/widgets/pve_node_overview.dart index 7236cf4..092bb54 100644 --- a/lib/widgets/pve_node_overview.dart +++ b/lib/widgets/pve_node_overview.dart @@ -115,7 +115,7 @@ class PveNodeOverview extends StatelessWidget { data: rrd.map((e) => Point( e.time!.millisecondsSinceEpoch, e.memused ?? 0)), - icon: Icon(FontAwesomeIcons.memory, + icon: FaIcon(FontAwesomeIcons.memory, color: fgColor), bottomRight: pageIndicator, dataRenderer: (data) => @@ -259,7 +259,7 @@ class PveNodeOverview extends StatelessWidget { child: ListTile( title: const Text('HD space (root)'), subtitle: ProxmoxCapacityIndicator( - icon: Icon( + icon: FaIcon( FontAwesomeIcons.solidHardDrive, color: Colors.blueGrey[300], ), @@ -385,7 +385,7 @@ class PveNodeOverview extends StatelessWidget { .map( (d) => ListTile( dense: true, - leading: Icon(FontAwesomeIcons.solidHardDrive, + leading: FaIcon(FontAwesomeIcons.solidHardDrive, color: state.isDiskHealthy(d) ? Colors.grey : Colors.red), diff --git a/lib/widgets/pve_qemu_overview.dart b/lib/widgets/pve_qemu_overview.dart index 3fb25ba..12e3b30 100644 --- a/lib/widgets/pve_qemu_overview.dart +++ b/lib/widgets/pve_qemu_overview.dart @@ -40,18 +40,6 @@ class PveQemuOverview extends StatelessWidget { const PveQemuOverview({super.key, required this.guestID}); - ActionCard createActionCard(String title, IconData icon, Function onTap) { - return ActionCard( - icon: Icon( - icon, - size: 55, - color: Colors.white24, - ), - title: title, - onTap: onTap, - ); - } - @override Widget build(BuildContext context) { final bloc = Provider.of(context); @@ -140,13 +128,13 @@ class PveQemuOverview extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ if (!(status?.template ?? false)) - createActionCard( + ActionCard.withIcon( 'Power Settings', Icons.power_settings_new, () => showPowerMenuBottomSheet(context, bloc)), if (!(status?.template ?? false)) - createActionCard( + ActionCard.withIcon( 'Console', Icons.queue_play_next, () => showConsoleMenuBottomSheet( @@ -157,19 +145,19 @@ class PveQemuOverview extends StatelessWidget { 'qemu', allowSpice: status?.spice ?? false, )), - createActionCard( + ActionCard.withIcon( 'Options', Icons.settings, () => Navigator.of(context) .push(_createOptionsRoute(bloc))), if (!rBloc.latestState.isStandalone) - createActionCard( + ActionCard.withFaIcon( 'Migrate', FontAwesomeIcons.paperPlane, () => Navigator.of(context).push( _createMigrationRoute(guestID, state.nodeID, bloc.apiClient))), - createActionCard( + ActionCard.withFaIcon( 'Backup', FontAwesomeIcons.floppyDisk, () => Navigator.of(context).push( @@ -191,7 +179,7 @@ class PveQemuOverview extends StatelessWidget { ), children: [ ListTile( - leading: const Icon(FontAwesomeIcons.memory), + leading: const FaIcon(FontAwesomeIcons.memory), title: Text('${config.memory}'), subtitle: const Text('Memory'), dense: true, @@ -204,20 +192,20 @@ class PveQemuOverview extends StatelessWidget { dense: true, ), ListTile( - leading: const Icon(FontAwesomeIcons.microchip), + leading: const FaIcon(FontAwesomeIcons.microchip), title: Text( config.bios?.name ?? 'Default (SeaBIOS)'), subtitle: const Text('BIOS'), dense: true, ), ListTile( - leading: const Icon(FontAwesomeIcons.gears), + leading: const FaIcon(FontAwesomeIcons.gears), dense: true, title: Text(config.machine ?? 'Default (i440fx)'), subtitle: const Text('Machine Type'), ), ListTile( - leading: const Icon(FontAwesomeIcons.database), + leading: const FaIcon(FontAwesomeIcons.database), title: Text( config.scsihw?.name ?? 'Default (i440fx)'), subtitle: const Text('SCSI Controller'), @@ -226,21 +214,23 @@ class PveQemuOverview extends StatelessWidget { for (var ide in config.ide!) ListTile( leading: - const Icon(FontAwesomeIcons.compactDisc), + const FaIcon(FontAwesomeIcons.compactDisc), title: Text(ide), subtitle: const Text('CD/DVD Drive'), dense: true, ), for (var scsi in config.scsi!) ListTile( - leading: const Icon(FontAwesomeIcons.hardDrive), + leading: + const FaIcon(FontAwesomeIcons.hardDrive), title: Text(scsi), subtitle: const Text('Hard Disk'), dense: true, ), for (var net in config.net!) ListTile( - leading: const Icon(FontAwesomeIcons.ethernet), + leading: + const FaIcon(FontAwesomeIcons.ethernet), dense: true, subtitle: const Text('Network Device'), title: Text(net), diff --git a/lib/widgets/pve_qemu_power_settings_widget.dart b/lib/widgets/pve_qemu_power_settings_widget.dart index b4c3c0a..84c3b83 100644 --- a/lib/widgets/pve_qemu_power_settings_widget.dart +++ b/lib/widgets/pve_qemu_power_settings_widget.dart @@ -86,7 +86,7 @@ class PveQemuPowerSettings extends StatelessWidget { context, PveClusterResourceAction.suspend, bloc), ), ListTile( - leading: const Icon(FontAwesomeIcons.download), + leading: const FaIcon(FontAwesomeIcons.download), title: const Text( "Hibernate", style: TextStyle(fontWeight: FontWeight.bold), @@ -106,7 +106,7 @@ class PveQemuPowerSettings extends StatelessWidget { context, PveClusterResourceAction.stop, bloc), ), ListTile( - leading: const Icon(FontAwesomeIcons.bolt), + leading: const FaIcon(FontAwesomeIcons.bolt), title: const Text( "Reset", style: TextStyle(fontWeight: FontWeight.bold), diff --git a/pubspec.lock b/pubspec.lock index ecaab97..def499d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -324,10 +324,10 @@ packages: dependency: "direct main" description: name: font_awesome_flutter - sha256: b9011df3a1fa02993630b8fb83526368cf2206a711259830325bab2f1d2a4eb0 + sha256: "09dcde8ab90ffae1a7d65ff2ef96fc62a17ad9d0ce7c127b317ded676b0d5935" url: "https://pub.dev" source: hosted - version: "10.12.0" + version: "11.0.0" glob: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index e15d57b..04c56a4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,7 +30,7 @@ dependencies: path: ^1.8.0 provider: ^6.0.1 meta: ^1.1.7 - font_awesome_flutter: ^10.0.0 + font_awesome_flutter: ^11.0.0 url_launcher: ^6.0.17 intl: ^0.20.1 path_provider: ^2.0.8 -- 2.50.1