Debug School

rakesh kumar
rakesh kumar

Posted on

How to Implement a Multi-Country Dial Code Picker in a Popup Modal in react native

LoginScreen Component (Parent)
The LoginScreen component is the entry point of the country picker. It allows users to select their country and input their phone number.

Key Features:
selectedCountry: Tracks the country selected by the user.

isPhoneEnabled: Toggles whether the phone number input field is enabled based on country selection.

isModalVisible: Controls the visibility of the modal dialog for country selection.

countries: List of all countries fetched from an external API via useMotoshareViewModel().

filteredCountries: Filters countries based on the search term.

setNewDialcode: Updates the dial code in the global state using React Context and saves it in AsyncStorage.

Functionality:
Fetch Countries: useEffect fetches the list of countries dynamically when the component mounts.

Country Selection: When a country is selected, handleCountrySelect is invoked to store the country name and its dial code in the global context.

Phone Number Input: Once a country is selected, the user can enter a phone number.

Modal for Country Selection: The CountrySelectModal is displayed when the "Select Country" button is clicked.

import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, TextInput, Alert } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useNavigation } from '@react-navigation/native';
import useMotoshareViewModel from '../controllers/MotoshareViewModel';  // Import the ViewModel for fetching countries dynamically
import CountrySelectModal from './CountrySelectModal'; // Import the new modal component
import { useBaseUrl } from '../contexts/BaseUrlContext'; // Import the useBaseUrl hook

const LoginScreen = () => {
  const [selectedCountry, setSelectedCountry] = useState(''); // State for selected country
  const [phoneNumber, setPhoneNumber] = useState(''); // State for phone number input
  const [isPhoneEnabled, setIsPhoneEnabled] = useState(false); // To enable/disable phone number input
  const [isModalVisible, setIsModalVisible] = useState(false); // To control the visibility of the modal
  const [countries, setCountries] = useState([]); // State to store countries
  const [filteredCountries, setFilteredCountries] = useState([]); // State to filter countries based on search input
  const [searchTerm, setSearchTerm] = useState(''); // State to store search term
  const { isLoading, _fetchCountries, message } = useMotoshareViewModel();  // Fetch countries from ViewModel
  const navigation = useNavigation(); // Hook to navigate to another screen
  const { setNewDialcode } = useBaseUrl(); // Get the function to update the dial_code

  // Fetch countries when the component mounts
  useEffect(() => {
    const fetchData = async () => {
      try {
        const fetchedCountries = await _fetchCountries();  // Fetch the countries from the ViewModel
        setCountries(fetchedCountries);  // Update the state with the fetched countries
        setFilteredCountries(fetchedCountries); // Set the initial filtered list to all countries
      } catch (error) {
        console.error("Error fetching countries:", error);
        Alert.alert("Error", "Unable to fetch countries");
      } finally {
        setIsLoading(false);  // Stop the loading state when data is fetched or failed
      }
    };

    fetchData();
  }, []); // Empty dependency array ensures the effect runs only once when the component mounts

  // Handle country selection
  const handleCountrySelect = async (country) => {
    try {
      setSelectedCountry(country.name); // Store only the country name
      setIsPhoneEnabled(true); // Enable the phone input after selecting a country
      setIsModalVisible(false); // Close the modal after selection
      await setNewDialcode(country.dial_code); // Ensure the context update is complete
      // Save country and dial_code in AsyncStorage
      console.log('Country and dial_code saved successfully');
    } catch (error) {
      console.error('Error saving country data:', error);
    }
  };

  // Handle search input change
  const handleSearchChange = (text) => {
    setSearchTerm(text);
    // Filter the countries based on the search term
    const filtered = countries.filter((country) =>
      country.name.toLowerCase().includes(text.toLowerCase())
    );
    setFilteredCountries(filtered); // Update the filtered countries
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Login</Text>

      <TouchableOpacity 
        style={styles.button} 
        onPress={() => setIsModalVisible(true)} // Show modal when button is pressed
      >
        <Text style={styles.buttonText}>
          {selectedCountry ? selectedCountry : 'Select Country'}  {/* Dynamically display country name */}
        </Text>
      </TouchableOpacity>

      {/* Phone number input field, initially disabled */}
      <TextInput
        style={[styles.input, { backgroundColor: isPhoneEnabled ? 'white' : '#f0f0f0' }]} 
        placeholder="Phone Number"
        keyboardType="phone-pad"
        editable={isPhoneEnabled} // Enable when a country is selected
        value={phoneNumber}
        onChangeText={setPhoneNumber}
      />

      <TouchableOpacity style={styles.button} disabled={!isPhoneEnabled}>
        <Text style={styles.buttonText}>Send OTP</Text>
      </TouchableOpacity>

      {/* Country Select Modal */}
      <CountrySelectModal 
        isModalVisible={isModalVisible} 
        setIsModalVisible={setIsModalVisible}
        countries={countries}
        handleCountrySelect={handleCountrySelect}
        isLoading={isLoading}
        searchTerm={searchTerm}
        handleSearchChange={handleSearchChange}
        filteredCountries={filteredCountries}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#f8f8f8',
    padding: 20,
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 20,
  },
  button: {
    backgroundColor: '#FF6347', // Red color
    padding: 15,
    marginTop: 20,
    width: '80%',
    alignItems: 'center',
    borderRadius: 5,
  },
  buttonText: {
    color: 'white',
    fontSize: 16,
  },
  input: {
    height: 45,
    width: '80%',
    marginTop: 20,
    paddingLeft: 10,
    borderRadius: 5,
    borderWidth: 1,
    borderColor: '#ccc',
  },
});

