[pve-devel] [PATCH pve_flutter_frontend] fix: ui: app bar text alignment issues in iOS
Shan Shaji
s.shaji at proxmox.com
Thu May 15 14:19:24 CEST 2025
By default iOS centers the title in the appbar, which can create a
visual inconsistency with the left aligned two line app name.
Added a custom `PveAppBar` widget to maintain the design and alignment
similliar to android. Also the new widget replaces all other `AppBar`
widgets that has the title property specified.
Signed-off-by: Shan Shaji <s.shaji at proxmox.com>
---
lib/pages/main_layout_slim.dart | 9 ++---
lib/widgets/pve_app_bar.dart | 47 +++++++++++++++++++++++
lib/widgets/pve_file_selector_widget.dart | 4 +-
lib/widgets/pve_guest_backup_widget.dart | 7 ++--
lib/widgets/pve_guest_migrate_widget.dart | 4 +-
lib/widgets/pve_lxc_overview.dart | 4 +-
lib/widgets/pve_node_overview.dart | 5 +--
lib/widgets/pve_qemu_overview.dart | 5 +--
8 files changed, 64 insertions(+), 21 deletions(-)
create mode 100644 lib/widgets/pve_app_bar.dart
diff --git a/lib/pages/main_layout_slim.dart b/lib/pages/main_layout_slim.dart
index ac5a6f9..5e1bfa5 100644
--- a/lib/pages/main_layout_slim.dart
+++ b/lib/pages/main_layout_slim.dart
@@ -23,6 +23,7 @@ import 'package:pve_flutter_frontend/widgets/proxmox_custom_icon.dart';
import 'package:pve_flutter_frontend/widgets/proxmox_gauge_chart.dart';
import 'package:pve_flutter_frontend/widgets/proxmox_heartbeat_indicator.dart';
import 'package:pve_flutter_frontend/widgets/proxmox_stream_builder_widget.dart';
+import 'package:pve_flutter_frontend/widgets/pve_app_bar.dart';
import 'package:pve_flutter_frontend/widgets/pve_file_selector_widget.dart';
import 'package:pve_flutter_frontend/widgets/pve_guest_icon_widget.dart';
import 'package:pve_flutter_frontend/widgets/pve_help_icon_button_widget.dart';
@@ -167,7 +168,7 @@ class MobileDashboard extends StatelessWidget {
final cBloc = Provider.of<PveClusterStatusBloc>(context);
final rBloc = Provider.of<PveResourceBloc>(context);
return Scaffold(
- appBar: AppBar(
+ appBar: PveAppBar(
title: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -185,7 +186,6 @@ class MobileDashboard extends StatelessWidget {
)
],
),
- elevation: 0.0,
leading: const Icon(
ProxmoxIcons.proxmox,
size: 36,
@@ -607,9 +607,8 @@ class MobileResourceOverview extends StatelessWidget {
child: ColoredSafeArea(
child: Scaffold(
endDrawer: _MobileResourceFilterSheet(),
- appBar: AppBar(
+ appBar: PveAppBar(
automaticallyImplyLeading: false,
- elevation: 0,
title: AppbarSearchTextField(
onChanged: (filter) =>
rBloc.events.add(FilterResources(nameFilter: filter)),
@@ -1051,7 +1050,7 @@ class MobileAccessManagement extends StatelessWidget {
return DefaultTabController(
length: 5,
child: Scaffold(
- appBar: AppBar(
+ appBar: PveAppBar(
title: const Text('Permissions'),
//backgroundColor: Colors.transparent,
elevation: 0.0,
diff --git a/lib/widgets/pve_app_bar.dart b/lib/widgets/pve_app_bar.dart
new file mode 100644
index 0000000..de24b96
--- /dev/null
+++ b/lib/widgets/pve_app_bar.dart
@@ -0,0 +1,47 @@
+import 'package:flutter/material.dart';
+
+class PveAppBar extends StatelessWidget implements PreferredSizeWidget {
+ PveAppBar({
+ super.key,
+ this.title,
+ this.automaticallyImplyLeading = true,
+ this.elevation,
+ this.leading,
+ this.actions,
+ this.centerTitle,
+ this.bottom,
+ this.toolbarHeight,
+ this.backgroundColor,
+ this.iconTheme,
+ }) : preferredSize = Size.fromHeight((toolbarHeight ?? kToolbarHeight) +
+ (bottom?.preferredSize.height ?? 0));
+
+ final Widget? title;
+ final double? elevation;
+ final Widget? leading;
+ final bool automaticallyImplyLeading;
+ final List<Widget>? actions;
+ final bool? centerTitle;
+ final PreferredSizeWidget? bottom;
+ final double? toolbarHeight;
+ final Color? backgroundColor;
+ final IconThemeData? iconTheme;
+
+ @override
+ final Size preferredSize;
+
+ @override
+ Widget build(BuildContext context) {
+ return AppBar(
+ centerTitle: centerTitle ?? false,
+ title: title,
+ elevation: elevation ?? 0,
+ leading: leading,
+ automaticallyImplyLeading: automaticallyImplyLeading,
+ actions: actions,
+ bottom: bottom,
+ backgroundColor: backgroundColor,
+ iconTheme: iconTheme,
+ );
+ }
+}
diff --git a/lib/widgets/pve_file_selector_widget.dart b/lib/widgets/pve_file_selector_widget.dart
index 9770ff8..f03a021 100644
--- a/lib/widgets/pve_file_selector_widget.dart
+++ b/lib/widgets/pve_file_selector_widget.dart
@@ -11,6 +11,7 @@ import 'package:pve_flutter_frontend/utils/renderers.dart';
import 'package:pve_flutter_frontend/widgets/proxmox_capacity_indicator.dart';
import 'package:pve_flutter_frontend/widgets/proxmox_stream_builder_widget.dart';
import 'package:pve_flutter_frontend/widgets/proxmox_stream_listener.dart';
+import 'package:pve_flutter_frontend/widgets/pve_app_bar.dart';
class PveFileSelector extends StatefulWidget {
final PveFileSelectorBloc? fBloc;
@@ -32,8 +33,7 @@ class _PveFileSelectorState extends State<PveFileSelector> {
Widget build(BuildContext context) {
return ProxmoxLayoutBuilder(
builder: (context, layout) => Scaffold(
- appBar: AppBar(
- elevation: 0,
+ appBar: PveAppBar(
title: const Text("Storage"),
),
body: PveFileSelectorWidget(
diff --git a/lib/widgets/pve_guest_backup_widget.dart b/lib/widgets/pve_guest_backup_widget.dart
index 8aa7e31..7bd38d4 100644
--- a/lib/widgets/pve_guest_backup_widget.dart
+++ b/lib/widgets/pve_guest_backup_widget.dart
@@ -12,6 +12,7 @@ import 'package:pve_flutter_frontend/utils/utils.dart';
import 'package:pve_flutter_frontend/utils/validators.dart';
import 'package:pve_flutter_frontend/widgets/proxmox_stream_builder_widget.dart';
import 'package:pve_flutter_frontend/widgets/proxmox_stream_listener.dart';
+import 'package:pve_flutter_frontend/widgets/pve_app_bar.dart';
import 'package:pve_flutter_frontend/widgets/pve_file_selector_widget.dart';
import 'package:pve_flutter_frontend/widgets/pve_storage_selector_widget.dart';
@@ -25,8 +26,7 @@ class PveGuestBackupWidget extends StatelessWidget {
final sBloc = Provider.of<PveStorageSelectorBloc>(context);
final fBloc = Provider.of<PveFileSelectorBloc>(context);
return Scaffold(
- appBar: AppBar(
- elevation: 0,
+ appBar: PveAppBar(
title: Text("Backup $guestID"),
),
body: Column(
@@ -417,10 +417,9 @@ class _PveBackupFormState extends State<PveBackupForm> {
return ScaffoldMessenger(
key: _scaffoldKey,
child: Scaffold(
- appBar: AppBar(
+ appBar: PveAppBar(
iconTheme: const IconThemeData(color: Colors.black),
backgroundColor: Colors.transparent,
- elevation: 0,
title: const Text(
"Schedule backup",
style: TextStyle(color: Colors.black),
diff --git a/lib/widgets/pve_guest_migrate_widget.dart b/lib/widgets/pve_guest_migrate_widget.dart
index d467dd7..5ef72eb 100644
--- a/lib/widgets/pve_guest_migrate_widget.dart
+++ b/lib/widgets/pve_guest_migrate_widget.dart
@@ -8,6 +8,7 @@ import 'package:pve_flutter_frontend/bloc/pve_resource_bloc.dart';
import 'package:pve_flutter_frontend/states/pve_migrate_state.dart';
import 'package:pve_flutter_frontend/states/pve_node_selector_state.dart';
import 'package:pve_flutter_frontend/widgets/proxmox_stream_builder_widget.dart';
+import 'package:pve_flutter_frontend/widgets/pve_app_bar.dart';
import 'package:pve_flutter_frontend/widgets/pve_guest_migration_connector_widget.dart';
import 'package:pve_flutter_frontend/widgets/pve_help_icon_button_widget.dart';
@@ -23,7 +24,7 @@ class PveGuestMigrate extends StatelessWidget {
bloc: migrateBloc,
builder: (context, migrateState) {
return Scaffold(
- appBar: AppBar(
+ appBar: PveAppBar(
title: const Text(
"Migration",
style: TextStyle(color: Colors.white, fontSize: 25),
@@ -34,7 +35,6 @@ class PveGuestMigrate extends StatelessWidget {
icon: const Icon(Icons.close),
onPressed: () => Navigator.of(context).pop(),
),
- elevation: 0.0,
actions: <Widget>[
PveHelpIconButton(
baseUrl: (Provider.of<PveResourceBloc>(context)
diff --git a/lib/widgets/pve_lxc_overview.dart b/lib/widgets/pve_lxc_overview.dart
index e8e6edb..2af58df 100644
--- a/lib/widgets/pve_lxc_overview.dart
+++ b/lib/widgets/pve_lxc_overview.dart
@@ -23,6 +23,7 @@ import 'package:pve_flutter_frontend/widgets/colored_safe_area.dart';
import 'package:pve_flutter_frontend/widgets/proxmox_stream_builder_widget.dart';
import 'package:pve_flutter_frontend/widgets/proxmox_stream_listener.dart';
import 'package:pve_flutter_frontend/widgets/pve_action_card_widget.dart';
+import 'package:pve_flutter_frontend/widgets/pve_app_bar.dart';
import 'package:pve_flutter_frontend/widgets/pve_guest_backup_widget.dart';
import 'package:pve_flutter_frontend/widgets/pve_guest_migrate_widget.dart';
import 'package:pve_flutter_frontend/widgets/pve_guest_overview_header.dart';
@@ -72,8 +73,7 @@ class PveLxcOverview extends StatelessWidget {
final config = state.config;
return ColoredSafeArea(
child: Scaffold(
- appBar: AppBar(
- elevation: 0,
+ appBar: PveAppBar(
title: Text(config?.hostname ?? 'CT $guestID'),
),
backgroundColor: Theme.of(context).colorScheme.surfaceContainer,
diff --git a/lib/widgets/pve_node_overview.dart b/lib/widgets/pve_node_overview.dart
index 85410fb..98767fe 100644
--- a/lib/widgets/pve_node_overview.dart
+++ b/lib/widgets/pve_node_overview.dart
@@ -9,6 +9,7 @@ import 'package:pve_flutter_frontend/states/pve_task_log_state.dart';
import 'package:pve_flutter_frontend/utils/renderers.dart';
import 'package:pve_flutter_frontend/utils/utils.dart';
import 'package:pve_flutter_frontend/widgets/colored_safe_area.dart';
+import 'package:pve_flutter_frontend/widgets/pve_app_bar.dart';
import 'package:pve_flutter_frontend/widgets/pve_node_power_settings_widget.dart';
import 'package:pve_flutter_frontend/widgets/proxmox_capacity_indicator.dart';
import 'package:pve_flutter_frontend/widgets/proxmox_stream_builder_widget.dart';
@@ -52,9 +53,7 @@ class PveNodeOverview extends StatelessWidget {
Theme.of(context).colorScheme.onPrimary.withValues(alpha: 0.75);
return ColoredSafeArea(
child: Scaffold(
- appBar: AppBar(
- //backgroundColor: Colors.transparent,
- elevation: 0,
+ appBar: PveAppBar(
title: Text(
"Node $nodeID",
style:
diff --git a/lib/widgets/pve_qemu_overview.dart b/lib/widgets/pve_qemu_overview.dart
index b019b0f..20bb648 100644
--- a/lib/widgets/pve_qemu_overview.dart
+++ b/lib/widgets/pve_qemu_overview.dart
@@ -23,6 +23,7 @@ import 'package:pve_flutter_frontend/widgets/colored_safe_area.dart';
import 'package:pve_flutter_frontend/widgets/proxmox_stream_builder_widget.dart';
import 'package:pve_flutter_frontend/widgets/proxmox_stream_listener.dart';
import 'package:pve_flutter_frontend/widgets/pve_action_card_widget.dart';
+import 'package:pve_flutter_frontend/widgets/pve_app_bar.dart';
import 'package:pve_flutter_frontend/widgets/pve_guest_backup_widget.dart';
import 'package:pve_flutter_frontend/widgets/pve_guest_overview_header.dart';
import 'package:pve_flutter_frontend/widgets/pve_guest_migrate_widget.dart';
@@ -73,9 +74,7 @@ class PveQemuOverview extends StatelessWidget {
return ColoredSafeArea(
child: Scaffold(
- appBar: AppBar(
- //backgroundColor: Colors.transparent,
- elevation: 0,
+ appBar: PveAppBar(
title: Text(config?.name ?? 'VM $guestID'),
),
backgroundColor:
--
2.39.5 (Apple Git-154)
More information about the pve-devel
mailing list