Home > front end >  Add search-function to RecyclerListview react Native
Add search-function to RecyclerListview react Native

Time:01-25

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