CategoriasAndroid

Como usar Android ExoPlayer con Jetpack Compose

Jetpack Compose ha venido para quedarse, es la solución moderna al desarrollo de interfaces de aplicaciones Android. Utilizando toda la potencia de Kotlin y su compilador, se ha logrado crear un Toolkit poderoso, fácil de utilizar y extensible. En este artículo vamos a hablar sobre cómo podemos utilizar Jetpack Compose con vistas existentes de Android como lo es Exoplayer.

¿Que es exoplayer? Es una librería de Android que contiene un reproductor que nos permite reproducir en nuestras aplicaciones de Android, videos, audio y streaming. Además de estas funcionalidades, también nos permite gestionar listas de reproducción, lo cual siempre se agradece.

Esta librería fue creada utilizando el toolkit de UI de Android existente, conocido también como Android Views, el cual es un mundo muy diferente al que manejaremos ahora y en el futuro con Jetpack compose. Para poder tener esta interoperabilidad entre las anteriores views y Jetpack Compose, existe un composable especial llamado Android View.

CategoriasSin categoria

Nueva charla disponible: Como usar Kotlin DSL para darle superpoderes a tu proyecto de Android.

¡Hola! En octubre tuve la oportunidad de dar una charla en el Android Developer Group de Madrid, sobre cómo utilizar Kotlin DSL para mejorar tu proyecto con Gradle. En este hablé sobre cómo puedes migrar código en Groovy a Kotlin en tus archivos de Gradle y así darle superpoderes al manejo de tu proyecto.

Tambien hablo sobre algunos plugins de Gradle que con Kolin DSL hacen cosas super interesantes. La charla la puedes ver aquí:

Enlace al vídeo de la charla

Este es el enlace al video de todo el evento.

Aprovecho de recordarte, que tenemos nuevo canal de Twitch en el que hablamos de programación y hacemos sesiones de live coding. Te dejo mas información en este enlace.

CategoriasSin categoria

Nuevo canal de Twitch

¡Hola! Quería comentar que desde hace par de semanas he creado un nuevo canal en Twitch en el que tenemos charlas sobre Kotlin y Android. Últimamente estamos también con el LiveCoding de aplicaciones.

Así que si te interesa el mundo del streaming puedes pasarte por allí, también si te pasas por el streaming puedes solicitar unirte a nuestro Discord. Donde tenemos una pequeña comunidad para ayudarnos entre los desarrolladores.

El enlace al canal es este twitch.tv/codingpizza.

Tambíen comentarte que hemos estado creando contenidos para Instagram, unos pequeños carruseles de información sobre Kotlin que siempre son buenos de tener bajo el brazo. El usuario de instagram de codingpizza en instagram, es: instagram.com/codingpizza

Les dejo un ejemplo:

Carousel-1
Carousel-1
Carousel-1

¡Nos vemos por allá!

CategoriasSin categoria

Mi experiencia usando Platzi

Hace poco mas de un año se realizó una conferencia de Platzi en Madrid, donde actualmente vivo. Me pareció interesante el contenido de la charla y fui a verla con mi novia, yo ya había escuchado de Platzi cuando vivía en Venezuela ya que recuerdo cuando un estudiante de la universidad donde yo me gradué fue contratado para ir a Colombia, donde tienen una de sus oficinas.

Freddy Vega dando una charla

¿Qué es Platzi?

Platzi es educación online efectiva, donde sus estudiantes tienen un completion rate del 70% o superior.

Seguramente pensaras que habré buscando en Google esa frase, pero no. Su CEO, Freddy Vega, lo repite tanto que se queda grabada en tu cabeza, además el equipo de marketing es increíble.

Disfruté mucho la charla y al final comentaron que estaban buscando profesores para dar cursos en su plataforma. A mi que siempre me ha gustado compartir las cosas que aprendo me pareció una idea increible y me acerqué a Christian Van der herst, uno de los cofundadores de Platzi, para preguntarle como podría postularme. Él muy amablemente me dió su correo y me pidió que le enviara un mail, al día siguiente ya tenia un correo mío en su bandeja de entrada.

Me piden que haga un video de una clase para ver qué tal es mi contenido y yo encantado, lo hago y lo envio. En ese proceso de crear el video me di cuenta de lo complejo que puede llegar a ser crear un curso de buena calidad, ya que transmitir el conocimiento de forma efectiva y organizada requiere muchos elementos que no había tomado en cuenta, tener el conocimiento no fue suficiente y no fui seleccionado.

En otros sitios probablemente me habrían dicho «muchas gracías, no nos llame mas». Pero en Platzi me dieron feedback, me explicaron que aunque demostraba tener mucho conocimiento debía mejorar en ciertos aspectos y hasta se tomaron el tiempo de recomendarme videos que explicaban como mejorarlos.

Hay cosas que no sabes que no sabes

Un profesor de matemática en mi universidad siempre repetía la famosa frase «Yo solo se que no se nada». Y nunca me había quedado tan claro como en esa oportunidad, así que decidí subscribirme a Platzi y descubrir lo que no sabía. Por menos de un euro al día pague la subscripción anual, fueron aproximadamente 240 euros porque en ese momento había una promoción.

Llevo trabajando como desarrollador Android un poco mas de 4 años y luego de haber trabajado en proyectos de consultoría y productos propios de una empresa, conozco lo que se usa en el mundillo de Android. Así que empece con esto en Platzi a ver que podía agregar a mis habilidades de desarrollador Android.

Esto sería aproximadamente en principios del 2019. Aunque tenían un gran abanico de cursos de frontend y backend, en Android estaban un poco limitados y anticuados respecto al estado del mercado para ese momento. Los cursos usaban componentes casi deprecados, y no utilizaban todavía Kotlin en los cursos de Android. Si bien es cierto que ya tenían un curso de Kotlin específicamente, no lo usaban en los cursos de Android.

Al pasar los meses fueron agregando más cursos en los que sí empezaron a utilizar Kotlin, ConstraintLayouts y patrones de diseño. Si quieres empezar a programar en Android estos son los cursos que yo te recomendaría:

