Сегодня я узнал о виджетах с отслеживанием состояния, базе данных Flutter, асинхронном программировании, поэтому пришло время поделиться своими знаниями с новичками, которые только начали изучать флаттер. Виджет, который мы узнали в предыдущей части, - это виджеты без состояния. Виджеты без сохранения состояния неизменяемы и не сохраняют состояние. Теперь, если вы работаете над проектом, который взаимодействует с пользователями, необходимо что-то изменить. Давайте разберемся с виджетами State и Stateful.
Что такое штат?
Состояние - это информация, которая может считываться синхронно при построении виджета и может быть изменена в течение жизненного цикла виджета. Давайте разберемся с определением.
Вышеупомянутое определение состоит из двух частей, которые приведены ниже.
- Процесс инициализации при создании виджета.
- Изменение состояния во время жизни виджета.
Фактически, виджет не меняется, его состояние будет изменено. Во Flutter классы, наследующие виджеты с отслеживанием состояния, неизменяемы, но само состояние может изменяться.
Как решить, какой виджет использовать?
Когда виджет не нужно изменять в течение жизненного цикла виджета, лучше расширить его с помощью виджета без сохранения состояния. Когда виджет необходимо изменить в любое время в жизненном цикле виджета, в этой ситуации лучше расширить с помощью виджета с отслеживанием состояния.
Реализация виджета без сохранения состояния
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); /* * State less widget * Hello world will not be changed in the life cycle of widget */ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return Center( child: Text( "Hello World", textDirection: TextDirection.ltr, ), ); } }
Реализация с отслеживанием состояния
void main() => runApp(MyApp()); class MyApp extends StatefulWidget { @override State<StatefulWidget> createState() { // TODO: implement createState return _State(); } } class _State extends State<MyApp> { String name = ""; @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( title: "State Full Widget", home: Scaffold( appBar: AppBar( title: Text("State Full Widget"), ), body: Column( children: <Widget>[ TextField( onChanged: (String s) { setState(() { name = s; }); }, ), Text("Hello " + name) ], ), ), ); } }
Создать список задач
Сначала создайте класс модели задачи, который содержит заголовок, описание, дату и приоритет задачи. Во Flutter вы можете создать один безымянный конструктор, если вы хотите перегрузить конструктор, вам нужно установить имя, проверьте многострочные комментарии ниже.
class Todo { // "_" show their access is private int _id; String _title; String _description; int _periority; String _date; /* * Constructor * * Note that you have only one constructor without name if you want * to add more constructor create a name constructor */ Todo(this._title, this._description, this._periority, this._date); /* * Name Constructor */ Todo.withId(this._id, this._title, this._description, this._periority, this._date); //getters String get date => _date; int get periority => _periority; String get description => _description; String get title => _title; int get id => _id; //setters set date(String value) { _date = value; } set periority(int value) { if(value > 0 && value <= 3) _periority = value; } set description(String value) { if(value.length <= 255) _description = value; } set title(String value) { if(value.length <= 255) _title = value; } /* * Convert the todos into map which is used to store in db */ Map<String, dynamic> toMap() { var map = Map<String, dynamic>(); map["title"] = _title; map["description"] = _description; map["periority"] = _periority; map["date"] = _date; if(_id != null) { map["id"] = _id; } return map; } /* * Convert the dynamic into todos */ Todo.fromObject(dynamic o) { this._id = o["id"]; this._title = o["title"]; this._description = o["description"]; this._periority = o["periority"]; this._date = o["date"]; } }
База данных во Flutter
Для взаимодействия с базой данных мы можем использовать sqflite, который является плагином SQLite для флаттера, прежде чем мы сможем его использовать, мы должны предоставить зависимость в файле pubspec.yml.
Мы должны использовать другие зависимости для нашего приложения todo, то есть path_provider, который обычно используется для доступа к физическому местоположению для базы данных, потому что обе операционные системы имеют другое местоположение, поэтому может пользователь path_provider, поэтому нам не нужно беспокоиться о местоположении файловой системы другой операционной системы. Мы также используем intl-зависимость для форматирования даты, которая используется для интернационализации, которая используется в нашем приложении для форматирования даты.
Чтобы добавить зависимости sqflite, path_provider и intl, давайте обновим наш файл pubspec.yml. Мы используем любую версию sqflite и path_provider. Для intl мы используем версию больше или равную 0.15.7.
dependencies: flutter: sdk: flutter sqflite: any path_provider: any intl: ^0.15.7
Создать вспомогательный класс базы данных
Создайте переменную таблицы и столбцов и сделайте этот класс одноэлементным, чтобы его можно было создать один раз и поделиться экземпляром со всем приложением. Цель этого класса - читать и записывать данные в базу данных.
Как сделать синглтон класса в Dart?
- Сначала вам нужно создать частный экземпляр класса.
- Создайте частный конструктор.
- Используйте фабрику, чтобы каждый раз возвращать один и тот же экземпляр.
Создайте частный экземпляр класса и сделайте конструктор частным. В Dart есть одна интересная функция, например Factory Constructor, которая позволяет вам переопределить поведение по умолчанию вместо того, чтобы создавать новый экземпляр, он вернет один и тот же экземпляр каждый раз.
import 'package:sqflite/sqflite.dart'; import 'dart:async'; import 'dart:io'; import 'package:path_provider/path_provider.dart'; import 'package:todos_app/models/todo.dart'; class DbHelper { static final DbHelper _dbHelper = DbHelper._internal(); static final DBVersion = 1; static Database _db; String tblTodo = "todo"; String colId = "id"; String colTitle = "title"; String coldescription = "description"; String colPeriority = "periority"; String colDate = "date"; /* * Named Constructor */ DbHelper._internal(); /* * Factory Constructor * * Use factory to always return the same instance */ factory DbHelper() { return _dbHelper; } /* * Fetch the db instance. if it is null initialize it */ Future<Database> get db async { if(_db == null) { _db = await initializeDB(); } return _db; }
Асинхронное программирование во флаттере
Когда мы запускаем приложение, создается один поток, который является основным потоком, также известным как поток пользовательского интерфейса. Ответственность потока пользовательского интерфейса заключается в том, чтобы нарисовать виджет на экране и ответить на ввод пользователя. Если вы выполняете сетевой вызов или операцию с базой данных в основном потоке, ваш пользовательский интерфейс не будет отвечать, и пользователь будет думать, что ваше приложение работает очень медленно.
Если ваше приложение не отвечает более 5 секунд, операционная система ответит, что ваше приложение не отвечает. Чтобы ваше приложение работало гладко, вы должны использовать отдельный поток для длительной задачи, чтобы ваш основной поток и вторичный поток работали параллельно. Это называется многопоточностью.
Во Flutter вы можете применять многопоточность, используя Future, async и await. давайте по очереди разберемся с Feature, async и await.
Будущее - это объект, который используется для получения ценности когда-нибудь в будущем. Если вы создаете метод, который возвращает future. Если вы вызовете этот метод, вы получите будущий объект немедленно, только когда оператор внутри будет успешно выполнен. Метод «then» будет вызван с результатом.
Другой способ справиться с асинхронным программированием во флаттере - использовать ключевое слово async-await.
Инициализируйте базу данных во вторичном потоке и выберите каталог приложения независимо от операционной системы. и создайте таблицу задач.
/* * initialize db in secondary thread (Worker thread) by using async * which will return Future of type DbHelper. * * await is used for long running operation */ Future<Database> initializeDB() async { Directory directory = await getApplicationDocumentsDirectory(); String path = directory.path + 'todos.db'; var dbTodos = await openDatabase(path, version: DBVersion, onCreate: _onCreate); return dbTodos; } /* * Create table todos */ void _onCreate(Database db, int version) async { await db.execute( "CREATE TABLE $tblTodo($colId INTEGER PRIMARY KEY, $colTitle TEXT, " "$coldescription TEXT, $colPeriority INTEGER, $colDate TEXT)" ); }
Создайте базу данных, которая будет доступна для всего класса, и создайте получатель для объекта базы данных. если он равен нулю, инициализируйте базу данных и в любом случае она вернет базу данных.
static Database _db; Future<Database> get db async { if(_db == null) { _db = await initializeDB(); } return _db; }
Следующая часть:
В следующей части мы напишем метод запроса для вставки данных, извлечения данных, удаления данных и обновления данных для нашего приложения todo с использованием флаттера.
📝 Прочтите этот рассказ позже в Журнале.
🗞 Просыпайтесь каждое воскресенье утром и слышите самые интересные истории, мнения и новости недели, ожидающие в вашем почтовом ящике: Получите заслуживающий внимания информационный бюллетень›