Skip to content

Commit

Permalink
Merge pull request #4457 from rokwire/4456-feature-add-search-to-bott…
Browse files Browse the repository at this point in the history
…omsheet

[FEATURE] Add search to Bottomsheet
  • Loading branch information
mihail-varbanov authored Nov 5, 2024
2 parents 476c1a3 + 4060796 commit 02a9050
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 19 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased
## Added
- Add search to Bottomsheet [#4456](https://github.com/rokwire/illinois-app/issues/4456)
## [6.0.56] - 2024-11-04
### Fixed
- Bug fixes on the stories sites bottomsheet [#4454](https://github.com/rokwire/illinois-app/issues/4454)
Expand Down
178 changes: 159 additions & 19 deletions lib/ui/explore/ExploreStoriedSightsBottomSheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@ class ExploreStoriedSightsBottomSheetState extends State<ExploreStoriedSightsBot
places_model.Place? _selectedDestination;
List<places_model.Place> _allPlaces = [];
ScrollController? _scrollController;
final FocusNode _searchFocusNode = FocusNode();

Map<String, Set<String>> _mainFilters = {};
Set<String> _regularFilters = {};
Set<String> _expandedMainTags = {};
List<places_model.Place>? _customPlaces;

TextEditingController _searchController = TextEditingController();
bool _isSearchExpanded = false;
static final String _customSelectionFilterKey = 'CustomSelection';


Expand All @@ -49,6 +51,28 @@ class ExploreStoriedSightsBottomSheetState extends State<ExploreStoriedSightsBot
_allPlaces = widget.places;
_collectAvailableTags();
_storiedSights = List.from(_allPlaces);

_searchController.addListener(_onSearchTextChanged);
_searchFocusNode.addListener(() {
if (_searchFocusNode.hasFocus) {
_controller.animateTo(
0.95,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
});
}

@override
void dispose() {
_searchFocusNode.dispose();
_searchController.dispose();
super.dispose();
}

void _onSearchTextChanged() {
_applyFilters();
}

void _collectAvailableTags() {
Expand Down Expand Up @@ -164,7 +188,14 @@ class ExploreStoriedSightsBottomSheetState extends State<ExploreStoriedSightsBot
}))];

double _calculateFilterButtonsHeight() {
return _expandedMainTags.isNotEmpty ? 120.0 : 60.0;
double height = 60.0; // Base height
if (_expandedMainTags.isNotEmpty) {
height += 60.0; // Additional height for subfilters
}
if (_isSearchExpanded) {
height += 70.0; // Additional height for the search field
}
return height;
}

List<Widget> _buildPlaceListView() {
Expand Down Expand Up @@ -312,14 +343,15 @@ class ExploreStoriedSightsBottomSheetState extends State<ExploreStoriedSightsBot
place.images!.first.imageUrl,
fit: BoxFit.cover,
)
: Styles().images.getImage('missing-building-photo') ??
: Styles().images.getImage('missing-building-photo', fit: BoxFit.cover) ??
SizedBox(width: 75, height: 75),
);
}

Widget _buildFilterButtons() {
List<Widget> filterButtons = [];

filterButtons.add(_buildSearchButton());
// Add Custom Selection Filter Button
if (_customPlaces != null) {
String label = '${_customPlaces!.length} Places';
Expand Down Expand Up @@ -368,12 +400,108 @@ class ExploreStoriedSightsBottomSheetState extends State<ExploreStoriedSightsBot
}
}

