Cloud Functions yang Dapat Dipanggil Mengembalikan Null di Flutter
Tujuanku:
Saya ingin memanggil Cloud Function di Aplikasi Flutter saya yang mengambil object JSON dari server Python-FastAPI dan menampilkan konten dalam Dialog Peringatan .
Kesalahan:
Layanan Fungsi yang Dapat Dipanggil dalam aplikasi flutter saya menerima nol. Dialog peringatan saya menampilkan "Null Value Error" seperti yang dipicu oleh kode saya.
Operasi Sisi Cloud:
My Cloud Function berfungsi dalam 2 bagian:
- Terima data dari klien (Flutter APP) di Fungsi Http yang Dapat Dipanggil
- Panggil Python API => kembali ke Cloud Function yang kembali ke Klien
Fungsi Cloud:
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});
});
Panggilan 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;
}
Muatan yang dihasilkan berfungsi dengan benar seperti yang terlihat di Log saya:
Operasi Sisi Klien:
Saat Tombol Klik di 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());
},
Fungsi Flutter getPassword () saya:
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';
}
}
Fungsi Kata Sandi Tampilan Saya:
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),
);
}
}
CATATAN * Saya ingin menyimpan pengambilan kata sandi sebagai Cloud Function, sehingga dapat digunakan pada aplikasi web maupun seluler. Namun saya terbuka untuk refactoring seluruh operasi jika solusi yang lebih baik muncul dengan sendirinya.
Jawaban
Larutan:
- Gunakan Konstruktor untuk widget tanpa negara
- menghindari
.then()- Cloud Functions harus ditampilkan
Promise.resolve()saat dipanggil dalamasyncfungsi.
Saat Tombol Klik:
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));
Untuk kemudahan membaca, saya menghapus penanganan pengecualian pada kode di atas
Menampilkan Dialog Peringatan:
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),
);
}
}
Fungsi Cloud:
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>;
}