Y esta es la lista de cursos que considero que hacen falta en Platzi sobre android:

  • Curso de Inyección de dependencias con Dagger2.
  • Curso de inyección de dependencias con Koin.
  • Curso de RxJava/RxKotlin en Android.
  • Curso de Apps multimodulos en Android.
  • Curso de Architecture Components en Android. (Ya tienen un curso en el que hablan de Room, LiveData y Navigation Component, pero uno más a fondo de estos tres e incluir WorkManager estaría genial)
  • Curso de Testing en Android Con Mockito o MockK.
  • Curso de Animaciones en Android.

Y estoy esperando en un futuro para cuando este estable el curso de Jetpack Compose ?.

El verdadero poder de Platzi

Si no sabes a dónde vas, cualquier camino te llevará.

En Platzi tienen el concepto de Rutas, estas rutas no son mas que un conjunto de cursos que debes tomar para llegar a cierto objetivo. Si tu estas empezando a obtener una habilidad de la que no conoces nada, no sabrás por donde empezar. A mi siempre me ha gustado crear cosas y aprender de todo un poco así que he tengo varias rutas en las que voy avanzando poco a poco y hasta ahora he completado 15 cursos.

Cursos completados en Platzi

Mis favoritos han sido:

  1. Curso de Finanzas Personales. Tema que considero deberían enseñar en todas las escuelas.
  2. Curso de Oratoria y hablar en publico. Te ayuda a entender, entre muchas cosas, que no sabemos hacer diapositivas ?.
  3. Los cursos de Instagram. Si te gusta el tema de redes sociales, estos son increíbles.

Podrás ver en la imágen que algunos cursos tienen una fecha al lado de su nombre, y es porque todos los meses Platzi va sacando cursos nuevos y va actualizando constantemente su contenido. Tambien tengo muchos cursos que he terminado pero no he hecho el examen final del curso ?.

Otras cosas interesantes de Platzi son sus podcast y una emisión en vivo que hacen los jueves llamada Platzi live, dónde hablan de temas muy interesantes, definitivamente recomendados.

¿Vale la pena?

Para mi, sí. Sobre todo si quieres aprender de temas de los que no conoces y no sabes por dónde empezar. Yo he aprovechado una oferta y he sumado a mi subscripción otro año. Este año quiero terminar la ruta de creación de video ?. Y si quieres que te regalen un mes gratis, puedes adquirir la subscripción con mi enlace de referidos y así nos dan a ambos un mes gratis.?

Solo tienes que hacer click aquí.

Eso es todo por ahora y como dice el equipo de Platzi, nunca pares de aprender.

CategoriasFlutterProgramación

? Como tomar una foto con Flutter o elegir una foto de la galería.

¡Hola! Ultimamente he estado trabajando en una app que requiere tomar una foto desde la app o de la galeria y mostrarla al usuario. He aprendido un par de cosas implementando esta feature y he querido compartirlo por acá. Existen varias formas de tomar una foto utilizando la cámara pero en esta oportunidad vamos a utilizar el imagePicker plugin.

Empecemos por las dependencias.

Como primer paso, vamos a agregar los plugins a nuestras dependencias. Abrimos nuestro pubspec.yaml y agregamos los siguientes plugins:

  • camera: Este plugin nos ayudará a trabajar con las cámaras de los dispositivos.
  • path_provider: Este plugin nos proporciona el path correcto para saber donde almacenar las imagenes en nuestro dispositivo, ya que cambian entre una plataforma y otra.
  • image_picker este nos ayuda a seleccionar una foto de la galería.

Despues de agregar estos plugins nuestro pubspec.yaml debería verse así.

name: fluttercamera
description: A Flutter project that takes a picture and shows the preview.
version: 1.0.0+1

environment:
  sdk: ">=2.1.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  camera:
  path_provider:
  path:
  image_picker:

  cupertino_icons: ^0.1.2

dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:

  uses-material-design: true

Aumentamos la versión minima de Android

El plugin de camera en Flutter solo funciona con sdk 21 en adelante en Android, lo que quiere decir que solo dispositivos Android con sistema operativo Lollipop o superior pueden utilizar una app con este plugin.

Para esto vamos a abrir el archivo build.gradle ubicado en android/app/build.gradle y buscar la linea que dice «minSdkVerrsion«. Finalmente cambiamos la versión de 16 a 21.

Luego de cambiar la versión debería verse así

¡Creemos nuestro primer Screen!

Para empezar vamos a crear un StatefulWidget llamado PhotoPreviewScreen. Debería verse de esta manera.

class PhotoPreviewScreen extends StatefulWidget {
  @override
  _PhotoPreviewScreenState createState() => _PhotoPreviewScreenState();
}

class _PhotoPreviewScreenState extends State<PhotoPreviewScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(

    );
  }
}

Ahora, vamos a crear un Scaffold con un widget Column centrado el cual va a mostrar una vista previa de nuestra imagen después que tomemos una foto o la seleccionemos de la galería. Y como paso final vamos a agregar un floatingActionButton, que al tocar nos mostrará un dialogo con las opciones para elegir una foto de la galería o desde la cámara.

class PhotoPreviewScreen extends StatefulWidget {
  @override
  _PhotoPreviewScreenState createState() => _PhotoPreviewScreenState();
}

class _PhotoPreviewScreenState extends State<PhotoPreviewScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            _setImageView()
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          _showSelectionDialog(context);
        },
        child: Icon(Icons.camera_alt),
      ),
    );
  }

¡Genial! Ahora seguramente te estés preguntando qué hace el _setImageView() y el _showSelectionDialog(Context).

El _setImageView() es un método que devuelve un Image Widget en caso de que la imagen obtenida no sea nula, de lo contrarío devolverá un Text Widget con un mensaje de Error. El método _showSelectionDialog(Context) muestra un dialogo con dos opciones, seleccionar una imagen de galería o de la cámara.

Empecemos creando el último, este método debe usar la función showDialog() y pasarle el Context y un Builder el cual va crear un AlertDialog con un titulo y dos opciones.

En el constructor del AlertDialog, vamos a pasar como contenido un SingleChildScrollView el cual es un Widget que nos ayudará a hacer scroll en la lista, y como child de este Widget vamos a pasarle un ListBody con dos GestureDetector como hijos para detectar cuándo el usuario ha tocado el texto.

Cada GestureDetector va a tener un Text Widget con el texto «Galería» y «Cámara» respectivamente. Y cada Text widget tambíen tendrá como parámetro de su función onTap el método _openGallery() y openCamera() segun corresponda. Estos métodos los crearemos más adelante. Al terminar el método _showSelectionDialog(context) estará así.

Future<void> _showSelectionDialog(BuildContext context) {
    return showDialog(
        context: context,
        builder: (BuildContext context) {
          return AlertDialog(
              title: Text("From where do you want to take the photo?"),
              content: SingleChildScrollView(
                child: ListBody(
                  children: <Widget>[
                    GestureDetector(
                      child: Text("Gallery"),
                      onTap: () {
                        _openGallery(context);
                      },
                    ),
                    Padding(padding: EdgeInsets.all(8.0)),
                    GestureDetector(
                      child: Text("Camera"),
                      onTap: () {
                        _openCamera(context);
                      },
                    )
                  ],
                ),
              ));
        });
  }

Ahora al tocar nuestro FloatingActionButton debería mostrar un Dialog como este.

Ahora a utilizar el plugin ImagePicker

Ahora vamos a agregarle la lógica a nuestro método _openGallery(context). Lo primero que vamos a hacer es crear un Field llamado imageFile, el cual va a ser una variable de tipo File en nuestro _LandingScreenState. Luego vamos a utilizar la función del ImagePicker, llamada pickImage() y le vamos a pasar como parámetro el enum ImageSource.gallery. Esta función es asíncrona así que vamos a tener que utilizar las palabras reservadas async y await. Luego vamos a almacenar esta variable y asignarla a nuestra variable imageFile. Como paso final, vamos a llamar al método setState() para notificar que el State ha cambiado. Y nuestra función deberá verse así.

void _openGallery(BuildContext context) async {
    var picture = await ImagePicker.pickImage(source: ImageSource.gallery);
    this.setState(() {
      imageFile = picture;
    });
    Navigator.of(context).pop();
  }

La función _openCamera(Context) es casi igual, la unica diferencía que tiene al respecto es que esta utiliza el enum imageSource.camera en lugar de gallery. Al realizar este cambio tu función de _openCamera() deberá verse así.

void _openCamera(BuildContext context) async {
    var picture = await ImagePicker.pickImage(source: ImageSource.camera);
    this.setState(() {
      imageFile = picture;
    });
    Navigator.of(context).pop();
  }

Previsualización

¿Recuerdas aquella función que mencionamos al principio llamada _setImageView()? Pues no podemos olvidarnos de ella. Para ello vamos a verificar si nuestra variable imageFile es distinta de null. Si lo es, devolvemos un Image Widget con la variable que teniamos almacenada y si aún es nula pues devolvemos un texto que diga que debemos seleccionar una imagen.

Así se vería la función terminada.

Widget _setImageView() {
    if (imageFile != null) {
      return Image.file(imageFile, width: 500, height: 500);
    } else {
      return Text("Please select an image");
    }
  }

Así es como se vería la app al final, luego de seleccionar una imagen de la galería.

Eso es todo

Espero que te haya gustado este artículo. Si te interesan mas contenido sobre Flutter o Dart puedes seguirme en las redes sociales.

Instagram.

Twitter.

?Adémas no te pierdas ningun artículo uniendote a mi newsletter

Puedes registrarte haciendo click acá debajo ?.

? Haz click aquí para unirte a la newsletter

CategoriasProgramación

Que es un unit test

Que es un unit test (Prueba unitaria)

Hola! Probablemente hayas visto en ofertas de trabajo la siguiente frase, «Experiencia con pruebas unitarias y TDD.» Pero si eres como yo y no tienes idea sobre tests, has oído de ellos, sabes que existen y sabes que las buenas compañías lo usan. Pero, ¿qué son y cómo funcionan?

? Empecemos con un test

Cuando alguien de QA (Quality assurance, aquellos que prueban tu app y encuentran fallos que tu no conocías) se acerca a mi y me pregunta si estoy seguro sobre lo que hace mi código, mi respuesta siempre es no, y no es porque no confié en mis habilidades de programación, es porque soy humano y como todo ser humano cometemos errores. Es por esto que empece a aprender sobre testing ya que los test pueden ayudarte a conseguir los errores que vas a introducir en el futuro.

Un test ayuda a probar algo, en nuestro caso, que nuestro código vaya como lo hemos planeado. Con mis simples palabras podría decir que un test es código que tu escribes para verificar que el código que vas a tener en producción funciona y hace lo que esperas que haga.

?‍??‍?Unidad

Una unidad es algo individual, digamos que un elemento, muchos elementos pueden crear algo mas grande que ellos. En nuestro caso, una unidad puede ser una clase o una función la cual junto con otras clases u otras funciones pueden crear una app mas compleja.

Al hacer pruebas de cada una de las unidades estamos haciendo pruebas unitarias.

?‍??‍? La prueba unitaria

Digamos que vamos a crear una nueva, nunca hecha, disruptiva app llamada calculadora. En esta app tendremos una función llamada sum(), la cual realiza la suma de dos números. Si lo hacemos en Kotlin, sería:

fun sum(int a,int b) : Int {
    return a+a;
}

Luego nos vamos a crear nuestra UI (Interfaz de usuario) la cual es fantástica y amamos hacer para que quede todo super lindo, pero cundo corremos la app y empezamos a probarla, nos damos cuenta de que la función de suma no funciona. ¿Por qué no funciona? Si los parámetros los he declarado bien y la suma va bien. Créeme, esto ocurre mucho mas de lo que imaginas.

