Skip to content

Instantly share code, notes, and snippets.

@coxato
Last active January 8, 2021 05:23
Show Gist options
  • Save coxato/1b90596d65bbf83952b8fdc6f2f3b7ce to your computer and use it in GitHub Desktop.
Save coxato/1b90596d65bbf83952b8fdc6f2f3b7ce to your computer and use it in GitHub Desktop.
React and flutter similarities [spanish]

haciendo una app en flutter

Flutter funciona de manera muy parecida a react.js de hecho, usa el mismo concepto de componentes con estado y componentes sin estado. Solo en vez de llamarse "componentes" en flutter se les llama "widgets", y asi como en react todo es un componente, pues aqui lo mismo, todo en flutter es un widget.

Hay muchas maneras de darle forma y estilo a una app de flutter y la más común de todas es usar los paquetes que vienen por defecto de package:flutter/material.dart, el cual no es mas que muchisimos elementos y widgets de la librería Material Design. Digamos que es el equivalente en react a usar react-bootstrap o semantic-ui-react

Un hello world básico en flutter seria asi:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'first app',
      home: Scaffold(
        appBar: AppBar(
          title: Text("my flutter app"),
        ),
        body: Center(child: Text("example text")),
      ),
    );
  }
}

Como se puede observar, se parece mucho a un componente clase de react, el típico Class MyComponent extends React.Component{ . . .}, incluso cada widget tiene un método build que es el equivalente al render de react.

Otra cosa que podemos notar es que se necesita un punto de entrada, que en este caso es la función main, la cual ejecuta toda la app.

App con Material Design

Ya que usaremos muchisimos de los widgets que vienen incorporados con Material Design, prácticamente estaremos haciendo una "app de material desing", por ello debemos hacer cosas como esta:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // retornando una app de material design
    return MaterialApp(
      title: 'first app',
      home: Scaffold(
        appBar: AppBar(
          title: Text("my flutter app"),
        ),
        body: Center(child: Text("example text")),
      ),
    );
  }
}

En el método build retornamos una app de Material Design, la cual tiene muchas props, Sí asi es, props tal cual como en react, cada componente (widget en este caso), tiene sus props para funcionar correctamente, MaterialApp tiene unas cuantas props, pero el más importante para recordar por ahora es home ya que aquí vive nuestra parte visible de la app, la cual es la que tendra interacción con el usuario. Además, podemos usar el widget de Scaffold:

home: Scaffold(
    appBar: AppBar(
        title: Text("my flutter app"),
    ),
    body: Center(child: Text("example text")),
),

Scaffold para simplificar digamos que es una especie de "layout" donde irán las partes (visuales) principales de nuestra app, como lo puede ser el appBar (parecido a un navbar de html) y el body que es el cuerpo central de la app y donde normalmente se muestra el contenido principal.

widgets básicos de flutter

Los widgets básicos son un conjunto de widgets los cuales tienen una finalidad básica y muy precisa, gracias a éstos widgets podemos juntarlos para hacer nuestros propios widgets más complejos. Podriamos decir también que los widgets básicos son como los componentes build-in que trae react native.

listado de widgets básicos

texto:

  • Text

posicionamiento:

  • Column
  • Row
  • Stack

contenedor (equivalente a div)

  • Container

Conocer más de los widgets en https://api.flutter.dev/flutter/widgets/widgets-library.html

widgets con estado y sin estado

Al igual que react hay widgets que tienen estados y otros no, el funcionamiento es muy parecido a react.

widgets con estado StateFulWidget:

Si el usuario puede interactuar con dicho widget, éste sería un widget con estado. Algunos de ellos normalmente son: Checkboxs, Radio, Slider, Forms, etc. En resumen, si tiene interacción, es un componente con estado:

widgets sin estado StateLessWidget:

widgets fijos que no interactuan con el usuario, como por ejemplo un icono, texto, etc

posicionamiento con Container, Stack, Column y Row

Guía rápida de como posicionar elementos como si usaramos CSS

Container y Stack

Primero que nada hablemos de Container, éste lo podemos usar principalmente para temas de margin y padding asi como también color, que es algo asi como un background-color de css.

Container y Stack se parecen, pero Stack tiene la particularidad de que puede alvergar varios hijos (widgets) y además los super-posiciona, es como una mezcla entre position: fixed; y position: absolute; de css. El orden del z-index (si quisieramos llamarlo asi) depende de la posición en la lista de hijos (children), el ultimo elemento es el que se super-posiciona por encima de todos los demas, digamos que seria algo asi:

  Stack(
    children: <Widget>[
      WidgetA(), // z-index 1
      WidgetB(), // z-index 2
      WidgetC(), // z-index 3
      WidgetD(), // z-index 4
    ]
  )

En el ejemplo anterior, el Widget WidgetD sería el que más arriba estaria en el eje z

No recomiendo usar Container si quieres hacer las propiedades flex de css, para ello mejor usar los Widgets que veremos dentro de poco, aún asi, para temas de margin, padding y color la cosa quedaría asi:

Container(
  // color: Colors.blue,
  margin: EdgeInsets.only(top: 220.0),
  padding: EdgeInsets.all(20),
  color: Colors.blue,

  child: // cualquier Widget

);

