Flutter Basics for Beginners (Part VI)

When you create various forms (for example: registration or login) on Flutter, you don't bother with customizing components, because you can change any form field to suit your style.





In addition to customization, Flutter provides error handling and form field validation capabilities.





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





Well, let's go!





Our plan
  • Part 1  - introduction to development, first appendix, concept 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 simplest;





  • Part 5 - http package. Creation of the Repository class, first requests, listing of posts;





  • Part 6 (current article) - Working with forms, text fields and creating a post.





  • Part 7 - working with pictures, displaying pictures in the form of a grid, receiving pictures from the network, adding your own to the application;





  • Part 8 - creating your own theme, adding custom fonts and animations;





  • Part 9 - a little about testing;





Form creation: adding a post

First, HomePage



let's add a button to our page by which we will add a new post:





@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text("Post List Page"),
    ),
    body: _buildContent(),
    //       FloatingActionButton
    floatingActionButton: FloatingActionButton(
      child: Icon(Icons.add),
      onPressed: () {

      },
    ),
  );
}
      
      



Next, let's 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> {
  
  // TextEditingController'       
  final TextEditingController titleController = TextEditingController();
  final TextEditingController contentController = TextEditingController();
  
  // _formKey    
  final _formKey = GlobalKey<FormState>();
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Post Add Page"),
        actions: [
          //    AppBar
          IconButton(
            icon: Icon(Icons.check),
            onPressed: () {
              //    
              if (_formKey.currentState!.validate()) {
                //     c  
              }
            },
          )
        ],
      ),
      body: Padding(
        padding: EdgeInsets.all(15),
        child: _buildContent(),
      ),
    );
  }
  Widget _buildContent() {
    //  
    return Form(
      key: _formKey,
      //     
      child: Column(
        children: [
          //    
          TextFormField(
            //    ,
            //    (hint)
            decoration: InputDecoration(
                border: OutlineInputBorder(),
                prefixIcon: Icon(Icons.face),
                hintText: ""
            ),
            //    TextEditingController
            controller: titleController,
            //  validator -  ,
            //   null   
            //    
            validator: (value) {
              //      2 
              if (value == null || value.isEmpty) {
                return " ";
              }
              if (value.length < 3) {
                return "     3 ";
              }
              return null;
            },
          ),
          //    
          SizedBox(height: 10),
          // Expanded ,   
          //       
          Expanded(
            child: TextFormField(
              // maxLines: null  expands: true 
              //        
              maxLines: null,
              expands: true,
              textAlignVertical: TextAlignVertical.top,
              decoration: InputDecoration(
                  border: OutlineInputBorder(),
                  hintText: "",
              ),
              //    TextEditingController
              controller: contentController,
              //    
              validator: (value) {
                if (value == null || value.isEmpty) {
                  return " ";
                }
                return null;
              },
            ),
          )
        ],
      ),
    );
  }
}
      
      



Don't forget to add a 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:





! .





. , .





100%- Flutter Dart :





  • Flutter 2.0.6





  • Dart SDK version: 2.12.3





null safety. , .





null safety. :





// !   ,   100% 
//  currentState   null 
_formKey.currentState!.validate()
      
      



null safety Dart , .





POST .





POST

POST, , HTTP .





Post



:





class Post {
  //    private
  //     
  final int? _userId;
  final int? _id;
  final String? _title;
  final String? _body;

  //  getters   
  //      
  int? get userId => _userId;
  int? get id => _id;
  String? get title => _title;
  String? get body => _body;

  //     
  Post(this._userId, this._id, this._title, this._body);

  // toJson()  Post   JSON
  String toJson() {
    return json.encode({
      "title": _title,
      "content": _body
    });
  }

  // Dart      
  //    Post.fromJson(json) -  
  //         
  //  ,  dynamic 
  //    : String, int, double  ..
  Post.fromJson(Map<String, dynamic> json) :
    this._userId = json["userId"],
    this._id = json["id"],
    this._title = json["title"],
    this._body = json["body"];
}

//      
abstract class PostAdd {}

