How to initialise a type ValueChanged in Dart/Flutter?

Viewed 979

I am new in Flutter, specially Flutter for Web. I am trying to reach something that's probably easy and basic, but I am facing difficulty.

This is my main.dart

Widget build(BuildContext context) {
bool loggedIn = false;
return MaterialApp(
  home: loggedIn ? Navigator(
    pages: [
      MaterialPage(child: DashboardPage())
    ],
    onPopPage: (route, result) => route.didPop(result),
  ) : LoginPage(didLoggedIn: (user) => print('Hello, ' + user) )
);
}
}

What I am trying to archive : if not logged in, go to the login screen. After the user successfully login, I'd like to execute a callback that will print hello (in fact I will set the state to logged in, but nevermind).

However I am facing difficulty to implement this callback, and maybe I am doing a wrong approach. This is the login page code:

import 'package:flutter/material.dart';
import 'package:email_validator/email_validator.dart';

class LoginPage extends StatefulWidget {
  @override
  LoginPageState createState() {
    return LoginPageState();
  }
}
class LoginPageState extends State<LoginPage> {
  final username = TextEditingController();
  final password = TextEditingController();
  final _formKey = GlobalKey<FormState>();
  bool rememberMe = true;
  //final ValueChanged didLoggedIn;
  Widget _buildUsernameField() {
    return TextFormField(
        controller: username,
        decoration: InputDecoration(labelText: 'Your E-Mail'),
        validator: (value) {
          if (value == null ||
              value.isEmpty ||
              !EmailValidator.validate(value)) {
            return 'Invalid E-Mail';
          }
          return null;
        });
  }

  Widget _buildPasswordField() {
    return TextFormField(
        controller: password,
        obscureText: true,
        decoration: InputDecoration(labelText: 'Password'),
        validator: (value) {
          if (value == null || value.isEmpty) {
            return 'Please enter your password';
          }
          return null;
        });
  }

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Container(
        color: Colors.white,
        alignment: Alignment.center,
        child: Container(
          //color: Colors.green,
          width: 600,
          height: 300,
          child: Column(
            children: [
              Form(
                key: _formKey,
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    _buildUsernameField(),
                    _buildPasswordField(),
                  ],
                ),
              ),
              Column(
                children: [
                  CheckboxListTile(title: Text('Remember me') ,
                      controlAffinity: ListTileControlAffinity.leading,
                      value: rememberMe, onChanged: (bool? value) {
                        setState(() {
                          rememberMe = value!;
                        });
                      }),
                  ElevatedButton(
                    onPressed: () {
                      if (_formKey.currentState!.validate()) {
                        print(username.text + "/" + password.text);
                        //didLoggedIn(username.text);
                      }
                    },
                    child: Text('Log me in'),
                  ),
                  Text('Forgot your password ?')
                ],
              )
            ],
          ),
        ),
      ),
    );
  }
}

The problem is this line : final ValueChanged didLoggedIn;

It says that I need to initialise it. How to do that ?

And by the way, as I said, I am newbie, so maybe this could not be the best way to archive my goals, so if someone wants to give me a better solution, this will be more than welcomed.

Thanks !

2 Answers

You need to change 'LoginPage' like below.

  • move 'didLoggedIn' to 'LoginPage' not 'LoginPageState'.
  • make a constructor of 'LoginPage' to receive 'didLoggedIn'
  • access 'didLoggedIn' using 'widget.' prefix.
import 'package:flutter/material.dart';
import 'package:email_validator/email_validator.dart';

class LoginPage extends StatefulWidget {
  final ValueChanged didLoggedIn;

  LoginPage({required this.didLoggedIn});

  @override
  LoginPageState createState() {
    return LoginPageState();
  }
}
class LoginPageState extends State<LoginPage> {
  final username = TextEditingController();
  final password = TextEditingController();
  final _formKey = GlobalKey<FormState>();
  bool rememberMe = true;

  Widget _buildUsernameField() {
    return TextFormField(
        controller: username,
        decoration: InputDecoration(labelText: 'Your E-Mail'),
        validator: (value) {
          if (value == null ||
              value.isEmpty ||
              !EmailValidator.validate(value)) {
            return 'Invalid E-Mail';
          }
          return null;
        });
  }

  Widget _buildPasswordField() {
    return TextFormField(
        controller: password,
        obscureText: true,
        decoration: InputDecoration(labelText: 'Password'),
        validator: (value) {
          if (value == null || value.isEmpty) {
            return 'Please enter your password';
          }
          return null;
        });
  }

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Container(
        color: Colors.white,
        alignment: Alignment.center,
        child: Container(
          //color: Colors.green,
          width: 600,
          height: 300,
          child: Column(
            children: [
              Form(
                key: _formKey,
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    _buildUsernameField(),
                    _buildPasswordField(),
                  ],
                ),
              ),
              Column(
                children: [
                  CheckboxListTile(title: Text('Remember me') ,
                      controlAffinity: ListTileControlAffinity.leading,
                      value: rememberMe, onChanged: (bool? value) {
                        setState(() {
                          rememberMe = value!;
                        });
                      }),
                  ElevatedButton(
                    onPressed: () {
                      if (_formKey.currentState!.validate()) {
                        print(username.text + "/" + password.text);
                        //didLoggedIn(username.text);
                        widget.didLoggedIn(username.text);
                      }
                    },
                    child: Text('Log me in'),
                  ),
                  Text('Forgot your password ?')
                ],
              )
            ],
          ),
        ),
      ),
    );
  }
}

You need to create a constructor for LoginPage to save the callback on this class, then when you need to use the callback in LoginPageState you use the widget.callbackName() or widget.callbackName.call().

Related