skip to Main Content

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;

2

Answers


  1. Chosen as BEST ANSWER

    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;```
    

  2. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search