From: Dominik Csapak <d.csapak@proxmox.com>
To: Shan Shaji <s.shaji@proxmox.com>, pve-devel@lists.proxmox.com
Subject: Re: [PATCH pve_flutter_frontend 2/4] fix: breaking changes due to the upgrade of font_awesome_flutter to v11
Date: Tue, 2 Jun 2026 10:54:37 +0200 [thread overview]
Message-ID: <aa729902-7d80-4f25-a97e-d7afa46d5879@proxmox.com> (raw)
In-Reply-To: <20260410150935.25870-3-s.shaji@proxmox.com>
LGTM
Reviewed-by: Dominik Csapak <d.csapak@proxmox.com>
On 4/10/26 5:09 PM, Shan Shaji wrote:
> 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 <s.shaji@proxmox.com>
> ---
> 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: <Widget>[
> - 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: <Widget>[
> - Icon(
> + FaIcon(
> FontAwesomeIcons.heartPulse,
> color: haError ? Colors.red : Colors.green[400],
> ),
> @@ -219,7 +219,7 @@ class _PveGuestHeaderRRDPageViewState extends State<PveGuestHeaderRRDPageView> {
> 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: <Widget>[
> 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: <Widget>[
> 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: <Widget>[
> 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<PveQemuOverviewBloc>(context);
> @@ -140,13 +128,13 @@ class PveQemuOverview extends StatelessWidget {
> mainAxisAlignment: MainAxisAlignment.spaceEvenly,
> children: <Widget>[
> 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
next prev parent reply other threads:[~2026-06-02 8:57 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-10 15:09 [PATCH proxmox{_login_manager,_dart_api_client}/pve_flutter_frontend 0/7] upgrade dependencies based on flutter v3.41 and migrate deprecated members Shan Shaji
2026-04-10 15:09 ` [PATCH pve_flutter_frontend 1/4] chore: upgrade dependencies based on flutter v3.41 Shan Shaji
2026-06-02 8:50 ` Dominik Csapak
2026-04-10 15:09 ` [PATCH pve_flutter_frontend 2/4] fix: breaking changes due to the upgrade of font_awesome_flutter to v11 Shan Shaji
2026-06-02 8:54 ` Dominik Csapak [this message]
2026-04-10 15:09 ` [PATCH pve_flutter_frontend 3/4] fix: migrate to UIScene lifecycle for iOS 26+ compatibility Shan Shaji
2026-04-10 15:09 ` [PATCH pve_flutter_frontend 4/4] chore: use latest ndkVersion from flutter Shan Shaji
2026-04-10 15:09 ` [PATCH proxmox_login_manager 1/1] chore: upgrade dependencies Shan Shaji
2026-04-10 15:09 ` [PATCH proxmox_dart_api_client 1/2] " Shan Shaji
2026-06-02 8:49 ` Dominik Csapak
2026-06-02 9:15 ` Shan Shaji
2026-04-10 15:09 ` [PATCH proxmox_dart_api_client 2/2] deps: add objective_c dependency to access NSError's code property Shan Shaji
2026-06-02 8:48 ` Dominik Csapak
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aa729902-7d80-4f25-a97e-d7afa46d5879@proxmox.com \
--to=d.csapak@proxmox.com \
--cc=pve-devel@lists.proxmox.com \
--cc=s.shaji@proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox