feat: space navigation enhancements

- add ravigation rail on large screens
- refactor space hierarchy dummy code
- add material 3 like radius to spaces drawer
- rename column router to catch the application-specific code inside

Signed-off-by: TheOneWithTheBraid <the-one@with-the-braid.cf>
This commit is contained in:
TheOneWithTheBraid 2022-07-12 17:44:23 +02:00 committed by The one with the Braid
parent 3e80e3f67e
commit 77661f1ead
7 changed files with 169 additions and 30 deletions

View File

@ -113,7 +113,7 @@ class AppRoutes {
List<VRouteElement> get _tabletRoutes => [
VNester(
path: '/rooms',
widgetBuilder: (child) => TwoColumnLayout(
widgetBuilder: (child) => FluffyChatTwoColumnLayout(
mainView: const ChatList(),
sideView: child,
),
@ -198,7 +198,7 @@ class AppRoutes {
),
VWidget(
path: '/rooms',
widget: const TwoColumnLayout(
widget: const FluffyChatTwoColumnLayout(
mainView: ChatList(),
sideView: EmptyPage(),
),
@ -206,7 +206,7 @@ class AppRoutes {
stackedRoutes: [
VNester(
path: '/settings',
widgetBuilder: (child) => TwoColumnLayout(
widgetBuilder: (child) => FluffyChatTwoColumnLayout(
mainView: const Settings(),
sideView: child,
),
@ -222,7 +222,7 @@ class AppRoutes {
),
VWidget(
path: '/archive',
widget: const TwoColumnLayout(
widget: const FluffyChatTwoColumnLayout(
mainView: Archive(),
sideView: EmptyPage(),
),

View File

@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/pages/chat_list/chat_list.dart';
import 'chat_list_body.dart';
import 'chat_list_navigation_rail.dart';
class ChatListBodyContainer extends StatelessWidget {
final ChatListController controller;
const ChatListBodyContainer(this.controller, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
if (MediaQuery.of(context).size.width > 1024) {
return Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 72.0,
height: constraints.maxHeight,
child: ChatListNavigationRail(controller),
),
Container(
width: 1.0,
color: Theme.of(context).dividerColor,
),
Expanded(child: ChatListViewBody(controller)),
],
);
}
return ChatListViewBody(controller);
});
}
}

View File

@ -12,10 +12,14 @@ import '../../config/app_config.dart';
class ChatListDrawer extends StatelessWidget {
final ChatListController controller;
const ChatListDrawer(this.controller, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) => Drawer(
// TODO(TheOneWithTheBraid): remove once migrated to MD3
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.horizontal(right: Radius.circular(32))),
child: SafeArea(
child: Column(
children: [

View File

@ -0,0 +1,76 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/pages/chat_list/chat_list.dart';
import 'package:fluffychat/pages/chat_list/spaces_drawer.dart';
import 'package:fluffychat/widgets/avatar.dart';
class ChatListNavigationRail extends StatelessWidget {
final ChatListController controller;
const ChatListNavigationRail(this.controller, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final spaceHierarchy = SpacesDrawer.getSpaceHierarchy(controller);
final currentIndex = controller.spacesEntries.indexWhere((space) =>
controller.activeSpacesEntry.runtimeType == space.runtimeType &&
(controller.activeSpaceId == space.getSpace(context)?.id));
return SingleChildScrollView(
child: SizedBox(
// wasted 1.5 h for finding out the proper sizing mechanism...
height: spaceHierarchy.length * 44 + 64 + 8,
child: NavigationRail(
destinations: List.generate(spaceHierarchy.length + 1, (i) {
if (i == spaceHierarchy.length) {
return NavigationRailDestination(
icon: Tooltip(
message: L10n.of(context)!.archive,
child: const Icon(
Icons.archive_outlined,
),
),
label: Text(L10n.of(context)!.archive),
);
}
final space = spaceHierarchy.keys.toList()[i];
final room = space.getSpace(context);
final active = currentIndex == i;
return NavigationRailDestination(
icon: Tooltip(
message: space.getName(context),
child: room == null
? space.getIcon(active)
: Avatar(
size: Avatar.defaultSize / 2,
mxContent: room.avatar,
name: space.getName(context),
)),
label: Text(
space.getName(context),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
);
}),
selectedIndex: currentIndex,
onDestinationSelected: (index) {
if (index == spaceHierarchy.length) {
VRouter.of(context).to('/archive');
} else {
controller.setActiveSpacesEntry(
context,
spaceHierarchy.keys.toList()[index],
);
}
},
labelType: NavigationRailLabelType.selected,
),
),
);
}
}

View File

@ -7,9 +7,9 @@ import 'package:keyboard_shortcuts/keyboard_shortcuts.dart';
import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/pages/chat_list/chat_list.dart';
import 'package:fluffychat/pages/chat_list/chat_list_body_container.dart';
import 'package:fluffychat/pages/chat_list/chat_list_drawer.dart';
import '../../widgets/matrix.dart';
import 'chat_list_body.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'chat_list_header.dart';
class ChatListView extends StatelessWidget {
@ -31,7 +31,7 @@ class ChatListView extends StatelessWidget {
},
child: Scaffold(
appBar: ChatListHeader(controller: controller),
body: ChatListViewBody(controller),
body: ChatListBodyContainer(controller),
drawer: ChatListDrawer(controller),
floatingActionButton: selectMode == SelectMode.normal
? KeyBoardShortcuts(

View File

@ -12,16 +12,24 @@ class SpacesDrawer extends StatelessWidget {
const SpacesDrawer({Key? key, required this.controller}) : super(key: key);
/// PLACEHOLDER for later computation of space hierarchy mapping
///
/// TODO(TheOeWithTheBraid): implement space hierarchy
static Map<SpacesEntry, dynamic> getSpaceHierarchy(
ChatListController controller) {
return Map.fromEntries(
controller.spacesEntries.map((e) => MapEntry(e, null)));
}
@override
Widget build(BuildContext context) {
/// keep this implementation in sync with [ChatListBodyContainer]
final currentIndex = controller.spacesEntries.indexWhere((space) =>
controller.activeSpacesEntry.runtimeType == space.runtimeType &&
(controller.activeSpaceId == space.getSpace(context)?.id));
final Map<SpacesEntry, dynamic> spaceHierarchy =
Map.fromEntries(controller.spacesEntries.map((e) => MapEntry(e, null)));
// TODO(TheOeWithTheBraid): wait for space hierarchy https://gitlab.com/famedly/company/frontend/libraries/matrix_api_lite/-/merge_requests/58
SpacesDrawer.getSpaceHierarchy(controller);
return ListView.builder(
itemCount: spaceHierarchy.length + 1,

View File

@ -1,37 +1,51 @@
import 'package:flutter/material.dart';
class TwoColumnLayout extends StatelessWidget {
import 'package:vrouter/vrouter.dart';
/// displays a two-column layout with some FluffyChat specific patches
///
/// On huge screens width > 1024, the navigation rail for quick navigation is
/// rendered surround
class FluffyChatTwoColumnLayout extends StatelessWidget {
final Widget mainView;
final Widget sideView;
const TwoColumnLayout({
const FluffyChatTwoColumnLayout({
Key? key,
required this.mainView,
required this.sideView,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ScaffoldMessenger(
child: Scaffold(
body: Row(
children: [
Container(
clipBehavior: Clip.antiAlias,
decoration: const BoxDecoration(),
width: 360.0,
child: mainView,
),
Container(
width: 1.0,
color: Theme.of(context).dividerColor,
),
Expanded(
child: ClipRRect(
child: sideView,
body: LayoutBuilder(builder: (context, constraints) {
final columnWidth = context.vRouter.path.startsWith('/rooms') &&
constraints.maxWidth > 1024
? 360.0 + 64
: 360.0;
return Row(
children: [
Container(
clipBehavior: Clip.antiAlias,
decoration: const BoxDecoration(),
width: columnWidth,
child: mainView,
),
),
],
),
Container(
width: 1.0,
color: Theme.of(context).dividerColor,
),
Expanded(
child: ClipRRect(
child: sideView,
),
),
],
);
}),
),
);
}