⚗️ Testing desktop layout
This commit is contained in:
@ -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,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -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);
|
||||
},
|
||||
),
|
||||
|
@ -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(
|
||||
|
Reference in New Issue
Block a user