#100DaysOfCodeChallenge - Crop Management Information System - Day 6
Tags: 100daysofcode, flutter, dart, programming
Published: 2021-06-29 03:44:40

## Recap

On [Day 5](https://dev.to/curtlycritchlow/100daysofcodechallenge-crop-management-information-system-day-5-jlf) we refactored the Farmer Registration form to improve the readability of our code. On [Day 2](https://dev.to/curtlycritchlow/100daysofcodechallenge-crop-management-information-system-day-2-3fif) we created the `FarmerService()` class and the `AddFarmerCommand()` class in keeping with [gskinner MVC+S architecture](https://blog.gskinner.com/archives/2020/09/flutter-state-management-with-mvcs.html)

## Overview

In this post we will receive user input data via our form and save the form data in cloud firestore.

## Going Deeper

We'll look at how all the various pieces fit together. For brevity, we'll select representative widgets and methods to show how the pieces fit together. NB: These code snippets were reused with appropriate variations to build out the form logics. 

### AddFarmerScreenController

```Dart
class AddFarmerScreen extends StatefulWidget {
  static String routeName = 'AddFarmerScreen';
  const AddFarmerScreen({
    Key? key,
  }) : super(key: key);

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

class _AddFarmerScreenController extends State<AddFarmerScreen> {
  @override
  Widget build(BuildContext context) => _AddFarmerScreenView(this);
  FarmerServiceModel farmer = FarmerServiceModel.form();
  final GlobalKey<FormState> _formkey = GlobalKey<FormState>();
  bool _formChanged = false;
  ...
}
```

This controller is responsible for all the AddFarmerScreen logic.
It contain an instance of the `FarmerServiceModel()` called `farmer`. This class will be sent to cloud Firestore via the `FarmerService` class.

`_formKey` tracks the state of the form. It allows us to validate and save the form data.

`_formChanged` this variable becomes true when the user first interacts with the form.


#### FocusNode
```Dart
class _AddFarmerScreenController extends State<AddFarmerScreen> {
  @override
  Widget build(BuildContext context) => _AddFarmerScreenView(this);
  ...
  late TextEditingController dateOfBirthController;
  late FocusNode nationalIdFocusNode;
  late FocusNode lastNameFocusNode;
  late FocusNode isHeadOfHouseholdFocusNode;
  // FocusNode was created for all form fields. Not shown for 
  // brevity.
  ...
}
```
By creating FocusNode for each form fields, I can programmatically control which form field should should he highlighted as the user interacts with the form. Similarly, I can programmatically control the text of the `DateOfBirthTextField` by the `DateOfBirthController`.


#### initState

```Dart
class _AddFarmerScreenController extends State<AddFarmerScreen> {
  @override
  Widget build(BuildContext context) => _AddFarmerScreenView(this);
    ...
@override
  void initState() {
    super.initState();
    // Initialize the focusNode
    nationalIdFocusNode = FocusNode();
    lastNameFocusNode = FocusNode();
    dateOfBirthFocusNode = FocusNode();
    dateOfBirthController = TextEditingController();
    isHeadOfHouseholdFocusNode = FocusNode();
  }
}
...
  
```
The focusNodes and controllers are initialized when the AddFarmerScreen is added to the widget tree.


#### Dispose
```Dart
class _AddFarmerScreenController extends State<AddFarmerScreen> {
  @override
  Widget build(BuildContext context) => _AddFarmerScreenView(this);
  
...

@override
  void dispose() {
    super.dispose();
    
    nationalIdFocusNode.dispose();
    lastNameFocusNode.dispose();
    dateOfBirthFocusNode.dispose();
    isHeadOfHouseholdFocusNode.dispose();
    dateOfBirthController.dispose();
  }
 ...
  }
```
FocusNodes and controller are destroyed when the AddFarmerScreen is removed from the widget tree.

#### Handle Register Farmer
```Dart
class _AddFarmerScreenController extends State<AddFarmerScreen> {
  @override
  Widget build(BuildContext context) => _AddFarmerScreenView(this);

...
  void _handleRegisterFarmer() async {
    if (_formkey.currentState!.validate()) {
      _formkey.currentState!.save();
      AddFarmerCommand(context)
          .run(farmerServiceModel: farmer, context: context);
    }
 ...
  }
  
  
```
This method is called when the user press the RegisterFarmer button. The form is first validated, if validation is successful the form data is added to the `FarmerServiceModel`. The `FarmerServiceModel` is then passed to the `FarmerService().addFarmer()` method via the `AddFarmerCommand().run()` method. These two methods were discussed in details on Day 2.

The screenshots below show the User input data being submitted via the form followed by the User Input data being stored in cloud firestore. 
![Farmer Form](https://drive.google.com/uc?id=1SrZA9CFlRXbCVXbfEnYcK5ahBzBPe_4K)
![Farmer Form](https://drive.google.com/uc?id=1ASLwWcFRURY-nqCN0gLTVlU2LAxHXjTs)

#### Handle on Will Pop
```Dart
class _AddFarmerScreenController extends State<AddFarmerScreen> {
  @override
  Widget build(BuildContext context) => _AddFarmerScreenView(this);
  
  ...
Future<bool> _handleOnWillPop() async {
    if (_formChanged) {
      return await showDialog<bool>(
          context: context,
          builder: (BuildContext context) {
            return AlertDialog(
              title: Text('Warning'),
              content: Text(
                  'Are you sure you want to abandon the form? Any changes will be lost.'),
              actions: <Widget>[
                TextButton(
                    onPressed: () {
                      Navigator.pop(context, false);
                    },
                    child: Text('Cancel')),
                TextButton(
                    onPressed: () {
                      Navigator.pop(context, true);
                    },
                    child: Text(
                      'Abandon',
                      style: TextStyle(color: Colors.red),
                    )),
              ],
            );
          }).then((value) => value!);
    } else {
      return Future<bool>.value(true);
    }
  }
```
To ensure good user experience, the `_handleOnWillPop()` method will be called if the user partially fill out the form but decided to leave the page before registering the Farmer. When this method is called an `alertDialog()` will be displayed asking to the confirm there intention to leave the page. Screenshot of this dialog is shown below,
![Farmer Form](https://drive.google.com/uc?id=140fhc8JqSNEl0ztiAyFUpujM34qkn42k)


#### Handle dropdown on changed

```Dart
class _AddFarmerScreenController extends State<AddFarmerScreen> {
  @override
  Widget build(BuildContext context) => _AddFarmerScreenView(this);
  
  ...
  void _handleDropdownOnChanged(FocusNode focusNode) {
    focusNode.requestFocus();
  }
``` 
This method assign focus or highlight the Dropdown Form Field the user is currently interacting with. A screenshot of the Marital Status Dropdown field is show below.
![Farmer Form](https://drive.google.com/uc?id=1LhLgF-K1EKJg3oKZ1NGWNIA1DK_e0cao)

####ShowDatePicker
```Dart
class _AddFarmerScreenController extends State<AddFarmerScreen> {
  @override
  Widget build(BuildContext context) => _AddFarmerScreenView(this);
  ...
  late DateTime _date;
  void _showDatePicker() {
    showDatePicker(
      context: context,
      initialDate: DateTime(1960),
      firstDate: DateTime(1900),
      lastDate: DateTime.now(),
    ).then(
      (value) {
        if (value != null) {
          setState(() {
            dateOfBirthController.text = DateFormat.yMMMd().format(value);

            _date = value;
          });
        }
      },
    );
  }
  
```
This methods displays a date picker when the user taps on the `dateOfBirthDropdownFormField()`. Screenshot of the Date picker shown below.
![Date Picker Dialog](https://drive.google.com/uc?id=1NyA-ewiyiYhA2UV8tSmwr9rE0DmhLPCq)

####HandleIsHeadOfHousehold
```Dart
class _AddFarmerScreenController extends State<AddFarmerScreen> {
  @override
  Widget build(BuildContext context) => _AddFarmerScreenView(this);
  ...
  bool _isHeadOfHousehold = false;
  bool? _handleIsHeadOfHouseholdOnChange(bool? value) {
    operationScaleFocusNode.unfocus();
    if (_isHeadOfHousehold == true) {
      setState(() {
        _isHeadOfHousehold = false;
      });
    } else {
      setState(() {
        setState(() {
          _isHeadOfHousehold = true;
        });
      });
    }
  }
```
This method allows the user to toggle between the isHeadOfHousehold checkbox.

## Wrap Up
We used `FocusNode`, `TextEditingController`, `TextFormField`, `showDatePicker`, `Checkbox` and `Form` to accept user input and send the Form Date to cloud firestore.

## Connect with me

Thank you for reading my post. Feel free to subscribe below to join me on the #100DaysOfCodeChallenge or connect with me on [LinkedIn](https://www.linkedin.com/in/curtlycritchlow/) and [Twitter](https://twitter.com/CritchlowCurtly).