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;
},
),
)
],
),
);
}
}
:
,
, .
, 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(" "))
);
}
});
},
),
:
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
Good code everyone)