Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter provider, Right way to use GlobalKey<FormState> in Provider

Tags:

flutter

dart

I'm new at Provider package. and Just making demo app for learning purpose.

Here is my code of simple Form Widget.

1) RegistrationPage (Where my app is start)

class RegistrationPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: Text("Title"),
      ),
      body: MultiProvider(providers: [
        ChangeNotifierProvider<UserProfileProvider>.value(value: UserProfileProvider()),
        ChangeNotifierProvider<RegiFormProvider>.value(value: RegiFormProvider()),
      ], child: AllRegistrationWidgets()),
    );
  }
}

class AllRegistrationWidgets extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        FocusScope.of(context).requestFocus(FocusNode());
      },
      child: Container(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Expanded(
              child: SingleChildScrollView(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.stretch,
                  children: <Widget>[
                    SetProfilePicWidget(),
                    RegistrationForm(),
                  ],
                ),
              ),
            ),
            BottomSaveButtonWidget()
          ],
        ),
      ),
    );
  }
}

class BottomSaveButtonWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final _userPicProvider =
        Provider.of<UserProfileProvider>(context, listen: false);

    final _formProvider =
    Provider.of<RegiFormProvider>(context, listen: false);

    return SafeArea(
      bottom: true,
      child: Container(
          margin: EdgeInsets.all(15),
          child: FloatingActionButton.extended(
            heroTag: 'saveform',
            icon: null,
            label: Text('SUBMIT',
                style: TextStyle(
                  fontSize: 16,
                  fontWeight: FontWeight.bold,
                )),
            onPressed: () {
               print(_userPicProvider.strImageFileName);
              _formProvider.globalFormKey.currentState.validate();

              print(_formProvider.firstName);
              print(_formProvider.lastName);
            },
          )),
    );
  }
}

2) RegistrationForm

class RegistrationForm extends StatefulWidget {
  @override
  _RegistrationFormState createState() => _RegistrationFormState();
}

class _RegistrationFormState extends State<RegistrationForm> {
  TextEditingController _editingControllerFname;
  TextEditingController _editingControllerLname;

  @override
  void initState() {
    _editingControllerFname = TextEditingController();
    _editingControllerLname = TextEditingController();    

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    final formProvider = Provider.of<RegiFormProvider>(context);
    return _setupOtherWidget(formProvider);
  }

  _setupOtherWidget(RegiFormProvider _formProvider) {
    return Container(
      padding: EdgeInsets.all(12),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          SizedBox(height: 20),
          Text(
            'Fields with (*) are required.',
            style: TextStyle(fontStyle: FontStyle.italic),
            textAlign: TextAlign.left,
          ),
          SizedBox(height: 20),
          _formSetup(_formProvider)
        ],
      ),
    );
  }

  _formSetup(RegiFormProvider _formProvider) {
    return Form(
      key: _formProvider.globalFormKey,
      child: Container(
        child: Column(
          children: <Widget>[
            TextFormField(
                controller: _editingControllerFname,
                textCapitalization: TextCapitalization.sentences,
                decoration: InputDecoration(
                  labelText: "First Name *",
                  hintText: "First Name *",
                ),
                onSaved: (value) {},
                validator: (String value) =>
                    _formProvider.validateFirstName(value)),
            SizedBox(height: 15),
            TextFormField(
              controller: _editingControllerLname,
              textCapitalization: TextCapitalization.sentences,
              validator: (String value) =>
                  _formProvider.validateLastName(value),
              onSaved: (value) {},
              decoration: InputDecoration(
                labelText: "Last Name *",
                hintText: "Last Name *",
              ),
            )                           
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _editingControllerFname.dispose();
    _editingControllerLname.dispose();    
    super.dispose();
  }
}

3) RegiFormProvider

class RegiFormProvider with ChangeNotifier {

  final GlobalKey<FormState> globalFormKey = GlobalKey<FormState>();

  String _strFirstName;
  String _strLasttName;

  String get firstName => _strFirstName;
  String get lastName => _strLasttName;

  String validateFirstName(String value) {
    if (value.trim().length == 0)
      return 'Please enter first name';
    else {
      _strFirstName = value;
      return null;
    }
  }

  String validateLastName(String value) {
    if (value.trim().length == 0)
      return 'Please enter last name';
    else {
      _strLasttName = value;
      return null;
    }
  }

}

Here you can see, RegiFormProvider is my first page where other is children widgets in widget tree. I'm using final GlobalKey<FormState> globalFormKey = GlobalKey<FormState>(); in the RegiFormProvider provider, Because I want to access this in the 1st RegistrationPage to check my firstName and lastName is valid or not.

like image 788
Govaadiyo Avatar asked Nov 07 '22 15:11

Govaadiyo


1 Answers

I'm using a builder widget to get form level context like below , and then easily we can get the form instance by using that context. by this way we don't need global key anymore.

Form(
        child: Builder(
          builder: (ctx) {
            return ListView(
              padding: EdgeInsets.all(12),
              children: <Widget>[
                TextFormField(
                  decoration: InputDecoration(labelText: "Title"),
                  textInputAction: TextInputAction.next,
                  onFieldSubmitted: (_) => FocusScope.of(context).nextFocus(),
                  initialValue: formProduct.title,
                  validator: validateTitle,
                  onSaved: (value) {
                    formProduct.title = value;
                  },
                ),
                TextFormField(
                  decoration: InputDecoration(labelText: "Price"),
                  textInputAction: TextInputAction.next,
                  onFieldSubmitted: (_) => FocusScope.of(context).nextFocus(),
                  initialValue: formProduct.price == null
                      ? ""
                      : formProduct.price.toString(),
                  keyboardType: TextInputType.number,
                  validator: validatePrice,
                  onSaved: (value) {
                    formProduct.price = double.parse(value);
                  },
                ),
                TextFormField(
                  decoration: InputDecoration(labelText: "Description"),
                  textInputAction: TextInputAction.next,
                  initialValue: formProduct.description,
                  maxLines: 3,
                  validator: validateDescription,
                  onFieldSubmitted: (_) => FocusScope.of(context).nextFocus(),
                  onSaved: (value) {
                    formProduct.description = value;
                  },
                ),
                TextFormField(
                  decoration: InputDecoration(labelText: "Image Url"),
                  textInputAction: TextInputAction.done,
                  onFieldSubmitted: (_) => FocusScope.of(context).unfocus(),
                  initialValue: formProduct.imageUrl,
                  validator: validateImageUrl,
                  onSaved: (value) {
                    formProduct.imageUrl = value;
                  },
                ),
                Padding(
                  padding: EdgeInsets.all(10),
                  child: FlatButton(
                    color: Colors.amberAccent,
                    onPressed: () {
                      if (Form.of(ctx).validate()) {
                        Form.of(ctx).save();
                        formProduct.id =
                            Random.secure().nextDouble().toString();
                        ProductsProvider provider =
                            Provider.of<ProductsProvider>(context,
                                listen: false);
                        editing
                            ? provider.setProduct(formProduct)
                            : provider.addProduct(formProduct);
                        Router.back(context);
                      }
                    },
                    child: Text("Save"),
                  ),
                )
              ],
            );
          },
        ),
      )

you can see the Form.of(ctx) gives us the current level form.

like image 112
smarteist Avatar answered Dec 07 '22 19:12

smarteist