Skip to main content

Simplificar el desarrollo de aplicaciones en Flutter

By 25 June, 2022Programación

Flutter es uno de los marcos móviles multiplataforma más populares de Google. Los desarrolladores han adoptado ampliamente el marco en todo el mundo, por lo que hay un bucle de versiones actualizadas de Flutter, y la última es Flutter 3 . Hoy vamos a hablar sobre cuáles son las mejores prácticas para el desarrollo de aplicaciones de Flutter , consultar este blog simplificará su proceso de desarrollo de una aplicación con Flutter.

Mejores prácticas para el desarrollo de aplicaciones de Flutter

Aquí aprenderá las mejores prácticas para que los desarrolladores de Flutter mejoren la calidad del código, la legibilidad, el mantenimiento y la productividad. Pongamos manos a la obra:

 

1. Haz que la función de compilación sea pura

El método de compilación está desarrollado de tal manera que tiene que ser puro/sin cosas no deseadas . Esto se debe a que hay ciertos factores externos que pueden desencadenar la creación de un nuevo widget, a continuación se muestran algunos ejemplos:

  • Ruta pop/push
  • Cambio de tamaño de la pantalla, generalmente debido a la apariencia del teclado o al cambio de orientación
  • El widget principal recreó su hijo
  • Un widget heredado del que depende el widget (Clase de patrón (contexto)) cambio

Evitar:

@override
Widget build(BuildContext context) {
  return FutureBuilder(
    future: httpCall(),
    builder: (context, snapshot) {
      // create some layout here
    },
  );
}

Debería ser así:

class Example extends StatefulWidget {
  @override
  _ExampleState createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
  Future<int> future;

  @override
  void initState() {
    future = repository.httpCall();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: future,
      builder: (context, snapshot) {
        // create some layout here
      },
    );
  }
}


2. Comprender el concepto de restricciones en Flutter

Hay una regla general de un diseño de Flutter que todo desarrollador de aplicaciones de Flutter debe conocer: las restricciones disminuyen, los tamaños aumentan y el padre establece la posición. Entendamos más sobre lo mismo:

Un widget tiene sus propias restricciones de su padre. Se sabe que una restricción es un conjunto de cuatro dobles: un ancho mínimo y máximo, y una altura mínima y máxima.

A continuación, el widget pasa por su propia lista de elementos secundarios. Uno tras otro, el widget ordena a sus hijos cuáles son sus restricciones (que pueden ser diferentes para cada hijo), y luego pregunta a cada hijo qué tamaño quiere tener.

A continuación, el widget coloca a sus elementos secundarios (horizontalmente en el eje x y verticalmente en el eje y) uno tras otro. Y luego, el widget notifica a su padre sobre su propio tamaño (dentro de las restricciones originales, por supuesto).

En Flutter, todos los widgets se asignan a sí mismos en función de sus restricciones principales o de cuadro. Pero esto tiene algunas limitaciones adjuntas.

Por ejemplo, si tiene un widget secundario dentro de un widget principal y desea decidir su tamaño. El widget no puede tener ningún tamaño por sí solo. El tamaño del widget debe estar dentro de las restricciones establecidas por su padre.

3. Uso inteligente de operadores para reducir el número de líneas a ejecutar

  • Usar operador de cascadas

Si se supone que debemos realizar una secuencia de operaciones en el mismo objeto, debemos optar por el operador Cascades(..).

//Do
var path = Path()
..lineTo(0, size.height)
..lineTo(size.width, size.height)
..lineTo(size.width, 0)
..close();  

//Do not
var path = Path();
path.lineTo(0, size.height);
path.lineTo(size.width, size.height);
path.lineTo(size.width, 0);
path.close();
  • Usar colecciones de distribución

Puede usar colecciones extendidas cuando los elementos existentes ya están almacenados en otra colección, la sintaxis de la colección extendida conduce a un código más simple y fácil.

//Do
var y = [4,5,6];
var x = [1,2,...y];

//Do not
var y = [4,5,6];
var x = [1,2];
x.addAll(y);
  • Utilice operadores Null Safe (??) y Null Aware (?.)

Siempre ve por ?? (si es nulo) y ?. operadores (conscientes de nulos) en lugar de comprobaciones nulas en expresiones condicionales.

//Do 	
v = a ?? b; 
//Do not
v = a == null ? b : a;