Row y Column

Tal cual como indican sus nombres, uno sirve para posicionar elementos horizontalmente y el otro verticalmente.

Ahora bien, si queremos usar algo asi como las propiedades flex de css, éstos 2 Widgets traen unas propiedades las cuales funcionan de manera parecida. Dichas propiedades son: mainAxisAlignment y crossAxisAlignment. Se pueden usar asi:

Column(
  mainAxisAlignment: MainAxisAlignment.spaceAround, // y mas props por parte de MainAxisAlignment
  crossAxisAlignment: CrossAxisAlignment.start, // asi como tambien de CrossAxisAlignment

  children: [] // childrenList,
)

lo mismo con Row:

Row(
  mainAxisAlignment: MainAxisAlignment.spaceAround, // y mas props por parte de MainAxisAlignment
  crossAxisAlignment: CrossAxisAlignment.start, // asi como tambien de CrossAxisAlignment

  children: [] // childrenList,
)

la diferencia está en que el mainAxisAlignment en Row en horizontal y el crossAxisAlignment es vertical

Mientras que obviamente en Column el mainAxisAlignment es vertical y el crossAxisAlignment es horizontal, es cosa de pensarlo 1 segundo para no confundirse entre estos 2 widgets (Row y Column), ekisde.

Como hago si quiero un Contenedor con padding y que tenga elementos posicionados de manera parecida a flexbox de css?

Fácil, con un Container haces el padding y dentro metes un Row ó Column dependiendo del diseño que quieras hacer. Podria ser algo asi:

Container(
  padding: EdgeInsets.all(20.0), // EdgeInsets.all es para top, left, right y bottom 

  child: Row(
    mainAxisAlignment: MainAxisAlignment.spaceAround,
    crossAxisAlignment: CrossAxisAlignment.center,
    
    children: [
      Text("txt 1"),
      Text("txt 2"),
      Text("txt 3"),
    ],
  ),
);

El código anterior es como si hubieramos hecho en css:

div{
  padding: 20px;
  display: flex;
  justify-content: space-around;
  align-items: center;
}

Widgets con estado StatefulWidget

Los widgets con estado se parecen mucho a los "componentes de tipo clase" en React, la diferencia es que aqui se crean 2 clases, una para crear el widget y el estado, y la otra para inicializar dicho estado, manejar eventos y llamar al render (build en este caso).

El siguiente código sirve para crear un botón que cambia de icono dependiendo la propiedad _tapped:

import 'package:flutter/material.dart';

class FloatingButtonFav extends StatefulWidget{
  @override
  State<StatefulWidget> createState() {
    return _FloatingButtonFav();
  }
}

// this class is for manage the "render -> build" and the event handlers
class _FloatingButtonFav extends State<FloatingButtonFav>{

  bool _tapped = false;

  void handleTap(){
    setState(() {
      _tapped = !_tapped;
    });
  }

  @override
  Widget build(BuildContext context) {
    return FloatingActionButton(
      backgroundColor: Colors.lightGreen,

      mini: true,

      onPressed: handleTap,

      child: Icon(_tapped ? Icons.favorite : Icons.favorite_border_outlined)
    );
  }
}

Como se puede notar hay 2 clases:

La primera que hereda de StatefulWidget sirve para crear el widget y el state, además es muy util para recibir las props del widget padre

class FloatingButtonFav extends StatefulWidget{
  @override
  State<StatefulWidget> createState() {
    return _FloatingButtonFav();
  }
}

La segunda clase que hereda de State<TipoDeState>, la cual se maneja de forma muy pero que muy parecida a React, en dicha clase podemos:

  • inicializar el estado
  • manejar eventos (handlers)
  • retornar vista con metodo build (parecido a render)
class _FloatingButtonFav extends State<FloatingButtonFav>{

  bool _tapped = false;

  void handleTap(){
    setState(() {
      _tapped = !_tapped;
    });
  }

  @override
  Widget build(BuildContext context) {
    return FloatingActionButton(
      backgroundColor: Colors.lightGreen,

      mini: true,

      onPressed: handleTap,

      child: Icon(_tapped ? Icons.favorite : Icons.favorite_border_outlined)
    );
  }
}

Aquí en esta segunda clase, el método build se llamará cada vez que el estado cambie. si una propiedad del estado (_tapped en este ejemplo) llega a ser modificada fuera del método setState simplemente el método build no será llamado. tal cual como pasa en React con el método render.

En resumen:

  • La clase que hereda de StatefulWidget es la que usaremos para llamar a dicho widget, es decir, es la clase que se usará en todos los demas widgets donde lo querramos usar. Dicha clase es donde se reciben props de los widgets padres, además se crea el estado, mas no se inicializa.

  • La clase que hereda de State<T> es la que llevará toda la lógica del componente. Inicialización del estado, event handlers y el método build. Mas sin embargo, ésta clase no sera pública, es decir, ésta clase es solo para manejar el estado y su comportamiento, la clase que si es pública y que se puede usar en otros widgets es la que hereda de StatefulWidget.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment