skip to Main Content

I have the following code that tries to create a multi steps forms :

import 'package:flutter/foundation.dart';

class UserModal extends ChangeNotifier {
  String? name;
  String? email;
  String? password;
  String? education;

  int activeIndex = 0;
  int totalIndex = 2;

  changeStep(int index) {
    activeIndex = index;
    notifyListeners();
  }
}



import 'package:flutter/material.dart';
import 'package:form_field_validator/form_field_validator.dart';
import 'package:im_stepper/stepper.dart';
import 'package:multi_step_form/modal/user_modal.dart';
import 'package:provider/provider.dart';

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

  @override
  _MultiPageProviderState createState() => _MultiPageProviderState();
}

class _MultiPageProviderState extends State<MultiPageProvider> {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<UserModal>(
      create: (context) => UserModal(),
      child: Scaffold(
        appBar: AppBar(
          title: const Text(
            "Using Provider",
          ),
        ),
        body: Consumer<UserModal>(
          builder: (context, modal, child) {
            switch (modal.activeIndex) {
              case 0:
                return const BasicDetails();
              case 1:
                return const EducationDetails();

              default:
                return const BasicDetails();
            }
          },
        ),
      ),
    );
  }
}

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

  @override
  _BasicDetailsState createState() => _BasicDetailsState();
}

class _BasicDetailsState extends State<BasicDetails> {
  GlobalKey<FormState> basicFormKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Consumer<UserModal>(builder: (context, modal, child) {
      return Form(
        key: basicFormKey,
        child: ListView(
          padding: const EdgeInsets.all(
            12.0,
          ),
          children: [
            Center(
              child: DotStepper(
                activeStep: modal.activeIndex,
                dotRadius: 20.0,
                shape: Shape.pipe,
                spacing: 10.0,
              ),
            ),
            Text(
              "Step ${modal.activeIndex + 1} of ${modal.totalIndex}",
              style: const TextStyle(
                fontSize: 20.0,
              ),
              textAlign: TextAlign.center,
            ),
            TextFormField(
              decoration: const InputDecoration(
                labelText: "Name",
              ),
              validator: RequiredValidator(
                errorText: "Required *",
              ),
            ),
            TextFormField(
                decoration: const InputDecoration(
                  labelText: "Email",
                ),
                validator: MultiValidator([
                  RequiredValidator(
                    errorText: "Required *",
                  ),
                  EmailValidator(
                    errorText: "Not Valid Email",
                  ),
                ])),
            TextFormField(
              decoration: const InputDecoration(
                labelText: "Passoword",
              ),
              validator: MinLengthValidator(
                6,
                errorText: "Min 6 characters required",
              ),
            ),
            const SizedBox(
              height: 12.0,
            ),
            SizedBox(
              height: 40.0,
              child: ElevatedButton(
                onPressed: () {
                  if (basicFormKey.currentState?.validate() ?? false) {
                    // next
                    modal.changeStep(1);
                  }
                },
                child: const Text(
                  "Next",
                  style: TextStyle(
                    fontSize: 20.0,
                  ),
                ),
              ),
            ),
          ],
        ),
      );
    });
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Consumer<UserModal>(builder: (context, modal, child) {
      return ListView(
        padding: const EdgeInsets.all(
          12.0,
        ),
        children: [
          Center(
            child: DotStepper(
              activeStep: modal.activeIndex,
              dotRadius: 20.0,
              shape: Shape.pipe,
              spacing: 10.0,
            ),
          ),
          Text(
            "Step ${modal.activeIndex + 1} of ${modal.totalIndex}",
            style: const TextStyle(
              fontSize: 20.0,
            ),
            textAlign: TextAlign.center,
          ),
          TextFormField(
            decoration: const InputDecoration(
              labelText: "Name",
            ),
            validator: RequiredValidator(
              errorText: "Required *",
            ),
          ),
          const SizedBox(
            height: 12.0,
          ),
          SizedBox(
            height: 40.0,
            child: ElevatedButton(
              onPressed: () {},
              child: const Text(
                "Register",
                style: TextStyle(
                  fontSize: 20.0,
                ),
              ),
            ),
          ),
        ],
      );
    });
  }
}

But it can not hold the form values after I click on Next or Back buttons and can not hold the form values. I don’t know what is the problem and howcan I fix it?
It seems maybe I should give a key value to provides inside the consumer but I don’t know how?

2

Answers


  1. I think this is due to the ListView optimization you can either go the wrong way: use shrinkWrap: true on ListView, or the right way: use TextEditingController or initialValue and onChange on TextFormField

    Login or Signup to reply.
  2. I first time misunderstood your question. Your problem is that you are changing the widgets in the consumer. To solve this problem, you need to either use a GlobalKeys for each case in the consumer, for example:

          switch (modal.activeIndex) {
              case 0:
                return const BasicDetails(key: _basicKey);
              case 1:
                return const EducationDetails(key: _educationKey);
    
              default:
                return const BasicDetails(key: _basicKey);
            }
    

    , or store data outside of these widgets(you can use BLoC for example) and initialize them in build methods

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search