Y es un problema si tus build times toman cierto tiempo. Ejecutar una compilación que tarde mucho puede romper el estado de concentración en el que te encontrabas, y espero que no seas como yo quien se distrae fácilmente y pierde el foco de que estaba haciendo. Por suerte, las pruebas unitarias pueden ayudarnos a solucionar estos dos problemas ya que los tests se encargan de verificar que el código haga lo que supone que debe hacer y como las pruebas unitarias son mas rápidas que compilar toda la app y probar cada cosa manualmente nos vienen de maravilla. Así que para empezar, vamos a crear nuestro primer test.

Casi todos los lenguajes tienen un framework para tests. Puedes revisar cual es el framework para test disponibles para tu lenguaje **aquí.** En nuestro caso como estoy usando Kotlin, vamos a usar JUnit.

Una prueba unitaria se parece a esto y por defecto está en la carpeta de tests del proyecto.

    @Test
    fun calculator_sum_shouldReturnTheSumBetweenTwoParameters() {
        //Given
        val calculator = Calculator()
        //When
        val result = calculator.sum(2, 1)
        //Then
        assertEquals(3,result)
    }

Analicemos un poco qué contiene este Test.

  • La anotación @Test que indica que es un Test.
  • El nombre de la función que indica [Unidad_NombreDeLoQueEstamosProbando_ResultadoEsperado], esto en ingles se hace de la siguiente forma: [Unit_WhatWeAreTesting_ExpectedBehavior].
  • El contenido del test.

Nuestro test debe seguir el patrón Arrange-Act-Assert (Organizar, actuar y afirmar) o el Given- When-Then (Dado que, cuando, entonces). A mi me gusta el patrón given, when, then debido a que me recuerda mucho a lo que suelo ver en las historias de usuario. Además, si te distraes puedes seguir los pasos y recordar el paso que te falta. Hablemos un poco sobre este patrón.

  • Given: En esta parte del patrón, los objetos que vas a necesitar son creados.
  • When: En este paso, llamamos a la función que queremos probar.
  • Then: Finalmente, en la ultima sección, hacemos la comprobación para evaluar el resultado.

Veamos un ejemplo:

Alt Text

En mi caso estoy usando Intellij Idea, en este IDE tengo un botón de play cerca de mi test y al hacerle click podremos ver en la siguiente imagen cómo el test falla.

Alt Text

Nuestro test ha fallado porque nuestra función está sumando dos veces el primer parámetro ?‍♂. Ahora que ya sabemos qué ocurre podremos cambiar nuestra función y ejecutar de nuevo el test para ver cómo pasa.

fun sum(int a,int b) : Int {
    return a+b;
}

Alt Text

Increíble. Tengo que admitir que ahora el verde se ha vuelto uno de mis colores favoritos ya que es el color de los tests al pasar.

Nota de Kotlin: En Kotlin, podemos utilizar las comillas invertidas como nombre de la función y hacer nuestro Test mas legible!

@Test
    fun `calculator sum should Return The Sum Between Two Parameters`() {
        //Given
        val calculator = Calculator()
        //When
        val result = calculator.sum(2, 1)
        //Then
        assertEquals(3,result)
    }

? La pirámide del testing

Hemos hablado sobre qué es un unit test, pero hay más tipos de test.

  • Unit Test, se encargan de probar una unidad en especifico.
  • Test de integración, verifica que dos o más unidades estén funcionando juntas correctamente como deberían.
  • End to End tests, verifican que todas las unidades están funcionando correctamente. En el caso de android esto incluye probar la UI.

Visto que tenemos varios tipos de test, debemos dividirlos razonablemente. No todos los tests pueden ser End to End o test de integración. Por suerte, hay una imagen que explica muy bien cómo debemos distribuir los tests y se llama la pirámide del testing.

Alt Text

La pirámide del testing fue creada por Mike Cohn en su libro, Succeeding with Agile. Y en este, comenta que los unit test deberían ser la base de la pirámide seguidos por los test de integración y finalmente los End to End.

? Otros beneficios del testing

Tener una test suite te puede hacer de malla protectora y ayudarte a detectar bugs que creas cuando estés programando una nueva funcionalidad o cuando estés haciendo un refactor. Además, tus tests pueden ayudar a futuros desarrolladores a entender mas el código que has creado ya que un test es la mejor documentación que hay.

¡Eso es todo por ahora! Espero que te haya gustado este post. Si te ha gustado, compártelo con tus amigos y compañeros de trabajo así todos podemos aprender un poco mas de testing.

Quieres saber más

Si te ha gustado este artículo y quieres saber sobre otros temas que te interesen puedes dejar un comentario o escribirme a cualquiera de mis redes sociales.

? Twitter

? Facebook

? Instagram

CategoriasSin categoria

Guardar en base de datos con Flutter

Guardar en base de datos con Flutter

Photo by Tim Evans on Unsplash.

Almacenar texto en la base de datos es junto a las llamadas a red, una de las operaciones que como desarrolladores más tenemos que realizar. Algunas veces, necesitamos guardar la información de los servidores en nuestra app para que los usuarios puedan abrir la app y ver información previamente guardada antes de que se actualice con nuevo contenido, o podemos también tener que implementar un modo offline.

En este artículo, vamos a hablar sobre cómo almacenar información en una app con Flutter. Nuestra app será una aplicación que almacenará unas Tareas por realizar (Todo’s), las cuales podremos consultar luego.

Una lista de las cosas que vamos a necesitar:

  • Una clase de Tareas por realizar (De ahora en adelante las llamaremos Todo).
  • Una Screen, la cual es un Widget que nos mostrará unos campos donde podremos crear nuestro Todo.
  • Agregar las dependencias de los paquetes que utilizaremos. Un paquete es lo que en otros lenguajes puede conocerse como librería, nosotros utilizaremos sqflite.
  • Una clase DatabaseHelper la cual se encargará de gestionar la base de datos.

https://www.codingpizza.com/wp-content/uploads/2020/01/Screenshot_1578338767.png

Primero, vamos a crear nuestra clase Todo.

class Todo {
  final int id;
  final String content;
  final String title;
  static const String TABLENAME = "todos";

  Todo({this.id, this.content, this.title});

  Map<String, dynamic> toMap() {
    return {'id': id, 'content': content, 'title': title};
  }
}

