From 45d69b0b9f382894e42e6a583a6ac294d4949d5e Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Fri, 2 Oct 2026 11:17:37 -0400 Subject: [PATCH] move NotificationsPage into sidebar via NavigationBar --- lib/controllers/notification.dart | 6 +- lib/models/nav_page.dart | 8 + lib/widgets/invites_list.dart | 22 ++ ...fications.dart => notifications_list.dart} | 153 ++++---- lib/widgets/room_list.dart | 325 +++++++++++++++++ lib/widgets/sidebar.dart | 335 ++---------------- 6 files changed, 459 insertions(+), 390 deletions(-) create mode 100644 lib/models/nav_page.dart create mode 100644 lib/widgets/invites_list.dart rename lib/widgets/{pages/notifications.dart => notifications_list.dart} (50%) create mode 100644 lib/widgets/room_list.dart diff --git a/lib/controllers/notification.dart b/lib/controllers/notification.dart index 04cb5ae..fe631d4 100644 --- a/lib/controllers/notification.dart +++ b/lib/controllers/notification.dart @@ -7,7 +7,7 @@ import "package:nexus/controllers/portal.dart"; import "package:nexus/main.dart"; import "package:flutter_local_notifications/flutter_local_notifications.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/widgets/pages/notifications.dart"; +import "package:nexus/widgets/notifications_list.dart"; import "package:xdg_desktop_portal/xdg_desktop_portal.dart"; class NotificationController @@ -37,7 +37,7 @@ class NotificationController if (navigatorKey.currentContext case final context?) { Navigator.of(context).push( MaterialPageRoute( - builder: (_) => NotificationsPage( + builder: (_) => NotificationsList( highlightedEvent: rowId, defaultToAllNotifications: true, ), @@ -61,7 +61,7 @@ class NotificationController if (navigatorKey.currentContext case final context?) { Navigator.of(context).push( MaterialPageRoute( - builder: (_) => NotificationsPage( + builder: (_) => NotificationsList( highlightedEvent: rowId, defaultToAllNotifications: true, ), diff --git a/lib/models/nav_page.dart b/lib/models/nav_page.dart new file mode 100644 index 0000000..2443c53 --- /dev/null +++ b/lib/models/nav_page.dart @@ -0,0 +1,8 @@ +import "package:flutter/widgets.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; + +abstract class const NavPage({super.key}) extends Widget { + String get title; + IconData get icon; + Future? badgeBuilder(WidgetRef ref) => null; +} diff --git a/lib/widgets/invites_list.dart b/lib/widgets/invites_list.dart new file mode 100644 index 0000000..35dd26f --- /dev/null +++ b/lib/widgets/invites_list.dart @@ -0,0 +1,22 @@ +import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:material_ui/material_ui.dart"; +import "package:nexus/models/nav_page.dart"; + +class const InvitesList({super.key}) + extends StatelessWidget + implements NavPage { + @override + String get title => "Invites"; + + @override + IconData get icon => Icons.inbox; + + // TODO: Show notification badge here + @override + Future? badgeBuilder(WidgetRef ref) => null; + + @override + Widget build(BuildContext context) { + return const Placeholder(); + } +} diff --git a/lib/widgets/pages/notifications.dart b/lib/widgets/notifications_list.dart similarity index 50% rename from lib/widgets/pages/notifications.dart rename to lib/widgets/notifications_list.dart index 824e5dc..5ce93bf 100644 --- a/lib/widgets/pages/notifications.dart +++ b/lib/widgets/notifications_list.dart @@ -7,18 +7,28 @@ import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:nexus/controllers/notifications.dart"; import "package:nexus/helpers/extensions/focus_room.dart"; import "package:nexus/models/event.dart"; -import "package:nexus/widgets/appbar.dart"; +import "package:nexus/models/nav_page.dart"; import "package:nexus/widgets/error_dialog.dart"; import "package:nexus/widgets/highlight_wrapper.dart"; import "package:nexus/widgets/loading.dart"; import "package:nexus/widgets/renderers/event.dart"; import "package:super_sliver_list/super_sliver_list.dart"; -class const NotificationsPage({ +class const NotificationsList({ final int? highlightedEvent, final bool defaultToAllNotifications = false, super.key, -}) extends HookConsumerWidget { +}) extends HookConsumerWidget implements NavPage { + @override + String get title => "Notifications"; + + @override + IconData get icon => Icons.notifications; + + // TODO: Show notification badge here + @override + Future? badgeBuilder(WidgetRef ref) => null; + @override Widget build(BuildContext context, WidgetRef ref) { final options = { @@ -91,81 +101,74 @@ class const NotificationsPage({ return () => scrollController.removeListener(listener); }, [scrollController, notifications]); - return Scaffold( - appBar: Appbar(title: Text("Notifications")), - body: Stack( - children: [ - Column( - children: [ - if (notifications is AsyncLoading && notifications.value != null) - const LinearProgressIndicator(minHeight: 2), - Expanded( - child: switch (notifications) { - AsyncData(:final value) || AsyncLoading(:final value?) => - value.isEmpty - ? Center( - child: Text( - "No notifications yet", - style: Theme.of(context).textTheme.headlineMedium, - ), - ) - : SuperListView.builder( - listController: listController.value, - controller: scrollController, - itemCount: value.length, - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 12, - ), - reverse: true, - itemBuilder: (context, index) { - final (rowId, roomId) = value[index]; - final isHighlighted = - rowId == highlightedId.value; - - return Padding( - padding: .only(top: 8), - child: HighlightWrapper( - InkWell( - onTap: () async { - Navigator.of(context).pop(); - await ref.focusRoom(roomId, rowId); - }, - child: IgnorePointer( - child: EventRenderer( - rowId, - roomId: roomId, - ), - ), - ), - isHighlighted: isHighlighted, - ), - ); - }, + return Stack( + children: [ + Column( + children: [ + if (notifications is AsyncLoading && notifications.value != null) + const LinearProgressIndicator(minHeight: 2), + Expanded( + child: switch (notifications) { + AsyncData(:final value) || AsyncLoading(:final value?) => + value.isEmpty + ? Center( + child: Text( + "No notifications yet", + style: Theme.of(context).textTheme.headlineMedium, ), - AsyncLoading() => const Loading(), - AsyncError(:final error, :final stackTrace) => ErrorDialog( - error, - stackTrace, - ), - }, - ), - ], - ), - Align( - alignment: .topRight, - child: Padding( - padding: .all(16), - child: M3EToggleButtonGroup( - selectedIndex: unreadTypeIndex.value, - onSelectedIndexChanged: (index) => - unreadTypeIndex.value = index ?? unreadTypeIndex.value, - actions: options.keys.toList(), - ), + ) + : SuperListView.builder( + listController: listController.value, + controller: scrollController, + itemCount: value.length, + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 12, + ), + reverse: true, + itemBuilder: (context, index) { + final (rowId, roomId) = value[index]; + final isHighlighted = rowId == highlightedId.value; + + return Padding( + padding: .only(top: 8), + child: HighlightWrapper( + InkWell( + onTap: () async { + Navigator.of(context).pop(); + await ref.focusRoom(roomId, rowId); + }, + child: IgnorePointer( + child: EventRenderer(rowId, roomId: roomId), + ), + ), + isHighlighted: isHighlighted, + ), + ); + }, + ), + AsyncLoading() => const Loading(), + AsyncError(:final error, :final stackTrace) => ErrorDialog( + error, + stackTrace, + ), + }, + ), + ], + ), + Align( + alignment: .topRight, + child: Padding( + padding: .all(16), + child: M3EToggleButtonGroup( + selectedIndex: unreadTypeIndex.value, + onSelectedIndexChanged: (index) => + unreadTypeIndex.value = index ?? unreadTypeIndex.value, + actions: options.keys.toList(), ), ), - ], - ), + ), + ], ); } } diff --git a/lib/widgets/room_list.dart b/lib/widgets/room_list.dart new file mode 100644 index 0000000..067458c --- /dev/null +++ b/lib/widgets/room_list.dart @@ -0,0 +1,325 @@ +import "package:hooks_riverpod/hooks_riverpod.dart"; +import "package:material_ui/material_ui.dart"; +import "package:navigation_rail_m3e/navigation_rail_m3e.dart"; +import "package:nexus/models/nav_page.dart"; +import "package:nexus/widgets/avatar_or_hash.dart"; +import "package:nexus/widgets/divider_widget.dart"; +import "package:nexus/widgets/pages/settings.dart"; +import "package:nexus/widgets/join_dialog.dart"; +import "package:nexus/widgets/room_menu.dart"; +import "package:nexus/controllers/key.dart"; +import "package:nexus/controllers/spaces.dart"; +import "package:nexus/models/room.dart"; +import "package:collection/collection.dart"; +import "package:fast_immutable_collections/fast_immutable_collections.dart"; + +// Needed for navigation_rail_m3e (#65). +import "package:flutter/material.dart" as old_mat; + +class const RoomList({super.key}) + extends HookConsumerWidget + implements NavPage { + @override + String get title => "Rooms"; + + @override + IconData get icon => Icons.list; + + // TODO: Show unread badge here and on sidebar hamburger + @override + Future? badgeBuilder(WidgetRef ref) => null; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final selectedSpaceProvider = KeyController.provider( + KeyController.spaceKey, + ); + final selectedSpaceId = ref.watch(selectedSpaceProvider).requireValue; + final selectedSpaceIdNotifier = ref.watch(selectedSpaceProvider.notifier); + + final selectedRoomController = KeyController.provider( + KeyController.roomKey, + ); + final selectedRoomId = ref.watch(selectedRoomController).requireValue; + final selectedRoomIdNotifier = ref.watch(selectedRoomController.notifier); + + final spaces = ref.watch(SpacesController.provider); + final indexOfSelected = spaces.indexWhere( + (space) => space.id == selectedSpaceId, + ); + final selectedIndex = indexOfSelected == -1 ? 0 : indexOfSelected; + + final selectedSpace = + spaces.firstWhereOrNull((space) => space.id == selectedSpaceId) ?? + spaces.first; + + final indexOfSelectedRoom = selectedSpace.children + .addAll( + selectedSpace.subSpaces.map((element) => element.children).flattened, + ) + .indexWhere((room) => room.metadata?.id == selectedRoomId); + final selectedRoomIndex = indexOfSelectedRoom == -1 + ? null + : indexOfSelectedRoom; + + List roomsToDestinations(IList rooms) => + rooms + .map( + (room) => NavigationRailM3EDestination( + label: room.metadata?.name ?? "Unnamed Room", + badgeCount: switch (room.metadata?.unreadNotifications) { + 0 || null => room.metadata?.unreadMessages == 0 ? null : 0, + int unread => unread, + }, + icon: AvatarOrHash( + room.metadata?.avatar, + room.metadata?.name ?? "Unnamed Room", + fallback: selectedSpaceId == "dms" + ? null + : Icon(Icons.numbers), + ), + ), + ) + .toList(); + + return Row( + children: [ + MaterialUiCompatibilityBridge( + child: Builder( + builder: (context) => old_mat.Theme( + data: old_mat.Theme.of(context).copyWith( + extensions: [ + NavigationRailM3ETheme( + itemCollapsedHeight: 48, + itemVerticalGap: 0, + ), + ], + ), + child: Container( + color: NavigationRailTokensAdapter(context).containerColor, + padding: EdgeInsets.only(top: 16), + child: NavigationRailM3E( + type: .alwaysCollapse, + labelBehavior: .alwaysHide, + scrollable: true, + onDestinationSelected: (value) { + selectedSpaceIdNotifier.set(spaces[value].id); + selectedRoomIdNotifier.set( + spaces[value].children.firstOrNull?.metadata?.id, + ); + }, + sections: [ + .new( + destinations: spaces + .map( + (space) => NavigationRailM3EDestination( + badgeCount: switch (space.children + .addAll( + space.subSpaces + .map((element) => element.children) + .flattened, + ) + .fold( + 0, + (previousValue, room) => + previousValue + + (room.metadata?.unreadNotifications ?? + 0), + )) { + 0 => + space.children + .addAll( + space.subSpaces + .map( + (element) => element.children, + ) + .flattened, + ) + .any( + (room) => + room.metadata?.unreadMessages != + 0, + ) + ? 0 + : null, + int badgeCount => badgeCount, + }, + short: true, + icon: AvatarOrHash( + height: 28, + space.room?.metadata?.avatar, + fallback: space.icon == null + ? null + : Icon(space.icon), + space.title, + ), + label: space.title, + ), + ) + .toList(), + ), + ], + selectedIndex: selectedIndex, + trailingAtBottom: true, + trailing: Padding( + padding: .symmetric(vertical: 16), + child: Column( + children: [ + PopupMenuButton( + itemBuilder: (_) => [ + PopupMenuItem( + onTap: () => showDialog( + context: context, + builder: (_) => JoinDialog(ref), + ), + child: ListTile( + title: Text("Join an existing room (or space)"), + leading: Icon(Icons.numbers), + ), + ), + PopupMenuItem( + onTap: null, + child: ListTile( + title: Text("Create a new room"), + leading: Icon(Icons.add), + ), + ), + ], + icon: Icon(Icons.add), + ), + IconButton( + tooltip: "Explore other rooms", + onPressed: null, + icon: Icon(Icons.explore), + ), + // IconButton( + // tooltip: "Open notifications", + // onPressed: () { + // if (Navigator.of(context).canPop()) + // Navigator.of(context).pop(); + // Navigator.of(context).push( + // MaterialPageRoute( + // builder: (_) => NotificationsPage(), + // ), + // ); + // }, + // icon: Icon(Icons.notifications), + // ), + IconButton( + tooltip: "Open settings", + onPressed: () => showDialog( + context: context, + builder: (_) => SettingsPage(), + ), + icon: Icon(Icons.settings), + ), + ], + ), + ), + ), + ), + ), + ), + ), + + Expanded( + child: Scaffold( + backgroundColor: Colors.transparent, + appBar: AppBar( + leading: AvatarOrHash( + selectedSpace.room?.metadata?.avatar, + fallback: selectedSpace.icon == null + ? null + : Icon(selectedSpace.icon), + + selectedSpace.title, + ), + title: Text(selectedSpace.title, overflow: .ellipsis), + backgroundColor: Colors.transparent, + actions: [ + RoomMenu( + selectedSpace.room, + children: selectedSpace.children.addAll( + selectedSpace.subSpaces + .map((element) => element.children) + .flattened, + ), + ), + ], + ), + body: MaterialUiCompatibilityBridge( + child: Builder( + builder: (context) => old_mat.Theme( + data: old_mat.Theme.of(context).copyWith( + extensions: [ + NavigationRailM3ETheme( + itemExpandedHeight: 48, + iconLabelGap: 16, + ), + ], + ), + child: NavigationRailM3E( + expandedWidth: double.infinity, + scrollable: true, + background: Colors.transparent, + type: .alwaysExpand, + selectedIndex: selectedRoomIndex ?? 0, + sections: [ + .new( + header: selectedSpace.room == null + ? null + : DividerWidget(Text("Rooms")), + destinations: roomsToDestinations( + selectedSpace.children, + ), + ), + for (final subSpace in selectedSpace.subSpaces) + .new( + header: DividerWidget( + Row( + mainAxisSize: MainAxisSize.min, + spacing: 8, + children: [ + if (subSpace.room.metadata?.avatar != null) + AvatarOrHash( + subSpace.room.metadata?.avatar, + subSpace.room.metadata?.name ?? + "Unnamed Room", + height: 16, + ), + Flexible( + child: Text( + subSpace.room.metadata?.name ?? + "Unnamed Space", + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ), + destinations: roomsToDestinations(subSpace.children), + ), + ], + onDestinationSelected: (value) { + final children = selectedSpace.children.addAll( + selectedSpace.subSpaces + .map((element) => element.children) + .flattened, + ); + selectedRoomIdNotifier.set( + children[value].metadata?.id, // + ); + if (Navigator.of(context).canPop()) + Navigator.of(context).pop(); + }, + ), + ), + ), + ), + ), + ), + ], + ); + } +} diff --git a/lib/widgets/sidebar.dart b/lib/widgets/sidebar.dart index bc0c421..2d1c728 100644 --- a/lib/widgets/sidebar.dart +++ b/lib/widgets/sidebar.dart @@ -1,323 +1,34 @@ -import "package:collection/collection.dart"; -import "package:fast_immutable_collections/fast_immutable_collections.dart"; +import "package:flutter_hooks/flutter_hooks.dart"; import "package:material_ui/material_ui.dart"; -import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:navigation_rail_m3e/navigation_rail_m3e.dart"; -import "package:nexus/controllers/key.dart"; -import "package:nexus/controllers/spaces.dart"; -import "package:nexus/models/room.dart"; -import "package:nexus/widgets/pages/notifications.dart"; -import "package:nexus/widgets/pages/settings.dart"; -import "package:nexus/widgets/avatar_or_hash.dart"; -import "package:nexus/widgets/divider_widget.dart"; -import "package:nexus/widgets/join_dialog.dart"; -import "package:nexus/widgets/room_menu.dart"; +import "package:nexus/models/nav_page.dart"; +import "package:nexus/widgets/invites_list.dart"; +import "package:nexus/widgets/notifications_list.dart"; +import "package:nexus/widgets/room_list.dart"; -// Needed for navigation_rail_m3e (#65). -import "package:flutter/material.dart" as old_mat; - -class const Sidebar({super.key}) extends HookConsumerWidget { +class const Sidebar({super.key}) extends HookWidget { @override - Widget build(BuildContext context, WidgetRef ref) { - final selectedSpaceProvider = KeyController.provider( - KeyController.spaceKey, - ); - final selectedSpaceId = ref.watch(selectedSpaceProvider).requireValue; - final selectedSpaceIdNotifier = ref.watch(selectedSpaceProvider.notifier); - - final selectedRoomController = KeyController.provider( - KeyController.roomKey, - ); - final selectedRoomId = ref.watch(selectedRoomController).requireValue; - final selectedRoomIdNotifier = ref.watch(selectedRoomController.notifier); - - final spaces = ref.watch(SpacesController.provider); - final indexOfSelected = spaces.indexWhere( - (space) => space.id == selectedSpaceId, - ); - final selectedIndex = indexOfSelected == -1 ? 0 : indexOfSelected; - - final selectedSpace = - spaces.firstWhereOrNull((space) => space.id == selectedSpaceId) ?? - spaces.first; - - final indexOfSelectedRoom = selectedSpace.children - .addAll( - selectedSpace.subSpaces.map((element) => element.children).flattened, - ) - .indexWhere((room) => room.metadata?.id == selectedRoomId); - final selectedRoomIndex = indexOfSelectedRoom == -1 - ? null - : indexOfSelectedRoom; - - List roomsToDestinations(IList rooms) => - rooms - .map( - (room) => NavigationRailM3EDestination( - label: room.metadata?.name ?? "Unnamed Room", - badgeCount: switch (room.metadata?.unreadNotifications) { - 0 || null => room.metadata?.unreadMessages == 0 ? null : 0, - int unread => unread, - }, - icon: AvatarOrHash( - room.metadata?.avatar, - room.metadata?.name ?? "Unnamed Room", - fallback: selectedSpaceId == "dms" - ? null - : Icon(Icons.numbers), - ), - ), - ) - .toList(); + Widget build(BuildContext context) { + final selectedIndex = useState(0); + const pages = [RoomList(), NotificationsList(), InvitesList()]; return Drawer( width: 330, shape: Border(), - child: Row( - children: [ - MaterialUiCompatibilityBridge( - child: Builder( - builder: (context) => old_mat.Theme( - data: old_mat.Theme.of(context).copyWith( - extensions: [ - NavigationRailM3ETheme( - itemCollapsedHeight: 48, - itemVerticalGap: 0, - ), - ], + child: Scaffold( + backgroundColor: Colors.transparent, + body: pages[selectedIndex.value], + bottomNavigationBar: NavigationBar( + selectedIndex: selectedIndex.value, + destinations: pages + .map( + (element) => NavigationDestination( + icon: Icon(element.icon), + label: element.title, ), - child: Container( - color: NavigationRailTokensAdapter(context).containerColor, - padding: EdgeInsets.only(top: 16), - child: NavigationRailM3E( - type: .alwaysCollapse, - labelBehavior: .alwaysHide, - scrollable: true, - onDestinationSelected: (value) { - selectedSpaceIdNotifier.set(spaces[value].id); - selectedRoomIdNotifier.set( - spaces[value].children.firstOrNull?.metadata?.id, - ); - }, - sections: [ - .new( - destinations: spaces - .map( - (space) => NavigationRailM3EDestination( - badgeCount: switch (space.children - .addAll( - space.subSpaces - .map((element) => element.children) - .flattened, - ) - .fold( - 0, - (previousValue, room) => - previousValue + - (room.metadata?.unreadNotifications ?? - 0), - )) { - 0 => - space.children - .addAll( - space.subSpaces - .map( - (element) => - element.children, - ) - .flattened, - ) - .any( - (room) => - room - .metadata - ?.unreadMessages != - 0, - ) - ? 0 - : null, - int badgeCount => badgeCount, - }, - short: true, - icon: AvatarOrHash( - height: 28, - space.room?.metadata?.avatar, - fallback: space.icon == null - ? null - : Icon(space.icon), - space.title, - ), - label: space.title, - ), - ) - .toList(), - ), - ], - selectedIndex: selectedIndex, - trailingAtBottom: true, - trailing: Padding( - padding: .symmetric(vertical: 16), - child: Column( - children: [ - PopupMenuButton( - itemBuilder: (_) => [ - PopupMenuItem( - onTap: () => showDialog( - context: context, - builder: (_) => JoinDialog(ref), - ), - child: ListTile( - title: Text( - "Join an existing room (or space)", - ), - leading: Icon(Icons.numbers), - ), - ), - PopupMenuItem( - onTap: null, - child: ListTile( - title: Text("Create a new room"), - leading: Icon(Icons.add), - ), - ), - ], - icon: Icon(Icons.add), - ), - IconButton( - tooltip: "Explore other rooms", - onPressed: null, - icon: Icon(Icons.explore), - ), - IconButton( - tooltip: "Open notifications", - onPressed: () { - if (Navigator.of(context).canPop()) - Navigator.of(context).pop(); - Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => NotificationsPage(), - ), - ); - }, - icon: Icon(Icons.notifications), - ), - IconButton( - tooltip: "Open settings", - onPressed: () => showDialog( - context: context, - builder: (_) => SettingsPage(), - ), - icon: Icon(Icons.settings), - ), - ], - ), - ), - ), - ), - ), - ), - ), - - Expanded( - child: Scaffold( - backgroundColor: Colors.transparent, - appBar: AppBar( - leading: AvatarOrHash( - selectedSpace.room?.metadata?.avatar, - fallback: selectedSpace.icon == null - ? null - : Icon(selectedSpace.icon), - - selectedSpace.title, - ), - title: Text(selectedSpace.title, overflow: .ellipsis), - backgroundColor: Colors.transparent, - actions: [ - RoomMenu( - selectedSpace.room, - children: selectedSpace.children.addAll( - selectedSpace.subSpaces - .map((element) => element.children) - .flattened, - ), - ), - ], - ), - body: MaterialUiCompatibilityBridge( - child: Builder( - builder: (context) => old_mat.Theme( - data: old_mat.Theme.of(context).copyWith( - extensions: [ - NavigationRailM3ETheme( - itemExpandedHeight: 48, - iconLabelGap: 16, - ), - ], - ), - child: NavigationRailM3E( - expandedWidth: double.infinity, - scrollable: true, - background: Colors.transparent, - type: .alwaysExpand, - selectedIndex: selectedRoomIndex ?? 0, - sections: [ - .new( - header: selectedSpace.room == null - ? null - : DividerWidget(Text("Rooms")), - destinations: roomsToDestinations( - selectedSpace.children, - ), - ), - for (final subSpace in selectedSpace.subSpaces) - .new( - header: DividerWidget( - Row( - mainAxisSize: MainAxisSize.min, - spacing: 8, - children: [ - if (subSpace.room.metadata?.avatar != null) - AvatarOrHash( - subSpace.room.metadata?.avatar, - subSpace.room.metadata?.name ?? - "Unnamed Room", - height: 16, - ), - Flexible( - child: Text( - subSpace.room.metadata?.name ?? - "Unnamed Space", - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - ), - destinations: roomsToDestinations( - subSpace.children, - ), - ), - ], - onDestinationSelected: (value) { - final children = selectedSpace.children.addAll( - selectedSpace.subSpaces - .map((element) => element.children) - .flattened, - ); - selectedRoomIdNotifier.set( - children[value].metadata?.id, // - ); - if (Navigator.of(context).canPop()) - Navigator.of(context).pop(); - }, - ), - ), - ), - ), - ), - ), - ], + ) + .toList(), + onDestinationSelected: (value) => selectedIndex.value = value, + ), ), ); }