//Do
v = a?.b; 
//Do not
v = a == null ? null : a.b;
  • Evite usar el operador “como” en lugar de eso, use el operador “es”

Generalmente, el asoperador de conversión lanza una excepción si la conversión no es posible. Para evitar que se lance una excepción, se puede usar `is`.

//Do
if (item is Animal)
item.name = 'Lion';

//Do not
(item as Animal).name = 'Lion';


4. Use transmisiones solo cuando sea necesario

Si bien los Streams son bastante poderosos, si los usamos, se nos impone una gran responsabilidad para hacer un uso efectivo de este recurso.

El uso de Streams con una implementación inferior puede conducir a un mayor uso de memoria y CPU. No solo eso, si olvida cerrar las transmisiones, provocará pérdidas de memoria.

Entonces, en tales casos, en lugar de usar Streams, puede usar algo más que consuma menos memoria, como ChangeNotifier para la IU reactiva. Para funcionalidades más avanzadas, podemos usar la biblioteca Bloc, que se esfuerza más en usar los recursos de manera eficiente y ofrece una interfaz simple para construir la interfaz de usuario reactiva.

Los flujos se limpiarán de forma efectiva siempre que no se utilicen más. Aquí la cuestión es que si simplemente elimina la variable, eso no es suficiente para asegurarse de que no se use. Todavía podría ejecutarse en segundo plano.

Debe llamar a Sink.close() para que detenga el StreamController asociado, para asegurarse de que el GC pueda liberar los recursos más adelante.

Para hacer eso, debe usar el método StatefulWidget.dispose of:

abstract class MyBloc {
  Sink foo;
  Sink bar;
}

class MyWiget extends StatefulWidget {
  @override
  _MyWigetState createState() => _MyWigetState();
}

class _MyWigetState extends State<MyWiget> {
  MyBloc bloc;

  @override
  void dispose() {
    bloc.bar.close();
    bloc.foo.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // ...
  }
}


5. Escriba pruebas para la funcionalidad crítica

Las contingencias de confiar en las pruebas manuales siempre estarán ahí, tener un conjunto de pruebas automatizadas puede ayudarlo a ahorrar una cantidad notable de tiempo y esfuerzo. Como Flutter se dirige principalmente a múltiples plataformas, probar todas y cada una de las funciones después de cada cambio llevaría mucho tiempo y requeriría mucho esfuerzo repetido.

Seamos realistas, tener una cobertura de código del 100 % para las pruebas siempre será la mejor opción; sin embargo, es posible que no siempre sea posible en función del tiempo y el presupuesto disponibles. No obstante, sigue siendo esencial tener al menos pruebas para cubrir la funcionalidad crítica de la aplicación.

Las pruebas unitarias y de widgets son las mejores opciones desde el principio y no son para nada tediosas en comparación con las pruebas de integración.


6. Usa hilo en bruto

Se puede usar una cadena sin procesar para que no se escapen solo barras invertidas y dólares.

//Do
var s = 'This is demo string \ and $';

//Do not
var s = 'This is demo string \\ and \$';


7. Utilice importaciones relativas en lugar de importaciones absolutas

Cuando se usan importaciones relativas y absolutas juntas, es posible crear confusión cuando la misma clase se importa de dos maneras diferentes. Para evitar este caso, deberíamos usar una ruta relativa en la carpeta lib/.

//Do
import '../../themes/style.dart';

//Do not
import 'package:myapp/themes/style.dart';


8. Usar SizedBox en lugar de Container en Flutter

Hay múltiples casos de uso en los que necesitará usar un marcador de posición. Aquí está el ejemplo ideal a continuación:

return _isNotLoaded ? Container() : YourAppropriateWidget();

El Contenedor es un gran widget que usarás mucho en Flutter. Container() amplía para ajustarse a las restricciones dadas por el padre y no es un constructor constante.

Por el contrario, SizedBox es un constructor constante y construye una caja de tamaño fijo. Los parámetros de ancho y alto pueden ser nulos para especificar que el tamaño del cuadro no debe estar restringido en la dimensión correspondiente.

Por lo tanto, cuando tenemos que implementar el marcador de posición, se debe usar SizedBox en lugar de usar un contenedor.

return _isNotLoaded ? SizedBox() : YourAppropriateWidget();


9. Use registro en lugar de imprimir

print() y debugPrint() siempre se aplican para iniciar sesión en la consola. Si está utilizando print() y obtiene una salida que es demasiado a la vez, Android descarta algunas líneas de registro a veces.

