conditionally show room avatar in header

This commit is contained in:
Henry Hiles 2025-11-14 17:27:43 -05:00
commit f0a397f576
No known key found for this signature in database
7 changed files with 110 additions and 95 deletions

View file

@ -1,3 +1,4 @@
import "package:flutter/material.dart";
import "package:flutter/widgets.dart"; import "package:flutter/widgets.dart";
import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
@ -118,3 +119,9 @@ extension ToMessage on Event {
}; };
} }
} }
extension ToTheme on ColorScheme {
ThemeData get theme => ThemeData.from(
colorScheme: this,
).copyWith(appBarTheme: AppBarTheme(titleSpacing: 0));
}

View file

@ -1,4 +1,5 @@
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/helpers/extension_helper.dart";
import "package:nexus/widgets/room_chat.dart"; import "package:nexus/widgets/room_chat.dart";
import "package:nexus/widgets/sidebar.dart"; import "package:nexus/widgets/sidebar.dart";
import "package:scaled_app/scaled_app.dart"; import "package:scaled_app/scaled_app.dart";
@ -31,18 +32,16 @@ class App extends StatelessWidget {
return MaterialApp( return MaterialApp(
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
theme: ThemeData.from( theme:
colorScheme: (lightDynamic ?? ColorScheme.fromSeed(seedColor: Colors.indigo))
lightDynamic ?? ColorScheme.fromSeed(seedColor: Colors.indigo), .theme,
), darkTheme:
darkTheme: ThemeData.from( (darkDynamic ??
colorScheme: ColorScheme.fromSeed(
darkDynamic ?? seedColor: Colors.indigo,
ColorScheme.fromSeed( brightness: Brightness.dark,
seedColor: Colors.indigo, ))
brightness: Brightness.dark, .theme,
),
),
home: Scaffold( home: Scaffold(
body: Builder( body: Builder(
builder: (context) => Row( builder: (context) => Row(

View file

@ -26,6 +26,7 @@ class AvatarOrHash extends StatelessWidget {
borderRadius: BorderRadius.all(Radius.circular(4)), borderRadius: BorderRadius.all(Radius.circular(4)),
child: SizedBox( child: SizedBox(
height: height, height: height,
width: height,
child: avatar == null child: avatar == null
? fallback ?? box ? fallback ?? box
: Image.network( : Image.network(

View file

@ -11,43 +11,56 @@ class MemberList extends ConsumerWidget {
const MemberList(this.room, {super.key}); const MemberList(this.room, {super.key});
@override @override
Widget build(BuildContext context, WidgetRef ref) => ColoredBox( Widget build(BuildContext context, WidgetRef ref) => Drawer(
color: Theme.of(context).colorScheme.surfaceContainerLow, shape: Border(),
child: SizedBox( child: ref
width: 240, .watch(MembersController.provider(room))
child: ref .betterWhen(
.watch(MembersController.provider(room)) data: (members) => ListView(
.betterWhen( children: [
data: (members) => ListView( AppBar(
children: [ scrolledUnderElevation: 0,
...members backgroundColor: Theme.of(
.where( context,
(membership) => ).colorScheme.surfaceContainerLow,
membership.content["membership"] == leading: Icon(Icons.people),
Membership.join.name, title: Text("Members"),
) actionsPadding: EdgeInsets.only(right: 4),
.map( actions: [
(member) => ListTile( if (Scaffold.of(context).hasEndDrawer)
leading: AvatarOrHash( IconButton(
ref onPressed: Scaffold.of(context).closeEndDrawer,
.watch( icon: Icon(Icons.close),
AvatarController.provider( ),
member.content["avatar_url"].toString(), ],
), ),
) ...members
.whenOrNull(data: (data) => data), .where(
member.content["displayname"].toString(), (membership) =>
headers: room.client.headers, membership.content["membership"] ==
), Membership.join.name,
title: Text( )
member.content["displayname"].toString(), .map(
overflow: TextOverflow.ellipsis, (member) => ListTile(
), leading: AvatarOrHash(
ref
.watch(
AvatarController.provider(
member.content["avatar_url"].toString(),
),
)
.whenOrNull(data: (data) => data),
member.content["displayname"].toString(),
headers: room.client.headers,
),
title: Text(
member.content["displayname"].toString(),
overflow: TextOverflow.ellipsis,
), ),
), ),
], ),
), ],
), ),
), ),
); );
} }

View file

@ -8,8 +8,8 @@ import "package:nexus/widgets/avatar_or_hash.dart";
class RoomAppbar extends StatelessWidget implements PreferredSizeWidget { class RoomAppbar extends StatelessWidget implements PreferredSizeWidget {
final bool isDesktop; final bool isDesktop;
final FullRoom room; final FullRoom room;
final VoidCallback onOpenMemberList; final void Function(BuildContext context) onOpenMemberList;
final VoidCallback onOpenDrawer; final void Function(BuildContext context) onOpenDrawer;
const RoomAppbar( const RoomAppbar(
this.room, { this.room, {
required this.isDesktop, required this.isDesktop,
@ -23,39 +23,37 @@ class RoomAppbar extends StatelessWidget implements PreferredSizeWidget {
@override @override
AppBar build(BuildContext context) => AppBar( AppBar build(BuildContext context) => AppBar(
leading: isDesktop ? null : DrawerButton(onPressed: onOpenDrawer), leading: isDesktop
? AvatarOrHash(
room.avatar,
room.title,
height: 32,
fallback: Icon(Icons.numbers),
headers: room.roomData.client.headers,
)
: DrawerButton(onPressed: () => onOpenDrawer(context)),
scrolledUnderElevation: 0,
backgroundColor: Theme.of(context).colorScheme.surfaceContainerLow,
actionsPadding: EdgeInsets.symmetric(horizontal: 8), actionsPadding: EdgeInsets.symmetric(horizontal: 8),
title: Row( title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
AvatarOrHash( Text(room.title, overflow: TextOverflow.ellipsis),
room.avatar, Text(
room.title, room.roomData.topic,
height: 32, maxLines: 1,
fallback: Icon(Icons.numbers), overflow: TextOverflow.ellipsis,
headers: room.roomData.client.headers, style: Theme.of(context).textTheme.labelMedium?.copyWith(
), color: Theme.of(context).colorScheme.onSurfaceVariant,
SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(room.title, overflow: TextOverflow.ellipsis),
Text(
room.roomData.topic,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.labelMedium?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
],
), ),
), ),
], ],
), ),
actions: [ actions: [
IconButton(onPressed: onOpenMemberList, icon: Icon(Icons.people)), IconButton(
onPressed: () => onOpenMemberList(context),
icon: Icon(Icons.people),
),
if (!(Platform.isAndroid || Platform.isIOS)) if (!(Platform.isAndroid || Platform.isIOS))
IconButton(onPressed: () => exit(0), icon: Icon(Icons.close)), IconButton(onPressed: () => exit(0), icon: Icon(Icons.close)),
], ],

View file

@ -58,9 +58,11 @@ class RoomChat extends HookConsumerWidget {
appBar: RoomAppbar( appBar: RoomAppbar(
room, room,
isDesktop: isDesktop, isDesktop: isDesktop,
onOpenDrawer: Scaffold.of(context).openDrawer, onOpenDrawer: (_) => Scaffold.of(context).openDrawer(),
onOpenMemberList: () => onOpenMemberList: (thisContext) {
memberListOpened.value = !memberListOpened.value, memberListOpened.value = !memberListOpened.value;
Scaffold.of(thisContext).openEndDrawer();
},
), ),
body: Row( body: Row(
children: [ children: [
@ -245,9 +247,11 @@ class RoomChat extends HookConsumerWidget {
), ),
), ),
if (memberListOpened.value == true) MemberList(room.roomData), if (memberListOpened.value == true && isDesktop)
MemberList(room.roomData),
], ],
), ),
endDrawer: isDesktop ? null : MemberList(room.roomData),
); );
}, },
); );

View file

@ -61,22 +61,15 @@ class Sidebar extends HookConsumerWidget {
return Scaffold( return Scaffold(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
appBar: AppBar( appBar: AppBar(
title: Row( leading: AvatarOrHash(
children: [ space.avatar,
AvatarOrHash( fallback: space.icon,
space.avatar, space.title,
fallback: space.icon, headers: space.client.headers,
space.title, ),
headers: space.client.headers, title: Text(
), space.title,
SizedBox(width: 12), overflow: TextOverflow.ellipsis,
Expanded(
child: Text(
space.title,
overflow: TextOverflow.ellipsis,
),
),
],
), ),
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
), ),