Русский
Русский
English
Статистика
Реклама

Основы Flutter для начинающих (Часть VI)

Когда вы создаете различные формы (например: регистрации или входа) на Flutter, вы не заморачиваетесь с кастомизацией компонентов, потому что вы можете изменить любое поле формы под свой стиль.

Помимо кастомизации, Flutter предоставляет возможность обработки ошибок и валидации полей формы.

И сегодня мы постараемся разобраться с этой темой на небольшом примере.

Ну что ж, погнали!

Наш план
  • Часть 1- введение в разработку, первое приложение, понятие состояния;

  • Часть 2- файл pubspec.yaml и использование flutter в командной строке;

  • Часть 3- BottomNavigationBar и Navigator;

  • Часть 4- MVC. Мы будем использовать именно этот паттерн, как один из самых простых;

  • Часть 5 - http пакет. Создание Repository класса, первые запросы, вывод списка постов;

  • Часть 6 (текущая статья) - работа с формами, текстовые поля и создание поста.

  • Часть 7 - работа с картинками, вывод картинок в виде сетки, получение картинок из сети, добавление своих в приложение;

  • Часть 8 - создание своей темы, добавление кастомных шрифтов и анимации;

  • Часть 9 - немного о тестировании;

Создание формы: добавление поста

Для начала добавим на нашу страницу HomePage кнопку по которой мы будем добавлять новый пост:

@overrideWidget build(BuildContext context) {  return Scaffold(    appBar: AppBar(      title: Text("Post List Page"),    ),    body: _buildContent(),    // в первой части мы уже рассматривали FloatingActionButton    floatingActionButton: FloatingActionButton(      child: Icon(Icons.add),      onPressed: () {      },    ),  );}

Далее создадим новую страницу в файле 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;              },            ),          )        ],      ),    );  }}

Не забудьте добавить переход на страницу формы:

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

Запускаем и нажимаем на кнопку:

Вуаля! Форма работает.

Небольшая заметка

У новичков могут возникнуть проблемы даже с готовым кодом. И это не издевательство, такое бывает.

Поэтому для 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();}// не забываем поменять на StateMVCclass _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. открывается окно с формой, вводим данные

  3. если все ок, то возвращаемся на предыдущую страницу и сообщаем об этом иначе выводим сообщение об ошибке.

Заключительный момент, добавим обработку результата в 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 на самом деле не добавляет пост и поэтому мы не сможем его увидеть среди прочих постов.

Заключение

Я надеюсь, что убедил вас в том, что работа с формами на Flutter очень проста и не требует почти никаких усилий.

Большая часть кода - это создание POST запроса на сервер и обработка ошибок.

Полезные ссылки

Всем хорошего кода)

Источник: habr.com
К списку статей
Опубликовано: 05.06.2021 14:06:41
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Разработка под android

Dart

Flutter

Forms

Post

Https

Mobile development

Android development

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru