How to Enable Keyboard in your Vega app

What will we be building?

While developing TV apps, enabling keyboard functionality becomes necessary. Today, we will learn how to enable keyboard in Vega. As a bonus, we will also implement search functionality.

Pre-requisites:

  1. Install Vega Studio from here

  2. Create your project. Check this guide.

  3. Ensure the app is running on the simulator or device. Check docs for the same.

:laptop: Let’s start coding:

  1. Create Search component :

We will create a component Search.jsx. This component will handle the search functionality using React Native’s TextInput component. We will use the following attributes:

a. placeholder

b. onChangeText

c. accessibilityLabel

// Search.jsx
import React, {useState} from 'react';
import {TextInput, StyleSheet} from 'react-native';

const Search = ({searchCharacters}) => {
  const Styles = getStyles();
  const [search, setSearch] = useState('Enter');

  const searchTerm = (value) => {
    setSearch(value);
    searchCharacters(value);
  };

  return (
    <TextInput
      style={Styles.inputContainer}
      placeholder="search"
      onChangeText={searchTerm}
      accessibilityLabel="search"
    />
  );
};

const getStyles = () =>
  StyleSheet.create({
    inputContainer: {
      padding: 10,
      color: 'white',
      borderBottomColor: 'gray',
      borderBottomWidth: 4,
      marginVertical: 10,
      width: 800,
      height: 90,
      fontSize: 42,
    },
  });

export default Search;

  1. Import in Root component:

We will import Search.tsx into the root component of our application, App.jsx

//App.jsx
import React from 'react';
import {View} from 'react-native';
import {Search} from './components/Search';

export const App = () => {
  return (
    <View>
      <Search />
    </View>
  );
};
  1. Update manifest.toml

This time, after running your application, you won’t see any keyboard. To enable the keyboard, we need to update settings in the manifest.toml, which is located at the root of your app. Open the file and add the following code at the end of the file (order doesn’t matter)

[wants]
[[wants.service]]
id = "com.amazon.inputmethod.service"
  1. Build, Run and Test the app

Build, and run the app. Please check the docs to know how to build and run the app.

Once the build is successful, check your app (either on simulator or device). Use the remote to focus on the search. If on simulator, use the mouse to focus on the search field. You should now see the device keyboard. :clap::clap:

:magnifying_glass_tilted_right: Bonus: Add search logic

What we will be building?

Now, we will add search functionality. We will search characters based on user’s input in search field. We will also add filter functionality based on character’s status. For this, we need to update the code for API calls and list creation.

For this we will need:

  1. List : This component will be responsible for:

     a. Creating a list using `FlatList` RN component
    
  2. Homepage (or App.jsx): This component will have multiple responsibilities:

     a. API call
     b. Assembling the List, Search, and Filter (basically the entire app)
     c. Have the logic of the Search, API, and Filter
    

1. Let’s create List component:

// List.jsx
import React from 'react';
import {FlatList, View, Text, StyleSheet} from 'react-native';
import Thumbnail from './Thumbnail';

const List = ({listHeading, character}) => {
  const Styles = getStyles();

  return (
    <View>
      <Text style={Styles.text}>{listHeading}</Text>
      <FlatList
        data={character}
        horizontal
        renderItem={({item}) => <Thumbnail character={item} />}
        keyExtractor={(item) => item.id}
      />
    </View>
  );
};

const getStyles = () =>
  StyleSheet.create({
    text: {
      color: 'white',
      fontSize: 42,
    },
  });

export default List;
// Thumbnail.jsx 
import React from 'react';
import {View, Text, StyleSheet, ImageBackground} from 'react-native';
import Like from './Like';

const Thumbnail = ({character}) => {
  const getCharacterStatus = () => {
    switch (character.status) {
      case 'Alive':
        return <Text style={Styles.alive}>{character.status}</Text>;
      case 'unknown':
        return <Text style={Styles.unknown}>{character.status}</Text>;
      default:
        return <Text style={Styles.status}>{character.status}</Text>;
    }
  };
  const Styles = getStyles();
  return (
    <View style={Styles.container}>
      <View style={Styles.subContainer}>
        <ImageBackground
          source={{uri: character.image}}
          style={Styles.imageSize}
          resizeMode="cover"
        />
        <Text style={Styles.text}>{character.name}</Text>
        <Text style={Styles.subtext}>{getCharacterStatus()}</Text>
        <Like />
      </View>
    </View>
  );
};

const getStyles = () =>
  StyleSheet.create({
    container: {
      marginHorizontal: 10,
      width: 350,
      padding: 10,
      marginTop: 50,
    },
    subContainer: {
      borderWidth: 2,
      borderColor: 'white',
      padding: 10,
      minHeight: '500',
    },
    text: {
      color: 'white',
      fontSize: 42,
      marginVertical: 10,
    },
    subtext: {
      color: 'white',
      fontSize: 28,
      marginBottom: 10,
    },
    unknown: {
      color: 'gray',
    },
    status: {
      color: 'red',
    },
    alive: {
      color: 'green',
    },
    imageSize: {
      width: '300',
      height: '300',
      padding: 10,
    },
  });

export default Thumbnail;
  1. Homepage

We can create a new component - Homepage.jsx or could write this code in App.jsx

searchCharacters() method is responsible for search logic. It performs the following:

a. Retrieves value from search component.

b. Utilizes the JavaScript filter method to map over Characters data (filtered data) .

c. Checks if characters.title matches the value of search using includes(). For this, we should convert both to LowerCase()

// Homepage.jsx
import React, {useState, useEffect} from 'react';
import {View, Text, StyleSheet} from 'react-native';
import List from '../components/List';
import Search from '../components/Search';
import {URL, CHARACTER_STATUS} from '../constants/URL';

const Homepage = () => {
  const [episodes, setEpisodes] = useState();
  const [status] = useState(CHARACTER_STATUS);
  const [filteredItems, setFilteredItems] = useState();
  const [searchTerm, setSearchTerm] = useState();

  const Styles = getStyles();

  const getEpisodes = async () => {
    const response = await fetch(URL);
    const data = await response.json();
    setEpisodes(data.results);
    setFilteredItems(data.results);
  };

  const searchCharacters = (value) => {
    setSearchTerm(value);
    const searchedData = episodes.filter((item) =>
      item.name.toLowerCase().includes(value.toLowerCase()),
    );
    setFilteredItems(searchedData);
  };

  useEffect(() => {
    getEpisodes();
  }, []);

  return (
    <View style={Styles.container}>
      <Search searchCharacters={searchCharacters} />
      <Text style={Styles.text}>{searchTerm}</Text>
      {episodes && <List listHeading="Characters" character={filteredItems} />}
    </View>
  );
};

const getStyles = () =>
  StyleSheet.create({
    container: {
      flex: 1,
      flexDirection: 'column',
      alignItems: 'center',
      paddingVertical: 20,
    },
    text: {
      color: 'gray',
      fontSize: 32,
      marginTop: 60,
    }
  });

export default Homepage;

Demo:

Simulator:

Device:

How to enable the keyboard in a Vega Web App

To enable the keyboard in a Vega Web App, do the following:

  1. Set the initial focus by adding a hasTVPreferredFocus={true} prop to the WebView component and add the following capability to their manifest.toml file.
# If the Web App needs keyboard support
[[wants.service]]
id = "com.amazon.inputmethod.service"
4 Likes