dart - plugin - ¿Cuál es la diferencia entre funciones y clases para crear widgets?
widget plugin (3)
Cuando llame al widget Flutter, asegúrese de utilizar la palabra clave const.
Por ejemplo,
const MyListWidget();
Me he dado cuenta de que es posible crear widgets utilizando funciones simples en lugar de subclasificar StatelessWidget . Un ejemplo sería este:
Widget function({ String title, VoidCallback callback }) {
return GestureDetector(
onTap: callback,
child: // some widget
);
}
Esto es interesante porque requiere mucho menos código que una clase completa. Ejemplo:
class SomeWidget extends StatelessWidget {
final VoidCallback callback;
final String title;
const SomeWidget({Key key, this.callback, this.title}) : super(key: key);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: callback,
child: // some widget
);
}
}
Así que me he estado preguntando: ¿hay alguna diferencia además de la sintaxis entre las funciones y las clases para crear widgets? ¿Y es una buena práctica usar funciones?
He estado investigando sobre este tema durante los últimos 2 días.
Llegué a la siguiente conclusión: está bien dividir partes de la aplicación en funciones.
Es ideal que esas funciones devuelvan un
StatelessWidget
, por lo que se pueden hacer optimizaciones, como hacer que el
StatelessWidget
const
, para que no se reconstruya si no es necesario.
Por ejemplo, esta pieza de código es perfectamente válida:
import ''package:flutter/material.dart'';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: ''Flutter Demo'',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: ''Flutter Demo Home Page''),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
++_counter;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
''You have pushed the button this many times:'',
),
Text(
''$_counter'',
style: Theme.of(context).textTheme.display1,
),
const MyWidgetClass(key: const Key(''const'')),
MyWidgetClass(key: Key(''non-const'')),
_buildSomeWidgets(_counter),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: ''Increment'',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
Widget _buildSomeWidgets(int val) {
print(''${DateTime.now()} Rebuild _buildSomeWidgets'');
return const MyWidgetClass(key: Key(''function''));
// This is bad, because it would rebuild this every time
// return Container(
// child: Text("hi"),
// );
}
}
class MyWidgetClass extends StatelessWidget {
const MyWidgetClass({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
print(''${DateTime.now()} Rebuild MyWidgetClass $key'');
return Container(
child: Text("hi"),
);
}
}
El uso de la función allí es perfectamente correcto, ya que devuelve un estado
const StatelessWidget
.
Por favor corrígeme si estoy equivocado.
TL; DR: Nunca utilice funciones sobre clases para hacer un árbol de widgets reutilizable . Siempre extraiga estos en un StatelessWidget lugar.
Hay una gran diferencia entre usar funciones en lugar de clases, es decir: el marco no tiene conocimiento de las funciones, pero puede ver las clases.
Considere la siguiente función "widget":
Widget functionWidget({ Widget child}) {
return Container(child: child);
}
utilizado de esta manera:
functionWidget(
child: functionWidget(),
);
Y es equivalente a la clase:
class ClassWidget extends StatelessWidget {
final Widget child;
const ClassWidget({Key key, this.child}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
child: child,
);
}
}
utilizado así
new ClassWidget(
child: new ClassWidget(),
);
En el papel, ambos parecen hacer exactamente lo mismo: crear 2
Container
, con uno anidado en el otro.
Pero la realidad es ligeramente diferente.
En el caso de las funciones, el árbol de widgets generado se ve así:
Container
Container
Mientras que con las clases, el árbol de widgets es:
ClassWidget
Container
ClassWidget
Container
Esto es muy importante porque cambia radicalmente el comportamiento del marco al actualizar un widget. Aquí hay una lista curada de las diferencias:
-
Clases:
- permitir la optimización del rendimiento (constructor const, operador == anulación, reconstrucción más granular)
- tener recarga en caliente
- se integran en el inspector de widgets (debugFillProperties)
- puede definir claves
- puede usar la API de contexto
- Asegurar que todos los widgets se usen de la misma manera (siempre un constructor)
- asegúrese de que al cambiar entre dos diseños diferentes se eliminen correctamente los recursos (las funciones pueden reutilizar algún estado anterior)
-
Funciones:
- tengo menos código (e incluso allí, hice un generador de código para hacer clases tan pequeñas como funciones: functional_widget )
- ?
La conclusión ya debería ser bastante clara:
No utilice funciones para crear widgets .