Esta es la clase Todo la cual contiene un id, el contenido del Todo y el título. Además, agregamos una constante que incluye el nombre de la tabla de Todos, así como también una función toMap que utilizaremos más adelante.

Con la clase Todo creada, vamos a agregar nuestra dependencia de la siguiente forma en el archivo pubspec.yml.

dependencies:
  flutter:
    sdk: flutter
  sqflite:
  path:

Ahora tenemos que crear nuestra clase DatabaseHelper, la cual tiene la función de tratar con la base de datos.

class DatabaseHelper {
  //Create a private constructor
  DatabaseHelper._();

  static const databaseName = 'todos_database.db';
  static final DatabaseHelper instance = DatabaseHelper._();
  static Database _database;

  Future<Database> get database async {
    if (_database == null) {
      return await initializeDatabase();
    }
    return _database;
  }

  initializeDatabase() async {
    return await openDatabase(join(await getDatabasesPath(), databaseName),
        version: 1, onCreate: (Database db, int version) async {
      await db.execute(
          "CREATE TABLE todos(id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, title TEXT, content TEXT)");
    });
  }
}

Tenemos varias cosas de las que hablar sobre esta clase. Para empezar, acceder a una base de datos es una operación asíncrona, lo que significa que debemos utilizar Futures para inicializar la propiedad database.

En la función initializeDatabase vamos a inicializar una base de datos siempre y cuando no tengamos una ya creada previamente, para inicializarla vamos a llamar la función initializeDatabase y dentro de ella llamaremos a la función openDatabase de la librería de sqflite.

La función openDatabase necesita los siguientes parámetros:

  • Un String que sería el Path de la base de datos. Para esto, debido a que tratamos con sistemas operativos diversos, hay que utilizar la función join() que se encargará de unir los path que vamos a obtener con la función getDatabasesPath().
  • Una función anónima que será ejecutada cuando la base de datos sea creada. En nuestro caso, una función anónima con una query creará una tabla dentro de la base de datos.

La función insertTodo() toma como parámetro un objeto Todo. Obtiene la base de datos que habíamos creado previamente y utiliza la función de la librería sqflite execute() para insertar los datos en nuestra base de datos. A esta función le vamos a pasar los siguientes parámetros.

  • El nombre de la tabla en la cual vamos a insertar la información. En nuestro caso tenemos el nombre declarado como una constante en nuestra clase de Todo.
  • Un map que incluye la información que queremos insertar. Para esto hemos creado la función toMap de nuestra clase Todo que convierte nuestro objeto Todo a un Map.
  • Y una constante definida por la librería que especificará qué acción tomar cuando se encuentre un conflicto. Nosotros vamos a tomar la acción de reemplazar siempre que tenga un conflicto.

Finalmente, podemos crear nuestra Screen la cual nos servirá para crear nuestro Todo object. Dado que los campos serán editables necesitaremos utilizar un StatefulWidget con su respectivo State. Los widgets que vamos a utilizar son Textfields y FloatingActionButton para guardar el Todo.

class CreateTodoScreen extends StatefulWidget {

  @override
  State<StatefulWidget> createState() {
    return _CreateTodoState();
  }
}

class CreateTodoScreen extends StatefulWidget {

  @override
  State<StatefulWidget> createState() {
    return _CreateTodoState();
  }
}

class _CreateTodoState extends State<CreateTodoScreen> {
  final descriptionTextController = TextEditingController();
  final titleTextController = TextEditingController();

  @override
  void dispose() {
    super.dispose();
    descriptionTextController.dispose();
    titleTextController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Retrieve Text Input'),
      ),
      body: Column(
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: TextField(
              decoration: InputDecoration(
                  border: OutlineInputBorder(), labelText: "Title"),
              maxLines: 1,
              controller: titleTextController,
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: TextField(
              decoration: InputDecoration(
                  border: OutlineInputBorder(), labelText: "Description"),
              maxLines: 10,
              controller: descriptionTextController,
            ),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
          child: Icon(Icons.check),
          onPressed: () {
            DatabaseHelper.instance.insertTodo(Todo(
              title: titleTextController.text,
              content: descriptionTextController.text));
            Navigator.pop(context, "Your todo has been saved.");
          }),
    );
  }
}

Luego de crear nuestro State necesitamos utilizar un TextEditingController para cada uno de nuestros TextFields, estos los declaramos al principio de nuestro Widget y hacemos un override de la función dispose() para llamar a la respectiva función dispose de cada TextEditingController.

Al momento de crear nuestros TextFields, le pasamos a cada TextField su respectivo controller. Finalmente, para llamar al DatabaseHelper e indicarle que llame a la función insertTodo debemos pasarle a la propiedad onPressed del floatingActionButton una función anónima con un Todo creado usando el texto obtenido de los TextEditingController.

https://www.codingpizza.com/wp-content/uploads/2020/01/Screenshot_1578338787.png

Actualizando nuestros Todo’s

Para actualizar nuestros Todo’s necesitamos hacer ciertas modificaciones. Primero, vamos a agregar esta nueva función a nuestro DatabaseHelper.

updateTodo(Todo todo) async {
    final db = await database;

    await db.update(Todo.TABLENAME, todo.toMap(),
        where: 'id = ?',
        whereArgs: [todo.id],
        conflictAlgorithm: ConflictAlgorithm.replace);
  }

En esta función obtenemos el objeto database y luego llamamos a la función update() de la librería y le pasamos los siguientes parámetros.

  • El nombre de la tabla que hemos declarado previamente en nuestro objeto Todo.
  • El objeto Todo convertido a un map usando la función toMap.
  • La cláusula where la cual aplicará cambios en cualquier fila que cumpla con esa condición
  • El parámetro whereArgs que sustituirá el signo de interrogación en nuestra cláusula where.
  • Y finalmente, una constante que especifica el tipo de algoritmo a usar en caso de que haya un conflicto.

