Funciones de nube invocables que devuelven nulos en Flutter
Mi meta:
Deseo llamar a una función en la nube en mi aplicación Flutter que recupera un objeto JSON de un servidor Python-FastAPI y muestra contenido en Alert Dialogue .
El error:
El servicio de función invocable dentro de mi aplicación flutter recibe un valor nulo. Mi cuadro de diálogo de alerta muestra "Error de valor nulo" según lo desencadena mi código.
Operaciones del lado de la nube:
My Cloud Function funciona en 2 partes:
- Reciba datos del cliente (aplicación Flutter) en la función Http invocable
- Llamar a la API de Python => volver a la función de nube que vuelve al cliente
Función de nube:
exports.getRandomPassword = functions.https.onCall(async (data, context) => {
const useSymbols = data.useSymbols;
const pwLength= data.pwLength;
const debug ={
received_data_type: typeof data,
received_data:data,
pwLen_type: typeof pwLength,
pwLength,
useSymbols_type:typeof useSymbols,
useSymbols,
}
console.log(debug);
await callAPI(pwLength,useSymbols).then((res:any) =>{
console.log(`Resulting Payload: ${res}`);
return res});
});
Llamada Python-FastAPI:
async function callAPI(pwLength: any, useSymbols: any) {
// BUILD URL STRING WITH PARAMS
const ROOT_URL = `http://[IP_Address]/password?pwd_length=${pwLength}&use_symbols=${useSymbols}`;
let res;
// let password: any; // password to be received
await http.get(ROOT_URL)
.then((response: any) => {
console.log("TO APP "+JSON.stringify(response));
console.log(response.getBody());
res = response.getBody() as Map<any, any>;
})
.catch((err: any) => {
console.log(err);
res= err;
});
return res;
}
La carga útil resultante funciona correctamente como se ve en mis registros:
Operaciones del lado del cliente:
Al hacer clic en el botón en Flutter:
onPressed: () async {
// call cloud function & use set state to store pw
await getPassword().then((String result) {
setState(() {
password = result;
});
});
showDialog(
context: context,
builder: (context) => DisplayPassword());
},
Función getPassword () de mi Flutter:
Future<String> getPassword() async {
var pw;
final HttpsCallable callable = new CloudFunctions()
.getHttpsCallable(functionName: 'getRandomPassword')
..timeout = const Duration(seconds: 30);
try {
await callable.call(
<String, dynamic>{
'pwLength': 10,
'useSymbols': true,
},
).then((value) {
print(value.data);
print(value.data.runtimeType);
pw = value.data;
return pw;
});
} on CloudFunctionsException catch (e) {
print('caught firebase functions exception');
print('Code: ${e.code}\nmessage: ${e.message}\ndetails: ${e.details}');
return '${e.details}'; } catch (e) { print('caught generic exception'); print(e); return 'caught generic exception\n$e';
}
}
Función de contraseña de mi pantalla:
class DisplayPassword extends StatelessWidget {
final String pw = (_MyPasswordGenPageState().password == null)
? 'null value error'
: _MyPasswordGenPageState().password;
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text(pw),
);
}
}
NOTA * Me gustaría mantener la recuperación de la contraseña como una función en la nube, por lo que se puede utilizar en aplicaciones web y móviles. Sin embargo, estoy dispuesto a refactorizar toda la operación si se presenta una solución mejor.
Respuestas
Solución:
- Usar constructores para widgets sin estado
- evitar
.then()- Cloud Functions debería regresar
Promise.resolve()cuando se invoca unaasyncfunción.
Al hacer clic en el botón:
dynamic result =
await callable.call(<String, dynamic>{
'pwLength': 10,
'useSymbols': true,
//await the result before continuing
});
setState(() {
// convert hashmap to list and get first val
password = result.data.values.toList()[0];
isLoading = false; // remove loading indicator
});
showDialog(
context: context,
builder: (context) =>
DisplayPassword(password));
Para facilitar la lectura, eliminé el manejo de excepciones en el código anterior
Visualización del cuadro de diálogo de alerta:
class DisplayPassword extends StatelessWidget {
var password; // check if null
DisplayPassword(this.password); // constructor
@override
Widget build(BuildContext context) {
// make sure null value isn't being passed to alert dialog widget
if (password == null) {
password = 'null value error';
}
return AlertDialog(
title: Text(password),
);
}
}
Función de nube:
exports.getRandomPassword = functions.https.onCall(async (data, context) => {
const useSymbols = data.useSymbols;
const pwLength= data.pwLength;
let password;
password = await callAPI(pwLength,useSymbols);
const debug ={
received_data_type: typeof data,
received_data:data,
pwLen_type: typeof pwLength,
pwLength,
useSymbols_type:typeof useSymbols,
useSymbols,
ResultingPayloadType: typeof password,
ResultingPayload: password,
}
console.log(debug);
return Promise.resolve(password)
});
async function callAPI(pwLength: any, useSymbols: any) {
// BUILD URL STRING WITH PARAMS
const ROOT_URL = `http://[My_Api_IP_Address]/password?pwd_length=${pwLength}&use_symbols=${useSymbols}`;
const res = await http.get(ROOT_URL);
console.log(res.getBody());
return res.getBody() as Map<String , any>;
}