'Flutter - onStepContinue called automatically on build

I'm using Stepper widget to make a form for profile creation. In the onStepContinue method, if its the last step I put the function call for sending data to backend and added the navigation route to home page to its .whenComplete method.

body: Stepper(
        type: StepperType.horizontal,
        currentStep: _activeCurrentStep,
        steps: stepList(),

        onStepContinue: () async {
          final isLastStep = _activeCurrentStep == stepList().length - 1;

          if (isLastStep) {
            final authenticationNotifier =
                Provider.of<AuthenticationNotifier>(context, listen: false);
            var userEmail =
                await authenticationNotifier.fetchUserEmail(context: context);
            var profileName = profileNameController.text;
            var profileBio = profileBioController.text;

            await profileNotifier(false)
                .createProfile(
                    context: context,
                    profileDTO: ProfileDTO(
                      useremail: userEmail,
                      profile_name: profileName,
                      profile_bio: profileBio,
                    ))
                .whenComplete(
                    () => Navigator.of(context).popAndPushNamed(HomeRoute));
          } else if (_activeCurrentStep < (stepList().length - 1)) {
            setState(() {
              _activeCurrentStep += 1;
            });
          }
        },

        onStepCancel: _activeCurrentStep == 0
            ? null
            : () {
                setState(() {
                  _activeCurrentStep -= 1;
                });
              },

        onStepTapped: (int index) {
          setState(() {
            _activeCurrentStep = index;
          });
        },
      ),

The stepper widget is in a page/scaffold of its own. Its loaded from onPressed of a button in authview.dart file.

                  onPressed: () {
                    authenticationNotifier.signup(
                        context: context,
                        useremail: signupEmailController.text,
                        userpassword: signupPasswordController.text);
                    Navigator.of(context)
                        .pushNamed(ProfileCreationRoute);
                  },

The problem is that as soon as I press the sign up button in authview the stepper page shows up for a fraction of a second and loads the homepage without letting me create the profile. I need it to just show the stepper and go to homepage only after I fill the profile details and click submit.

I thought .whenComplete would be called only when the button is pressed and its parent function finishes its work, and in this case I guess the problem is somehow with the stepper widget itself.

I also added && profileNameController.text.isNotEmpty in the if (isLastStep) condition but it doesn't work. Clicking on sign up button is bypassing the stepper widget and going to homeview as soon as stepper finishes building itself.

I don't understand what's going on. Please help.

EDIT

The createProfile function in notifier is

class ProfileNotifier extends ChangeNotifier {
  final ProfileAPI _profileAPI = ProfileAPI();

  Future createProfile({
    required BuildContext context,
    required ProfileDTO profileDTO,
  }) async {
    try {
      await _profileAPI.createProfile(profileDTO);
    } catch (e) {
      print(e.toString());
    }
  }
}

And the API call that sends data to node backend is

class ProfileAPI {
  final client = http.Client();
  final headers = {
    'Content-type': 'application/json',
    'Accept': 'application/json',
    "Access-Control-Allow-Origin": "*",
  };

  // Create new Profile
  Future createProfile(ProfileDTO profileDTO) async {
    final String subUrl = "/profile/create/${profileDTO.useremail}";
    final Uri uri = Uri.parse(APIRoutes.BaseURL + subUrl);
    try {
      final http.Response response = await client.post(uri,
          body: jsonEncode(profileDTO.toJson()), headers: headers);

      final statusCode = response.statusCode;
      final body = response.body;

      if (statusCode == 200) {
        return body;
      }
    } catch (e) {
      print(e.toString());
    }
  }
}

EDIT 2 On changing whenComplete to then the linter shows this error.

The argument type 'Future<Object?> Function()' can't be assigned to the parameter type 'FutureOr<dynamic> Function(dynamic)'. dart(argument_type_not_assignable)

What to do? Please help



Solution 1:[1]

So from what is see is you are using the whencomplete, but the reason that's happening is the when complete will run every time either its and failure or success. So what i think is you should be using the then Method instead on whencomplete which will only run in success condition.

Sources

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

Source: Stack Overflow

Solution Source
Solution 1 Sagar Acharya