Cloud Functions yang Dapat Dipanggil Mengembalikan Null di Flutter

Oct 09 2020

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

RafaelZasas Oct 12 2020 at 21:55

Larutan:

  • Gunakan Konstruktor untuk widget tanpa negara
  • menghindari .then()
  • Cloud Functions harus ditampilkan Promise.resolve()saat dipanggil dalam asyncfungsi.

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>;

}