Refactor SidebarCommunitiesList to be a StatelessWidget and update its usage across SpaceTreeView and SidebarWidget for improved performance and maintainability.

This commit is contained in:
Faris Armoush
2025-04-16 14:09:36 +03:00
parent afdd44e098
commit 8b441aaf46
3 changed files with 167 additions and 166 deletions

View File

@ -2,34 +2,19 @@ import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
class SidebarCommunitiesList extends StatefulWidget { class SidebarCommunitiesList extends StatelessWidget {
const SidebarCommunitiesList({ const SidebarCommunitiesList({
required this.communities, required this.communities,
required this.itemBuilder, required this.itemBuilder,
required this.scrollController,
required this.onScrollToEnd,
super.key, super.key,
}); });
final List<CommunityModel> communities; final List<CommunityModel> communities;
final Widget Function(BuildContext context, int index) itemBuilder; final Widget Function(BuildContext context, int index) itemBuilder;
final ScrollController scrollController;
@override final void Function() onScrollToEnd;
State<SidebarCommunitiesList> createState() => _SidebarCommunitiesListState();
}
class _SidebarCommunitiesListState extends State<SidebarCommunitiesList> {
late final ScrollController _scrollController;
@override
void initState() {
_scrollController = ScrollController();
super.initState();
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -40,13 +25,22 @@ class _SidebarCommunitiesListState extends State<SidebarCommunitiesList> {
child: Scrollbar( child: Scrollbar(
scrollbarOrientation: ScrollbarOrientation.left, scrollbarOrientation: ScrollbarOrientation.left,
thumbVisibility: true, thumbVisibility: true,
controller: _scrollController, controller: scrollController,
child: NotificationListener(
onNotification: (notification) {
if (notification is ScrollEndNotification &&
notification.metrics.extentAfter == 0) {
onScrollToEnd.call();
}
return false;
},
child: ListView.builder( child: ListView.builder(
padding: const EdgeInsetsDirectional.only(start: 16), padding: const EdgeInsetsDirectional.only(start: 16),
shrinkWrap: true, shrinkWrap: true,
itemCount: widget.communities.length, itemCount: communities.length,
controller: _scrollController, controller: scrollController,
itemBuilder: widget.itemBuilder, itemBuilder: itemBuilder,
),
), ),
), ),
), ),

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/common/widgets/search_bar.dart'; import 'package:syncrow_web/common/widgets/search_bar.dart';
import 'package:syncrow_web/common/widgets/sidebar_communities_list.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart'; import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart'; import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_state.dart'; import 'package:syncrow_web/pages/space_tree/bloc/space_tree_state.dart';
@ -34,23 +35,27 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<SpaceTreeBloc, SpaceTreeState>(builder: (context, state) { return BlocBuilder<SpaceTreeBloc, SpaceTreeState>(builder: (context, state) {
List<CommunityModel> list = final communities = state.searchQuery.isNotEmpty
state.searchQuery.isNotEmpty ? state.filteredCommunity : state.communityList; ? state.filteredCommunity
: state.communityList;
return Container( return Container(
height: MediaQuery.sizeOf(context).height, height: MediaQuery.sizeOf(context).height,
decoration: widget.isSide == true decoration: widget.isSide == true
? subSectionContainerDecoration.copyWith(color: ColorsManager.whiteColors) ? subSectionContainerDecoration.copyWith(
color: ColorsManager.whiteColors)
: const BoxDecoration(color: ColorsManager.whiteColors), : const BoxDecoration(color: ColorsManager.whiteColors),
child: state is SpaceTreeLoadingState child: state is SpaceTreeLoadingState
? const Center(child: CircularProgressIndicator()) ? const Center(child: CircularProgressIndicator())
: Column( : Column(
children: [ children: [
widget.isSide == true if (widget.isSide == true)
? Container( Container(
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: ColorsManager.circleRolesBackground, color: ColorsManager.circleRolesBackground,
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
topRight: Radius.circular(20), topLeft: Radius.circular(20)), topRight: Radius.circular(20),
topLeft: Radius.circular(20),
),
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
@ -59,15 +64,23 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
Expanded( Expanded(
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(20)), borderRadius: const BorderRadius.all(
border: Border.all(color: ColorsManager.grayBorder)), Radius.circular(20),
),
border: Border.all(
color: ColorsManager.grayBorder,
),
),
child: TextFormField( child: TextFormField(
style: context.textTheme.bodyMedium style: context.textTheme.bodyMedium?.copyWith(
?.copyWith(color: ColorsManager.blackColor), color: ColorsManager.blackColor,
onChanged: (value) { ),
context.read<SpaceTreeBloc>().add(SearchQueryEvent(value)); onChanged: (value) =>
}, context.read<SpaceTreeBloc>().add(
decoration: textBoxDecoration(radios: 20)!.copyWith( SearchQueryEvent(value),
),
decoration:
textBoxDecoration(radios: 20)?.copyWith(
fillColor: Colors.white, fillColor: Colors.white,
suffixIcon: Padding( suffixIcon: Padding(
padding: const EdgeInsets.only(right: 16), padding: const EdgeInsets.only(right: 16),
@ -77,10 +90,12 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
height: 24, height: 24,
), ),
), ),
hintStyle: context.textTheme.bodyMedium?.copyWith( hintStyle:
context.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
fontSize: 12, fontSize: 12,
color: ColorsManager.textGray), color: ColorsManager.textGray,
),
), ),
), ),
), ),
@ -89,105 +104,89 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
), ),
), ),
) )
: CustomSearchBar( else
onSearchChanged: (query) { CustomSearchBar(
context.read<SpaceTreeBloc>().add(SearchQueryEvent(query)); onSearchChanged: (query) => context.read<SpaceTreeBloc>().add(
}, SearchQueryEvent(query),
),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Expanded( Expanded(
child: state.isSearching child: state.isSearching
? const Center(child: CircularProgressIndicator()) ? const Center(child: CircularProgressIndicator())
: ListView( : SidebarCommunitiesList(
shrinkWrap: true, onScrollToEnd: () => context.read<SpaceTreeBloc>().add(
scrollDirection: Axis.horizontal, PaginationEvent(
children: [ state.paginationModel,
Container( state.communityList,
width: MediaQuery.sizeOf(context).width * 0.5,
padding: const EdgeInsets.all(8.0),
child: list.isEmpty
? Center(
child: Text(
'No results found',
style: Theme.of(context).textTheme.bodySmall!.copyWith(
color: ColorsManager.lightGrayColor,
fontWeight: FontWeight.w400,
), ),
), ),
) scrollController: _scrollController,
: Scrollbar( communities: communities,
scrollbarOrientation: ScrollbarOrientation.left,
thumbVisibility: true,
controller: _scrollController,
child: NotificationListener(
onNotification: (notification) {
if (notification is ScrollEndNotification &&
notification.metrics.extentAfter == 0) {
// If the user has reached the end of the list Load more data
context.read<SpaceTreeBloc>().add(PaginationEvent(
state.paginationModel, state.communityList));
}
return false;
},
child: Padding(
padding: const EdgeInsets.only(left: 16),
child: ListView.builder(
shrinkWrap: true,
itemCount: list.length,
controller: _scrollController,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return CustomExpansionTileSpaceTree( return CustomExpansionTileSpaceTree(
title: list[index].name, title: communities[index].name,
isSelected: state.selectedCommunities isSelected: state.selectedCommunities
.contains(list[index].uuid), .contains(communities[index].uuid),
isSoldCheck: state.selectedCommunities isSoldCheck: state.selectedCommunities
.contains(list[index].uuid), .contains(communities[index].uuid),
onExpansionChanged: () { onExpansionChanged: () =>
context.read<SpaceTreeBloc>().add( context.read<SpaceTreeBloc>().add(
OnCommunityExpanded(list[index].uuid)); OnCommunityExpanded(
}, communities[index].uuid,
isExpanded: state.expandedCommunities ),
.contains(list[index].uuid), ),
isExpanded: state.expandedCommunities.contains(
communities[index].uuid,
),
onItemSelected: () { onItemSelected: () {
context.read<SpaceTreeBloc>().add( context.read<SpaceTreeBloc>().add(
OnCommunitySelected(list[index].uuid, OnCommunitySelected(
list[index].spaces)); communities[index].uuid,
communities[index].spaces,
),
);
widget.onSelect(); widget.onSelect();
}, },
children: list[index].spaces.map((space) { children: communities[index].spaces.map(
(space) {
return CustomExpansionTileSpaceTree( return CustomExpansionTileSpaceTree(
title: space.name, title: space.name,
isExpanded: state.expandedSpaces isExpanded:
.contains(space.uuid), state.expandedSpaces.contains(space.uuid),
onItemSelected: () { onItemSelected: () {
context.read<SpaceTreeBloc>().add( context.read<SpaceTreeBloc>().add(
OnSpaceSelected( OnSpaceSelected(
list[index], communities[index],
space.uuid ?? '', space.uuid ?? '',
space.children)); space.children,
),
);
widget.onSelect(); widget.onSelect();
}, },
onExpansionChanged: () { onExpansionChanged: () =>
context.read<SpaceTreeBloc>().add( context.read<SpaceTreeBloc>().add(
OnSpaceExpanded(list[index].uuid, OnSpaceExpanded(
space.uuid ?? '')); communities[index].uuid,
}, space.uuid ?? '',
),
),
isSelected: state.selectedSpaces isSelected: state.selectedSpaces
.contains(space.uuid) || .contains(space.uuid) ||
state.soldCheck.contains(space.uuid), state.soldCheck.contains(space.uuid),
isSoldCheck: isSoldCheck:
state.soldCheck.contains(space.uuid), state.soldCheck.contains(space.uuid),
children: _buildNestedSpaces( children: _buildNestedSpaces(
context, state, space, list[index]), context,
state,
space,
communities[index],
),
); );
}).toList(), },
).toList(),
); );
}), },
),
),
),
),
],
), ),
), ),
if (state.paginationIsLoading) const CircularProgressIndicator(), if (state.paginationIsLoading) const CircularProgressIndicator(),
@ -198,22 +197,28 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
} }
List<Widget> _buildNestedSpaces( List<Widget> _buildNestedSpaces(
BuildContext context, SpaceTreeState state, SpaceModel space, CommunityModel community) { BuildContext context,
SpaceTreeState state,
SpaceModel space,
CommunityModel community,
) {
return space.children.map((child) { return space.children.map((child) {
return CustomExpansionTileSpaceTree( return CustomExpansionTileSpaceTree(
isSelected: isSelected: state.selectedSpaces.contains(child.uuid) ||
state.selectedSpaces.contains(child.uuid) || state.soldCheck.contains(child.uuid), state.soldCheck.contains(child.uuid),
isSoldCheck: state.soldCheck.contains(child.uuid), isSoldCheck: state.soldCheck.contains(child.uuid),
title: child.name, title: child.name,
isExpanded: state.expandedSpaces.contains(child.uuid), isExpanded: state.expandedSpaces.contains(child.uuid),
onItemSelected: () { onItemSelected: () {
context context.read<SpaceTreeBloc>().add(
.read<SpaceTreeBloc>() OnSpaceSelected(community, child.uuid ?? '', child.children),
.add(OnSpaceSelected(community, child.uuid ?? '', child.children)); );
widget.onSelect(); widget.onSelect();
}, },
onExpansionChanged: () { onExpansionChanged: () {
context.read<SpaceTreeBloc>().add(OnSpaceExpanded(community.uuid, child.uuid ?? '')); context.read<SpaceTreeBloc>().add(
OnSpaceExpanded(community.uuid, child.uuid ?? ''),
);
}, },
children: _buildNestedSpaces(context, state, child, community), children: _buildNestedSpaces(context, state, child, community),
); );

View File

@ -120,6 +120,8 @@ class _SidebarWidgetState extends State<SidebarWidget> {
visible: filteredCommunities.isNotEmpty, visible: filteredCommunities.isNotEmpty,
replacement: const EmptySearchResultWidget(), replacement: const EmptySearchResultWidget(),
child: SidebarCommunitiesList( child: SidebarCommunitiesList(
scrollController: _scrollController,
onScrollToEnd: () {},
communities: filteredCommunities, communities: filteredCommunities,
itemBuilder: (context, index) => _buildCommunityTile( itemBuilder: (context, index) => _buildCommunityTile(
context, context,