mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-11-05 19:49:29 +01:00
style: Use SliverList for chatlist
This commit is contained in:
parent
dd4f2fcc35
commit
20c1dbd00a
@ -16,7 +16,7 @@ abstract class AppConfig {
|
||||
static const double messageFontSize = 15.75;
|
||||
static const bool allowOtherHomeservers = true;
|
||||
static const bool enableRegistration = true;
|
||||
static const Color primaryColor = Color.fromARGB(255, 135, 103, 172);
|
||||
static const Color primaryColor = Color(0xFF5625BA);
|
||||
static const Color primaryColorLight = Color(0xFFCCBDEA);
|
||||
static const Color secondaryColor = Color(0xFF41a2bc);
|
||||
static String _privacyUrl =
|
||||
|
@ -8,6 +8,7 @@ import 'package:fluffychat/pages/chat_list/chat_list.dart';
|
||||
import 'package:fluffychat/pages/chat_list/chat_list_item.dart';
|
||||
import 'package:fluffychat/pages/chat_list/search_title.dart';
|
||||
import 'package:fluffychat/pages/chat_list/space_view.dart';
|
||||
import 'package:fluffychat/pages/chat_list/start_chat_fab.dart';
|
||||
import 'package:fluffychat/pages/chat_list/stories_header.dart';
|
||||
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
|
||||
import 'package:fluffychat/utils/matrix_sdk_extensions/client_stories_extension.dart';
|
||||
@ -19,6 +20,7 @@ import 'package:fluffychat/widgets/public_room_bottom_sheet.dart';
|
||||
import '../../config/themes.dart';
|
||||
import '../../widgets/connection_status_header.dart';
|
||||
import '../../widgets/matrix.dart';
|
||||
import 'chat_list_header.dart';
|
||||
|
||||
class ChatListViewBody extends StatelessWidget {
|
||||
final ChatListController controller;
|
||||
@ -70,15 +72,13 @@ class ChatListViewBody extends StatelessWidget {
|
||||
ActiveFilter.messages,
|
||||
}.contains(controller.activeFilter) &&
|
||||
client.storiesRooms.isNotEmpty;
|
||||
return ListView.builder(
|
||||
return CustomScrollView(
|
||||
controller: controller.scrollController,
|
||||
// add +1 space below in order to properly scroll below the spaces bar
|
||||
itemCount: rooms.length + 1,
|
||||
itemBuilder: (BuildContext context, int i) {
|
||||
if (i == 0) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
slivers: [
|
||||
ChatListHeader(controller: controller),
|
||||
SliverList(
|
||||
delegate: SliverChildListDelegate(
|
||||
[
|
||||
if (controller.isSearchMode) ...[
|
||||
SearchTitle(
|
||||
title: L10n.of(context)!.publicRooms,
|
||||
@ -187,7 +187,7 @@ class ChatListViewBody extends StatelessWidget {
|
||||
title: L10n.of(context)!.chats,
|
||||
icon: const Icon(Icons.forum_outlined),
|
||||
),
|
||||
if (rooms.isEmpty && !controller.isSearchMode)
|
||||
if (rooms.isEmpty && !controller.isSearchMode) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(32.0),
|
||||
child: Column(
|
||||
@ -201,12 +201,24 @@ class ChatListViewBody extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: StartChatFloatingActionButton(
|
||||
activeFilter: controller.activeFilter,
|
||||
roomsIsEmpty: true,
|
||||
scrolledToTop: controller.scrolledToTop,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
i--;
|
||||
],
|
||||
),
|
||||
),
|
||||
SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(BuildContext context, int i) {
|
||||
if (!rooms[i]
|
||||
.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!))
|
||||
.getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context)!),
|
||||
)
|
||||
.toLowerCase()
|
||||
.contains(
|
||||
controller.searchController.text.toLowerCase(),
|
||||
@ -216,14 +228,20 @@ class ChatListViewBody extends StatelessWidget {
|
||||
return ChatListItem(
|
||||
rooms[i],
|
||||
key: Key('chat_list_item_${rooms[i].id}'),
|
||||
selected: controller.selectedRoomIds.contains(rooms[i].id),
|
||||
selected:
|
||||
controller.selectedRoomIds.contains(rooms[i].id),
|
||||
onTap: controller.selectMode == SelectMode.select
|
||||
? () => controller.toggleSelection(rooms[i].id)
|
||||
: null,
|
||||
onLongPress: () => controller.toggleSelection(rooms[i].id),
|
||||
onLongPress: () =>
|
||||
controller.toggleSelection(rooms[i].id),
|
||||
activeChat: controller.activeChat == rooms[i].id,
|
||||
);
|
||||
},
|
||||
childCount: rooms.length,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
const dummyChatCount = 5;
|
||||
|
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pages/chat_list/chat_list.dart';
|
||||
import 'package:fluffychat/pages/chat_list/client_chooser_button.dart';
|
||||
import '../../widgets/matrix.dart';
|
||||
@ -16,7 +17,12 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final selectMode = controller.selectMode;
|
||||
|
||||
return AppBar(
|
||||
return SliverAppBar(
|
||||
floating: true,
|
||||
pinned: FluffyThemes.isColumnMode(context),
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
backgroundColor: Colors.transparent,
|
||||
automaticallyImplyLeading: false,
|
||||
leading: selectMode == SelectMode.normal
|
||||
? null
|
||||
@ -38,15 +44,17 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
)
|
||||
: SizedBox(
|
||||
height: 44,
|
||||
child: Material(
|
||||
elevation:
|
||||
Theme.of(context).appBarTheme.scrolledUnderElevation ??
|
||||
4,
|
||||
shadowColor: Theme.of(context).appBarTheme.shadowColor,
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
child: TextField(
|
||||
controller: controller.searchController,
|
||||
textInputAction: TextInputAction.search,
|
||||
onChanged: controller.onSearchEnter,
|
||||
decoration: InputDecoration(
|
||||
fillColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondaryContainer
|
||||
.withAlpha(128),
|
||||
border: UnderlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
borderRadius:
|
||||
@ -59,11 +67,13 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
tooltip: L10n.of(context)!.cancel,
|
||||
icon: const Icon(Icons.close_outlined),
|
||||
onPressed: controller.cancelSearch,
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
color:
|
||||
Theme.of(context).colorScheme.onBackground,
|
||||
)
|
||||
: Icon(
|
||||
Icons.search_outlined,
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
color:
|
||||
Theme.of(context).colorScheme.onBackground,
|
||||
),
|
||||
suffixIcon: controller.isSearchMode
|
||||
? controller.isSearching
|
||||
@ -100,6 +110,7 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: selectMode == SelectMode.share
|
||||
? [
|
||||
Padding(
|
||||
|
@ -15,7 +15,6 @@ import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/unread_rooms_badge.dart';
|
||||
import '../../widgets/matrix.dart';
|
||||
import 'chat_list_body.dart';
|
||||
import 'chat_list_header.dart';
|
||||
import 'start_chat_fab.dart';
|
||||
|
||||
class ChatListView extends StatelessWidget {
|
||||
@ -46,12 +45,12 @@ class ChatListView extends StatelessWidget {
|
||||
icon: UnreadRoomsBadge(
|
||||
badgePosition: badgePosition,
|
||||
filter: controller.getRoomFilterByActiveFilter(ActiveFilter.groups),
|
||||
child: const Icon(Icons.groups_outlined),
|
||||
child: const Icon(Icons.group_outlined),
|
||||
),
|
||||
selectedIcon: UnreadRoomsBadge(
|
||||
badgePosition: badgePosition,
|
||||
filter: controller.getRoomFilterByActiveFilter(ActiveFilter.groups),
|
||||
child: const Icon(Icons.groups),
|
||||
child: const Icon(Icons.group),
|
||||
),
|
||||
label: L10n.of(context)!.groups,
|
||||
),
|
||||
@ -174,7 +173,6 @@ class ChatListView extends StatelessWidget {
|
||||
excludeFromSemantics: true,
|
||||
behavior: HitTestBehavior.translucent,
|
||||
child: Scaffold(
|
||||
appBar: ChatListHeader(controller: controller),
|
||||
body: ChatListViewBody(controller),
|
||||
bottomNavigationBar: controller.displayNavigationBar
|
||||
? NavigationBar(
|
||||
@ -185,12 +183,7 @@ class ChatListView extends StatelessWidget {
|
||||
destinations: getNavigationDestinations(context),
|
||||
)
|
||||
: null,
|
||||
floatingActionButtonLocation:
|
||||
controller.filteredRooms.isEmpty
|
||||
? FloatingActionButtonLocation.centerFloat
|
||||
: null,
|
||||
floatingActionButton: selectMode == SelectMode.normal
|
||||
? KeyBoardShortcuts(
|
||||
floatingActionButton: KeyBoardShortcuts(
|
||||
keysToPress: {
|
||||
LogicalKeyboardKey.controlLeft,
|
||||
LogicalKeyboardKey.keyN
|
||||
@ -198,11 +191,16 @@ class ChatListView extends StatelessWidget {
|
||||
onKeysPressed: () =>
|
||||
VRouter.of(context).to('/newprivatechat'),
|
||||
helpLabel: L10n.of(context)!.newChat,
|
||||
child: StartChatFloatingActionButton(
|
||||
controller: controller,
|
||||
),
|
||||
child: selectMode == SelectMode.normal &&
|
||||
controller.filteredRooms.isNotEmpty &&
|
||||
!controller.isSearchMode
|
||||
? StartChatFloatingActionButton(
|
||||
activeFilter: controller.activeFilter,
|
||||
roomsIsEmpty: false,
|
||||
scrolledToTop: controller.scrolledToTop,
|
||||
)
|
||||
: null,
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -14,6 +14,7 @@ import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import '../../utils/localized_exception_extension.dart';
|
||||
import '../../widgets/matrix.dart';
|
||||
import 'chat_list_header.dart';
|
||||
|
||||
class SpaceView extends StatefulWidget {
|
||||
final ChatListController controller;
|
||||
@ -154,10 +155,13 @@ class _SpaceViewState extends State<SpaceView> {
|
||||
)
|
||||
.toList();
|
||||
|
||||
return ListView.builder(
|
||||
itemCount: rootSpaces.length,
|
||||
return CustomScrollView(
|
||||
controller: widget.scrollController,
|
||||
itemBuilder: (context, i) {
|
||||
slivers: [
|
||||
ChatListHeader(controller: widget.controller),
|
||||
SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, i) {
|
||||
final rootSpace = rootSpaces[i];
|
||||
final displayname = rootSpace.getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context)!),
|
||||
@ -179,11 +183,16 @@ class _SpaceViewState extends State<SpaceView> {
|
||||
.numChats(rootSpace.spaceChildren.length.toString()),
|
||||
),
|
||||
onTap: () => widget.controller.setActiveSpace(rootSpace.id),
|
||||
onLongPress: () => _onSpaceChildContextMenu(null, rootSpace),
|
||||
onLongPress: () =>
|
||||
_onSpaceChildContextMenu(null, rootSpace),
|
||||
trailing: const Icon(Icons.chevron_right_outlined),
|
||||
),
|
||||
);
|
||||
},
|
||||
childCount: rootSpaces.length,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
return FutureBuilder<GetSpaceHierarchyResponse>(
|
||||
@ -208,7 +217,16 @@ class _SpaceViewState extends State<SpaceView> {
|
||||
);
|
||||
}
|
||||
if (response == null) {
|
||||
return const Center(child: CircularProgressIndicator.adaptive());
|
||||
return CustomScrollView(
|
||||
slivers: [
|
||||
ChatListHeader(controller: widget.controller),
|
||||
const SliverFillRemaining(
|
||||
child: Center(
|
||||
child: CircularProgressIndicator.adaptive(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
final parentSpace = allSpaces.firstWhereOrNull(
|
||||
(space) =>
|
||||
@ -224,10 +242,13 @@ class _SpaceViewState extends State<SpaceView> {
|
||||
return;
|
||||
}
|
||||
},
|
||||
child: ListView.builder(
|
||||
itemCount: spaceChildren.length + 1 + (canLoadMore ? 1 : 0),
|
||||
child: CustomScrollView(
|
||||
controller: widget.scrollController,
|
||||
itemBuilder: (context, i) {
|
||||
slivers: [
|
||||
ChatListHeader(controller: widget.controller),
|
||||
SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, i) {
|
||||
if (i == 0) {
|
||||
return ListTile(
|
||||
leading: BackButton(
|
||||
@ -245,7 +266,8 @@ class _SpaceViewState extends State<SpaceView> {
|
||||
icon: snapshot.connectionState != ConnectionState.done
|
||||
? const CircularProgressIndicator.adaptive()
|
||||
: const Icon(Icons.refresh_outlined),
|
||||
onPressed: snapshot.connectionState != ConnectionState.done
|
||||
onPressed:
|
||||
snapshot.connectionState != ConnectionState.done
|
||||
? null
|
||||
: _refresh,
|
||||
),
|
||||
@ -267,17 +289,20 @@ class _SpaceViewState extends State<SpaceView> {
|
||||
if (room != null && !room.isSpace) {
|
||||
return ChatListItem(
|
||||
room,
|
||||
onLongPress: () => _onSpaceChildContextMenu(spaceChild, room),
|
||||
onLongPress: () =>
|
||||
_onSpaceChildContextMenu(spaceChild, room),
|
||||
activeChat: widget.controller.activeChat == room.id,
|
||||
);
|
||||
}
|
||||
final isSpace = spaceChild.roomType == 'm.space';
|
||||
final topic =
|
||||
spaceChild.topic?.isEmpty ?? true ? null : spaceChild.topic;
|
||||
final topic = spaceChild.topic?.isEmpty ?? true
|
||||
? null
|
||||
: spaceChild.topic;
|
||||
if (spaceChild.roomId == activeSpaceId) {
|
||||
return SearchTitle(
|
||||
title:
|
||||
spaceChild.name ?? spaceChild.canonicalAlias ?? 'Space',
|
||||
title: spaceChild.name ??
|
||||
spaceChild.canonicalAlias ??
|
||||
'Space',
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10.0),
|
||||
child: Avatar(
|
||||
@ -311,7 +336,8 @@ class _SpaceViewState extends State<SpaceView> {
|
||||
spaceChild.canonicalAlias ??
|
||||
L10n.of(context)!.chat,
|
||||
maxLines: 1,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
style:
|
||||
const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
if (!isSpace) ...[
|
||||
@ -328,7 +354,8 @@ class _SpaceViewState extends State<SpaceView> {
|
||||
],
|
||||
),
|
||||
onTap: () => _onJoinSpaceChild(spaceChild),
|
||||
onLongPress: () => _onSpaceChildContextMenu(spaceChild, room),
|
||||
onLongPress: () =>
|
||||
_onSpaceChildContextMenu(spaceChild, room),
|
||||
subtitle: Text(
|
||||
topic ??
|
||||
(isSpace
|
||||
@ -339,10 +366,15 @@ class _SpaceViewState extends State<SpaceView> {
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
),
|
||||
),
|
||||
trailing:
|
||||
isSpace ? const Icon(Icons.chevron_right_outlined) : null,
|
||||
trailing: isSpace
|
||||
? const Icon(Icons.chevron_right_outlined)
|
||||
: null,
|
||||
);
|
||||
},
|
||||
childCount: spaceChildren.length + 1 + (canLoadMore ? 1 : 0),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -7,13 +7,19 @@ import '../../config/themes.dart';
|
||||
import 'chat_list.dart';
|
||||
|
||||
class StartChatFloatingActionButton extends StatelessWidget {
|
||||
final ChatListController controller;
|
||||
final ActiveFilter activeFilter;
|
||||
final bool scrolledToTop;
|
||||
final bool roomsIsEmpty;
|
||||
|
||||
const StartChatFloatingActionButton({Key? key, required this.controller})
|
||||
: super(key: key);
|
||||
const StartChatFloatingActionButton({
|
||||
Key? key,
|
||||
required this.activeFilter,
|
||||
required this.scrolledToTop,
|
||||
required this.roomsIsEmpty,
|
||||
}) : super(key: key);
|
||||
|
||||
void _onPressed(BuildContext context) {
|
||||
switch (controller.activeFilter) {
|
||||
switch (activeFilter) {
|
||||
case ActiveFilter.allChats:
|
||||
case ActiveFilter.messages:
|
||||
VRouter.of(context).to('/newprivatechat');
|
||||
@ -28,10 +34,10 @@ class StartChatFloatingActionButton extends StatelessWidget {
|
||||
}
|
||||
|
||||
IconData get icon {
|
||||
switch (controller.activeFilter) {
|
||||
switch (activeFilter) {
|
||||
case ActiveFilter.allChats:
|
||||
case ActiveFilter.messages:
|
||||
return Icons.edit_outlined;
|
||||
return Icons.add_outlined;
|
||||
case ActiveFilter.groups:
|
||||
return Icons.group_add_outlined;
|
||||
case ActiveFilter.spaces:
|
||||
@ -40,10 +46,10 @@ class StartChatFloatingActionButton extends StatelessWidget {
|
||||
}
|
||||
|
||||
String getLabel(BuildContext context) {
|
||||
switch (controller.activeFilter) {
|
||||
switch (activeFilter) {
|
||||
case ActiveFilter.allChats:
|
||||
case ActiveFilter.messages:
|
||||
return controller.filteredRooms.isEmpty
|
||||
return roomsIsEmpty
|
||||
? L10n.of(context)!.startFirstChat
|
||||
: L10n.of(context)!.newChat;
|
||||
case ActiveFilter.groups:
|
||||
@ -58,15 +64,13 @@ class StartChatFloatingActionButton extends StatelessWidget {
|
||||
return AnimatedContainer(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
width: controller.filteredRooms.isEmpty
|
||||
width: roomsIsEmpty
|
||||
? null
|
||||
: controller.scrolledToTop
|
||||
: scrolledToTop
|
||||
? 144
|
||||
: 56,
|
||||
child: controller.scrolledToTop
|
||||
child: scrolledToTop
|
||||
? FloatingActionButton.extended(
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
foregroundColor: Theme.of(context).colorScheme.onPrimary,
|
||||
onPressed: () => _onPressed(context),
|
||||
icon: Icon(icon),
|
||||
label: Text(
|
||||
@ -75,8 +79,6 @@ class StartChatFloatingActionButton extends StatelessWidget {
|
||||
),
|
||||
)
|
||||
: FloatingActionButton(
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
foregroundColor: Theme.of(context).colorScheme.onPrimary,
|
||||
onPressed: () => _onPressed(context),
|
||||
child: Icon(icon),
|
||||
),
|
||||
|
@ -47,11 +47,11 @@ class SettingsStyleController extends State<SettingsStyle> {
|
||||
|
||||
static final List<Color?> customColors = [
|
||||
AppConfig.chatColor,
|
||||
Colors.blue.shade800,
|
||||
Colors.green.shade800,
|
||||
Colors.orange.shade700,
|
||||
Colors.pink.shade700,
|
||||
Colors.blueGrey.shade600,
|
||||
Colors.indigo,
|
||||
Colors.green,
|
||||
Colors.orange,
|
||||
Colors.pink,
|
||||
Colors.blueGrey,
|
||||
null,
|
||||
];
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user