Home > Software engineering >  How to fix error: type 'Null' is not a subtype of type '() => void'
How to fix error: type 'Null' is not a subtype of type '() => void'

Time:12-19

I have an admin sign in page where admins can add an id and password to gain access to the Admin area. There is an admin collection in the Firebase Database that stores the id and password. The admin collection is separate from the user sign in page and separate from the user collection which uses Firebase Authentication before allowing access. The user access continues to work correctly. When I fill in the two input boxes on the admin sign in screen and click the button to gain access my error dialog message appears indicating that there isn't any data in the two input fields even though there is data. If I do nothing to the code and then hot reload and click the button again I am able to access the admin but I get the following error message in the console.

The following _TypeError was thrown building ShoppingAdminSignInPage(dirty, dependencies: [_LocalizationsScope-[GlobalKey#2c797]], state: _ShoppingAdminSignInPageState#e3b3d): type 'Null' is not a subtype of type '() => void'

I have obviously written something or several things incorrectly in my code. It appears the error is in the ShoppingAdminSignInButton. Thank you in advance for any help.

class ShoppingAdminSignInPage extends StatefulWidget {
  const ShoppingAdminSignInPage({Key? key}) : super(key: key);

  @override
  State<ShoppingAdminSignInPage> createState() =>
      _ShoppingAdminSignInPageState();
}

class _ShoppingAdminSignInPageState extends State<ShoppingAdminSignInPage> {
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  final TextEditingController _adminIDController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return AdaptiveLayoutScaffold(
      appBar: const ShoppingAdminSignInPageAppBar(),
      landscapeBodyWidget: Container(),
      portraitBodyWidget: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16.0),
          child: Column(
            mainAxisSize: MainAxisSize.max,
            children: [
              const ShoppingAdminSignInHeader(),
              Form(
                key: _formKey,
                child: Column(
                  children: [
                    const SizedBox(
                      height: 50.0,
                    ),
                    AdminSignInTextField(
                      controller: _adminIDController,
                      labelText: TextFieldLabel.adminID,
                      prefixIcon: Icons.person,
                      textInputAction: TextInputAction.next,
                    ),
                    AdminSignInTextField(
                      controller: _passwordController,
                      labelText: TextFieldLabel.password,
                      prefixIcon: Icons.password,
                      textInputAction: TextInputAction.done,
                    ),
                    ShoppingAdminSignInButton(
                      onPressed: _adminIDController.text.isNotEmpty &&
                              _passwordController.text.isNotEmpty
                          ? logInAdmin()
                          : () => showDialog(
                              context: context,
                              builder: (ctx) {
                                return const ErrorAlertDialog(
                                  message: DialogString.addAdminIDAndPassword,
                                );
                              }),
                    ),
                    const NotAnAdminButton(),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  logInAdmin() {
    FirebaseFirestore.instance.collection('admins').get().then((snapshot) {
      snapshot.docs.forEach((result) {
        if (result.data()['id'] != _adminIDController.text.trim()) {
          SnackBarUtil.showSnackBar(
            context,
            SnackBarString.idNotCorrect,
          );
        } else if (result.data()['password'] !=
            _passwordController.text.trim()) {
          SnackBarUtil.showSnackBar(
            context,
            SnackBarString.passwordNotCorrect,
          );
        } else {
          SnackBarUtil.showSnackBar(
            context,
            'Welcome ${result.data()['name']}',
          );
          setState(() {
            _adminIDController.text = '';
            _passwordController.text = '';
          });
          Navigator.pushReplacement(
            context,
            MaterialPageRoute(
              builder: (context) => const UploadItemsPage(),
            ),
          );
        }
      });
    });
  }
}

CodePudding user response:

onPressed: _adminIDController.text.isNotEmpty && _passwordController.text.isNotEmpty
  ? logInAdmin()
  : () => showDialog(

above, what you are saying is if a condition is true (in this case the condition is both _adminIDController and _passwordController to not be empty) then it should run logInAdmin and wait for it to finish and then run whatever logInAdmin returned.

Dart thinks logInAdmin will return a function and it should run that function. This is not the case, you want the button to directly run logInAdmin.

To fix this, remove the parenthesis:

onPressed: _adminIDController.text.isNotEmpty && _passwordController.text.isNotEmpty
  ? logInAdmin
  : () => showDialog(

This way, you are not assigning the result of the function, you are assigning the function itself.

Also as a general recommendation, you should always declare a return type on your functions so dart can tell you whenever this happens

void logInAdmin() {
  ...
  • Related