⚗️ Testing desktop layout

This commit is contained in:
2025-05-18 23:50:50 +08:00
parent ad41de3674
commit 911439f3af
7 changed files with 342 additions and 168 deletions

View File

@ -1,6 +1,7 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:island/route.gr.dart';
import 'package:island/services/responsive.dart';
import 'package:material_symbols_icons/symbols.dart';
@ -11,46 +12,107 @@ class TabsScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final useHorizontalLayout =
MediaQuery.of(context).size.width > kWideScreenWidth;
final useHorizontalLayout = isWideScreen(context);
final useExpandableLayout = isWidestScreen(context);
return AutoTabsRouter.pageView(
routes: const [
ExploreRoute(),
ChatListRoute(),
RealmListRoute(),
AccountRoute(),
],
final destinations = [
NavigationDestination(
label: 'explore'.tr(),
icon: const Icon(Symbols.explore),
),
NavigationDestination(label: 'chat'.tr(), icon: const Icon(Symbols.chat)),
NavigationDestination(
label: 'realms'.tr(),
icon: const Icon(Symbols.workspaces),
),
NavigationDestination(
label: 'account'.tr(),
icon: const Icon(Symbols.account_circle),
),
];
final routes = [
ExploreRoute(),
ChatListRoute(),
RealmListRoute(),
AccountRoute(),
];
return AutoTabsRouter.tabBar(
routes: routes,
scrollDirection: useHorizontalLayout ? Axis.vertical : Axis.horizontal,
physics: const NeverScrollableScrollPhysics(),
builder: (context, child, _) {
final tabsRouter = AutoTabsRouter.of(context);
// Check if current route is a tab route
final currentRoute = context.router.topRoute;
final isTabRoute = routes.any(
(route) => route.routeName == currentRoute.name,
);
return Scaffold(
extendBodyBehindAppBar: true,
backgroundColor: Colors.transparent,
body: child,
bottomNavigationBar: NavigationBar(
selectedIndex: tabsRouter.activeIndex,
onDestinationSelected: tabsRouter.setActiveIndex,
destinations: [
NavigationDestination(
label: 'explore'.tr(),
icon: const Icon(Symbols.explore),
),
NavigationDestination(
label: 'chat'.tr(),
icon: const Icon(Symbols.chat),
),
NavigationDestination(
label: 'realms'.tr(),
icon: const Icon(Symbols.workspaces),
),
NavigationDestination(
label: 'account'.tr(),
icon: const Icon(Symbols.account_circle),
),
],
),
body:
useHorizontalLayout
? Row(
children: [
if (isTabRoute)
Column(
children: [
Gap(MediaQuery.of(context).padding.top + 8),
if (useExpandableLayout)
Expanded(
child: NavigationDrawer(
backgroundColor: Colors.transparent,
children: [
for (final destination in destinations)
NavigationDrawerDestination(
label: Text(destination.label),
icon: destination.icon,
),
],
),
)
else
Expanded(
child: NavigationRail(
selectedIndex: tabsRouter.activeIndex,
onDestinationSelected:
tabsRouter.setActiveIndex,
labelType: NavigationRailLabelType.all,
destinations:
destinations
.map(
(d) => NavigationRailDestination(
icon: d.icon,
label: Text(d.label),
),
)
.toList(),
),
),
Gap(MediaQuery.of(context).padding.bottom + 8),
],
),
if (isTabRoute)
VerticalDivider(
color: Theme.of(context).dividerColor,
width: 1 / MediaQuery.of(context).devicePixelRatio,
),
Expanded(child: child),
],
)
: child,
bottomNavigationBar:
!useHorizontalLayout && isTabRoute
? NavigationBar(
selectedIndex: tabsRouter.activeIndex,
onDestinationSelected: tabsRouter.setActiveIndex,
destinations: destinations,
)
: null,
);
},
);

View File

@ -16,11 +16,13 @@ import 'package:island/pods/network.dart';
import 'package:island/route.gr.dart';
import 'package:island/screens/realm/realms.dart';
import 'package:island/services/file.dart';
import 'package:island/services/responsive.dart';
import 'package:island/widgets/account/account_picker.dart';
import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/content/cloud_files.dart';
import 'package:island/widgets/realms/selection_dropdown.dart';
import 'package:island/widgets/response.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:styled_widget/styled_widget.dart';
@ -69,11 +71,7 @@ class ChatRoomListTile extends StatelessWidget {
? Text(room.members!.map((e) => '@${e.account.name}').join(', '))
: Text(room.description ?? 'descriptionNone'.tr()),
trailing: trailing,
onTap:
onTap ??
() {
context.pushRoute(ChatRoomRoute(id: room.id));
},
onTap: onTap,
);
}
}
@ -88,9 +86,32 @@ Future<List<SnChatRoom>> chatroomsJoined(Ref ref) async {
.toList();
}
@RoutePage()
class ChatShellScreen extends HookConsumerWidget {
const ChatShellScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final isWide = isWideScreen(context);
if (isWide) {
return Row(
children: [
SizedBox(width: 320, child: ChatListScreen(isAside: true)),
VerticalDivider(width: 1),
Expanded(child: AutoRouter()),
],
);
}
return AutoRouter();
}
}
@RoutePage()
class ChatListScreen extends HookConsumerWidget {
const ChatListScreen({super.key});
final bool isAside;
const ChatListScreen({super.key, this.isAside = false});
@override
Widget build(BuildContext context, WidgetRef ref) {
@ -112,6 +133,12 @@ class ChatListScreen extends HookConsumerWidget {
}
}
final isWide = isWideScreen(context);
if (isWide && !isAside) {
return SizedBox.shrink();
}
return AppScaffold(
appBar: AppBar(
title: Text('chat').tr(),
@ -191,15 +218,25 @@ class ChatListScreen extends HookConsumerWidget {
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return ChatRoomListTile(room: item, isDirect: item.type == 1);
return ChatRoomListTile(
room: item,
isDirect: item.type == 1,
onTap: () {
if (isWide) {
context.router.replace(ChatRoomRoute(id: item.id));
} else {
context.router.push(ChatRoomRoute(id: item.id));
}
},
);
},
),
),
loading: () => const Center(child: CircularProgressIndicator()),
error:
(error, stack) => GestureDetector(
child: Center(child: Text('Error: $error')),
onTap: () {
(error, stack) => ResponseErrorWidget(
error: error,
onRetry: () {
ref.invalidate(chatroomsJoinedProvider);
},
),

View File

@ -17,7 +17,9 @@ import 'package:island/pods/network.dart';
import 'package:island/pods/websocket.dart';
import 'package:island/route.gr.dart';
import 'package:island/screens/posts/compose.dart';
import 'package:island/services/responsive.dart';
import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/chat/message_item.dart';
import 'package:island/widgets/content/cloud_files.dart';
import 'package:island/widgets/response.dart';
@ -415,46 +417,96 @@ class ChatRoomScreen extends HookConsumerWidget {
}
}
final compactHeader = isWideScreen(context);
return Scaffold(
appBar: AppBar(
toolbarHeight: 64,
leading: !compactHeader ? const Center(child: PageBackButton()) : null,
automaticallyImplyLeading: false,
toolbarHeight: compactHeader ? null : 64,
title: chatRoom.when(
data:
(room) => Column(
spacing: 4,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 26,
width: 26,
child:
(room!.type == 1 && room.pictureId == null)
? SplitAvatarWidget(
filesId:
room.members!
.map((e) => e.account.profile.pictureId)
.toList(),
)
: room.pictureId != null
? ProfilePictureWidget(
fileId: room.pictureId,
fallbackIcon: Symbols.chat,
)
: CircleAvatar(
child: Text(
room.name![0].toUpperCase(),
style: const TextStyle(fontSize: 12),
),
),
),
Text(
(room.type == 1 && room.name == null)
? room.members!.map((e) => e.account.nick).join(', ')
: room.name!,
).fontSize(15),
],
),
(room) =>
compactHeader
? Row(
spacing: 8,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 26,
width: 26,
child:
(room!.type == 1 && room.pictureId == null)
? SplitAvatarWidget(
filesId:
room.members!
.map(
(e) =>
e.account.profile.pictureId,
)
.toList(),
)
: room.pictureId != null
? ProfilePictureWidget(
fileId: room.pictureId,
fallbackIcon: Symbols.chat,
)
: CircleAvatar(
child: Text(
room.name![0].toUpperCase(),
style: const TextStyle(fontSize: 12),
),
),
),
Text(
(room.type == 1 && room.name == null)
? room.members!
.map((e) => e.account.nick)
.join(', ')
: room.name!,
).fontSize(19),
],
)
: Column(
spacing: 4,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 26,
width: 26,
child:
(room!.type == 1 && room.pictureId == null)
? SplitAvatarWidget(
filesId:
room.members!
.map(
(e) =>
e.account.profile.pictureId,
)
.toList(),
)
: room.pictureId != null
? ProfilePictureWidget(
fileId: room.pictureId,
fallbackIcon: Symbols.chat,
)
: CircleAvatar(
child: Text(
room.name![0].toUpperCase(),
style: const TextStyle(fontSize: 12),
),
),
),
Text(
(room.type == 1 && room.name == null)
? room.members!
.map((e) => e.account.nick)
.join(', ')
: room.name!,
).fontSize(15),
],
),
loading: () => const Text('Loading...'),
error:
(err, __) => ResponseErrorWidget(