//  
class PostAddSuccess extends PostAdd {}
// 
class PostAddFailure extends PostAdd {}
      
      



Repository



:





//    
Future<PostAdd> addPost(Post post) async {
  final url = Uri.parse("$SERVER/posts");
  //  POST ,   
  //  JSON   
  final response = await http.post(url, body: post.toJson());
  //     
  if (response.statusCode == 201) {
    // ,   
    return PostAddSuccess();
  } else {
    //  
    return PostAddFailure();
  }
}
      
      



PostController



:





//  
//  addPost   callback,
//      
void addPost(Post post, void Function(PostAdd) callback) async {
  try {
    final result = await repo.addPost(post);
    //   
    callback(result);
  } catch (error) {
    //  
    callback(PostAddFailure());
  }
}
      
      



PostAddPage



:





class PostDetailPage extends StatefulWidget {

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

//     StateMVC
class _PostDetailPageState extends StateMVC {

  // _controller   null
  PostController? _controller;

  //  PostController
  _PostDetailPageState() : super(PostController()) {
    _controller = controller as PostController;
  }

  // TextEditingController'       
  final TextEditingController titleController = TextEditingController();
  final TextEditingController contentController = TextEditingController();

  // _formKey    
  final _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Post Add Page"),
        actions: [
          //    AppBar
          IconButton(
            icon: Icon(Icons.check),
            onPressed: () {
              //    
              if (_formKey.currentState!.validate()) {
                //  
                //    TextEditingController'
                final post = Post(
                  -1, -1, titleController.text, contentController.text
                );
                //  
                _controller!.addPost(post, (status) {
                  if (status is PostAddSuccess) {
                    //     
                    //     
                    // 
                    Navigator.pop(context, status);
                  } else {
                    //      
                    // SnackBar -  
                    ScaffoldMessenger.of(context).showSnackBar(
                      SnackBar(content: Text("    "))
                    );
                  }
                });
              }
            },
          )
        ],
      ),
      body: Padding(
        padding: EdgeInsets.all(15),
        child: _buildContent(),
      ),
    );
  }

  Widget _buildContent() {
    //  
    return Form(
      key: _formKey,
      //     
      child: Column(
        children: [
          //    
          TextFormField(
            //    ,
            //    (hint)
            decoration: InputDecoration(
                border: OutlineInputBorder(),
                prefixIcon: Icon(Icons.face),
                hintText: ""
            ),
            //  TextEditingController
            controller: titleController,
            //  validator -  ,
            //   null   
            //    
            validator: (value) {
              //      2 
              if (value == null || value.isEmpty) {
                return " ";
              }
              if (value.length < 3) {
                return "     3 ";
              }
              return null;
            },
          ),
          //    
          SizedBox(height: 10),
          // Expanded ,   
          //       
          Expanded(
            child: TextFormField(
              // maxLines: null  expands: true
              //    
              maxLines: null,
              expands: true,
              textAlignVertical: TextAlignVertical.top,
              decoration: InputDecoration(
                  border: OutlineInputBorder(),
                  hintText: "",
              ),
              //  TextEditingController
              controller: contentController,
              //    
              validator: (value) {
                if (value == null || value.isEmpty) {
                  return " ";
                }
                return null;
              },
            ),
          )
        ],
      ),
    );
  }

}
      
      



:









  1. ,





  2. , .





, PostListPage



:





floatingActionButton: FloatingActionButton(
  child: Icon(Icons.add),
  onPressed: () {
    // then   Future
    //       
    Navigator.push(context, MaterialPageRoute(
      builder: (context) => PostDetailPage()
    )).then((value) {
      if (value is PostAddSuccess) {
        // SnackBar -  
        ScaffoldMessenger.of(context).showSnackBar(
         SnackBar(content: Text("   "))
        );
      }
    });
  },
),
      
      



:





JSONPlaceholder .





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 making a POST request to the server and handling errors.





useful links





  • Source code on Github





  • Build a form with validation (EN)





  • Dart null safety





Good code everyone)








All Articles