An Interest In:
Web News this Week
- March 19, 2024
- March 18, 2024
- March 17, 2024
- March 16, 2024
- March 15, 2024
- March 14, 2024
- March 13, 2024
100DaysOfCodeChallenge -Crop Management Information System- Day 3
Recap
On Day 2 I created the FarmerServce()
class that is responsible for making network calls to Firebase Auth Farmer collection and documents. I also created the AddFarmerCommand()
class that is responsible for passing an instance of the FarmerServiceModel()
to FarmerService()
.
Overview
Today I created the Farmer registration form. This form will accept user input than will create an instance of the FarmerServiceModel()
that will be stored in cloud firestore.
Building this form exposed weaknesses in my understanding for Flutter layout and using dart late variable.
Add Farmer Form
Below is a screenshot of the form design followed by the code.
NB: 436 lines of code
class AddFarmerPage extends StatefulWidget { static String routeName = 'AddFarmerPage'; const AddFarmerPage({ Key? key, }) : super(key: key); @override _AddFarmerPageController createState() => _AddFarmerPageController();}class _AddFarmerPageController extends State<AddFarmerPage> { @override Widget build(BuildContext context) => _AddFarmerPageView(this); FarmerServiceModel farmer = FarmerServiceModel.form(); final GlobalKey<FormState> _formkey = GlobalKey<FormState>(); bool _formChanged = false; late FocusNode registrationNumberFocusNode; late FocusNode nationalIdFocusNode; late FocusNode profilePictureFocusNode; late FocusNode firstNameFocusNode; late FocusNode lastNameFocusNode; late FocusNode nicknameFocusNode; late FocusNode dateOfBirthFocusNode; late FocusNode genderFocusNode; late FocusNode ethnicityFocusNode; late FocusNode maritalStatusFocusNode; late FocusNode addressFocusNode; late FocusNode isHeadOfHouseholdFocusNode; late FocusNode isFarmingPrimaryIncomeSourceFocusNode; late FocusNode isActiveFarmerFocusNode; late FocusNode farmerCategoryFocusNode; late FocusNode subsectorFocusNode; late FocusNode operationScaleFocusNode; @override void initState() { super.initState(); registrationNumberFocusNode = FocusNode(); nationalIdFocusNode = FocusNode(); profilePictureFocusNode = FocusNode(); firstNameFocusNode = FocusNode(); lastNameFocusNode = FocusNode(); nicknameFocusNode = FocusNode(); dateOfBirthFocusNode = FocusNode(); genderFocusNode = FocusNode(); ethnicityFocusNode = FocusNode(); maritalStatusFocusNode = FocusNode(); addressFocusNode = FocusNode(); isHeadOfHouseholdFocusNode = FocusNode(); isFarmingPrimaryIncomeSourceFocusNode = FocusNode(); isActiveFarmerFocusNode = FocusNode(); farmerCategoryFocusNode = FocusNode(); subsectorFocusNode = FocusNode(); operationScaleFocusNode = FocusNode(); } @override void dispose() { super.dispose(); registrationNumberFocusNode.dispose(); nationalIdFocusNode.dispose(); profilePictureFocusNode.dispose(); firstNameFocusNode.dispose(); lastNameFocusNode.dispose(); nicknameFocusNode.dispose(); dateOfBirthFocusNode.dispose(); genderFocusNode.dispose(); ethnicityFocusNode.dispose(); maritalStatusFocusNode.dispose(); addressFocusNode.dispose(); isHeadOfHouseholdFocusNode.dispose(); isFarmingPrimaryIncomeSourceFocusNode.dispose(); isActiveFarmerFocusNode.dispose(); farmerCategoryFocusNode.dispose(); subsectorFocusNode.dispose(); operationScaleFocusNode.dispose(); } //FORM LEVEL METHODS void _hanldeOnFormChanged() { if (_formChanged) return; setState(() { _formChanged = true; }); } void _handleRegisterFarmer() async { if (_formkey.currentState!.validate()) { _formkey.currentState!.save(); // todo: create register farmer command } } void _handleDropdownOnChanged(String? value) {} String _date = ''; void _showDatePicker() { showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(1900), lastDate: DateTime.now(), ).then((value) { if (value != null) { setState(() { // farmer.saveDateOfBirth(value); // print(farmer.dateOfBirth); print(value.toString()); _date = value.toString(); // print(_date); }); } }); }}class _AddFarmerPageView extends WidgetView<AddFarmerPage, _AddFarmerPageController> { final state; const _AddFarmerPageView(this.state) : super(state); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Add Farmer'), ), body: Padding( padding: const EdgeInsets.all(24.0), child: Form( key: state._formkey, onChanged: state._hanldeOnFormChanged, child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Padding( padding: const EdgeInsets.only(bottom: 24.0), child: Center( child: Text( 'Farmer Registration Form', style: TextStyles.title.bold, ), ), ), Expanded( child: ListView(children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ // Registration Number Expanded( child: TextFormField( decoration: FormStyles.textFieldDecoration( labelText: 'Registration Number'), focusNode: state.registrationNumberFocusNode, textInputAction: TextInputAction.next, keyboardType: TextInputType.visiblePassword, autovalidateMode: AutovalidateMode.onUserInteraction, validator: state.farmer.validateRegistrationNumber, onSaved: state.farmer.saveRegistrationNumber, ), ), SizedBox( width: 20.0, ), // National ID Number Expanded( child: TextFormField( decoration: FormStyles.textFieldDecoration( labelText: 'National ID Number'), focusNode: state.nationalIdFocusNode, textInputAction: TextInputAction.next, autovalidateMode: AutovalidateMode.onUserInteraction, validator: state.farmer.validateNationalId, onSaved: state.farmer.saveNationalId, ), ) ], ), Row( children: [ Expanded( child: Column( // mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ SizedBox( height: 300, width: 300, child: Container( color: Colors.blue, ), ), ElevatedButton.icon( onPressed: () {}, icon: Icon(Icons.camera_alt_outlined), label: Text('Take Picture')), ], ), ), SizedBox(width: 30.0), Expanded( child: Column(children: [ // FIRST NAME TextFormField( decoration: FormStyles.textFieldDecoration( labelText: 'First Name'), focusNode: state.firstNameFocusNode, textInputAction: TextInputAction.next, autovalidateMode: AutovalidateMode.onUserInteraction, validator: state.farmer.validateFirstName, onSaved: state.farmer.saveFirstName, ), // NICKNAME TextFormField( decoration: FormStyles.textFieldDecoration( labelText: 'Nickname'), focusNode: state.nicknameFocusNode, textInputAction: TextInputAction.next, autovalidateMode: AutovalidateMode.onUserInteraction, validator: state.farmer.validateNickname, onSaved: state.farmer.saveNickname, ), // LAST NAME TextFormField( decoration: FormStyles.textFieldDecoration( labelText: 'Last Name'), focusNode: state.lastNameFocusNode, textInputAction: TextInputAction.next, autovalidateMode: AutovalidateMode.onUserInteraction, validator: state.farmer.validateLastName, onSaved: state.farmer.saveLastName, ), ]), ) ], ), SizedBox( height: 15, ), Row( children: [ Expanded( child: TextFormField( readOnly: true, focusNode: state.dateOfBirthFocusNode, decoration: InputDecoration( border: OutlineInputBorder(), labelText: 'Date of Birth', hintText: state._date, suffixIcon: GestureDetector( child: Icon(Icons.calendar_today_outlined), onTap: state._showDatePicker, )), ), ), SizedBox(width: 30.0), Expanded( child: DropdownButtonFormField( focusNode: state.genderFocusNode, decoration: FormStyles.textFieldDecoration( labelText: 'Gender'), value: Gender.all[1], onChanged: state._handleDropdownOnChanged, onSaved: state.farmer.saveGender, items: Gender.all .map((e) => DropdownMenuItem( child: Text(e), value: e, )) .toList(), ), ), SizedBox(width: 30.0), Expanded( child: DropdownButtonFormField( focusNode: state.ethnicityFocusNode, decoration: FormStyles.textFieldDecoration( labelText: 'Ethnicity'), onChanged: state._handleDropdownOnChanged, onSaved: state.farmer.saveGender, items: Ethnicity.all .map((e) => DropdownMenuItem( child: Text(e), value: e, )) .toList(), ), ), SizedBox(width: 30.0), Expanded( child: DropdownButtonFormField( focusNode: state.maritalStatusFocusNode, decoration: FormStyles.textFieldDecoration( labelText: 'Marital Status'), onChanged: state._handleDropdownOnChanged, onSaved: state.farmer.saveGender, items: MaritalStatus.all .map((e) => DropdownMenuItem( child: Text(e), value: e, )) .toList(), ), ), ], ), TextFormField( focusNode: state.addressFocusNode, decoration: FormStyles.textFieldDecoration(labelText: 'Address'), textInputAction: TextInputAction.next, autovalidateMode: AutovalidateMode.onUserInteraction, validator: state.farmer.validateAddress, onSaved: state.farmer.saveAddress, maxLines: 5, minLines: 2, ), Row( children: [ // FARMER CATEGORY Expanded( child: DropdownButtonFormField( focusNode: state.farmerCategoryFocusNode, decoration: FormStyles.textFieldDecoration( labelText: 'Farmer Category'), onChanged: state._handleDropdownOnChanged, onSaved: state.farmer.saveFarmerCategory, items: FarmerCategory.all .map((e) => DropdownMenuItem( child: Text(e), value: e, )) .toList(), ), ), SizedBox(width: 30.0), // SUBSECTOR Expanded( child: DropdownButtonFormField( focusNode: state.subsectorFocusNode, decoration: FormStyles.textFieldDecoration( labelText: 'Subsector'), onChanged: state._handleDropdownOnChanged, onSaved: state.farmer.saveSubsector, items: Subsector.all .map((e) => DropdownMenuItem( child: Text(e), value: e, )) .toList(), ), ), SizedBox(width: 30.0), // OPERATION SCALE Expanded( child: DropdownButtonFormField( focusNode: state.operationScaleFocusNode, decoration: FormStyles.textFieldDecoration( labelText: 'Operation Scale'), onChanged: state._handleDropdownOnChanged, onSaved: state.farmer.saveOperationScale, items: OperationScale.all .map((e) => DropdownMenuItem( child: Text(e), value: e, )) .toList(), ), ), ], ), SizedBox( height: 15, ), Row( children: [ Expanded( child: FormField(builder: (builder) { return CheckboxListTile( title: Text('Head of Household'), value: false, onChanged: (bool) {}); }), ), Expanded( child: FormField(builder: (builder) { return CheckboxListTile( title: Text('Farming Primary Income Source'), value: false, onChanged: (bool) {}); }), ), Expanded( child: FormField(builder: (builder) { return CheckboxListTile( title: Text('Active Farmer'), value: true, onChanged: (bool) {}); }), ), ], ), SizedBox( height: 15, ), ElevatedButton( onPressed: () {}, child: Text('Register Farmer')) ]), ) ], )), ), ); }}
This code need to be refactored to improve readability and maintainability. I will refactor this code in a future post.
Building this form made me realize the need to better understand how Layout and constraints in flutter works. We will build our capacity in this area in a future post as well.
Wrap up
On Day 3, I built the Farmer Form but more importantly I realized the need to improve my knowledge of flutter layout and constraints.
Coming to flutter from a Django background made we realize the need of a dart package that automate the creation of forms from dart Models. This is a beautiful problem that I hope to solve in the future.
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 and Twitter.
Original Link: https://dev.to/curtlycritchlow/100daysofcodechallenge-crop-management-information-system-day-3-5b45
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To