export default LoginScreen;
Enter fullscreen mode Exit fullscreen mode
  1. CountrySelectModal Component (Child) This modal displays the list of countries in a FlatList. It allows the user to select a country, which will update the selectedCountry and store its dial_code in AsyncStorage using React Context.

Key Features:
isModalVisible: Controls whether the modal is visible.

handleCountrySelect: This function is triggered when a user selects a country, and it passes the selected country to the parent.

searchTerm: Filters countries based on search input.

Code for CountrySelectModal:

import React, { useState, useEffect } from 'react';
import { Modal, View, Text, StyleSheet, TouchableOpacity, TextInput, FlatList, ActivityIndicator, ScrollView } from 'react-native';

const CountrySelectModal = ({
  isModalVisible,
  setIsModalVisible,
  countries,
  handleCountrySelect,
  isLoading,
  searchTerm,
  handleSearchChange,
  filteredCountries
}) => {
  return (
    <Modal
      visible={isModalVisible}
      animationType="slide"
      transparent={true}
      onRequestClose={() => setIsModalVisible(false)} // Close the modal when the back button is pressed
    >
      <View style={styles.modalContainer}>
        <View style={styles.modalContent}>
          <Text style={styles.modalTitle}>Select Country</Text>

          {/* Search bar */}
          <TextInput
            style={styles.searchInput}
            placeholder="Search Country"
            value={searchTerm}
            onChangeText={handleSearchChange}
          />

          {isLoading ? (
            <ActivityIndicator size="large" color="#FF6347" />
          ) : (
            <ScrollView style={styles.scrollContainer}>
              <FlatList
                data={filteredCountries} // Dynamically filtered countries
                keyExtractor={(item) => item.id}  // Ensure key is unique
                renderItem={({ item }) => (
                  <TouchableOpacity
                    style={styles.modalButton}
                    onPress={() => handleCountrySelect(item)} // Pass the whole item object
                  >
                    <Text style={styles.modalButtonText}>{item.name}</Text>  {/* Display country name */}
                  </TouchableOpacity>
                )}
              />
            </ScrollView>
          )}

          <TouchableOpacity
            style={styles.closeButton}
            onPress={() => setIsModalVisible(false)} // Close the modal when 'Close' is pressed
          >
            <Text style={styles.closeButtonText}>Close</Text>
          </TouchableOpacity>
        </View>
      </View>
    </Modal>
  );
};

const styles = StyleSheet.create({
  modalContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'rgba(0,0,0,0.5)', // Semi-transparent background
  },
  modalContent: {
    backgroundColor: 'white',
    width: '80%',
    padding: 20,
    borderRadius: 10,
  },
  modalTitle: {
    fontSize: 20,
    fontWeight: 'bold',
    marginBottom: 20,
    textAlign: 'center',
  },
  searchInput: {
    height: 40,
    width: '100%',
    borderColor: '#ccc',
    borderWidth: 1,
    marginBottom: 15,
    paddingLeft: 10,
    borderRadius: 5,
  },
  modalButton: {
    backgroundColor: '#FF6347', // Red color
    padding: 10,
    marginTop: 10,
    alignItems: 'center',
    borderRadius: 5,
  },
  modalButtonText: {
    color: 'white',
    fontSize: 16,
  },
  closeButton: {
    marginTop: 20,
    padding: 10,
    backgroundColor: '#ccc',
    borderRadius: 5,
  },
  closeButtonText: {
    color: 'white',
    fontSize: 16,
  },
  scrollContainer: {
    maxHeight: 300, // Limit the height of the list inside the modal
  },
});

export default CountrySelectModal;
Enter fullscreen mode Exit fullscreen mode

Summary
This setup allows you to implement a Multi-Country Dial Code Picker using a popup modal in React Native:

LoginScreen: Manages the country selection, phone input, and controls the modal visibility.

CountrySelectModal: Displays the list of countries in a FlatList, with a search bar to filter countries.

React Context: The selected country’s dial_code is stored globally using React Context and AsyncStorage.

Image description

Top comments (0)