Home Development for Android Flutter Basics for Beginners (Part VI)

Flutter Basics for Beginners (Part VI)

by admin

When you create different forms (for example :registration or login) in Flutter, you don’t get bogged down with component customization because you can change any form field to your own style.

In addition to customization, Flutter provides error handling and validation of form fields.

And today we’ll try to deal with this topic with a small example.

Well, let’s go!

Our plan
  • Part 1 – Introduction to development, first application, notion of state;

  • Part 2 – pubspec.yaml file and using flutter on the command line;

  • Part 3 – BottomNavigationBar and Navigator;

  • Part 4 – MVC. We will use this particular pattern as one of the easiest;

  • Part 5 – http package. Creation of Repositoryclass, first requests, post list output;

  • Part 6 (current article) – working with forms, text boxes, and post creation.

  • Part 7 – working with pictures, displaying pictures as a grid, getting pictures from the net, adding your own to the app;

  • Part 8 – Creating your own theme, adding custom fonts and animations;

  • Part 9 – a little bit about testing;

Creating form :adding a post

First, let’s add to our page HomePage button which we will use to add a new post :

@overrideWidget build(BuildContext context) {return Scaffold(appBar:AppBar(title:Text("PostList Page"), ), body:_buildContent(), // in the first part we have already considered FloatingActionButtonfloatingActionButton: FloatingActionButton(child: Icon(Icons.add), onPressed: () {}, ), );}

Next, create a new page in the file post_add_page.dart :

import 'package:flutter/material.dart';class PostDetailPage extends StatefulWidget {@override_PostDetailPageState createState() => _PostDetailPageState();}class _PostDetailPageState extends State<PostDetailPage> {// TextEditingControllers will allow us to get the text from the form fieldsfinal TextEditingController titleController = TextEditingController();final TextEditingController contentController = TextEditingController();// _formKey will be useful to us for validationfinal _formKey = GlobalKey<FormState> ();@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("Post Add Page"), actions: [// menu item in AppBarIconButton(icon: Icon(Icons.check), onPressed: () {// first we run form validationif (_formKey.currentState!.validate()) {// here we will make a request to the server}}, )], ), body: Padding(padding: EdgeInsets.all(15), child: _buildContent(, )), );}Widget _buildContent() {//build formreturn Form(key: _formKey, // we will have two fieldschild: Column(children: [// header fieldsTextFormField(// specify for the field a border, // icon and hintdecoration: InputDecoration(border: OutlineInputBorder(), prefixIcon: Icon(Icons.face), hintText: "Title"), // don't forget to specify TextEditingControllercontroller: titleController, // the validator parameter is a function which// must return null if the check is successful// or a line if it failsvalidator: (value) {// here we add two checks for illustrationif (value == null || value.isEmpty) {return "header empty";}if (value.length < 3) {return "The header must be no shorter than 3 characters";}return null;}, ), // small indent between the fieldsSizedBox(height: 10), // Expanded means that we must// expand our box into the whole available spaceExpanded(child: TextFormField(// maxLines: null and expands: true// specified to expand the field into the entire available spacemaxLines: null, expands: true, textAlignVertical: TextAlignVertical.top, decoration: InputDecoration(border: OutlineInputBorder(), hintText: "Content", ), // do not forget to specify the TextEditingControllercontroller: contentController, // also add a field validatorvalidator: (value) {if (value == null || value.isEmpty) {return "content is empty";}return null;}, ), )], ), );}}

Don’t forget to add the transition to the form page :

floatingActionButton: FloatingActionButton(child: Icon(Icons.add), onPressed: () {Navigator.push(context, MaterialPageRoute(builder: (context) => PostDetailPage()));}, ), 

Launch and click on the button:

Flutter Basics for Beginners (Part VI)

Voila! The form works.

A small note

Beginners can have problems even with ready-made code. And it’s not bullying, it happens.

So try to use similar versions of Flutter and Dart to my :

  • Flutter 2.0.6

  • Dart SDK version: 2.12.3

Also in the comments I pointed out to null safety This is very important, I forgot about it and it’s my fault.

I already added support to the app null safety You may have noticed the exclamation point :

// ! indicates that we are 100% sure// that currentState does not contain a null value_formKey.currentState!.validate()

About null safety and its support in Dart, you could do a whole series of articles, and possibly write a whole book about it.

We won’t take too long and go on to create the POST request.

POST request to add the data to the server

POST, as noted, is one of the HTTP methods and serves to add new data to the server.

First, let’s add a model for our result and change the class a bit Post :

class Post {// all fields are private// this is done to encapsulate datafinal int? _userId;final int? _id;final String? _title;final String? _body;// we create getters for our fields// so that only we can read themint? get userId => _userId;int? get id => _id;String? get title => _title;String? get body => _body;// add a new constructor for the postPost(this._userId, this._id, this._title, this._body);// toJson() turns the Post into a JSON stringString toJson() {return json.encode({"title": _title, "content": _body});}// Dart allows to create constructors with different names// In this case Post.fromJson(json) is a constructor// here we take a post object and get its fields// note that the dynamic variable// can have different types: String, int, double, etc.Post.fromJson(Map<String, dynamic> json) :This._userId = json["userId"], this._id = json["id"], this._title = json["title"], this._body = json["body"];}// we will only have two statesabstract class PostAdd {}// successful addclass PostAddSuccess extends PostAdd {}//errorclass PostAddFailure extends PostAdd {}

Then create a new method in our Repository :

// add a post to the serverFuture<PostAdd> addPost(Post post) async {final url = Uri.parse("$SERVER/posts");// make a POST request with the body// specify the JSON string of the new postfinal response = await http.post(url, body: post.toJson());// if the post was successfully addedif (response.statusCode == 201) {// we say that everything is okayreturn PostAddSuccess();}else {// otherwise errorreturn PostAddFailure();}}

Next, let’s add some code to PostController :

//addPost// the addPost function will take a callback, //through which we will receive the resultvoid addPost(Post post, void Function(PostAdd) callback) async {try {final result = await repo.addPost(post);// the server returned the resultcallback(result);}catch (error) {// an error occurredcallback(PostAddFailure())}}

Well, it’s time to get back to our introduction PostAddPage :

class PostDetailPage extends StatefulWidget {@override_PostDetailPageState createState() => _PostDetailPageState();}// don't forget to change to StateMVCclass _PostDetailPageState extends StateMVC {// _controller can be nullPostController? _controller;// getting PostController _PostDetailPageState() : super(PostController()) { _controller= controller as PostController;}// TextEditingControllers will allow us to get the text from the form fieldsfinal TextEditingController titleController = TextEditingController();final TextEditingController contentController = TextEditingController();// _formKey is needed for form validationfinal _formKey = GlobalKey<FormState> ();@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("Post Add Page"), actions: [// menu item in AppBarIconButton(icon: Icon(Icons.check), onPressed: () {// first we run the validation of the formif (_formKey.currentState!.validate()) {// creating a post// get text via TextEditingControllersfinal post = Post(-1, -1, titleController.text, contentController.text);// add post_controller!.addPost(post, (status) {if (status is PostAddSuccess) {// if all is successful then we return// to the previous page and return//resultNavigator.pop(context, status);} else {// otherwise, we report an error// SnackBar - popup messageScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("An error occurred when adding the post")));}});}}, )], ), body: Padding(padding: EdgeInsets.all(15), child: _buildContent(), ), );}Widget _buildContent() {// buildinga return Form(key: _formKey, // we will have two fieldschild: Column(children: [// header fieldsTextFormField(// specify for the field a border, // icon and hintdecoration: InputDecoration(border: OutlineInputBorder(), prefixIcon: Icon(Icons.face), hintText: "Title"), // specify TextEditingControllercontroller: titleController, // parameter validator is a function which// must return null if the check is successful// and a line if it is not successfulvalidator: (value) {// here we add two checks for illustrationif (value == null || value.isEmpty) {return "header empty";}if (value.length < 3) {return "The header must be no shorter than 3 characters";}return null;}, ), // small indent between the fieldsSizedBox(height: 10), // Expanded means that we must// expand our box into the whole available spaceExpanded(child: TextFormField(// maxLines: null and expands: true// specified to expand the fieldmaxLines: null, expands: true, textAlignVertical: TextAlignVertical.top, decoration: InputDecoration(border: OutlineInputBorder(), hintText: "Content", ), // specify TextEditingControllercontroller: contentController, // also add a field validatorvalidator: (value) {if (value == null || value.isEmpty) {return "content is empty";}return null;}, ), )], ), );}}

The logic of operation is as follows :

  1. we click to add a new post

  2. opens a window with a form, enter the data

if everything is ok, we return to the previous page and report it, otherwise we display an error message.

The final point, let’s add the processing of the result in PostListPage :

floatingActionButton: FloatingActionButton(child: Icon(Icons.add), onPressed: () {// then returns the object Future// which we subscribe to and wait for the resultNavigator.push(context, MaterialPageRoute(builder: (context) => PostDetailPage())).then((value) {If (value is PostAddSuccess) {// SnackBar - popup messageScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("Post was successfully added")));}});}, ), 

Now testing :

Flutter Basics for Beginners (Part VI)

Unfortunately JSONPlaceholder doesn’t actually add the post and so we won’t be able to see it among other posts.

Conclusion

I hope I’ve convinced you that working with forms in Flutter is very easy and requires almost no effort.

Most of the code is creating a POST request to the server and handling errors.

Useful links

Good code, everyone)

You may also like