Ahora debemos actualizar nuestra UI. Primero, vamos a modificar nuestra DetailTodoScreen para poder editar nuestro Todo. Necesitaremos agregar nuestro Todo como un parámetro opcional en el constructor del DetailTodoScreen, luego crear una property Todo en nuestro Widget y debido a que es un StatefulWidget debemos pasarle este Todo al State.

class DetailTodoScreen extends StatefulWidget {
    final Todo todo;

  const DetailTodoScreen({Key key, this.todo}) : super(key: key);

  @override
  State<StatefulWidget> createState() => _CreateTodoState(todo);
}

En nuestro State necesitamos agregar también nuestro Todo al constructor, crear la property Todo y sobrescribir la función initState de nuestro Widget para que podamos asignar el Título y la descripción de nuestro Todo a los respectivos TextField.

class _CreateTodoState extends State<DetailTodoScreen> {
  Todo todo;
  final descriptionTextController = TextEditingController();
  final titleTextController = TextEditingController();

  _CreateTodoState(this.todo);

  @override
  void initState() {
    super.initState();
    if (todo != null) {
      descriptionTextController.text = todo.content;
      titleTextController.text = todo.title;
    }
  }

Finalmente, en la función anónima que le pasamos como parámetro onPressed vamos a llamar a otra función llamada saveTodo, la cual se encargará de insertar un nuevo Todo en caso de que no tengamos ningún Todo para editar o de modificar un Todo existente que hayamos pasado como parámetro a nuestro State. Al final debemos llamar también a la función setState.

Así es como se verá nuestro State luego de esas modificaciones.

class _CreateTodoState extends State<DetailTodoScreen> {
  Todo todo;
  final descriptionTextController = TextEditingController();
  final titleTextController = TextEditingController();

  _CreateTodoState(this.todo);

  @override
  void initState() {
    super.initState();
    if (todo != null) {
      descriptionTextController.text = todo.content;
      titleTextController.text = todo.title;
    }
  }

  @override
  void dispose() {
    super.dispose();
    descriptionTextController.dispose();
    titleTextController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Retrieve Text Input'),
      ),
      body: Column(
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: TextField(
              decoration: InputDecoration(
                  border: OutlineInputBorder(), labelText: "Title"),
              maxLines: 1,
              controller: titleTextController,
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: TextField(
              decoration: InputDecoration(
                  border: OutlineInputBorder(), labelText: "Description"),
              maxLines: 10,
              controller: descriptionTextController,
            ),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
          child: Icon(Icons.check),
          onPressed: () async {
            _saveTodo(titleTextController.text, descriptionTextController.text);
            setState(() {});
          }),
    );
  }

  _saveTodo(String title, String content) async {
    if (todo == null) {
      DatabaseHelper.instance.insertTodo(Todo(
          title: titleTextController.text,
          content: descriptionTextController.text));
      Navigator.pop(context, "Your todo has been saved.");
    } else {
      await DatabaseHelper.instance
          .updateTodo(Todo(id: todo.id, title: title, content: content));
      Navigator.pop(context);
    }
  }
}

Deleting a Todo

Para borrar un Todo necesitamos comenzar por borrar una función de nuestra clase DatabaseHelper.

deleteTodo(int id) async {
    var db = await database;
    db.delete(Todo.TABLENAME, where: 'id = ?', whereArgs: [id]);
  }

Esta función llama a la función delete de la librería Sqflite a la cual le pasamos el nombre de la tabla la cláusula where y el id del Todo a eliminar.

Luego, para agregar una función de borrado en nuestra lista de todos vamos a necesitar realizar los siguientes pasos. Primero, en nuestro ListTile widget vamos a necesitar a utilizar un parámetro opcional llamado trailing, en este parámetro vamos a pasarle un IconButton, dentro de ese parámetro onPressed vamos a pasarle una función anónima que llamará a la función del DatabaseHelper encargada de borrar el Todo.

class  ReadTodoScreen extends StatefulWidget {
  @override
  _ReadTodoScreenState createState() => _ReadTodoScreenState();
}

class _ReadTodoScreenState extends State<ReadTodoScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Saved Todos'),
      ),
      body: FutureBuilder<List<Todo>>(
        future: DatabaseHelper.instance.retrieveTodos(),
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            return ListView.builder(
              itemCount: snapshot.data.length,
              itemBuilder: (BuildContext context, int index) {
                return ListTile(
                  title: Text(snapshot.data[index].title),
                  leading: Text(snapshot.data[index].id.toString()),
                  subtitle: Text(snapshot.data[index].content),
                  onTap: () => _navigateToDetail(context, snapshot.data[index]),
                  trailing: IconButton(
                      alignment: Alignment.center,
                      icon: Icon(Icons.delete),
                      onPressed: () async {
                        _deleteTodo(snapshot.data[index]);
                        setState(() {});
                      }),
                );
              },
            );
          } else if (snapshot.hasError) {
            return Text("Oops!");
          }
          return Center(child: CircularProgressIndicator());
        },
      ),
    );
  }
}

_deleteTodo(Todo todo) {
  DatabaseHelper.instance.deleteTodo(todo.id);
}

_navigateToDetail(BuildContext context, Todo todo) async {
  Navigator.push(
    context,
    MaterialPageRoute(builder: (context) => DetailTodoScreen(todo: todo)),
  );
}

https://www.codingpizza.com/wp-content/uploads/2020/01/Screenshot_1578338793.png

¡Y eso es todo! Ahora tenemos un app que puede almacenar un Todo, leerlo desde base de datos, actualizarlo y borrarlo.

El código está disponible aquí. Si quieres leer más artículos como estos o tienes alguna duda, puedes encontrarme en las siguientes redes sociales.

Instagram: www.instagram.com/codingpizza
Twitter: www.twitter.com/coding__pizza
Facebook: www.facebook.com/codingpizza
Site: www.codingpizza.com

CategoriasDartProgramación

Funciones increibles que debes conocer al trabajar con Dart

Funciones increibles que debes conocer al trabajar con Dart

En los post anteriores sobre Dart, hablamos sobre las Collections. Hablamos sobre Lists,Maps y sets. Y en esta oportunidad vamos a hablar sobre funciones asombrosas y que nos pueden ayudar en muchos casos.

