Haciendo hablar a nuestro dispositivo móvil con expo

Tiempo de lectura: 15 minutos.

Público objetivo: Desarrolladores.

Nivel:  Básico.

Pre-requisito: Javascript - React (básico).

Meta:  Realizar una pequeña aplicación “text to speech” usando algunos elementos de React , React native y Expo.

Fecha publicación: 15 de Diciembre de 2017.

Introducción:

 

Hola de nuevo!. Hoy, vamos realizar una aplicación muy sencilla pero divertida la cual nos permitirá realizar “Text to speech” (Texto a voz) en nuestro dispositivo móvil (cool, no ?).

 

Antes de empezar, te invito a leer este blog [Primeros Pasos React Native] si no tienes conocimiento alguno con react native o expo!, ahora sí, manos a la obra.

 

No está de más recordar que este tutorial funciona tanto en IOS y Android sin tener que variar una línea de código :O (Ricos y pobres podemos convivir en paz).

(les debo el sonido ajhajja)

1- Iniciando el proyecto:

Utilizaremos una herramienta estupenda que incorpora todo lo necesario para empezar! (más información aquí, si me animo haré un blog con una descripción de esta magnífica herramienta).

$ create-react-native-app
$ cd my-app/
$ npm start ó yarn start

 " Que no cunda el pánico ", aquí una breve descripción de los archivos generados más importantes y relevantes para este ejercicio:

 

Nombre del archivo

Descripción

Más información

package.json

Archivo de configuración del ecosistema de Node.js

Nodesource

app.json

Archivo de configuración de nuestra aplicación.

Expo

App.js

Es el index de React Native, a partir de este se desprende todas las aplicaciones

Expo

.babelrc

Opciones de la API de Babel (traspilador)

Babel

.watchmanconfig

Archivo de configuración de watchman (script que permite desplegar nuestra aplicación en la red local y hacer live reload (entre otras cosas))

watchman

2- Hora de programar:

Crearemos una nueva carpeta en el root del proyecto llamada “components”, los componentes a crear serán los siguientes:

Componentes

 

Button.js

Componente personalizado (botón). Ejecutará la función pasada como parámetro cuando sea presionado:

import React from 'react'
import { StyleSheet, Text, TouchableOpacity } from 'react-native'

 

Importamos lo necesario para que nuestro botón funcione:

TouchableOpacity: Componente de react native que simula un efecto de  opacidad al tocarlo, es una muy buena idea utilizarlo en los elementos que responden a toques (touches).  [Más información]

Text: Componente de react native usado para mostrar cadenas de texto al  usuario. [Más información]

StyleSheet: Objeto que describe estilos similares a CSS [Más información]

 

export function Button({ onPress }) {
  return (
    <TouchableOpacity
      onPress={onPress}
      style={[styles.touchable, colors.button]}
     >
       <Text style={[styles.text, colors.text]}>Tap to talk!</Text>
       </TouchableOpacity>
   )
}

 

Línea 10: Exportamos nuestra función (componente) para que otro módulo pueda usarla.  También, pasamos como parámetro una función (onPress) que será ejecutada al momento de tocar este componente (en sú método onPress -> Línea 13).

Línea 14: Damos algo de estilos.

Línea 15: Muchos elementos pueden tener un padre, como es el caso de Text que tiene un “wrapper”. Ponemos nuestro texto y agregamos estilos.

Línea 11 a 18 :Retornamos el componente de react native en conjunto con nuestros estilos.

 

TextInputMod.js

 

Componente personalizado con algunos estilos para nuestro input de texto.

import React from 'react'
import { StyleSheet, TextInput, View } from 'react-native'

 

TextInput: Componente de react native que actúa como  entrada de texto. [Más información]

View: Componente de react native. Unidad fundamental para la construcción  de Interfaces de usuario. Muy usado para anidar componentes hijos. Tiene una connotación muy parecida a <div> (dentro de la programación web - html). [Más información]

 

export function TextInputMod({ onChangeText }) {
  return (
    <View style={styles.inputContainer}>
      <TextInput
        style={styles.textInput}
        placeholder="Write something to speech"
        placeholderTextColor="white"
        multiline
        numberOfLines={4}
        underlineColorAndroid="transparent"
        onChangeText={onChangeText}
      />
    </View>
  )
}​

 

Línea 10: Exportamos nuestra  y pasamos como parámetro una función (onChangeText) que será ejecutada al momento de cambiar el texto en este input (en sú método onChangeText -> Línea 19). Un claro ejemplo de Two way data binding.

Línea 13 a 19: Propiedades que podemos colocar para mejorar la interacción con el usuario y hacer mejorar nuestro componente.

Línea 25 a la 37: Estilos bien nice!

 

ImageMod.js

 

Otro componente personalizado, el cual contiene la imagen de mi perfil en github (creado porque no sabía que poner entre el título y el input text) porque se ve genial!.

import React from 'react'
import { Image, TouchableOpacity } from 'react-native'
import { WebBrowser } from 'expo'

 

Image: Componente de react native, encargado de mostrar diferentes tipos   de imágenes (Network, locales, Temporales) [Más información]

WebBrowser: Componente de la api de expo. Prove acceso al web browser del sistema. [Más información]

 

export function ImageMod() {
  return (
    <TouchableOpacity
      onPress={() => openWebBrowser('https://github.com/NoRoboto')}
    >
      <Image
        source={{
uri:'https://avatars1.githubusercontent.com/u/8453089?s=400&u=9873e91bd823c820ae77540203d3388d340b55a9&v=4'
        }}
        style={{ width: 125, height: 125 }}
      />
    </TouchableOpacity>
  )
}​

 

