'How to pass an object as an argument which may be null at the time of build?

I have the following Pop up which is essentially an AlertDialog with some data entry fields and two buttons. The widget tree is given below:

ProductPurchaseForm

class ProductPurchaseForm extends StatelessWidget {
  ProductPurchaseForm({Key? key}) : super(key: key);

  final _formGlobalKey = GlobalKey<FormBuilderState>();

  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      scrollable: true,
      content: FormBuilder(
        key: _formGlobalKey,
        child: Column(
          children: [
            const DateField(
              name: 'date',
            ),
            const TextInputField(
              name: 'amount',
              hintText: 'Enter the Amount',
            ),
            FormFooter(
              formKey: _formGlobalKey,
              transaction: TransactionModel.productPurchase(
                tDate: _formGlobalKey.currentState!.value['date'],
                amount: _formGlobalKey.currentState!.value['amount'],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

FormFooter

class FormFooter extends ConsumerWidget {
  final TransactionModel transaction;
  final GlobalKey<FormBuilderState> formKey;
  const FormFooter({
    Key? key,
    required this.formKey,
    required this.transaction,
  }) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final _databaseService = ref.watch(databaseServiceRiverpod);
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        CustomButton(
          buttontext: 'Cancel',
          buttonWidth: 300,
          onTapFunc: () {
            formKey.currentState!.reset();
            VRouter.of(context).pop();
          },
        ),
        CustomButton(
          buttontext: 'Add Transaction',
          buttonWidth: 300,
          onTapFunc: () => addTransactionFunction(
            context: context,
            transaction: transaction,
            databaseService: _databaseService,
            formKey: formKey,
          ),
        ),
      ],
    );
  }
}

addTransactionFunction

addTransactionFunction({
  required BuildContext context,
  required TransactionModel transaction,
  required GlobalKey<FormBuilderState> formKey,
  required DatabaseService databaseService,
}) async {
  formKey.currentState!.save();
  if (formKey.currentState!.validate()) {
    final result = await databaseService.addTransaction(transaction);
    formKey.currentState!.reset();
    if (result.isError) {
      debugPrint(result.message);
      buildSnackBar(
        context: context,
        snackBarContent: 'ERROR: Please try again!',
      );
    } else {
      VRouter.of(context).pop();
      buildSnackBar(
        context: context,
        snackBarContent: 'SUCCESS: Transaction Added',
      );
    }
  } else {
    buildSnackBar(
      context: context,
      snackBarContent: 'Invalid Values',
    );
  }
}

As you can see, I am using a 3rd party package called flutter_form_builder and using a GlobalKey to get values from the FormBuilder. As I am passing the TransactionModel to the FormFooter, I use _formGlobalKey.currentState which does not have any value (or is null) at the time when the widget is first built. This is why I am getting the red screen error of Unexpected Null Value.

I know why this is happening and, technically, the TransactionModel.productPurchase is only "used" when the CustomButton is pressed and that is when I am sure that the values will NOT be null. But because of the way this widget tree is set up, the TransactionModel is referenced when the FormFooter is built rather than when it is clicked.

Is there a better way to set up my widget tree so that I can work around this null error?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source