Nota:En este post usamos varios elementos que probablemente quieras repasar, como las funciones anónimas. Si es tu primera vez leyendo esta serie de artículos puedes aquí debajo te dejo el link para que leas sobre funciones en Dart.

Funciones en Dart

Map

The map function exists in many programming languages, and Dart is not an exception. This function creates a new list after transform every element of the previous list. This function takes as a parameter an anonymous function. Let’s see an example.

var list = List.of({1,2,3,4});
var mappedList = list.map( (number) => number *2);
print(mappedList);

Esta función existe en muchos lenguajes de programación y dart no es la excepción. Esta función crea una nueva lista después de transformar cada elemento de la lista anterior. Esta función recibe como parámetro una función anónima. Veamos un ejemplo.

En este ejemplo, hemos creado una función anónima que tiene un número como parámetro y hemos multiplicado este número por dos. El resultado de la función es
(2,4,6,8).

Sort

Muchas veces necesitamos recibir una lista desde el servidor y mostrarla al usuario. Pero ¿qué ocurre si necesitamos aplicar algunos filtros y ordenarla de forma ascendente? Esta función está aquí para ayudarnos con eso, veamos un ejemplo.

var randomNumbers = List.of({14, 51, 23, 45, 6, 3, 22, 1});
randomNumbers.sort();
print(randomNumbers);

El resultado es el siguiente.

[1, 3, 6, 14, 22, 23, 45, 51]

Generate

La función generate es genial cuando necesitas crear una lista de números para hacer una prueba rápida. Toma como parámetro un número el cual indica el tamaño de la lista y una función anónima.

var generatedList = List.generate(10, (number) => number * Random().nextInt(50));
  print(generatedList);

Dentro de la función anónima obtenemos un número y lo multiplicamos por un número aleatorio entre 0 y 50.

Take

Ya solo con su nombre en inglés te explica que hace esta función simplemente toma los primeros elementos de la lista. Puede ser útil cuando tengas una lista de competidores y quieras solo obtener el top tres de todos los competidores.

var list = List.from([1,2,3,4,5,6]);
var topThreeList = list.take(3);
print(topThreeList);

El resultado es: 1,2,3

Skip

Esta función es el opuesto de la función Take, está ignora la cantidad de elementos que se le indique. En caso de que se le indiquen 3 elementos obviara los primeros tres elementos de la lista.

var list = List.from([1,2,3,4,5,6]);
var skipList = list.skip(3);
print(skipList);

El resultado es: 4,5,6

Where

Esta función es una de mis favoritas ya que nos ayuda a crear una lista con los elementos que cumplan con el predicado que se le pasa como parámetro. Esto quiere decir que solo los elementos que cumplan con la condición dada se agregaran a la lista.

Digamos que tenemos una lista aleatoria de números en caso de que queramos una lista con solo numeros pares bastará realizar esa comprobación en la función anónima. Veamos el siguiente ejemplo.

var randomNumbers = List.of({14, 51, 23, 45, 6, 3, 22, 1});
var evenNumbers = randomNumbers.where((number => number.isEven));
print(evenNumbers);

El resultado del ejemplo anterior es el siguiente:
14,6,22.

Un buen tip

Estas funciones pueden combinarse para lograr una mejor solución. Puedes combinar la función where con la función sort para obtener los números pares ordenados de forma ascendente por ejemplo.

var randomNumbers = List.of({14, 51, 23, 45, 6, 3, 22, 1});
var evenNumbers = randomNumbers.where((number) => number.isEven);
evenNumbers = evenNumbers.toList()..sort();
print(evenNumbers);

En este ejemplo tomamos solamente los números pares de la lista randomNumberList, luego convertimos esos números a una Lista y finalmente usamos el operador de cascada .. para ordenar la lista de forma ascendente.

El resultado final es: [6, 14, 22]

Eso es todo

Espero que te haya gustado. Estoy creando nuevas CodingSlices sobre Flutter en Instagram, puedes seguirme en @codingpizza y en Twitter como @coding__pizza para aprender sobre Flutter.

Ahora es tu turno

Puedes probar estos conceptos en Entornos de desarrollo (IDE) como Intellij Idea Community, que es gratis e instalar el plugin de Dart, si te gusta Visual Studio Code tambien puedes probar estos conceptos en él y por último, si prefieres algo online puedes utilizar Dartpad.

¿Quieres saber más sobre Dart?

Si te ha gustado este post y estás interesado en aprender Dart, actualmente estoy escribiendo más artículos como este en un ebook, el cual es un curso básico de Dart que te ayudará a tener un buen conocimiento que luego podrás utilizar para empezar con Flutter ;). Puedes darte de alta en este link ya que el ebook será totalmente gratis.

Post anterior

Si estás interesado en más post como este puedesrevisar mis otros artículos sobre Dart.

CategoriasDart

Collections en Dart

¡Hola! Esta semana vamos a hablar sobre Collections. Las Collections son muy importantes en la mayoría de proyectos en los que trabajamos. Como siempre, empecemos hablando sobre qué son la Collections.

Las Collections son objetos que agrupan múltiples elementos dentro de ellos, un objeto List por ejemplo pertenece a los Collections.

Digamos que tenemos una lista de números de lotería. Si creamos una lista con estos numeros se veria así.

var lotteryNumbers = List.of({18,23,43,65});

Esta variable llamada lotteryNumbers es una lista. Y esta pertenece a las Collections.

En Dart, podemos crear una lista de diferentes maneras.

Usando la palabra reservada var

Usando la palabra reservada var y el nombre de la lista crea una variable vacía.

var myVarList;

Null VS Initialized

Existe una diferencia entre una lista nula y una lista vacía. Hace poco me he encontrado una imagen que lo explica muy bien.

Una lista vacia es cuando la lista ha sido creada pero no hay ningún elemento en ella, una lista es null cuando no ha sido inicializada. Por supuesto existen otras formas más complejas de explicar esto, pero no viene al caso en este momento.

Podemos crear una lista de ambas maneras, la primera forma creará una lista nula y la segunda creará una lista vacía.