Línea 14: Llamamos a nuestra función openWebBrowser como un callback, usando una arrow function ejecutando esta acción si se le da tap a la imágen!

Línea 17: Fijamos una uri para la imagen de github.

Línea 21: Nótese que también podemos usar inline styles!.

function openWebBrowser(url) {
  WebBrowser.openBrowserAsync(url)
}
​

 

Línea 32: Función que recibe una url y abre nuestro navegador web!.

La idea es modularizar nuestra app, para así aprovechar las bondades de React!

Creamos una carpeta con el nombre de config, esta contendrá sólo el archivo config.js (el cual crearemos manualmente).

  • Configuraciones:
    • app.json (por defecto cuando se inicia el proyecto):

       

      {
        "expo": {
          "sdkVersion": "23.0.0",
          "name": "text-to-speech",
          "slug": "text2speech",
          "description": "a simple text to speech app",
          "version": "1.0.0"
        }
      }
      

       

      Archivo de configuración global. Es muy recomendable que le des una leída a la documentación de expo y más si quieres publicar tu aplicación en alguna store de aplicaciones.

    • config.js (archivo para configurar el módulo Speech de expo):

      export const speechOptions = {
        language: 'es-CO',
        pitch: 1,
        rate: 1
      }
      

 

3- Poniendo todo en su lugar:

Para que nuestra aplicación funcione, debemos conectar lógicamente todos los componentes y realizar sus llamados para que ejecuten lo que les corresponde. Ejecutaremos todo en el archivo App.js (que a su vez será un componente de react!)

import React from 'react'
import {
  StyleSheet,
  Text,
  View,
  Image,
  KeyboardAvoidingView,
  ScrollView
} from 'react-native'
import { Speech, Constants } from 'expo'

import { TextInputMod } from './components/TextInputMod.js'
import { ImageMod } from './components/ImageMod.js'
import { Button } from './components/Button.js'
import { speechOptions } from './config/config'

 

KeyboardAvoidingView: Este componente de react native resuelve el problema de mover toda la vista de la aplicación cuando está activo el teclado virtual. En otras palabras, hace que se pueda ver toda la app sin que el teclado oculte información [Más información]

ScrollView: Componente que utilizaremos para hacer scroll cuando el teclado esté activo o cuando la pantalla de nuestro dispositivo sea muy pequeña para mostrar todos los elementos. [Más información]

Speech: Módulo encargado de realizar el text to speech [Más información]

Constants: Información general de nuestra aplicación, usada en este proyecto para saber de qué tamaño es la barra de estatus (estilos). [Más información]

TextInputMod, ImageMod, Button, speechOptions: Importamos los elementos anteriormente creados.

  constructor() {
    super()
    this.state = { text: '' }
    this.handleTalk = this.handleTalk.bind(this)
    this.onChangeText = this.onChangeText.bind(this)
}

 

Línea 23 a 28: Constructor de react, text será la variable de estado que usaremos para almacenar el texto. handleTalk es la función que llamará al módulo Speech cuando opriman el componente botón y onChangeText será la función encargada de establecer el valor actual del input cada vez que manipulen el componente TextInput.

 

handleTalk() {
  const { text } = this.state
  text.length > 0 && Speech.speak(text, speechOptions)
}

 

Línea 36: Obtenemos la variable texto del estado.

Línea 37: En caso de que tamaño de texto sea mayor de cero, se ejecutará la función speak del módulo Speech. Nótece que recibe en su primer parámetro texto y en el otro alguna configuración.

 

 onChangeText(message) {
    this.setState({
      text: message
    })
}

 

Azúcar lógico de react, simplemente guarda el valor del parámetro message en el estado cada vez que sea invocado (onChangeText de nuestro componente TextInputMod)

Todo junto:

  render() {
    return (
      <KeyboardAvoidingView behavior="padding" style={styles.fullWidth}>
        <View style={[styles.fullWidth, colors.background]}>
          <View style={[styles.containerTitle]}>
            <Text style={styles.title}>Text To Speech</Text>
          </View>
          <ScrollView contentContainerStyle={styles.scroll}>
            <View style={styles.containerBody}>
              <ImageMod />
              <TextInputMod onChangeText={this.onChangeText} />
              <Button onPress={this.handleTalk} />
            </View>
          </ScrollView>
        </View>
      </KeyboardAvoidingView>
    )
  }
}

 

Remitirse al repo para mirar qué estilos lleva ya que no quiero saturar más esta entrada de blog!

4- Disfrutando:

Todo listo para hacer esas bromas telefónicas  la prueba con nuestro dispositivo móvil.

$ npm start ó yarn start

5- Disfrutando:

Por supuesto, muchas cosas se podrían afinar y agregar , la tarea podría ser:

  • Agregar un “spinner” indicando que se encuentra ejecutando la acción de text to speech gracias a la función onDone disponible en la API descrita anteriormente.
  • Mejorar la apariencia de nuestra app (Obvio).
  • Un botón de Stop gracias a la función onStopped  que también está disponible en la API de Expo.Speech.
  • Leer un archivo de texto.

Aquí el código fuente por si se me escapa algo :D https://github.com/NoRoboto/react-native/tree/master/text-to-speech

Mil gracias! y hasta la próxima.

 

 

Compartir Compartir Compartir