Para no enfrentar esto nuevamente, use debugPrint(). Si sus datos de registro tienen datos más que suficientes, use dart: developer log(). Esto le permite agregar un poco más de granularidad e información en la salida del registro.

//Do
log('data: $data');

//Do not
print('data: $data');
  • Utilice el operador ternario para casos de una sola línea.
String alert = isReturningCustomer ? 'Welcome back to our site!' : 'Welcome, please sign up.';
  • Use la condición if en lugar del operador ternario para el caso como el siguiente.
Widget getText(BuildContext context) {
    return Row(
        children:
        [
            Text("Hello"),
            if (Platform.isAndroid) Text("Android") (here if you use ternary then that is wrong)
        ]
    );
}
  • Siempre trate de usar widgets const. El widget no cambiará cuando llame a setState, debemos definirlo como constante. Impedirá que el widget se reconstruya, por lo que renovará el rendimiento.
//Do
const SizedBox(height: Dimens.space_normal)

//Do not
SizedBox(height: Dimens.space_normal)

10. No inicialice explícitamente variables nulas

En Dart, la variable se inicializa intuitivamente en nulo cuando no se especifica su valor, por lo que agregar nulo es redundante y no es necesario.

//Do
int _item;

//Do not
int _item = null;
  • Resalte siempre el tipo de miembro cuando se conoce su tipo de valor. No use var cuando no sea necesario. Como var es un tipo dinámico, requiere más espacio y tiempo para resolverse.
//Do
int item = 10;
final Car bar = Car();
String name = 'john';
const int timeOut = 20;

//Do not
var item = 10;
final car = Car();
const timeOut = 2000;


11. Usa la palabra clave const siempre que sea posible

El uso de un constructor const para widgets puede disminuir el trabajo requerido para los recolectores de basura. Esto probablemente parezca un rendimiento pequeño al principio, pero en realidad se suma y marca la diferencia cuando la aplicación es lo suficientemente grande o hay una vista que se reconstruye con frecuencia.

Las declaraciones de const también son más fáciles de recargar en caliente. Además, debemos ignorar la palabra clave innecesaria const. Echa un vistazo al siguiente código:

const Container(
  width: 100,
  child: const Text('Hello World')
);

No requerimos usar const para el widget de texto ya que const ya se aplicó al widget principal.

Dart ofrece las siguientes reglas de Linter para const:

prefer_const_constructors
prefer_const_declarations
prefer_const_literals_to_create_immutables
Unnecessary_const


12. Algunos puntos cosméticos a tener en cuenta

  • Nunca deje de envolver sus widgets raíz en un área segura.
  • Puede declarar múltiples variables con acceso directo- (int mark = 10, total = 20, cantidad = 30;)
  • Asegúrese de utilizar variables de clase final/const siempre que exista la posibilidad.
  • Trate de no usar códigos comentados no requeridos.
  • Cree variables y métodos privados siempre que sea posible.
  • Cree diferentes clases para colores, estilos de texto, dimensiones, cadenas constantes, duración, etc.
  • Desarrolle constantes de API para claves de API.
  • Trate de no usar palabras clave en espera dentro del bloque.
  • Trate de no usar variables y funciones globales. Tienen que estar atados a la clase.
  • Consulta el análisis de dardos y sigue sus recomendaciones
  • Marque el subrayado que sugiere errores tipográficos o sugerencias de optimización
  • Use _ (guión bajo) si el valor no se usa dentro del bloque de código.
//Do
someFuture.then((_) => someFunc());

//Do not
someFuture.then((DATA_TYPE VARIABLE) => someFunc());
  • Los números mágicos siempre tienen nombres adecuados para la legibilidad humana.
//Do
final _frameIconSize = 13.0;
 SvgPicture.asset(
 Images.frameWhite,
 height: _frameIconSize,
 width: _frameIconSize,
 );

//Do not
SvgPicture.asset(
Images.frameWhite,
height: 13.0,
width: 13.0,
);

Bien, aquí estamos

Entonces, se trataba de las mejores prácticas para el desarrollo de Flutter que facilitan relativamente el trabajo de cada desarrollador de Flutter.

Si tiene dificultades para desarrollar una aplicación de Flutter o desea contratar desarrolladores de Flutter dedicados para su proyecto, consúltenos . Contamos con un equipo competente de desarrolladores de Flutter para ayudarlo con su proyecto.