List nullList;
List emptyList = [];

Si eres curioso, puedes intentar imprimir el tamaño de cada lista usando la propiedad .length y comparar los resultados.

Creando una lista para un tipo específico

En caso de que necesitemos crear una lista de un objeto específico, necesitamos especificarlo después de la lista de la siguiente manera.

List<String> colorList = ["Red,Yellow,Purple"];

En este ejemplo estamos creando una lista de solo objetos Strings.

En caso de que necesitemos crear una lista de varios tipos de objetos necesitamos utilizar la palabra reservada dynamic.

List<dynamic> dynamicList = ["Red",1]

Por último también podemos crear una lista utilizando la función List.of.

List numberList = List.of({1,2,3,4});

Obteniendo elementos de una lista

En caso de que quieras obtener un elemento de una lista, existen varias formas de hacerlo.

  • Utilizando los corchetes luego del nombre de la lista colocando dentro la posición del objeto que quieres obtener.
  • Utilizando la función .elementAt(posición).

Aquí hay un ejemplo.

List numberList = List.of({1,2,3,4,5});
var firstNumber = numberList[0];
var elementAt = numberList.elementAt(0);

El resultado de este ejemplo va a ser el primer elemento de nuestra lista, en nuestro caso el número 1.

Agregando un elemento a la lista

Ahora vamos a ver cómo agregar un elemento a la lista que ya ha sido previamente creada. Todo lo que tenemos que hacer es usar la función .add y pasar como parámetro el número que queremos agregar.

List numberList = List.of({1,2,3,4,5});
var newNumber = 6;
numberList.add(newNumber);
print(numberList);
// Result: [1,2,3,4,5,6]

Eliminando un elemento de la lista

Esta vez vamos a hacer lo contrario, vamos a remover un elemento de la lista. Para esto necesitamos tener la posición del objeto en la lista y utilizar la función .removeAt().

List numberList = List.of({1,2,3,4,5});
numberList.removeAt(1)

¿Cuál número crees que hemos eliminado de la lista? Si tu respuesta fue el número 1, estabas cerca, pero es incorrecto, hemos removido el número 2. Esto se debe a que las posiciones de las listas empiezan desde 0.

Value  -> [1,2,3,4,5]
Position->[0,1,2,3,4]

Otro objeto de Collections

Presentamos los Maps, estos son una colección de clave-valor que nos ayudan a asociar una clave a un valor. Para crear los mapas hay que especificar el tipo de objeto de la clave y del valor, haciéndolo de la siguiente forma.

Map<int,String> nullMap;
//or
Map<String,String> powerRangersMap = {"red": "Tyrannosaurus","blue":"Triceratops","pink":"pterodactyl"};

Después de esta referencia de los noventas, podemos ver como cada Power Ranger tiene un dinosaurio asociado.

Obteniendo un elemento de un map

En ciertas ocasiones vamos a necesitar llamar solo a uno de nuestros Zords ¿no? Pero, ¿cómo podemos hacerlo en un Map? Para hacerlo necesitamos utilizar su clave en lugar de la posición.

Ejemplo:

var powerRanger = powerRangerMap["red"];

Para eliminar un elemento del Map

En caso de que necesitemos remover un elemento del Map lo que debemos hacer es utilizar la función .remove() pasando como parámetro la clave del elemento que queremos eliminar.

Ejemplo:

Map<String,String> powerRangersMap = {"red": "Tyrannosaurus","blue":"Triceratops","pink":"pterodactyl"};
powerRangersMap.remove("red");

Cómo agregar un elemento al Map

Ha aparecido un nuevo Ranger y queremos agregarlo a nuestro grupo creado previamente. Para esto debemos crear un Map, luego asignar una clave y un valor a nuestro nuevo mapa y por último debemos utilizar la función .addAll() para agregar al nuevo Ranger al grupo existente.

Ejemplo:

var newRanger = Map<String,String>();
newRanger["yellow"] = "Sabertooth Tiger";
powerRangersMap.addAll(newRanger);

Por último pero no menos importante

Hasta el momento hemos hablado de Collections sin mencionar los Sets. Un Set es una Collection de elementos únicos. Existen dos cosas que vale la pena mencionar sobre los sets

  1. No puedes obtener elementos de el Set utilizando la posición del elemento.
  1. Si intentas agregar un segundo elemento igual a uno ya existente no tendrá ningún efecto.

Ejemplo:

Set colorSet = Set.from(["Red","Blue","Yellow","Black","Pink"]);

Eliminando elementos del set

Para eliminar un elemento del Set debemos utilizar la función .remove() e indicar el elemento a eliminar.

colorSet.remove("Red");

Agregando un elemento a un set

Para agregar un elemento a un set todo lo que debemos hacer es utilizar la función .add() y pasarle el parámetro que queremos agregar.

colorSet.add("White");

Como he mencionado anteriormente si intentas agregar otro elemento al set que contenga el mismo valor, no surtirá ningún efecto.

Eso es todo

Si eres nuevo programando, espero que esto te pueda ayudar y si no lo eres, espero que te haya gustado. También estoy creando nuevas CodingSlices sobre Flutter en Instagram, puedes seguirme como @codingpizza y en Twitter como @coding__pizza para ver mas contenido.

Ahora es tu turno

Puedes probar estos conceptos en Entornos de desarrollo (IDE) como Intellij Idea Community, que es gratis e instalar el plugin de Dart, si te gusta Visual Studio Code tambien puedes probar estos conceptos en él y por último, si prefieres algo online puedes utilizar Dartpad.

Post anterior

Si estás interesado en más post como este puedes revisar mis otros artículos sobre Dart.

¿Quieres saber más sobre Dart?

Si te ha gustado este post y estás interesado en aprender Dart, actualmente estoy escribiendo más artículos como este en un ebook, el cual es un curso básico de Dart que te ayudará a tener un buen conocimiento que luego podrás utilizar para empezar con Flutter ;). Puedes darte de alta en este link ya que el ebook será totalmente gratis.

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.

ACEPTAR
Aviso de cookies