if (_isSearchExpanded) {
filterWidgets.add(
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0),
child: TextField(
controller: _searchController,
focusNode: _searchFocusNode,
decoration: InputDecoration(
hintText: 'Search places',
prefixIcon: Styles().images.getImage("search") ?? const SizedBox(),
suffixIcon: _searchController.text.isNotEmpty
? GestureDetector(
onTap: () {
setState(() {
_searchController.clear();
_applyFilters();
});
},
child: Styles().images.getImage("close") ?? const SizedBox(),
)
: null,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(24.0),
borderSide: BorderSide(
color: Styles().colors.fillColorSecondary,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(24.0),
borderSide: BorderSide(
color: Styles().colors.fillColorPrimary,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(24.0),
borderSide: BorderSide(
color: Styles().colors.fillColorPrimary,
),
),
contentPadding: EdgeInsets.symmetric(vertical: 0, horizontal: 16.0),
),
onChanged: (text) {
setState(() {
_applyFilters();
});
},
),
),
);
}

return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: filterWidgets,
);
}

Widget _buildSearchButton() {
return Padding(
padding: const EdgeInsets.only(right: 8.0),
child: ElevatedButton(
onPressed: () {
setState(() {
_isSearchExpanded = !_isSearchExpanded;
if (!_isSearchExpanded) {
// Clear the search text when collapsing the search field
_searchController.clear();
}
});
},
style: ElevatedButton.styleFrom(
foregroundColor: Styles().colors.fillColorPrimary,
backgroundColor: Colors.white,
side: BorderSide(
color: Styles().colors.surfaceAccent,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24.0),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Styles().images.getImage('search', color: Styles().colors.fillColorPrimary, excludeFromSemantics: true, size: 16.0) ??
Icon(Icons.search, color: Styles().colors.fillColorPrimary),
SizedBox(width: 4),
Text(
'Search',
style: Styles().textStyles.getTextStyle("widget.button.title.small")?.apply(
color: Styles().colors.fillColorPrimary,
),
),
SizedBox(width: 4,),
(_isSearchExpanded
? Styles().images.getImage("chevron-down-dark-blue")
: Styles().images.getImage("chevron-up-dark-blue")) ?? const SizedBox(),
],
),
),
);
}

Widget _buildCustomFilterButton(String label, bool isSelected) {
return Padding(
padding: const EdgeInsets.only(right: 8.0),
Expand Down Expand Up @@ -527,32 +655,44 @@ class ExploreStoriedSightsBottomSheetState extends State<ExploreStoriedSightsBot
}

void _applyFilters() {
if (_selectedFilters.isEmpty) {
setState(() {
_storiedSights = List.from(_allPlaces);
});
} else if (_selectedFilters.contains(_customSelectionFilterKey)) {
setState(() {
_storiedSights = List.from(_customPlaces!);
});
} else {
setState(() {
_storiedSights = _allPlaces.where((place) {
List<places_model.Place> filteredPlaces = List.from(_allPlaces);

// Apply search text filter first
String searchText = _searchController.text.toLowerCase();
if (searchText.isNotEmpty) {
filteredPlaces = filteredPlaces.where((place) {
return place.name?.toLowerCase().contains(searchText) ?? false;
}).toList();
}

// Apply selected filters
if (_selectedFilters.isNotEmpty) {
if (_selectedFilters.contains(_customSelectionFilterKey)) {
filteredPlaces = filteredPlaces.where((place) => _customPlaces!.contains(place)).toList();
} else {
filteredPlaces = filteredPlaces.where((place) {
bool matchesFilter = false;
if (_selectedFilters.contains('Visited')) {
if (place.userData?.visited != null && place.userData!.visited!.isNotEmpty) {
if (_selectedFilters.length > 1) {
return _matchesOtherFilters(place);
matchesFilter = _matchesOtherFilters(place);
} else {
matchesFilter = true;
}
return true;
} else {
return false;
matchesFilter = false;
}
} else {
return place.tags != null && place.tags!.any((tag) => _selectedFilters.contains(tag));
matchesFilter = place.tags != null && place.tags!.any((tag) => _selectedFilters.contains(tag));
}
return matchesFilter;
}).toList();
});
}
}

setState(() {
_storiedSights = filteredPlaces;
});
}

bool _matchesOtherFilters(places_model.Place place) {
Expand Down

0 comments on commit 02a9050

Please sign in to comment.