Tipo de dato Function vs VoidCalback
Desarrollo móvil
En los lenguajes de programación tipados es importante hacer uso correcto de cada tipo, por seguridad, por rendimiento, por buenas prácticas etc.
Una de esas cosas que di por sentado aplicando la mítica frase "igual funciona" fue la declaración de parámetros con Fuction y VoidCallback, siempre usando Function, pero vamos a detallar cada uno, claro y al grano.
VoidCallback
Es el tipo de función especifico de Function() que no recibe ni retorna nada (es el nombre abreviado para void Function() ), es ideal usarlo como su mismo nombre lo dice, callback, cuando solo necesitamos ejecutar una acción, una notificación simple de un hijo a un padre.
Tomando en cuenta lo anteriormente dicho veamos este ejemplo...
class MiBoton extends StatelessWidget {
final VoidCallback alPresionar; // No recibe ni devuelve nada
const MiBoton({required this.alPresionar});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: alPresionar, //simplemente notifica que algo pasó
child: Text('Presionar'));
}
}
Y desde el padre...
class Padre extends StatefulWidget {
@override
_PadreState createState() => _PadreState();
}
class _PadreState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Come frutas y verduras")),
body: MiBoton(
alPresionar: () => print('Precionando ando'),
) ,
);
}
}
Function()
Es la clase abstracta de todas las funciones, y aqui es donde viene la mala práctica, declarar un parámetro solo con Function el compilador de Dart dejara de verificar el tipado y dejará pasar cualquier tipo, aumentando el riesgo de error de tipo en tiempos de ejecución.
Tomando como base el ejemplo anterior modifiquemos la clase MiBoton para que reciba un parametro de tipo Function que recibe un String...
class MiBoton extends StatelessWidget {
final void Function(String) alPresionar; //Function recibe un parametro de tipo String
const MiBoton({required this.alPresionar});
@override
Widget build(BuildContext context) {
return Column(
children: [
ElevatedButton(
onPressed: () {
alPresionar('Boton 1 presionado');
},
child: Text('Presionar boton 1'),
),
ElevatedButton(
onPressed: () {
alPresionar('Boton 2 presionado');
},
child: Text('Presionar boton 2'),
),
],
);
}
}
Y el padre...
class Padre extends StatefulWidget {
@override
_PadreState createState() => _PadreState();
}
class _PadreState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Obedece a tus padres")),
body: MiBoton(
alPresionar: (String value) => print(value),
) ,
);
}
}
Un ejemplo burdo pero sirve para mostrar como se pasa el valor del hijo al padre. El padre para poder llamar la clase MiBoton debe pasar al parámetro llamado alPresionar una función que recibe un String como parámetro; Lo que haga esa función con el parámetro la clase MiBoton no lo sabe y tampoco le importa, lo que le importa es que ese pase de parámetro exista.
Otro caso es cuando el parámetro de tipo Function() a parte de recibir un parámetro también retorna un valor, pero ese caso lo dejamos para otra ocasión.