I want to add a search/filter functionality to my RecyclerListView so that I can search items by typing the name inside a searchbar on top. I'm using this library: https://github.com/Flipkart/recyclerlistview
When the searchbar is empty, all items should be shown.
I tried looking at the library-documentation to see what they say on searching inside RecyclerListView, but no cigar. Also, I found the React Native Search Filter package but have no idea how to implement this into a RecyclerListView.
Here's my code:
import React, { Component } from "react";
import { View, StyleSheet, Text, Profile, TextInput, Dimensions } from "react-native";
import { Profiler } from "react/cjs/react.production.min";
import { AudioContext } from "../context/AudioProvider";
import * as MediaLibrary from "expo-media-library";
import Screen from "../components/Screen";
import { DataProvider, LayoutProvider } from "recyclerlistview";
import { RecyclerListView } from "recyclerlistview";
import AudioListItem from "../components/AudioListItem";
import OptionModal from "../components/OptionModal";
import { Audio } from "expo-av";
import {
play,
pause,
resume,
playNext,
updatePitch,
selectAudio,
AddToQueue,
} from "../misc/AudioController";
import { goBack } from "./PlayListDetail";
import { storeAudioForNextOpening } from "../misc/helper";
//import { PlayRate } from "./Settings";
import { hidePlayList } from "./Playlist";
import "./Settings";
//console.log(global.PlayRate);
export class AudioList extends Component {
static contextType = AudioContext;
constructor(props) {
super(props);
this.state = { optionModalVisible: false };
this.currentItem = {};
}
dataProvider = new DataProvider((r1, r2) => {
return r1 !== r2;
});
layoutProvider = new LayoutProvider(
(i) => "audio",
(type, dim) => {
dim.width = Dimensions.get("window").width;
dim.height = 63;
}
);
// onPlaybackStatusUpdate = async (playbackStatus) => {
// console.log("hier");
// if (playbackStatus.isLoaded && playbackStatus.isPlaying) {
// this.context.updateState(this.context, {
// playbackPosition: playbackStatus.positionMillis,
// playbackDuration: playbackStatus.durationMillis,
// });
// }
//
// if (playbackStatus.didJustFinish) {
// const nextAudioIndex = this.context.currentAudioIndex 1;
// if (nextAudioIndex >= this.context.totalAudioCount) {
// this.context.playbackObj.unloadAsync();
// return this.context.updateState(this.context, {
// soundObj: null,
// currentAudio: this.context.audioFiles[0],
// isPlaying: false,
// currentAudioIndex: 0,
// playbackPosition: null,
// playbackDuration: null,
// });
// }
// const audio = this.context.audioFiles[nextAudioIndex];
// const status = await playNext(this.context.playbackObj, audio.uri);
// this.context.updateState(this.context, {
// soundObj: status,
// currentAudio: audio,
// isPlaying: true,
// currentAudioIndex: nextAudioIndex,
// });
// }
// };
handleAudioPress = async (audio) => {
await selectAudio(audio,this.context);
};
componentDidMount() {
this.context.loadPreviousAudio()
}
rowRenderer = (type, item, index, extendedState) => {
return (
<AudioListItem
title={item.filename}
isPlaying={extendedState.isPlaying}
activeListItem={this.context.currentAudioIndex === index}
duration={item.duration}
onAudioPress={() => this.handleAudioPress(item)}
onOptionPress={() => {
this.currentItem = item;
this.setState({ ...this.state, optionModalVisible: true });
}}
/>
);
};
navigateToPlaylist = () => {
//
console.log("navigate");
this.context.updateState(this.context, {
addToPlayList: this.currentItem,
});
this.props.navigation.navigate("Playlist");
}
// addToQueue = () => {
// }
render() {
if(global.Hz === undefined){
global.Hz = 440
}
return (
<>
<View style = {styles.viewStyle}>
<Text style = {{fontWeight: 'bold',fontSize: 24, }}>{global.Hz} Hz mode</Text>
</View>
<AudioContext.Consumer>
{({ dataProvider, isPlaying }) => {
if(!dataProvider._data.length) return null;
return (
<View style={{ minHeight: 1, minWidth: 1, flex: 1 }}>
{dataProvider && dataProvider.getSize() > 0 && (
<RecyclerListView
dataProvider={dataProvider}
layoutProvider={this.layoutProvider}
rowRenderer={this.rowRenderer}
extendedState={{ isPlaying }}
/>
)}
<OptionModal
// onPlayPress={() => console.log("play")}
// onPlaylistPress={() => {
// this.context.updateState(this.context, {
// addToPlayList: this.currentItem,
// });
// this.props.navigation.navigate("Playlist");
// }}
options ={[{title: 'Add to playlist',onPress: this.navigateToPlaylist } /* , {title: 'Add to qeue', onPress: console.log("yes")}*/]}
currentItem={this.currentItem}
onClose={() =>
this.setState({ ...this.state, optionModalVisible: false })
}
visible={this.state.optionModalVisible}
/>
</View>
);
}}
</AudioContext.Consumer>
</>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
viewStyle: {
alignItems: 'center',
paddingLeft: 20,
paddingBottom: 10
}
});
export default AudioList;
CodePudding user response:
For starters, replace RecyclerListView with @shopify/flashlist. It uses the same recyclerListView under the hood but offers the familiar Flatlist props. Then follow one of the many available guides on building a search bar for a flatlist.The process should be mostly the same since flatlist and flashlist share most of the same props.
CodePudding user response:
Thank you for your reply. However, I'm wondering what kind of list I should pass into the data-prop. In my case it must be the list of all audiofiles stored on the device, but I'm not sure as to how to obtain this list and pass it into the 'data-prop'. The code I'm currently using to obtain a list of all audioFiles is here: (I'm using a dataProvider to pass the data to the recyclerlistView):
import {Text, View, Alert } from 'react-native'
import * as MediaLibrary from 'expo-media-library';
import { DataProvider } from 'recyclerlistview';
import {Audio} from 'expo-av';
import { play, pause, resume, playNext, updatePitch } from "../misc/AudioController";
import AsyncStorage from "@react-native-async-storage/async-storage";
export const AudioContext = createContext();
export class AudioProvider extends Component {
constructor(props) {
super(props);
this.state = {
audioFiles: [],
playList: [],
addToPlayList: null,
permissionError: false,
dataProvider: new DataProvider((r1, r2) => r1 !== r2),
playbackObj: null,
soundObj: null,
currentAudio: {},
isPlaying: false,
isPlayListRunning: false,
activePlayList: [],
currentAudioIndex: null,
playbackPosition: null,
playbackDuration: null,
rate: 3,
};
this.totalAudioCount = 0;
}
permissionAlert = () => {
Alert.alert("Permission Required", "This app needs to read audio files", [
{ text: "I am ready", onPress: () => this.getPermission() },
{
text: "cancel",
onPress: () => this.permissionAlert(),
},
]);
};
getAudioFiles = async () => {
const { dataProvider, audioFiles } = this.state;
let media = await MediaLibrary.getAssetsAsync({
mediaType: "audio",
});
media = await MediaLibrary.getAssetsAsync({
mediaType: "audio",
first: media.totalCount,
});
this.totalAudioCount = media.totalCount;
this.setState({
...this.state,
dataProvider: dataProvider.cloneWithRows([
...audioFiles,
...media.assets,
]),
audioFiles: [...audioFiles, ...media.assets],
});
};
loadPreviousAudio = async () => {
let previousAudio = await AsyncStorage.getItem("previousAudio");
let currentAudio;
let currentAudioIndex;
if (previousAudio === null) {
currentAudio = this.state.audioFiles[0];
currentAudioIndex = 0;
} else {
previousAudio = JSON.parse(previousAudio);
currentAudio = previousAudio.audio;
currentAudioIndex = previousAudio.index;
}
this.setState({ ...this.state, currentAudio, currentAudioIndex });
};
getPermission = async () => {
// {
// "canAskAgain": true,
// "expires": "never",
// "granted": false,
// "status": "undetermined",
// }
const permission = await MediaLibrary.getPermissionsAsync();
if (permission.granted) {
this.getAudioFiles();
}
if (!permission.canAskAgain && !permission.granted) {
this.setState({ ...this.state, permissionError: true });
}
if (!permission.granted && permission.canAskAgain) {
const { status, canAskAgain } =
await MediaLibrary.requestPermissionsAsync();
if (status === "denied" && canAskAgain) {
this.permissionAlert();
}
if (status === "granted") {
this.getAudioFiles();
}
if (status === "denied" && !canAskAgain) {
this.setState({ ...this.state, permissionError: true });
}
}
};
onPlaybackStatusUpdate = async (playbackStatus) => {
//console.log(global.PlayRate);
if (playbackStatus.isLoaded && playbackStatus.isPlaying) {
this.updateState(this, {
playbackPosition: playbackStatus.positionMillis,
playbackDuration: playbackStatus.durationMillis,
});
}
if(playbackStatus.isLoaded && !playbackStatus.isPlaying){
storeAudioForNextOpening(this.state.currentAudio,
this.state.currentAudioIndex, playbackStatus.positionMillis)
}
if (playbackStatus.didJustFinish) {
if(this.state.isPlayListRunning) {
let audio;
const indexOnPlayList = this.state.activePlayList.audios.findIndex(({id}) => id === this.state.currentAudio.id )
const nextIndex = indexOnPlayList 1;
audio = this.state.activePlayList.audios[nextIndex];
if(!audio) audio = this.state.activePlayList.audios[0];
const indexOnAllList = this.state.audioFiles.findIndex(({id}) => id === audio.id)
const status = await playNext(this.state.playbackObj, audio.uri)
return this.updateState(this, {soundObj: status,
isPlaying: true,
currentAudio: audio,
currentAudioIndex: indexOnAllList,
});
}
const nextAudioIndex = this.state.currentAudioIndex 1;
if (nextAudioIndex >= this.totalAudioCount) {
this.state.playbackObj.unloadAsync();
this.updateState(this, {
soundObj: null,
currentAudio: this.state.audioFiles[0],
isPlaying: false,
currentAudioIndex: 0,
playbackPosition: null,
playbackDuration: null,
});
return await storeAudioForNextOpening(
this.state.audioFiles[0],
0
);
}
//otherwise, select the next audio
const audio = this.state.audioFiles[nextAudioIndex];
const status = await playNext(this.state.playbackObj, audio.uri);
this.updateState(this, {
soundObj: status,
currentAudio: audio,
isPlaying: true,
currentAudioIndex: nextAudioIndex,
});
await storeAudioForNextOpening(audio, nextAudioIndex);
}
};
componentDidMount() {
this.getPermission();
if (this.state.playbackObj === null) {
this.setState({ ...this.state, playbackObj: new Audio.Sound() });
}
}
updateState = (prevState, newState = {}) => {
this.setState({ ...prevState, ...newState });
};
render() {
const {
audioFiles,
dataProvider,
playList,
addToPlayList,
permissionError,
playbackObj,
soundObj,
currentAudio,
isPlaying,
currentAudioIndex,
playbackPosition,
playbackDuration,
rate,
isPlayListRunning,
activePlayList,
} = this.state;
if (permissionError)
return (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
<Text>It looks like you haven't accepted the permission</Text>
</View>
);
return (
<AudioContext.Provider
value={{
audioFiles,
dataProvider,
playList,
addToPlayList,
playbackObj,
soundObj,
currentAudio,
isPlaying,
currentAudioIndex,
totalAudioCount: this.totalAudioCount,
playbackPosition,
playbackDuration,
isPlayListRunning,
activePlayList,
rate,
updateState: this.updateState,
loadPreviousAudio: this.loadPreviousAudio,
onPlaybackStatusUpdate: this.onPlaybackStatusUpdate,
}}
>
{this.props.children}
</AudioContext.Provider>
);
}
}
import {Component} from 'react';
import { storeAudioForNextOpening } from '../misc/helper';
export default AudioProvider;```