问题
My Goal:
I wish to call a Cloud Function in my Flutter App which retrieves a JSON obj from a Python-FastAPI server and displays content in Alert Dialogue.
The Error:
The Callable Function Service within my flutter app recieves null. My alert dialog displays "Null Value Error" as triggered by my code.
Cloud Side Operations:
My Cloud Function works in 2 parts:
- Receive data from client (Flutter APP) in Callable Http Function
 - Call Python API => return to Cloud Function which returns to Client
 
Cloud Function:
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});
});
Python-FastAPI Call:
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;
}
The resulting payload works correctly as seen in my Logs:
Client Side Operations:
On Button Click in 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());
                        },
My Flutter getPassword() Function:
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';
  }
}
My Display Password Function:
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),
    );
  }
}
NOTE* I would like to keep the password retrieval as a Cloud Function, so it can be used on web-apps as well as mobile. I am however open to refactoring the entire operation if a better solution presents itself.
回答1:
Solution:
- Use Constructors for stateless widgets
 - avoid
 .then()- Cloud Functions should return
 Promise.resolve()when called inasyncfunction.
On Button Click:
    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));
For ease of reading I removed exception handling in the code above
Displaying the Alert Dialog:
    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),
        );
      }
    }
Cloud Function:
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>;
}
    来源:https://stackoverflow.com/questions/64286054/callable-cloud-functions-returning-null-in-flutter