From 727e442c94d9f47650d8ccebd769e420a4213a7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 15 Feb 2026 10:34:03 +0100 Subject: [PATCH] chore: Adjust design of navrail --- lib/config/routes.dart | 1 + lib/pages/chat_list/chat_list_view.dart | 18 +- lib/pages/chat_list/start_chat_fab.dart | 21 + lib/pages/settings/settings_view.dart | 421 ++++++++++----------- lib/widgets/layouts/two_column_layout.dart | 6 +- lib/widgets/navigation_rail.dart | 21 +- 6 files changed, 229 insertions(+), 259 deletions(-) create mode 100644 lib/pages/chat_list/start_chat_fab.dart diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 83431003f..60e788786 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -197,6 +197,7 @@ abstract class AppRoutes { ? TwoColumnLayout( mainView: Settings(key: state.pageKey), sideView: child, + hasNavigationRail: false, ) : child, ), diff --git a/lib/pages/chat_list/chat_list_view.dart b/lib/pages/chat_list/chat_list_view.dart index 56b04a1b3..3c332ce4b 100644 --- a/lib/pages/chat_list/chat_list_view.dart +++ b/lib/pages/chat_list/chat_list_view.dart @@ -1,11 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/themes.dart'; -import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/chat_list/chat_list.dart'; +import 'package:fluffychat/pages/chat_list/start_chat_fab.dart'; import 'package:fluffychat/widgets/navigation_rail.dart'; import 'chat_list_body.dart'; @@ -48,16 +46,10 @@ class ChatListView extends StatelessWidget { child: Scaffold( body: ChatListViewBody(controller), floatingActionButton: - !controller.isSearchMode && controller.activeSpaceId == null - ? FloatingActionButton( - backgroundColor: Theme.of(context).colorScheme.primary, - foregroundColor: Theme.of( - context, - ).colorScheme.onPrimary, - onPressed: () => context.go('/rooms/newprivatechat'), - tooltip: L10n.of(context).newChat, - child: const Icon(Icons.edit_square), - ) + !controller.isSearchMode && + controller.activeSpaceId == null && + !FluffyThemes.isColumnMode(context) + ? StartChatFab() : const SizedBox.shrink(), ), ), diff --git a/lib/pages/chat_list/start_chat_fab.dart b/lib/pages/chat_list/start_chat_fab.dart new file mode 100644 index 000000000..3f206b2a3 --- /dev/null +++ b/lib/pages/chat_list/start_chat_fab.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +import 'package:go_router/go_router.dart'; + +import 'package:fluffychat/l10n/l10n.dart'; + +class StartChatFab extends StatelessWidget { + const StartChatFab({super.key}); + + @override + Widget build(BuildContext context) { + return FloatingActionButton( + heroTag: 'start_chat_fab', + backgroundColor: Theme.of(context).colorScheme.primary, + foregroundColor: Theme.of(context).colorScheme.onPrimary, + onPressed: () => context.go('/rooms/newprivatechat'), + tooltip: L10n.of(context).newChat, + child: const Icon(Icons.edit_square), + ); + } +} diff --git a/lib/pages/settings/settings_view.dart b/lib/pages/settings/settings_view.dart index 419db1ced..476c56d81 100644 --- a/lib/pages/settings/settings_view.dart +++ b/lib/pages/settings/settings_view.dart @@ -6,13 +6,11 @@ import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/utils/fluffy_share.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/matrix.dart'; -import 'package:fluffychat/widgets/navigation_rail.dart'; import '../../widgets/mxc_image_viewer.dart'; import 'settings.dart'; @@ -29,243 +27,208 @@ class SettingsView extends StatelessWidget { context, ).routeInformationProvider.value.uri.path; - return Row( - children: [ - if (FluffyThemes.isColumnMode(context)) ...[ - SpacesNavigationRail( - activeSpaceId: null, - onGoToChats: () => context.go('/rooms'), - onGoToSpaceId: (spaceId) => context.go('/rooms?spaceId=$spaceId'), - ), - Container(color: Theme.of(context).dividerColor, width: 1), - ], - Expanded( - child: Scaffold( - appBar: FluffyThemes.isColumnMode(context) - ? null - : AppBar( - title: Text(L10n.of(context).settings), - leading: Center( - child: BackButton(onPressed: () => context.go('/rooms')), - ), - ), - body: ListTileTheme( - iconColor: theme.colorScheme.onSurface, - child: ListView( - key: const Key('SettingsListViewContent'), - children: [ - FutureBuilder( - future: controller.profileFuture, - builder: (context, snapshot) { - final profile = snapshot.data; - final avatar = profile?.avatarUrl; - final mxid = - Matrix.of(context).client.userID ?? - L10n.of(context).user; - final displayname = - profile?.displayName ?? mxid.localpart ?? mxid; - return Row( + return Scaffold( + appBar: AppBar( + title: Text(L10n.of(context).settings), + leading: Center( + child: BackButton(onPressed: () => context.go('/rooms')), + ), + ), + body: ListTileTheme( + iconColor: theme.colorScheme.onSurface, + child: ListView( + key: const Key('SettingsListViewContent'), + children: [ + FutureBuilder( + future: controller.profileFuture, + builder: (context, snapshot) { + final profile = snapshot.data; + final avatar = profile?.avatarUrl; + final mxid = + Matrix.of(context).client.userID ?? L10n.of(context).user; + final displayname = + profile?.displayName ?? mxid.localpart ?? mxid; + return Row( + children: [ + Padding( + padding: const EdgeInsets.all(32.0), + child: Stack( children: [ - Padding( - padding: const EdgeInsets.all(32.0), - child: Stack( - children: [ - Avatar( - mxContent: avatar, - name: displayname, - size: Avatar.defaultSize * 2.5, - onTap: avatar != null - ? () => showDialog( - context: context, - builder: (_) => - MxcImageViewer(avatar), - ) - : null, - ), - if (profile != null) - Positioned( - bottom: 0, - right: 0, - child: FloatingActionButton.small( - elevation: 2, - onPressed: controller.setAvatarAction, - heroTag: null, - child: const Icon( - Icons.camera_alt_outlined, - ), - ), - ), - ], + Avatar( + mxContent: avatar, + name: displayname, + size: Avatar.defaultSize * 2.5, + onTap: avatar != null + ? () => showDialog( + context: context, + builder: (_) => MxcImageViewer(avatar), + ) + : null, + ), + if (profile != null) + Positioned( + bottom: 0, + right: 0, + child: FloatingActionButton.small( + elevation: 2, + onPressed: controller.setAvatarAction, + heroTag: null, + child: const Icon(Icons.camera_alt_outlined), + ), + ), + ], + ), + ), + Expanded( + child: Column( + mainAxisAlignment: .center, + crossAxisAlignment: .start, + children: [ + TextButton.icon( + onPressed: controller.setDisplaynameAction, + icon: const Icon(Icons.edit_outlined, size: 16), + style: TextButton.styleFrom( + foregroundColor: theme.colorScheme.onSurface, + iconColor: theme.colorScheme.onSurface, + ), + label: Text( + displayname, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle(fontSize: 18), ), ), - Expanded( - child: Column( - mainAxisAlignment: .center, - crossAxisAlignment: .start, - children: [ - TextButton.icon( - onPressed: controller.setDisplaynameAction, - icon: const Icon( - Icons.edit_outlined, - size: 16, - ), - style: TextButton.styleFrom( - foregroundColor: - theme.colorScheme.onSurface, - iconColor: theme.colorScheme.onSurface, - ), - label: Text( - displayname, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: const TextStyle(fontSize: 18), - ), - ), - TextButton.icon( - onPressed: () => - FluffyShare.share(mxid, context), - icon: const Icon( - Icons.copy_outlined, - size: 14, - ), - style: TextButton.styleFrom( - foregroundColor: - theme.colorScheme.secondary, - iconColor: theme.colorScheme.secondary, - ), - label: Text( - mxid, - maxLines: 1, - overflow: TextOverflow.ellipsis, - // style: const TextStyle(fontSize: 12), - ), - ), - ], + TextButton.icon( + onPressed: () => FluffyShare.share(mxid, context), + icon: const Icon(Icons.copy_outlined, size: 14), + style: TextButton.styleFrom( + foregroundColor: theme.colorScheme.secondary, + iconColor: theme.colorScheme.secondary, + ), + label: Text( + mxid, + maxLines: 1, + overflow: TextOverflow.ellipsis, + // style: const TextStyle(fontSize: 12), ), ), ], - ); - }, - ), - FutureBuilder( - future: Matrix.of(context).client.getWellknown(), - builder: (context, snapshot) { - final accountManageUrl = snapshot - .data - ?.additionalProperties - .tryGetMap( - 'org.matrix.msc2965.authentication', - ) - ?.tryGet('account'); - if (accountManageUrl == null) { - return const SizedBox.shrink(); - } - return ListTile( - leading: const Icon(Icons.account_circle_outlined), - title: Text(L10n.of(context).manageAccount), - trailing: const Icon(Icons.open_in_new_outlined), - onTap: () => launchUrlString( - accountManageUrl, - mode: LaunchMode.inAppBrowserView, - ), - ); - }, - ), - Divider(color: theme.dividerColor), - if (showChatBackupBanner == null) - ListTile( - leading: const Icon(Icons.backup_outlined), - title: Text(L10n.of(context).chatBackup), - trailing: const CircularProgressIndicator.adaptive(), - ) - else - SwitchListTile.adaptive( - controlAffinity: ListTileControlAffinity.trailing, - value: controller.showChatBackupBanner == false, - secondary: const Icon(Icons.backup_outlined), - title: Text(L10n.of(context).chatBackup), - onChanged: controller.firstRunBootstrapAction, - ), - Divider(color: theme.dividerColor), - ListTile( - leading: const Icon(Icons.format_paint_outlined), - title: Text(L10n.of(context).changeTheme), - tileColor: activeRoute.startsWith('/rooms/settings/style') - ? theme.colorScheme.surfaceContainerHigh - : null, - onTap: () => context.go('/rooms/settings/style'), - ), - ListTile( - leading: const Icon(Icons.notifications_outlined), - title: Text(L10n.of(context).notifications), - tileColor: - activeRoute.startsWith('/rooms/settings/notifications') - ? theme.colorScheme.surfaceContainerHigh - : null, - onTap: () => context.go('/rooms/settings/notifications'), - ), - ListTile( - leading: const Icon(Icons.devices_outlined), - title: Text(L10n.of(context).devices), - onTap: () => context.go('/rooms/settings/devices'), - tileColor: activeRoute.startsWith('/rooms/settings/devices') - ? theme.colorScheme.surfaceContainerHigh - : null, - ), - ListTile( - leading: const Icon(Icons.forum_outlined), - title: Text(L10n.of(context).chat), - onTap: () => context.go('/rooms/settings/chat'), - tileColor: activeRoute.startsWith('/rooms/settings/chat') - ? theme.colorScheme.surfaceContainerHigh - : null, - ), - ListTile( - leading: const Icon(Icons.shield_outlined), - title: Text(L10n.of(context).security), - onTap: () => context.go('/rooms/settings/security'), - tileColor: - activeRoute.startsWith('/rooms/settings/security') - ? theme.colorScheme.surfaceContainerHigh - : null, - ), - Divider(color: theme.dividerColor), - ListTile( - leading: const Icon(Icons.dns_outlined), - title: Text( - L10n.of(context).aboutHomeserver( - Matrix.of(context).client.userID?.domain ?? - 'homeserver', ), ), - onTap: () => context.go('/rooms/settings/homeserver'), - tileColor: - activeRoute.startsWith('/rooms/settings/homeserver') - ? theme.colorScheme.surfaceContainerHigh - : null, - ), - ListTile( - leading: const Icon(Icons.privacy_tip_outlined), - title: Text(L10n.of(context).privacy), - onTap: () => launchUrl(AppConfig.privacyUrl), - ), - ListTile( - leading: const Icon(Icons.info_outline_rounded), - title: Text(L10n.of(context).about), - onTap: () => PlatformInfos.showDialog(context), - ), - Divider(color: theme.dividerColor), - ListTile( - leading: const Icon(Icons.logout_outlined), - title: Text(L10n.of(context).logout), - onTap: controller.logoutAction, - ), - ], - ), + ], + ); + }, ), - ), + FutureBuilder( + future: Matrix.of(context).client.getWellknown(), + builder: (context, snapshot) { + final accountManageUrl = snapshot.data?.additionalProperties + .tryGetMap( + 'org.matrix.msc2965.authentication', + ) + ?.tryGet('account'); + if (accountManageUrl == null) { + return const SizedBox.shrink(); + } + return ListTile( + leading: const Icon(Icons.account_circle_outlined), + title: Text(L10n.of(context).manageAccount), + trailing: const Icon(Icons.open_in_new_outlined), + onTap: () => launchUrlString( + accountManageUrl, + mode: LaunchMode.inAppBrowserView, + ), + ); + }, + ), + Divider(color: theme.dividerColor), + if (showChatBackupBanner == null) + ListTile( + leading: const Icon(Icons.backup_outlined), + title: Text(L10n.of(context).chatBackup), + trailing: const CircularProgressIndicator.adaptive(), + ) + else + SwitchListTile.adaptive( + controlAffinity: ListTileControlAffinity.trailing, + value: controller.showChatBackupBanner == false, + secondary: const Icon(Icons.backup_outlined), + title: Text(L10n.of(context).chatBackup), + onChanged: controller.firstRunBootstrapAction, + ), + Divider(color: theme.dividerColor), + ListTile( + leading: const Icon(Icons.format_paint_outlined), + title: Text(L10n.of(context).changeTheme), + tileColor: activeRoute.startsWith('/rooms/settings/style') + ? theme.colorScheme.surfaceContainerHigh + : null, + onTap: () => context.go('/rooms/settings/style'), + ), + ListTile( + leading: const Icon(Icons.notifications_outlined), + title: Text(L10n.of(context).notifications), + tileColor: activeRoute.startsWith('/rooms/settings/notifications') + ? theme.colorScheme.surfaceContainerHigh + : null, + onTap: () => context.go('/rooms/settings/notifications'), + ), + ListTile( + leading: const Icon(Icons.devices_outlined), + title: Text(L10n.of(context).devices), + onTap: () => context.go('/rooms/settings/devices'), + tileColor: activeRoute.startsWith('/rooms/settings/devices') + ? theme.colorScheme.surfaceContainerHigh + : null, + ), + ListTile( + leading: const Icon(Icons.forum_outlined), + title: Text(L10n.of(context).chat), + onTap: () => context.go('/rooms/settings/chat'), + tileColor: activeRoute.startsWith('/rooms/settings/chat') + ? theme.colorScheme.surfaceContainerHigh + : null, + ), + ListTile( + leading: const Icon(Icons.shield_outlined), + title: Text(L10n.of(context).security), + onTap: () => context.go('/rooms/settings/security'), + tileColor: activeRoute.startsWith('/rooms/settings/security') + ? theme.colorScheme.surfaceContainerHigh + : null, + ), + Divider(color: theme.dividerColor), + ListTile( + leading: const Icon(Icons.dns_outlined), + title: Text( + L10n.of(context).aboutHomeserver( + Matrix.of(context).client.userID?.domain ?? 'homeserver', + ), + ), + onTap: () => context.go('/rooms/settings/homeserver'), + tileColor: activeRoute.startsWith('/rooms/settings/homeserver') + ? theme.colorScheme.surfaceContainerHigh + : null, + ), + ListTile( + leading: const Icon(Icons.privacy_tip_outlined), + title: Text(L10n.of(context).privacy), + onTap: () => launchUrl(AppConfig.privacyUrl), + ), + ListTile( + leading: const Icon(Icons.info_outline_rounded), + title: Text(L10n.of(context).about), + onTap: () => PlatformInfos.showDialog(context), + ), + Divider(color: theme.dividerColor), + ListTile( + leading: const Icon(Icons.logout_outlined), + title: Text(L10n.of(context).logout), + onTap: controller.logoutAction, + ), + ], ), - ], + ), ); } } diff --git a/lib/widgets/layouts/two_column_layout.dart b/lib/widgets/layouts/two_column_layout.dart index 4c43d424a..9bcbd515b 100644 --- a/lib/widgets/layouts/two_column_layout.dart +++ b/lib/widgets/layouts/two_column_layout.dart @@ -5,11 +5,13 @@ import 'package:fluffychat/config/themes.dart'; class TwoColumnLayout extends StatelessWidget { final Widget mainView; final Widget sideView; + final bool hasNavigationRail; const TwoColumnLayout({ super.key, required this.mainView, required this.sideView, + this.hasNavigationRail = true, }); @override Widget build(BuildContext context) { @@ -22,7 +24,9 @@ class TwoColumnLayout extends StatelessWidget { Container( clipBehavior: Clip.antiAlias, decoration: const BoxDecoration(), - width: FluffyThemes.columnWidth + FluffyThemes.navRailWidth, + width: + FluffyThemes.columnWidth + + (hasNavigationRail ? FluffyThemes.navRailWidth : 0), child: mainView, ), Container(width: 1.0, color: theme.dividerColor), diff --git a/lib/widgets/navigation_rail.dart b/lib/widgets/navigation_rail.dart index f6df4e29a..a287f608c 100644 --- a/lib/widgets/navigation_rail.dart +++ b/lib/widgets/navigation_rail.dart @@ -7,6 +7,7 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/chat_list/navi_rail_item.dart'; +import 'package:fluffychat/pages/chat_list/start_chat_fab.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/utils/stream_extension.dart'; import 'package:fluffychat/widgets/avatar.dart'; @@ -27,9 +28,6 @@ class SpacesNavigationRail extends StatelessWidget { @override Widget build(BuildContext context) { final client = Matrix.of(context).client; - final isSettings = GoRouter.of( - context, - ).routeInformationProvider.value.uri.path.startsWith('/rooms/settings'); return Material( child: SafeArea( child: StreamBuilder( @@ -55,7 +53,7 @@ class SpacesNavigationRail extends StatelessWidget { itemBuilder: (context, i) { if (i == 0) { return NaviRailItem( - isSelected: activeSpaceId == null && !isSettings, + isSelected: activeSpaceId == null, onTap: onGoToChats, icon: const Padding( padding: EdgeInsets.all(10.0), @@ -110,18 +108,9 @@ class SpacesNavigationRail extends StatelessWidget { }, ), ), - NaviRailItem( - isSelected: isSettings, - onTap: () => context.go('/rooms/settings'), - icon: const Padding( - padding: EdgeInsets.all(10.0), - child: Icon(Icons.settings_outlined), - ), - selectedIcon: const Padding( - padding: EdgeInsets.all(10.0), - child: Icon(Icons.settings), - ), - toolTip: L10n.of(context).settings, + Padding( + padding: const EdgeInsets.all(12.0), + child: StartChatFab(), ), ], ),