From 8b441aaf46a7697c10c4512722cb04aa1dc2898a Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 16 Apr 2025 14:09:36 +0300 Subject: [PATCH] Refactor SidebarCommunitiesList to be a StatelessWidget and update its usage across SpaceTreeView and SidebarWidget for improved performance and maintainability. --- .../widgets/sidebar_communities_list.dart | 48 ++- .../space_tree/view/space_tree_view.dart | 283 +++++++++--------- .../all_spaces/widgets/sidebar_widget.dart | 2 + 3 files changed, 167 insertions(+), 166 deletions(-) diff --git a/lib/common/widgets/sidebar_communities_list.dart b/lib/common/widgets/sidebar_communities_list.dart index f5f34f24..acc8471d 100644 --- a/lib/common/widgets/sidebar_communities_list.dart +++ b/lib/common/widgets/sidebar_communities_list.dart @@ -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/utils/extension/build_context_x.dart'; -class SidebarCommunitiesList extends StatefulWidget { +class SidebarCommunitiesList extends StatelessWidget { const SidebarCommunitiesList({ required this.communities, required this.itemBuilder, + required this.scrollController, + required this.onScrollToEnd, super.key, }); final List communities; final Widget Function(BuildContext context, int index) itemBuilder; - - @override - State createState() => _SidebarCommunitiesListState(); -} - -class _SidebarCommunitiesListState extends State { - late final ScrollController _scrollController; - - @override - void initState() { - _scrollController = ScrollController(); - super.initState(); - } - - @override - void dispose() { - _scrollController.dispose(); - super.dispose(); - } + final ScrollController scrollController; + final void Function() onScrollToEnd; @override Widget build(BuildContext context) { @@ -40,13 +25,22 @@ class _SidebarCommunitiesListState extends State { child: Scrollbar( scrollbarOrientation: ScrollbarOrientation.left, thumbVisibility: true, - controller: _scrollController, - child: ListView.builder( - padding: const EdgeInsetsDirectional.only(start: 16), - shrinkWrap: true, - itemCount: widget.communities.length, - controller: _scrollController, - itemBuilder: widget.itemBuilder, + controller: scrollController, + child: NotificationListener( + onNotification: (notification) { + if (notification is ScrollEndNotification && + notification.metrics.extentAfter == 0) { + onScrollToEnd.call(); + } + return false; + }, + child: ListView.builder( + padding: const EdgeInsetsDirectional.only(start: 16), + shrinkWrap: true, + itemCount: communities.length, + controller: scrollController, + itemBuilder: itemBuilder, + ), ), ), ), diff --git a/lib/pages/space_tree/view/space_tree_view.dart b/lib/pages/space_tree/view/space_tree_view.dart index 5b6d5593..cae4af1c 100644 --- a/lib/pages/space_tree/view/space_tree_view.dart +++ b/lib/pages/space_tree/view/space_tree_view.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.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_event.dart'; import 'package:syncrow_web/pages/space_tree/bloc/space_tree_state.dart'; @@ -34,160 +35,158 @@ class _SpaceTreeViewState extends State { @override Widget build(BuildContext context) { return BlocBuilder(builder: (context, state) { - List list = - state.searchQuery.isNotEmpty ? state.filteredCommunity : state.communityList; + final communities = state.searchQuery.isNotEmpty + ? state.filteredCommunity + : state.communityList; return Container( height: MediaQuery.sizeOf(context).height, decoration: widget.isSide == true - ? subSectionContainerDecoration.copyWith(color: ColorsManager.whiteColors) + ? subSectionContainerDecoration.copyWith( + color: ColorsManager.whiteColors) : const BoxDecoration(color: ColorsManager.whiteColors), child: state is SpaceTreeLoadingState ? const Center(child: CircularProgressIndicator()) : Column( children: [ - widget.isSide == true - ? Container( - decoration: const BoxDecoration( - color: ColorsManager.circleRolesBackground, - borderRadius: BorderRadius.only( - topRight: Radius.circular(20), topLeft: Radius.circular(20)), - ), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - children: [ - Expanded( - child: Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(20)), - border: Border.all(color: ColorsManager.grayBorder)), - child: TextFormField( - style: context.textTheme.bodyMedium - ?.copyWith(color: ColorsManager.blackColor), - onChanged: (value) { - context.read().add(SearchQueryEvent(value)); - }, - decoration: textBoxDecoration(radios: 20)!.copyWith( - fillColor: Colors.white, - suffixIcon: Padding( - padding: const EdgeInsets.only(right: 16), - child: SvgPicture.asset( - Assets.textFieldSearch, - width: 24, - height: 24, + if (widget.isSide == true) + Container( + decoration: const BoxDecoration( + color: ColorsManager.circleRolesBackground, + borderRadius: BorderRadius.only( + topRight: Radius.circular(20), + topLeft: Radius.circular(20), + ), + ), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + Expanded( + child: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all( + Radius.circular(20), + ), + border: Border.all( + color: ColorsManager.grayBorder, + ), + ), + child: TextFormField( + style: context.textTheme.bodyMedium?.copyWith( + color: ColorsManager.blackColor, + ), + onChanged: (value) => + context.read().add( + SearchQueryEvent(value), ), - ), - hintStyle: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w400, - fontSize: 12, - color: ColorsManager.textGray), + decoration: + textBoxDecoration(radios: 20)?.copyWith( + fillColor: Colors.white, + suffixIcon: Padding( + padding: const EdgeInsets.only(right: 16), + child: SvgPicture.asset( + Assets.textFieldSearch, + width: 24, + height: 24, ), ), + hintStyle: + context.textTheme.bodyMedium?.copyWith( + fontWeight: FontWeight.w400, + fontSize: 12, + color: ColorsManager.textGray, + ), ), ), - ], + ), ), - ), - ) - : CustomSearchBar( - onSearchChanged: (query) { - context.read().add(SearchQueryEvent(query)); - }, + ], ), + ), + ) + else + CustomSearchBar( + onSearchChanged: (query) => context.read().add( + SearchQueryEvent(query), + ), + ), const SizedBox(height: 16), Expanded( child: state.isSearching ? const Center(child: CircularProgressIndicator()) - : ListView( - shrinkWrap: true, - scrollDirection: Axis.horizontal, - children: [ - Container( - 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, - ), - ), - ) - : Scrollbar( - 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().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) { - return CustomExpansionTileSpaceTree( - title: list[index].name, - isSelected: state.selectedCommunities - .contains(list[index].uuid), - isSoldCheck: state.selectedCommunities - .contains(list[index].uuid), - onExpansionChanged: () { - context.read().add( - OnCommunityExpanded(list[index].uuid)); - }, - isExpanded: state.expandedCommunities - .contains(list[index].uuid), - onItemSelected: () { - context.read().add( - OnCommunitySelected(list[index].uuid, - list[index].spaces)); - widget.onSelect(); - }, - children: list[index].spaces.map((space) { - return CustomExpansionTileSpaceTree( - title: space.name, - isExpanded: state.expandedSpaces - .contains(space.uuid), - onItemSelected: () { - context.read().add( - OnSpaceSelected( - list[index], - space.uuid ?? '', - space.children)); - widget.onSelect(); - }, - onExpansionChanged: () { - context.read().add( - OnSpaceExpanded(list[index].uuid, - space.uuid ?? '')); - }, - isSelected: state.selectedSpaces - .contains(space.uuid) || - state.soldCheck.contains(space.uuid), - isSoldCheck: - state.soldCheck.contains(space.uuid), - children: _buildNestedSpaces( - context, state, space, list[index]), - ); - }).toList(), - ); - }), + : SidebarCommunitiesList( + onScrollToEnd: () => context.read().add( + PaginationEvent( + state.paginationModel, + state.communityList, + ), + ), + scrollController: _scrollController, + communities: communities, + itemBuilder: (context, index) { + return CustomExpansionTileSpaceTree( + title: communities[index].name, + isSelected: state.selectedCommunities + .contains(communities[index].uuid), + isSoldCheck: state.selectedCommunities + .contains(communities[index].uuid), + onExpansionChanged: () => + context.read().add( + OnCommunityExpanded( + communities[index].uuid, ), ), + isExpanded: state.expandedCommunities.contains( + communities[index].uuid, + ), + onItemSelected: () { + context.read().add( + OnCommunitySelected( + communities[index].uuid, + communities[index].spaces, + ), + ); + widget.onSelect(); + }, + children: communities[index].spaces.map( + (space) { + return CustomExpansionTileSpaceTree( + title: space.name, + isExpanded: + state.expandedSpaces.contains(space.uuid), + onItemSelected: () { + context.read().add( + OnSpaceSelected( + communities[index], + space.uuid ?? '', + space.children, + ), + ); + widget.onSelect(); + }, + onExpansionChanged: () => + context.read().add( + OnSpaceExpanded( + communities[index].uuid, + space.uuid ?? '', + ), + ), + isSelected: state.selectedSpaces + .contains(space.uuid) || + state.soldCheck.contains(space.uuid), + isSoldCheck: + state.soldCheck.contains(space.uuid), + children: _buildNestedSpaces( + context, + state, + space, + communities[index], ), - ), - ], + ); + }, + ).toList(), + ); + }, ), ), if (state.paginationIsLoading) const CircularProgressIndicator(), @@ -198,22 +197,28 @@ class _SpaceTreeViewState extends State { } List _buildNestedSpaces( - BuildContext context, SpaceTreeState state, SpaceModel space, CommunityModel community) { + BuildContext context, + SpaceTreeState state, + SpaceModel space, + CommunityModel community, + ) { return space.children.map((child) { return CustomExpansionTileSpaceTree( - isSelected: - state.selectedSpaces.contains(child.uuid) || state.soldCheck.contains(child.uuid), + isSelected: state.selectedSpaces.contains(child.uuid) || + state.soldCheck.contains(child.uuid), isSoldCheck: state.soldCheck.contains(child.uuid), title: child.name, isExpanded: state.expandedSpaces.contains(child.uuid), onItemSelected: () { - context - .read() - .add(OnSpaceSelected(community, child.uuid ?? '', child.children)); + context.read().add( + OnSpaceSelected(community, child.uuid ?? '', child.children), + ); widget.onSelect(); }, onExpansionChanged: () { - context.read().add(OnSpaceExpanded(community.uuid, child.uuid ?? '')); + context.read().add( + OnSpaceExpanded(community.uuid, child.uuid ?? ''), + ); }, children: _buildNestedSpaces(context, state, child, community), ); diff --git a/lib/pages/spaces_management/all_spaces/widgets/sidebar_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/sidebar_widget.dart index 9be0c746..198ebc51 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/sidebar_widget.dart @@ -120,6 +120,8 @@ class _SidebarWidgetState extends State { visible: filteredCommunities.isNotEmpty, replacement: const EmptySearchResultWidget(), child: SidebarCommunitiesList( + scrollController: _scrollController, + onScrollToEnd: () {}, communities: filteredCommunities, itemBuilder: (context, index) => _buildCommunityTile( context,