<?xml version="1.0" encoding="utf-8" ?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:tt="http://teletype.in/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"><title>IT KPI Dart &amp; Flutter</title><subtitle>🎯 Dart &amp; Flutter at IT KPI
Chat: https://t.me/itkpi_dart

More related channels and chats: https://t.me/itkpi_list</subtitle><author><name>IT KPI Dart &amp; Flutter</name></author><id>https://teletype.in/atom/itkpi_dart</id><link rel="self" type="application/atom+xml" href="https://teletype.in/atom/itkpi_dart?offset=0"></link><link rel="alternate" type="text/html" href="https://teletype.in/@itkpi_dart?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=itkpi_dart"></link><link rel="next" type="application/rss+xml" href="https://teletype.in/atom/itkpi_dart?offset=10"></link><link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></link><updated>2026-04-20T02:43:59.481Z</updated><entry><id>itkpi_dart:VS-Code-server-for-Android-Dev</id><link rel="alternate" type="text/html" href="https://teletype.in/@itkpi_dart/VS-Code-server-for-Android-Dev?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=itkpi_dart"></link><title>Розробка під Android на Flutter з VS Code на сервері</title><published>2022-08-19T07:08:19.039Z</published><updated>2022-08-19T07:18:07.634Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img3.teletype.in/files/a4/c4/a4c4de08-e38a-4833-9fb5-10cc28ac622b.png"></media:thumbnail><category term="flutter" label="Flutter"></category><summary type="html">&lt;img src=&quot;https://img1.teletype.in/files/06/30/06303c23-0636-45a6-9649-7ed119abd323.png&quot;&gt;Гнучкість VS Code дозволяє займатися розробкою прямо у браузері. Прикладами є vscode.dev, що працює виключно на фронтенді, та github.dev з послугою GitHub Codespaces, що використовує віддалений сервер як потужний бекенд для аналізу коду, тоді як фронтенд є лише відображенням результатів обчислень на сервері.</summary><content type="html">
  &lt;figure id=&quot;4E0p&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/06/30/06303c23-0636-45a6-9649-7ed119abd323.png&quot; width=&quot;1200&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://stackoverflow.com/a/73412266/9004442&quot; target=&quot;_blank&quot;&gt;Read in English&lt;/a&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;rGBm&quot;&gt;Гнучкість VS Code дозволяє займатися розробкою прямо у браузері. Прикладами є vscode.dev, що працює виключно на фронтенді, та github.dev з послугою GitHub Codespaces, що використовує віддалений сервер як потужний бекенд для аналізу коду, тоді як фронтенд є лише відображенням результатів обчислень на сервері.&lt;/p&gt;
  &lt;p id=&quot;4K2c&quot;&gt;Безкоштовною альтернативою GitHub Codespaces є self-hosted проєкт &lt;code&gt;code-server&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;mzmw&quot;&gt;Давайте розберемося як завдяки &lt;code&gt;code-server&lt;/code&gt; та SSH налаштувати середовище для &lt;strong&gt;Android розробки на Flutter&lt;/strong&gt; так, щоб потужний віддалений сервер виконував усі обчислення, а локальна малопотужна машина відображала GUI та підключала фізичний Android пристрій.&lt;/p&gt;
  &lt;p id=&quot;QhM6&quot;&gt;Перевірено на Ubuntu 22.04 (сервер), Fedora 36 (локальна машина) та Flutter 3.0.5.&lt;/p&gt;
  &lt;h2 id=&quot;%D0%B2%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%BD%D1%8F-%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE-%D0%B7%D0%B0%D0%B1%D0%B5%D0%B7%D0%BF%D0%B5%D1%87%D0%B5%D0%BD%D0%BD%D1%8F-%D1%82%D0%B0-sdk-%D0%BD%D0%B0-%D1%81%D0%B5%D1%80%D0%B2%D0%B5%D1%80%D1%96&quot;&gt;Встановлення програмного забезпечення та SDK на сервері&lt;/h2&gt;
  &lt;h3 id=&quot;1-%D0%B2%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D1%96%D1%82%D1%8C-code-server-%D0%B7-%D0%BE%D1%84%D1%96%D1%86%D1%96%D0%B9%D0%BD%D0%BE%D0%B3%D0%BE-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D1%96%D1%8E&quot;&gt;1. Встановіть &lt;code&gt;code-server&lt;/code&gt; з &lt;a href=&quot;https://github.com/coder/code-server&quot; target=&quot;_blank&quot;&gt;офіційного репозиторію&lt;/a&gt;&lt;/h3&gt;
  &lt;blockquote id=&quot;ZDmR&quot;&gt;&lt;strong&gt;Важливо&lt;/strong&gt;: Після встановлення &lt;code&gt;code-server&lt;/code&gt; просить налаштувати автозапуск серверу через systemd шляхом виконання команди &lt;code&gt;systemctl&lt;/code&gt;. Не дотримуйтесь цих вказівок, інакше VS Code не знаходитиме пристрої через ADB. Способу примусити ADB працювати сумісно з systemd поки не знайшлось.&lt;/blockquote&gt;
  &lt;h3 id=&quot;2-%D0%B2%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D1%96%D1%82%D1%8C-flutter-sdk-%D1%82%D0%B0-%D0%BE%D0%BD%D0%BE%D0%B2%D1%96%D1%82%D1%8C-path&quot;&gt;2. Встановіть Flutter SDK та оновіть &lt;code&gt;PATH&lt;/code&gt;&lt;/h3&gt;
  &lt;p id=&quot;GA8c&quot;&gt;Вашій системі також можуть знадобитися додаткові залежності для запуску Flutter SDK. Рекомендовано дотримуватися &lt;a href=&quot;https://docs.flutter.dev/get-started/install/linux&quot; target=&quot;_blank&quot;&gt;офіційного посібника&lt;/a&gt;. Надавайте перевагу описаним там ручним способам встановлення.&lt;/p&gt;
  &lt;p id=&quot;NlnD&quot;&gt;Після встановлення оновіть змінну &lt;code&gt;PATH&lt;/code&gt; у &lt;code&gt;~/.bashrc&lt;/code&gt;, щоб включити папку &lt;code&gt;/bin&lt;/code&gt; з Flutter SDK, наприклад, додайте такий рядок:&lt;/p&gt;
  &lt;pre id=&quot;EH1q&quot; data-lang=&quot;bash&quot;&gt;export PATH=&amp;quot;$PATH:$HOME/path/to/flutter/bin&amp;quot;&lt;/pre&gt;
  &lt;p id=&quot;vS8U&quot;&gt;після чого, застосуйте зміни:&lt;/p&gt;
  &lt;pre id=&quot;AyBF&quot; data-lang=&quot;bash&quot;&gt;source ~/.bashrc&lt;/pre&gt;
  &lt;h3 id=&quot;3-%D0%B2%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%BD%D1%8F-%D1%96%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%D1%80%D1%96%D1%8E-android&quot;&gt;3. Встановлення інструментарію Android&lt;/h3&gt;
  &lt;p id=&quot;miSq&quot;&gt;Припустимо, на вашому сервері немає графічного середовища, тому ми встановимо інструментарій Android без Android Studio (оскільки для роботи Studio потрібен DE).&lt;/p&gt;
  &lt;h4 id=&quot;%D1%81%D0%BA%D0%B0%D1%87%D1%83%D0%B2%D0%B0%D0%BD%D0%BD%D1%8F-cmdline-tools&quot;&gt;Скачування cmdline-tools&lt;/h4&gt;
  &lt;p id=&quot;f72F&quot;&gt;Перейдіть на &lt;a href=&quot;https://developer.android.com/studio#command-tools&quot; target=&quot;_blank&quot;&gt;сайт Android Studio&lt;/a&gt; та завантажте пакет &amp;quot;Command line tools only&amp;quot;. Розпакуйте архів командою &lt;code&gt;unzip&lt;/code&gt; за бажаним шляхом. Краще зробити структуру папок наступною:&lt;/p&gt;
  &lt;pre id=&quot;aANf&quot; data-lang=&quot;bash&quot;&gt;~/path/to/android-sdk/cmdline-tools&lt;/pre&gt;
  &lt;p id=&quot;8ZO6&quot;&gt;Таким чином, коли &lt;code&gt;sdkmanager&lt;/code&gt; завантажуватиме свої пакети, нові папки будуть створені всередині папки &lt;code&gt;android-sdk&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;XT7w&quot;&gt;Станом на серпень 2022 року для &lt;code&gt;sdkmanager&lt;/code&gt; в інструментах командного рядка Android потрібна спеціальна ієрархія папок, яку можна досягти, помістивши вміст папки &lt;code&gt;cmdline-tools&lt;/code&gt; в папку &lt;code&gt;latest&lt;/code&gt; під нею за допомогою цієї команди:&lt;/p&gt;
  &lt;pre id=&quot;VOHs&quot; data-lang=&quot;bash&quot;&gt;mv ./cmdline-tools/ ./latest &amp;amp;&amp;amp; mkdir cmdline-tools &amp;amp;&amp;amp; mv ./latest/ ./cmdline-tools/latest/&lt;/pre&gt;
  &lt;p id=&quot;loQx&quot;&gt;Додайте інструменти до свого &lt;code&gt;PATH&lt;/code&gt; у &lt;code&gt;.bashrc&lt;/code&gt; і вкажіть &lt;code&gt;ANDROID_SDK_ROOT&lt;/code&gt;, додавши нові рядки:&lt;/p&gt;
  &lt;pre id=&quot;qlGR&quot; data-lang=&quot;bash&quot;&gt;export ANDROID_SDK_ROOT=&amp;quot;$HOME/path/to/android-sdk&amp;quot;
export PATH=&amp;quot;$PATH:$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$ANDROID_SDK_ROOT/platform-tools&amp;quot;&lt;/pre&gt;
  &lt;p id=&quot;sAqj&quot;&gt;Не забудьте запустити &lt;code&gt;source ~/.bashrc&lt;/code&gt;&lt;/p&gt;
  &lt;h4 id=&quot;%D0%B2%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D1%96%D1%82%D1%8C-sdk&quot;&gt;Встановіть SDK&lt;/h4&gt;
  &lt;p id=&quot;4CvR&quot;&gt;Flutter SDK вимагає встановлення двох пакетів: &lt;code&gt;build-tools&lt;/code&gt; і &lt;code&gt;platform-tools&lt;/code&gt;.&lt;/p&gt;
  &lt;pre id=&quot;BE8Z&quot; data-lang=&quot;bash&quot;&gt;sdkmanager &amp;quot;build-tools;33.0.0&amp;quot; &amp;quot;platform-tools&amp;quot;&lt;/pre&gt;
  &lt;p id=&quot;3mar&quot;&gt;&lt;code&gt;33.0.0&lt;/code&gt; — це остання версія &lt;code&gt;build-tools&lt;/code&gt; станом на серпень 2022 року. Дізнайтеся, яка остання версія &lt;code&gt;build-tools&lt;/code&gt; доступна, запустивши:&lt;/p&gt;
  &lt;pre id=&quot;usTA&quot; data-lang=&quot;bash&quot;&gt;sdkmanager --list | grep build-tools&lt;/pre&gt;
  &lt;h4 id=&quot;%D0%BF%D0%BE%D0%B3%D0%BE%D0%B4%D1%8C%D1%82%D0%B5%D1%81%D1%8C-%D0%B7-%D0%BB%D1%96%D1%86%D0%B5%D0%BD%D0%B7%D1%96%D1%8F%D0%BC%D0%B8&quot;&gt;Погодьтесь з ліцензіями&lt;/h4&gt;
  &lt;p id=&quot;Gx0u&quot;&gt;Запустіть &lt;code&gt;sdkmanager --licenses&lt;/code&gt; і прийміть усі ліцензії, натискаючи клавішу &lt;code&gt;y&lt;/code&gt;&lt;/p&gt;
  &lt;h2 id=&quot;%D0%BD%D0%B0%D0%BB%D0%B0%D1%88%D1%82%D1%83%D0%B2%D0%B0%D0%BD%D0%BD%D1%8F-vs-code&quot;&gt;Налаштування VS Code&lt;/h2&gt;
  &lt;p id=&quot;Zcvw&quot;&gt;Після встановлення &lt;code&gt;code-server&lt;/code&gt; ви можете отримати доступ до редактора зі свого браузера.&lt;/p&gt;
  &lt;p id=&quot;JKKa&quot;&gt;Також рекомендується встановити його як PWA, щоб у вас було більше місця на екрані, більше можливостей для комбінацій клавіш і можливість запускати редактор із системної панелі запуску програм.&lt;/p&gt;
  &lt;ol id=&quot;QxJw&quot;&gt;
    &lt;li id=&quot;rD8p&quot;&gt;Встановіть розширення Flutter&lt;/li&gt;
    &lt;li id=&quot;WPHL&quot;&gt;Додайте ці опції до користувацьких налаштувань VS Code:&lt;/li&gt;
  &lt;/ol&gt;
  &lt;pre id=&quot;gNWF&quot; data-lang=&quot;javascript&quot;&gt;{
    &amp;quot;dart.flutterRunAdditionalArgs&amp;quot;: [
        // Dart Developer Service port (debugger)
        &amp;quot;--dds-port=10388&amp;quot;,
        // Dart VM Service instance port (device)
        &amp;quot;--host-vmservice-port=10389&amp;quot;
    ],
    &amp;quot;dart.devToolsPort&amp;quot;: 9100,
    &amp;quot;dart.devToolsLocation&amp;quot;: &amp;quot;external&amp;quot;
}&lt;/pre&gt;
  &lt;p id=&quot;GZVU&quot;&gt;За замовчуванням Dart вибирає випадкові порти для підключення між налагоджувачем і пристроєм. Встановлюючи ці параметри, ми робимо порти статичними, тому ми можемо їх легко переадресовувати між пристроями.&lt;/p&gt;
  &lt;p id=&quot;6Mz1&quot;&gt;Можливо, ви захочете налаштувати &lt;code&gt;dart.devToolsLocation: external&lt;/code&gt; через помилку &lt;code&gt;code-server&lt;/code&gt;, яка не дозволяє завантажувати iframe Dart DevTools. Цей параметр запускає DevTools у вашому браузері за умовчанням. Причиною проблеми може бути &lt;a href=&quot;https://github.com/coder/code-server/issues/1936&quot; target=&quot;_blank&quot;&gt;цей issue&lt;/a&gt;.&lt;/p&gt;
  &lt;h2 id=&quot;%D0%BF%D0%B5%D1%80%D0%B5%D0%B0%D0%B4%D1%80%D0%B5%D1%81%D0%B0%D1%86%D1%96%D1%8F-%D0%BF%D0%BE%D1%80%D1%82%D1%96%D0%B2-%D0%B2%D0%B8%D0%BA%D0%BE%D1%80%D0%B8%D1%81%D1%82%D0%BE%D0%B2%D1%83%D1%8E%D1%87%D0%B8-ssh-%D0%BD%D0%B0-%D0%BB%D0%BE%D0%BA%D0%B0%D0%BB%D1%8C%D0%BD%D1%96%D0%B9-%D0%BC%D0%B0%D1%88%D0%B8%D0%BD%D1%96&quot;&gt;Переадресація портів використовуючи SSH на локальній машині&lt;/h2&gt;
  &lt;p id=&quot;Ss5N&quot;&gt;Для налагодження програми Android за допомогою Flutter нам доведеться перенаправити 4 порти. У таблиці наведено номери портів відповідно до параметрів VS Code вище. Ви можете використовувати власні номери портів, але тоді вам доведеться відповідно змінити конфігурації.&lt;/p&gt;
  &lt;figure id=&quot;xiwo&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/9a/1b/9a1b16b3-a568-49f5-83c1-38c44632b357.png&quot; width=&quot;671&quot; /&gt;
  &lt;/figure&gt;
  &lt;h3 id=&quot;%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B8&quot;&gt;Команди&lt;/h3&gt;
  &lt;p id=&quot;C7Sq&quot;&gt;SSH можна використовувати для перенаправлення портів.&lt;/p&gt;
  &lt;p id=&quot;cCZB&quot;&gt;Щоб перенаправити порт з локального хоста на віддалений хост, запустіть на локальному комп’ютері:&lt;/p&gt;
  &lt;pre id=&quot;gxRR&quot; data-lang=&quot;bash&quot;&gt;ssh -R XXXX:localhost:XXXX user@host&lt;/pre&gt;
  &lt;p id=&quot;xcN4&quot;&gt;Щоб перенаправити порт із віддаленого хоста на локальний, запустіть на локальному комп’ютері:&lt;/p&gt;
  &lt;pre id=&quot;3UrC&quot; data-lang=&quot;bash&quot;&gt;ssh -L XXXX:localhost:XXXX user@host&lt;/pre&gt;
  &lt;p id=&quot;7wcp&quot;&gt;Ви можете з’єднати параметри команди &lt;code&gt;ssh&lt;/code&gt;, наприклад:&lt;/p&gt;
  &lt;pre id=&quot;byVQ&quot; data-lang=&quot;bash&quot;&gt;ssh -R 5037:localhost:5037 -L 10388:localhost:10388 -R 10389:localhost:10389 -L 9100:localhost:9100 user@host&lt;/pre&gt;
  &lt;p id=&quot;Y0iK&quot;&gt;Перенаправлення портів буде активним, доки ви не закриєте з’єднання SSH.&lt;/p&gt;
  &lt;p id=&quot;uHvm&quot;&gt;Переконайтеся, що ваш брандмауер налаштовано на переадресацію портів.&lt;/p&gt;
  &lt;h2 id=&quot;%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82-%D0%B4%D0%BB%D1%8F-%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D1%96%D1%97&quot;&gt;Скрипт для автоматизації&lt;/h2&gt;
  &lt;p id=&quot;PfOi&quot;&gt;Сценарій, який автоматизує процес:&lt;/p&gt;
  &lt;ol id=&quot;UuDY&quot;&gt;
    &lt;li id=&quot;IKkB&quot;&gt;Вимикає локальний ADB і перезапускає його, щоб звільнити використані порти&lt;/li&gt;
    &lt;li id=&quot;yXkx&quot;&gt;Налаштовує переадресацію портів для вказаних портів на віддалений хост. Сценарій передбачає використання ключів для автентифікації SSH.&lt;/li&gt;
    &lt;li id=&quot;1Whz&quot;&gt;Знищує всі запущені екземпляри &lt;code&gt;code-server&lt;/code&gt;, &lt;code&gt;node&lt;/code&gt; і &lt;code&gt;adb&lt;/code&gt;. Якщо вам це не підходить, відредагуйте скрипт.&lt;/li&gt;
    &lt;li id=&quot;ZmSx&quot;&gt;Запускає ADB і запускає &lt;code&gt;code-server&lt;/code&gt;.&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;Hf1K&quot;&gt;Призначений для запуску на локальній машині.&lt;/p&gt;
  &lt;pre id=&quot;zrJj&quot; data-lang=&quot;bash&quot;&gt;#!/bin/bash

# Remote machine
CE_MACHINE=&amp;quot;user@host&amp;quot;

# Local machine, no need to change
CE_LOCALHOST=&amp;quot;localhost&amp;quot;

# Default port for ADB daemon is 5037
CE_ADB_PORT=&amp;quot;5037&amp;quot;

# You might need to specify exact path to adb on your
# remote system since .bashrc is not sourced for
# non-interactive sessions (see https://stackoverflow.com/a/6212684)
CE_ADB_EXECUTABLE=&amp;quot;~/dev/tools/android-sdk/platform-tools/adb&amp;quot;

# &amp;quot;Dart Developer Service port
CE_DDS_PORT=&amp;quot;10388&amp;quot;

# Dart VM Service instance port
CE_HOST_VMSERVICE_PORT=&amp;quot;10389&amp;quot;

# Flutter DevTools port
CE_DEVTOOLS_PORT=&amp;quot;9100&amp;quot;

#### VSCode Settings ####
# &amp;quot;dart.flutterRunAdditionalArgs&amp;quot;: [
# &amp;quot;--dds-port=10388&amp;quot;,
# &amp;quot;--host-vmservice-port=10389&amp;quot;,
# ],
# &amp;quot;dart.devToolsPort&amp;quot;: 9100,
# &amp;quot;dart.devToolsLocation&amp;quot;: &amp;quot;external&amp;quot;,
#### VSCode Settings ####


# Reset ADB on local machine
# so it releases used ports
killall adb
adb devices

# When &amp;#x60;adb devices&amp;#x60; is called, ADB checks the daemon port.
# If it detects there&amp;#x27;s no response on the port, it
# launches a new daemon.
#
# After killing ADB and forwarding the ADB port to local machine,
# a newly launched ADB client will bind to the existing connection
# (which is our physical device) instead of launching a daemon on
# the remote machine.
#
# Restart code-server
# ADB doesn&amp;#x27;t detect devices if code-server is managed by systemctl
# WARNING, killing all Node processes here. Customize if needed.
#
# 1. ADB forwarding Local -&amp;gt; Remote
# 2. Dart Dev Server forwarding Remote -&amp;gt; Local
# 3. Dart on-device debugger client forwarding Local -&amp;gt; Remote
# 4. Flutter DevTools Remote -&amp;gt; Local forwarding
ssh -R $CE_ADB_PORT:$CE_LOCALHOST:$CE_ADB_PORT \
-L $CE_DDS_PORT:$CE_LOCALHOST:$CE_DDS_PORT \
-R $CE_HOST_VMSERVICE_PORT:$CE_LOCALHOST:$CE_HOST_VMSERVICE_PORT \
-L $CE_DEVTOOLS_PORT:$CE_LOCALHOST:$CE_DEVTOOLS_PORT \
$CE_MACHINE &amp;quot;killall code-server; killall node; killall adb; $CE_ADB_EXECUTABLE devices; code-server&amp;quot;&lt;/pre&gt;

</content></entry><entry><id>itkpi_dart:Future-Completer</id><link rel="alternate" type="text/html" href="https://teletype.in/@itkpi_dart/Future-Completer?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=itkpi_dart"></link><title>Completer: створюємо власні Future</title><published>2021-05-05T13:21:36.952Z</published><updated>2021-05-06T09:06:13.131Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://teletype.in/files/da/8e/da8e977f-b642-4257-89fb-f51e537766d2.png"></media:thumbnail><category term="dart" label="Dart"></category><summary type="html">&lt;img src=&quot;https://teletype.in/files/78/77/7877ea4d-c3df-4a5e-8b45-a5fea6f2c7c4.png&quot;&gt;Future є базовим поняттям для Dart. Детальний розбір асинхронного програмування ми вже робили у перекладі англомовної статті з dart.dev.</summary><content type="html">
  &lt;p&gt;Future є базовим поняттям для Dart. Детальний розбір асинхронного програмування ми вже робили &lt;a href=&quot;https://teletype.in/@itkpi_dart/Asynchronous-programming&quot; target=&quot;_blank&quot;&gt;у перекладі англомовної статті&lt;/a&gt; з dart.dev.&lt;/p&gt;
  &lt;p&gt;Та іноді, під час написання коду, інтерфейсу Future починає не вистачати. Таке трапляється частіше за все під час написання власних бібліотек та контролерів.&lt;/p&gt;
  &lt;blockquote&gt;Також ми переклали цю статтю &lt;a href=&quot;https://teletype.in/@itkpi_dart_en/Future-Completer&quot; target=&quot;_blank&quot;&gt;англійською&lt;/a&gt;&lt;/blockquote&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/78/77/7877ea4d-c3df-4a5e-8b45-a5fea6f2c7c4.png&quot; width=&quot;1200&quot; /&gt;
  &lt;/figure&gt;
  &lt;h2&gt;Як це, не вистачає?&lt;/h2&gt;
  &lt;p&gt;Розгляньмо наявний інтерфейс класу Future:&lt;/p&gt;
  &lt;pre data-lang=&quot;dart&quot;&gt;Future(FutureOr&amp;lt;T&amp;gt; computation())
/* Виклик функції computation() асинхронно,
використовуючи Timer.run */
Future.delayed(Duration duration, [FutureOr&amp;lt;T&amp;gt; computation()])/* Аналогічно Future(), але виконання відбувається
після затримки */
Future.error(Object error, [StackTrace? stackTrace])// Створення Future, що завершується помилкою
Future.microtask(FutureOr&amp;lt;T&amp;gt; computation())
// Аналогічно Future(), але використовується scheduleMicrotask
Future.sync(FutureOr&amp;lt;T&amp;gt; computation())
// Аналогічно Future(), але функція викликається негайно (синхронно)
Future.value([FutureOr&amp;lt;T&amp;gt;? value])
// Створення Future з уже вказаним результатом
&lt;/pre&gt;
  &lt;p&gt;Як бачимо, наданий інтерфейс дає лише можливість продукувати Future як результати інших обчислень, але не дає ніяких інструментів, щоб повернути індикацію обчислень, що ще тривають.&lt;/p&gt;
  &lt;p&gt;Наданий інтерфейс корисно використовувати хіба що у синхронному коді, якщо, наприклад, ми імплементуємо який-небудь інтерфейс, що вимагає повернення Future у методі, у той час як наша реалізація є синхронною:&lt;/p&gt;
  &lt;pre data-lang=&quot;dart&quot;&gt;import &amp;#x27;dart:io&amp;#x27;;

abstract class FileReader {
  Future&amp;lt;String&amp;gt; readFile(String name);
}

class SyncFileReader implements FileReader {
  @override
  Future&amp;lt;String&amp;gt; readFile(String name) {
    return Future(() {
      return File(name).readAsStringSync();
    });
  }
}&lt;/pre&gt;
  &lt;p&gt;Тепер уявімо ситуацію, що ми створюємо контролер для бази даних. Для того, щоб почати працювати з базою даних, до неї треба підключитися. До того ж, необхідно зберегти підключення до БД у контролері — адже створення багатьох паралельних підключень є неефективним.&lt;/p&gt;
  &lt;p&gt;Спробуймо написати такий код з тими знаннями, що у нас є:&lt;/p&gt;
  &lt;pre data-lang=&quot;dart&quot;&gt;import &amp;#x27;package:sqflite/sqflite.dart&amp;#x27;;

class DbConnection {
  Database? _database;

  Future&amp;lt;Database&amp;gt; use() async {
    _database ??= await openDatabase(
      &amp;#x27;my.db&amp;#x27;,
      onCreate: (db, version) async {
        ...
      },
      version: 1,
    );

    return _database!;
  }
}&lt;/pre&gt;
  &lt;p&gt;Даний клас має метод &lt;code&gt;use()&lt;/code&gt;, який:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;Перевіряє наявність вже активного підключення&lt;/li&gt;
    &lt;li&gt;Якщо підключення немає, виконує його й повертає результат&lt;/li&gt;
    &lt;li&gt;Якщо підключення вже є, повертає його&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p&gt;Здавалось би, все гаразд, але у даній реалізації можна знайти недолік — якщо кілька незалежних частин коду спробують отримати доступ до бази даних до того, як було ініційовано перше підключення, на кожний запит буде відкрите окреме підключення: &lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;iframe src=&quot;https://player.vimeo.com/video/545413122/?autoplay=false&amp;loop=false&amp;muted=false&amp;title=true&quot;&gt;&lt;/iframe&gt;
    &lt;figcaption&gt;Демонстрація того, що відбувається у даному прикладі&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p&gt;Ось ще один наглядний приклад цієї проблеми:&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;iframe src=&quot;https://dartpad.dev/embed-dart.html?id=c6fba0c5cebbded8f6d905384c15a8f5&amp;split=75&amp;null_safety=true&quot;&gt;&lt;/iframe&gt;
  &lt;/figure&gt;
  &lt;p&gt;Розглянемо його детальніше:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;Клас &lt;code&gt;NumberHolder&lt;/code&gt; має один раз отримати значення з асинхронної функції &lt;code&gt;asyncNumberGet()&lt;/code&gt;, після чого зберегти його та щоразу повертати збережене значення&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;asyncNumberGet()&lt;/code&gt; після кожного виклику збільшує своє значення&lt;/li&gt;
    &lt;li&gt;У функції &lt;code&gt;main()&lt;/code&gt; ми:&lt;/li&gt;
    &lt;ul&gt;
      &lt;li&gt;Паралельно запускаємо два запити на отримання значення числа&lt;/li&gt;
      &lt;li&gt;Окремо запускаємо один запит на отримання числа&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/ul&gt;
  &lt;p&gt;За логікою:&lt;/p&gt;
  &lt;ol&gt;
    &lt;li&gt;Ми створюємо екземпляр &lt;code&gt;NumberHolder&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;Виклик 1: Клас &lt;code&gt;NumberHolder&lt;/code&gt; має отримати значення числа з функцї &lt;code&gt;asyncNumberGet()&lt;/code&gt; &lt;strong&gt;один раз&lt;/strong&gt; та записати його у змінну &lt;code&gt;_result&lt;/code&gt;. Результат: 1&lt;/li&gt;
    &lt;li&gt;Виклик 2: змінна &lt;code&gt;_result&lt;/code&gt; вже наявна, тому використовуємо її. Результат: 1&lt;/li&gt;
    &lt;li&gt;Виклик 3: змінна &lt;code&gt;_result&lt;/code&gt; вже наявна, тому використовуємо її. Результат: 1&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p&gt;Тобто, очікуваним виводом даної програми мають бути такі рядки:&lt;/p&gt;
  &lt;pre&gt;[1, 1]
1&lt;/pre&gt;
  &lt;p&gt;Але якщо ми запустимо приклад, то побачимо інший результат:&lt;/p&gt;
  &lt;pre&gt;[1, 2]
2&lt;/pre&gt;
  &lt;p&gt;Даний результат виникає через проблему, що називається Race Condition, або конкуренція: коли від порядку виконання коду залежить результат.&lt;/p&gt;
  &lt;p&gt;У прикладі з базою даних стався б витік ресурсів через непотрібне та незакрите з&amp;#x27;єднання з БД.&lt;/p&gt;
  &lt;p&gt;Давайте розглянемо, чому так стається. Переглянувши реалізацію &lt;code&gt;Future.wait&lt;/code&gt;, робимо висновок, що всередині &lt;code&gt;Future.wait([task1(), task2()])&lt;/code&gt; виклик відбувається так:&lt;/p&gt;
  &lt;pre data-lang=&quot;dart&quot;&gt;task1();
task2();&lt;/pre&gt;
  &lt;p&gt;Тобто обидва асинхронні завдання виконуються конкурентно, не очікуючи один на одного. &lt;/p&gt;
  &lt;h2&gt;Конкурентність у контексті Dart&lt;/h2&gt;
  &lt;p&gt;JavaScript-розробники вже мають бути знайомі з поняттям черги подій (event loop).&lt;/p&gt;
  &lt;p&gt;Коротко: хоча й Dart підтримує справжню багатопоточність (isolates), увесь звичайний (синхронний та асинхронний) код виконується в одному потоці. Це означає, що усі команди можуть виконуватись виключно &lt;strong&gt;одна за одною&lt;/strong&gt;.&lt;/p&gt;
  &lt;p&gt;Але одночасно у логіки роботи черги є одна особливість: як тільки інтерпретатор потрапляє на асинхронну команду, ця команда переміщується у кінець черги, очікуючи на виконання синхронного коду, що розміщується після неї. &lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/d9/22/d9221700-8859-4161-a910-99b77df79524.png&quot; width=&quot;704&quot; /&gt;
    &lt;figcaption&gt;Логіка роботи Future&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p&gt;У нашому прикладі замість &lt;code&gt;task1&lt;/code&gt; ми викликаємо &lt;code&gt;number.use&lt;/code&gt;. Перегляньмо цю функцію.&lt;/p&gt;
  &lt;pre data-lang=&quot;dart&quot;&gt;Future&amp;lt;int&amp;gt; use() async {
    _result ??= await asyncNumberGet();
    return _result!;
}&lt;/pre&gt;
  &lt;p&gt;Перепишемо дану функцію простіше та без використання &lt;code&gt;async&lt;/code&gt;, щоб зрозуміти логіку роботи:&lt;/p&gt;
  &lt;pre data-lang=&quot;dart&quot;&gt;Future&amp;lt;int&amp;gt; use() {
  if (_result == null) {
    return asyncNumberGet() # Future
    .then((number) {
      _result = number;
      return number;
    });
  }
  return Future.value(_result);
}&lt;/pre&gt;
  &lt;p&gt;Застосуємо наші знання про конкурентність та проілюструємо порядок виконання очима інтерпретатора Dart:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/e3/ab/e3aba160-836c-4e8c-9169-06837bb77de0.png&quot; width=&quot;711&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Тоді викличмо нашу функцію двічі підряд, як це робить &lt;code&gt;Future.wait&lt;/code&gt; й відсортуймо команди, як це робить Dart:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/ed/b9/edb92057-ba42-4197-ab15-d8d2eef22b58.png&quot; width=&quot;1045&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Так як асинхронний код було переміщено, виклик &lt;code&gt;asyncNumberGet()&lt;/code&gt; поки що не стався, а отже й значення _result не було встановлено. Через це друга перевірка &lt;code&gt;_result == null&lt;/code&gt; є істинною, через що дану ділянку коду не буде пропущено, а функцію &lt;code&gt;asyncNumberGet()&lt;/code&gt; буде викликано двічі:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/fe/e1/fee13e3e-5c26-493f-b5b9-9fcc1443be26.png&quot; width=&quot;1054&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Врешті-решт, асинхронна функція виконується синхронно в кінці черги. &lt;/p&gt;
  &lt;p&gt;У перенесеному коді записуємо усі конструкції &lt;code&gt;Future().then()&lt;/code&gt; аналогічною конструкцією з &lt;code&gt;await&lt;/code&gt; для спрощення сприйняття:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/1b/a3/1ba3d491-670b-40de-b040-a65d6bf9b01a.png&quot; width=&quot;945&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;У результаті маємо два виклики &lt;code&gt;asyncNumberGet()&lt;/code&gt; через те, що перевірка умови під час другого виклику відбувається раніше, ніж завершення першого виклику.&lt;/p&gt;
  &lt;h2&gt;Completer на допомогу&lt;/h2&gt;
  &lt;p&gt;Але ж що робити, якщо хочеться створити безшовний доступ до кешованого результату? &lt;/p&gt;
  &lt;p&gt;&lt;a href=&quot;https://api.dart.dev/stable/2.12.4/dart-async/Completer-class.html&quot; target=&quot;_blank&quot;&gt;Completer&lt;/a&gt; це вбудований у Dart контролер Future. Він здатний продукувати Future будь-якого виду та завершувати (&lt;em&gt;resolve/complete&lt;/em&gt;) їх у будь-який час.&lt;/p&gt;
  &lt;p&gt;Completer дуже простий у використанні. Екземляр даного класу має такі методи:&lt;/p&gt;
  &lt;pre data-lang=&quot;dart&quot;&gt;complete([FutureOr&amp;lt;T&amp;gt;? value]) → void
// Завершує Future наданим значенням
completeError(Object error, [StackTrace? stackTrace]) → void
// Завершує Future з помилкою&lt;/pre&gt;
  &lt;p&gt;Та такі властивості:&lt;/p&gt;
  &lt;pre data-lang=&quot;dart&quot;&gt;future → Future&amp;lt;T&amp;gt;
// Екземляр Future, який ми контролюємо
isCompleted → bool
// Повертає істину, якщо Future вже завершено&lt;/pre&gt;
  &lt;p&gt;Отож, перепишемо наш приклад з використанням Completer:&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;iframe src=&quot;https://dartpad.dev/embed-dart.html?id=614d3688f99a622125858b6861bd9093&amp;split=75&amp;null_safety=true&quot;&gt;&lt;/iframe&gt;
  &lt;/figure&gt;
  &lt;p&gt;Отримуємо результат, що підтверджує правильність.&lt;/p&gt;
  &lt;pre&gt;CALLED
[1, 1]
1&lt;/pre&gt;
  &lt;h2&gt;Висновки&lt;/h2&gt;
  &lt;p&gt;Завдяки Completer ми змогли зробити контрольований екземпляр Future, який можна повертати, поки тривають конкурентні обчислення, у результаті чого ми змогли уникнути помилки Race Condition.&lt;/p&gt;
  &lt;blockquote&gt;Запрошуємо усіх зацікавлених у Dart і Flutter приєднуватись до нашого &lt;a href=&quot;https://t.me/itkpi_dart&quot; target=&quot;_blank&quot;&gt;чату&lt;/a&gt;, щоб отримувати більше цікавих матеріалів та новин про ці технології.&lt;/blockquote&gt;

</content></entry><entry><id>itkpi_dart:Asynchronous-programming</id><link rel="alternate" type="text/html" href="https://teletype.in/@itkpi_dart/Asynchronous-programming?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=itkpi_dart"></link><title>Асинхронне програмування у Dart: Future, async, await</title><published>2021-04-30T11:35:45.112Z</published><updated>2021-04-30T17:02:57.472Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://teletype.in/files/bf/64/bf64b38f-5d20-4dc9-8c37-6ce3794a5767.png"></media:thumbnail><category term="dart" label="Dart"></category><summary type="html">&lt;img src=&quot;https://teletype.in/files/f9/02/f9023d80-ba17-427e-bb50-95b86c7da7c8.png&quot;&gt;Асинхронність є однією з фундаментальних умов для роботи з Dart. Навіть якщо дане поняття вам відоме з JavaScript, асинхронність у Dart має несуттєві відмінності, тож радимо прочитати цю статтю кожному.</summary><content type="html">
  &lt;p&gt;Асинхронність є о&lt;u&gt;д&lt;/u&gt;нією з фундаментальних умов для роботи з Dart. Навіть якщо дане поняття вам відоме з JavaScript, асинхронність у Dart має несуттєві відмінності, тож радимо прочитати цю статтю кожному.&lt;/p&gt;
  &lt;p&gt;Дана стаття є перекладом &lt;a href=&quot;https://dart.dev/codelabs/async-await&quot; target=&quot;_blank&quot;&gt;англомовного туторіалу з dart.dev&lt;/a&gt;.&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/f9/02/f9023d80-ba17-427e-bb50-95b86c7da7c8.png&quot; width=&quot;1200&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Ця кодова лабораторія навчить вас писати асинхронний код за допомогою об&amp;#x27;єкту Future та ключових слів &lt;code&gt;async&lt;/code&gt;, &lt;code&gt;await&lt;/code&gt;.&lt;/p&gt;
  &lt;p&gt;Щоб отримати максимальну віддачу від цієї кодової лабораторії, вам слід мати:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;Знання &lt;a href=&quot;https://dart.dev/samples&quot; target=&quot;_blank&quot;&gt;базового синтаксису Dart&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;Деякий досвід написання асинхронного коду іншою мовою.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p&gt;Приблизний час для проходження цієї кодової лабораторії: 40-60 хвилин.&lt;/p&gt;
  &lt;h3&gt;Чому асинхронний код важливий&lt;/h3&gt;
  &lt;p&gt;Асинхронні операції дозволяють вашій програмі продовжувати працювати, одночасно чекаючи закінчення іншої операції. Ось декілька поширених асинхронних операцій:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;Отримання даних через мережу.&lt;/li&gt;
    &lt;li&gt;Запис у базу даних.&lt;/li&gt;
    &lt;li&gt;Зчитування даних з файлу.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p&gt;Для виконання асинхронних операцій в Dart ви можете використовувати клас &lt;code&gt;Future&lt;/code&gt; та ключові слова &lt;code&gt;async&lt;/code&gt; і &lt;code&gt;await&lt;/code&gt;.&lt;/p&gt;
  &lt;h4&gt;Приклад: Неправильне використання асинхронної функції&lt;/h4&gt;
  &lt;p&gt;Наступний приклад показує неправильний спосіб використання асинхронної функції ( &lt;code&gt;fetchUserOrder()&lt;/code&gt;). Пізніше ви виправите приклад за допомогою &lt;code&gt;async&lt;/code&gt; та &lt;code&gt;await&lt;/code&gt;. Перш ніж запускати цей приклад, спробуйте виявити проблему — який, на вашу думку, буде результат?&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;iframe src=&quot;https://dartpad.dev/embed-dart.html?id=07943e44a22c3bbc53906cd9a58778e4&amp;split=75&amp;null_safety=true&quot;&gt;&lt;/iframe&gt;
  &lt;/figure&gt;
  &lt;p&gt;Ось чому в прикладі не вдається вивести значення, яке &lt;code&gt;fetchUserOrder()&lt;/code&gt; повертає з запізненням у 2 секунди:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;code&gt;fetchUserOrder()&lt;/code&gt; — це асинхронна функція, яка після затримки надає рядок, що описує замовлення користувача: “Large Latte”.&lt;/li&gt;
    &lt;li&gt;Щоб отримати замовлення користувача, &lt;code&gt;createOrderMessage()&lt;/code&gt; слід викликати &lt;code&gt;fetchUserOrder()&lt;/code&gt; і зачекати, поки замовлення буде повернуто. Через те, що &lt;code&gt;createOrderMessage()&lt;/code&gt; виконує виведення до того, як &lt;code&gt;fetchUserOrder()&lt;/code&gt; завершує роботу, &lt;code&gt;createOrderMessage()&lt;/code&gt;не може отримати текстове значення, яке &lt;code&gt;fetchUserOrder()&lt;/code&gt; ще не повернуло.&lt;/li&gt;
    &lt;li&gt;Натомість &lt;code&gt;createOrderMessage()&lt;/code&gt; отримує об&amp;#x27;єкт, що позначає результат, який ще очікується: незавершену Future. Докладніше про Future ви дізнаєтесь у наступному розділі.&lt;/li&gt;
    &lt;li&gt;Оскільки &lt;code&gt;createOrderMessage()&lt;/code&gt; не вдається отримати значення, що описує замовлення користувача, приклад виводить не “Large Latte”, а “Ваше замовлення: &lt;code&gt;Instance of &amp;#x27;_Future&amp;lt;String&amp;gt;&amp;#x27;&lt;/code&gt;”.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p&gt;У наступних розділах ви дізнаєтеся про Future та про роботу з ними (за допомогою &lt;code&gt;async&lt;/code&gt; та &lt;code&gt;await&lt;/code&gt;), що дасть вам змогу написати код, який зможе коректно вивести &amp;quot;Large Latte&amp;quot; у консолі, який повертає функція &lt;code&gt;fetchUserOrder()&lt;/code&gt;.&lt;/p&gt;
  &lt;h3&gt;Ключові терміни:&lt;/h3&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Синхронна операція&lt;/strong&gt;: синхронна операція блокує виконання інших операцій до свого завершення.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Синхронна функція&lt;/strong&gt;: синхронна функція виконує лише синхронні операції.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Асинхронна операція&lt;/strong&gt;: після запуску асинхронна операція дозволяє паралельно виконувати інші операції впродовж своєї роботи.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Асинхронна функція&lt;/strong&gt;: асинхронна функція виконує принаймні одну асинхронну операцію, а також може виконувати &lt;em&gt;синхронні&lt;/em&gt; операції.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h2&gt;Що таке Future?&lt;/h2&gt;
  &lt;p&gt;Future - це екземпляр класу &lt;a href=&quot;https://api.dart.dev/stable/dart-async/Future-class.html&quot; target=&quot;_blank&quot;&gt;Future&lt;/a&gt;. Future це представлення результату роботи асинхронної операції, що може мати два стани: незавершений або завершений.&lt;/p&gt;
  &lt;blockquote&gt;&lt;strong&gt;Примітка: &lt;/strong&gt;Незавершений (&lt;em&gt;uncompleted)&lt;/em&gt; - термін у мові Dart, що стосується стану Future до того, як повертається значення.&lt;/blockquote&gt;
  &lt;h3&gt;Незавершений&lt;/h3&gt;
  &lt;p&gt;Коли ви викликаєте асинхронну функцію, вона повертає незавершений Future. Цей Future чекає на успішне завершення асинхронної операції функції або на видачу помилки.&lt;/p&gt;
  &lt;h3&gt;Завершений&lt;/h3&gt;
  &lt;p&gt;Якщо асинхронна операція вдається, Future завершується (&lt;em&gt;completes&lt;/em&gt;) значенням. В іншому випадку Future завершується помилкою.&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;Завершення зі значенням&lt;/strong&gt;&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;code&gt;Future&amp;lt;T&amp;gt;&lt;/code&gt; завершується значенням типу &lt;code&gt;T&lt;/code&gt;. Наприклад, Future з типом &lt;code&gt;Future&amp;lt;String&amp;gt;&lt;/code&gt; створює значення String (рядка). Якщо Future не повертає будь-якого значення, тоді це тип &lt;code&gt;Future&amp;lt;void&amp;gt;&lt;/code&gt;.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p&gt;&lt;strong&gt;Завершення з помилкою&lt;/strong&gt;&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;Якщо асинхронна операція, виконана функцією, не вдається з будь-якої причини, Future завершується помилкою.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h3&gt;Приклад: вводимо Future у код&lt;/h3&gt;
  &lt;p&gt;У цьому прикладі &lt;code&gt;fetchUserOrder()&lt;/code&gt; повертає Future, яке завершується вже після виводу в консолі. Оскільки функція не повертає яке-небудь корисне значення, &lt;code&gt;fetchUserOrder()&lt;/code&gt;має тип &lt;code&gt;Future&amp;lt;void&amp;gt;&lt;/code&gt;. Перш ніж запускати приклад, спробуйте спрогнозувати, що буде виведено першим: “Large Latte” або “Отримання замовлення користувача…”.&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;iframe src=&quot;https://dartpad.dev/embed-dart.html?id=0b7f74b7cfd01182fc0458ba104bdddc&amp;split=75&amp;null_safety=true&quot;&gt;&lt;/iframe&gt;
  &lt;/figure&gt;
  &lt;p&gt;У попередньому прикладі, незважаючи на те, що &lt;code&gt;fetchUserOrder()&lt;/code&gt; виконується до виклику &lt;code&gt;print()&lt;/code&gt; на рядку 8, консоль відображає дані з рядка 8 (“Fetching user order…”) перед даними з &lt;code&gt;fetchUserOrder()&lt;/code&gt;(“Large Latte”). Це трапляється тому, що &lt;code&gt;fetchUserOrder()&lt;/code&gt; затримується перед тим, як надрукувати “Large Latte”.&lt;/p&gt;
  &lt;h3&gt;Приклад: Завершення з помилкою&lt;/h3&gt;
  &lt;p&gt;Запустіть цей приклад, щоб побачити, як Future завершується помилкою. Трохи пізніше ви дізнаєтесь, як правильно працювати з помилками у Future.&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;iframe src=&quot;https://dartpad.dev/embed-dart.html?id=04efee97ab19debc266ae5711cf65c73&amp;split=75&amp;null_safety=true&quot;&gt;&lt;/iframe&gt;
  &lt;/figure&gt;
  &lt;p&gt;У цьому прикладі &lt;code&gt;fetchUserOrder()&lt;/code&gt; завершується помилкою, яка вказує на те, що ідентифікатор користувача недійсний.&lt;/p&gt;
  &lt;p&gt;Ви дізналися про Future та про те, як вони повертають значення, але як використати результати асинхронних операцій? У наступному розділі ви дізнаєтесь, як отримати результати за допомогою ключових слів &lt;code&gt;async&lt;/code&gt; та &lt;code&gt;await&lt;/code&gt;.&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;Короткий огляд&lt;/strong&gt;&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;Об&amp;#x27;єкт типу &lt;code&gt;Future&amp;lt;T&amp;gt;&lt;/code&gt; продукує значення типу &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;Якщо Future не продукує корисне значення, це тип &lt;code&gt;Future&amp;lt;void&amp;gt;&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;Future&lt;/code&gt; має два стани: завершений та незавершений&lt;/li&gt;
    &lt;li&gt;Коли ви викликаєте функцію, що повертає Future, дана асинхронна задача стає у чергу та повертає значення через якийсь час, з затримкою&lt;/li&gt;
    &lt;li&gt;Коли виконання Future завершується, результатом є значення заданого типу або помилка&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p&gt;&lt;strong&gt;Ключові терміни&lt;/strong&gt;&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Future&lt;/strong&gt;: &lt;a href=&quot;https://api.dart.dev/stable/dart-async/Future-class.html&quot; target=&quot;_blank&quot;&gt;клас Future&lt;/a&gt; у Dart&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;a future&lt;/strong&gt; (з малої літери): екземпляр класу Future, що позначає асинхронну задачу&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h2&gt;Працюємо з Future: async та await&lt;/h2&gt;
  &lt;p&gt;Ключові слова &lt;code&gt;async&lt;/code&gt; та &lt;code&gt;await&lt;/code&gt; створені для декларування асинхронних функцій та для використання їх результатів. Запам’ятайте ці дві основні рекомендації при використанні &lt;code&gt;async&lt;/code&gt; та &lt;code&gt;await&lt;/code&gt;:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;Щоб створити асинхронну функцію, додайте &lt;code&gt;async&lt;/code&gt; перед тілом функції&lt;/li&gt;
    &lt;li&gt;Ключове слово &lt;code&gt;await&lt;/code&gt; можна використовувати тільки в async функціях&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p&gt;Ось як перетворити синхронну функцію в асинхронну:&lt;/p&gt;
  &lt;p&gt;Спочатку додайте ключове слово &lt;code&gt;async&lt;/code&gt; перед тілом функції:&lt;/p&gt;
  &lt;pre data-lang=&quot;dart&quot;&gt;void main() async { ··· }&lt;/pre&gt;
  &lt;p&gt;Якщо функція має оголошений тип, який вона повертає, змініть тип на &lt;code&gt;Future&amp;lt;T&amp;gt;&lt;/code&gt;, де &lt;code&gt;T&lt;/code&gt; є саме тим типом, що повертає функція. Якщо функція не повертає явного значення, тоді слід вказати тип &lt;code&gt;Future&amp;lt;void&amp;gt;&lt;/code&gt;:&lt;/p&gt;
  &lt;pre data-lang=&quot;dart&quot;&gt;Future&amp;lt;void&amp;gt; main() async { ··· }&lt;/pre&gt;
  &lt;p&gt;Тепер, коли у вас є &lt;code&gt;async&lt;/code&gt;-функція, ви можете використовувати ключове слово &lt;code&gt;await&lt;/code&gt;, щоб дочекатися результату &lt;code&gt;Future&lt;/code&gt;:&lt;/p&gt;
  &lt;pre data-lang=&quot;dart&quot;&gt;print(await createOrderMessage());&lt;/pre&gt;
  &lt;p&gt;Як показують наступні два приклади, ключові слова &lt;code&gt;async&lt;/code&gt; та &lt;code&gt;await&lt;/code&gt; продукують асинхронний код, який дуже схожий на синхронний код. Єдині відмінності виділено в асинхронному прикладі, який знаходиться праворуч від синхронного прикладу:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/7a/b7/7ab70589-890f-4a46-858d-bd2459bb728b.png&quot; width=&quot;910&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Асинхронний приклад має три відмінності:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;Тип, що повертає функція &lt;code&gt;createOrderMessage()&lt;/code&gt; &lt;strong&gt;змінено &lt;/strong&gt;з &lt;code&gt;String&lt;/code&gt; на &lt;code&gt;Future&amp;lt;String&amp;gt;&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;Ключове слово &lt;strong&gt;&lt;code&gt;async&lt;/code&gt;&lt;/strong&gt; &lt;strong&gt;додано &lt;/strong&gt;перед тілом функції &lt;code&gt;createOrderMessage()&lt;/code&gt; і &lt;code&gt;main()&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;Ключове слово &lt;strong&gt;&lt;code&gt;await&lt;/code&gt; додано&lt;/strong&gt; перед викликом асинхронних функцій &lt;code&gt;fetchUserOrder()&lt;/code&gt; і &lt;code&gt;createOrderMessage()&lt;/code&gt;.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p&gt;&lt;strong&gt;Ключові терміни&lt;/strong&gt;&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;async&lt;/strong&gt;: Використовуйте ключове слово async, щоб зробити функцію асинхронною.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;async function&lt;/strong&gt;: &lt;code&gt;async&lt;/code&gt;-функція - це функція, позначена ключовим словом &lt;code&gt;async&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;await&lt;/strong&gt;: Ви можете використовувати ключове слово &lt;code&gt;await&lt;/code&gt;, щоб отримати завершений результат асинхронного виразу. Ключове слово &lt;code&gt;await&lt;/code&gt;працює тільки в межах &lt;code&gt;async&lt;/code&gt;-функцій.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h2&gt;Порядок виконання з async та await&lt;/h2&gt;
  &lt;blockquote&gt;&lt;strong&gt;Примітка&lt;/strong&gt;: До версії Dart 2.0, &lt;code&gt;async&lt;/code&gt;-функція негайно повертала значення, не виконуючи код у тілі функції&lt;/blockquote&gt;
  &lt;h3&gt; Приклад: Виконання всередині async-функції&lt;/h3&gt;
  &lt;p&gt;Запустіть цей приклад, щоб побачити, як працює виконання у тілі &lt;code&gt;async&lt;/code&gt;-функції. Як ви думаєте, яким буде результат?&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;iframe src=&quot;https://dartpad.dev/embed-dart.html?id=8a8dc054f725551ddf8d36bd9dc72c28&amp;split=75&amp;null_safety=true&quot;&gt;&lt;/iframe&gt;
  &lt;/figure&gt;
  &lt;p&gt;Після запуску коду в попередньому прикладі спробуйте поміняти рядки 2 і 3 місцями:&lt;/p&gt;
  &lt;pre data-lang=&quot;dart&quot;&gt;var order = await fetchUserOrder();
print(&amp;#x27;Awaiting user order...&amp;#x27;);&lt;/pre&gt;
  &lt;p&gt;Зверніть увагу як змінюється порядок виводу: тепер &lt;code&gt;print(&amp;#x27;Awaiting user order&amp;#x27;)&lt;/code&gt; з&amp;#x27;являється після першого використання ключового слова &lt;code&gt;await&lt;/code&gt; в &lt;code&gt;printOrderMessage()&lt;/code&gt;.&lt;/p&gt;
  &lt;h3&gt;Вправа: Попрактикуйтеся з async й await&lt;/h3&gt;
  &lt;p&gt;Наступна вправа — це непрацюючий юніт-тест, який містить частково готові фрагменти коду. Ваше завдання — виконати вправу, написати такий код, щоб тест проходив успішно. Вам не потрібно реалізовувати функцію &lt;code&gt;main()&lt;/code&gt;.&lt;/p&gt;
  &lt;p&gt;Щоб імітувати асинхронні операції, використовуйте надані вам функції:&lt;/p&gt;
  &lt;pre data-lang=&quot;dart&quot;&gt;Future&amp;lt;String&amp;gt; fetchRole() {...} // Отримує короткий опис ролі користувача
Future&amp;lt;int&amp;gt; fetchLoginAmount() {...}
// Отримує кількість входів користувача в систему.&lt;/pre&gt;
  &lt;p&gt;&lt;strong&gt;Частина 1:&lt;/strong&gt; &lt;code&gt;reportUserRole()&lt;/code&gt;&lt;/p&gt;
  &lt;p&gt;Доробіть функцію &lt;code&gt;reportUserRole()&lt;/code&gt; так, щоб вона робила наступне:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;Функція має повертати Future, яке завершується наступним рядком: &lt;code&gt;&amp;quot;User role: &amp;lt;user role&amp;gt;&amp;quot;&lt;/code&gt;&lt;/li&gt;
    &lt;ul&gt;
      &lt;li&gt;Примітка: Ви повинні використовувати фактичне значення, яке повертає &lt;code&gt;fetchRole()&lt;/code&gt;; копіювання та вставка значення з прикладу не дасть вам пройти тест&lt;/li&gt;
      &lt;li&gt;Приклад поверненого значення: &lt;code&gt;&amp;quot;User role: tester&amp;quot;&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
    &lt;li&gt;Функція має отримувати роль користувача, викликаючи надану функцію &lt;code&gt;fetchRole()&lt;/code&gt;.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p&gt;&lt;strong&gt;Частина 2: &lt;/strong&gt;&lt;code&gt;reportLogins()&lt;/code&gt;&lt;/p&gt;
  &lt;p&gt;Реалізуйте &lt;code&gt;async&lt;/code&gt;-функцію &lt;code&gt;reportLogins()&lt;/code&gt; так, щоб вона робила наступне:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;Функція має повертати рядок &lt;code&gt;&amp;quot;Total number of logins: &amp;lt;# of logins&amp;gt;&amp;quot;&lt;/code&gt;.&lt;/li&gt;
    &lt;ul&gt;
      &lt;li&gt;Примітка: Ви повинні використовувати фактичне значення, яке повертає &lt;code&gt;fetchLoginAmount()&lt;/code&gt;; копіювання та вставка значення з прикладу не дасть вам пройти тест&lt;/li&gt;
      &lt;li&gt;Приклад поверненого значення з &lt;code&gt;reportLogins()&lt;/code&gt;:&lt;code&gt;&amp;quot;Total number of logins: 57&amp;quot;&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
    &lt;li&gt;Функція має отримувати кількість входів, викликаючи надану функцію &lt;code&gt;fetchLoginAmount()&lt;/code&gt;.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;iframe src=&quot;https://dartpad.dev/embed-dart.html?id=a657df144a77805cbb8fc25ef6e8f461&amp;split=75&amp;null_safety=true&quot;&gt;&lt;/iframe&gt;
  &lt;/figure&gt;
  &lt;blockquote&gt;&lt;strong&gt;Зауважте&lt;/strong&gt;: Якщо ваш код проходить усі тести, ви можете ігнорувати &lt;a href=&quot;https://dart.dev/guides/language/analysis-options#customizing-analysis-rules&quot; target=&quot;_blank&quot;&gt;попередження з міткою &lt;code&gt;info&lt;/code&gt;&lt;/a&gt;&lt;/blockquote&gt;
  &lt;h2&gt;Обробка помилок&lt;/h2&gt;
  &lt;p&gt;Для обробки помилок у &lt;code&gt;async&lt;/code&gt;-функції, використовуйте конструкцію try-catch:&lt;/p&gt;
  &lt;pre data-lang=&quot;dart&quot;&gt;try {
  var order = await fetchUserOrder();
  print(&amp;#x27;Awaiting user order...&amp;#x27;);
} catch (err) {
  print(&amp;#x27;Caught error: $err&amp;#x27;);
}&lt;/pre&gt;
  &lt;p&gt;У межах &lt;code&gt;async&lt;/code&gt;-функції ви можете використовувати &lt;a href=&quot;https://dart.dev/guides/language/language-tour#catch&quot; target=&quot;_blank&quot;&gt;try-catch конструкції&lt;/a&gt; так само, як і в синхронному коді.&lt;/p&gt;
  &lt;h3&gt;Приклад: async та await з використанням try-catch&lt;/h3&gt;
  &lt;p&gt;Запустіть даний приклад, щоб побачити, як обробляти помилку в асинхронній функції. Як ви думаєте, яким буде результат?&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;iframe src=&quot;https://dartpad.dev/embed-dart.html?id=85d6a2c2527b5bd7335f8e51687543bb&amp;split=75&amp;null_safety=true&quot;&gt;&lt;/iframe&gt;
  &lt;/figure&gt;
  &lt;h3&gt;Вправа: Практикуємося обробляти помилки&lt;/h3&gt;
  &lt;p&gt;Наступна вправа надає практичні навички в асинхронному коді, використовуючи підхід, описаний у попередньому розділі. Для імітації асинхронних операцій вам надано цю функцію:&lt;/p&gt;
  &lt;pre data-lang=&quot;dart&quot;&gt;Future&amp;lt;String&amp;gt; fetchNewUsername() {...} /* Повертає нове ім’я користувача, 
яке можна використовувати для заміни старого. */&lt;/pre&gt;
  &lt;p&gt;Використовуйте &lt;code&gt;async&lt;/code&gt;і &lt;code&gt;await&lt;/code&gt; для реалізації асинхронної функції &lt;code&gt;changeUsername()&lt;/code&gt;, що має робити наступне:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;Функція має викликати надану асинхронну функцію &lt;code&gt;fetchNewUsername()&lt;/code&gt; і повертати її результат.&lt;/li&gt;
    &lt;ul&gt;
      &lt;li&gt;Приклад поверненого значення з &lt;code&gt;changeUsername()&lt;/code&gt;: &lt;code&gt;&amp;quot;jane_smith_92&amp;quot;&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
    &lt;li&gt;Ловить (&lt;em&gt;catch&lt;/em&gt;) будь-яку помилку, що виникає, і повертає значення рядка помилки.&lt;/li&gt;
    &lt;ul&gt;
      &lt;li&gt;Ви можете використовувати метод &lt;a href=&quot;https://api.dart.dev/stable/dart-core/ArgumentError/toString.html&quot; target=&quot;_blank&quot;&gt;toString()&lt;/a&gt; як для &lt;a href=&quot;https://api.dart.dev/stable/dart-core/Exception-class.html&quot; target=&quot;_blank&quot;&gt;винятків (Exception)&lt;/a&gt;, так і для &lt;a href=&quot;https://api.dart.dev/stable/dart-core/Error-class.html&quot; target=&quot;_blank&quot;&gt;помилок (Error)&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/ul&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;iframe src=&quot;https://dartpad.dev/embed-dart.html?id=5f14e25ba35b3bcd08a982ee95fa7d33&amp;split=75&amp;null_safety=true&quot;&gt;&lt;/iframe&gt;
  &lt;/figure&gt;
  &lt;h3&gt;Вправа: Складемо все докупи&lt;/h3&gt;
  &lt;p&gt;Настав час потренуватися в тому, що ви дізналися, в одній заключній вправі. Для імітації асинхронних операцій ця вправа надає асинхронні функції &lt;code&gt;fetchUsername()&lt;/code&gt;та &lt;code&gt;logoutUser()&lt;/code&gt;:&lt;/p&gt;
  &lt;pre data-lang=&quot;dart&quot;&gt;Future&amp;lt;String&amp;gt; fetchUsername() {...}// Повертає ім&amp;#x27;я поточного користувача
Future&amp;lt;String&amp;gt; logoutUser() {...}
/* Виконує вихід із поточного користувача
та повертає ім’я користувача, що вийшов з системи */&lt;/pre&gt;
  &lt;p&gt;Напишіть наступне:&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;Частина 1:&lt;/strong&gt; &lt;code&gt;addHello()&lt;/code&gt;&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;Напишіть функцію, &lt;code&gt;addHello()&lt;/code&gt;яка приймає один аргумент String.&lt;/li&gt;
    &lt;li&gt;Функція &lt;code&gt;addHello()&lt;/code&gt; повертає свій аргумент типу String, перед яким стоїть &amp;quot;Hello&amp;quot;.&lt;br /&gt;Приклад: &lt;code&gt;addHello(&amp;#x27;Jon&amp;#x27;)&lt;/code&gt;повертає &lt;code&gt;&amp;#x27;Hello Jon&amp;#x27;&lt;/code&gt;.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p&gt;&lt;strong&gt;Частина 2: &lt;/strong&gt;&lt;code&gt;greetUser()&lt;/code&gt;&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;Напишіть функцію, &lt;code&gt;greetUser()&lt;/code&gt;, що не приймає аргументів.&lt;/li&gt;
    &lt;li&gt;Щоб отримати ім’я користувача, &lt;code&gt;greetUser()&lt;/code&gt;викликає надану асинхронну функцію &lt;code&gt;fetchUsername()&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;greetUser()&lt;/code&gt; створює привітання для користувача, викликаючи &lt;code&gt;addHello()&lt;/code&gt;, передаючи йому ім’я користувача та повертаючи результат.&lt;br /&gt;Приклад: Якщо &lt;code&gt;fetchUsername()&lt;/code&gt; повертає &lt;code&gt;&amp;#x27;Jenny&amp;#x27;&lt;/code&gt;, то &lt;code&gt;greetUser()&lt;/code&gt;повертає &lt;code&gt;&amp;#x27;Hello Jenny&amp;#x27;&lt;/code&gt;.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p&gt;&lt;strong&gt;Частина 3: &lt;/strong&gt;&lt;code&gt;sayGoodbye()&lt;/code&gt;&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;Напишіть функцію &lt;code&gt;sayGoodbye()&lt;/code&gt;, яка робить наступне:&lt;/li&gt;
    &lt;ul&gt;
      &lt;li&gt;Не приймає аргументів.&lt;/li&gt;
      &lt;li&gt;Ловить будь-які помилки.&lt;/li&gt;
      &lt;li&gt;Викликає надану асинхронну функцію &lt;code&gt;logoutUser()&lt;/code&gt;.&lt;/li&gt;
    &lt;/ul&gt;
    &lt;li&gt;Якщо &lt;code&gt;logoutUser()&lt;/code&gt; не вдається, &lt;code&gt;sayGoodbye()&lt;/code&gt;повертає довільний рядок String.&lt;/li&gt;
    &lt;li&gt;У разі успіху &lt;code&gt;logoutUser()&lt;/code&gt;, &lt;code&gt;sayGoodbye()&lt;/code&gt;повертає рядок &lt;code&gt;&amp;#x27;&amp;lt;result&amp;gt; Thanks, see you next time&amp;#x27;&lt;/code&gt;, де &lt;code&gt;&amp;lt;result&amp;gt;&lt;/code&gt; — значення рядка, яке повертається за допомогою виклику &lt;code&gt;logoutUser()&lt;/code&gt;.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;iframe src=&quot;https://dartpad.dev/embed-dart.html?id=89d2f295f7b006069e26bf71182f03c0&amp;split=75&amp;null_safety=true&quot;&gt;&lt;/iframe&gt;
  &lt;/figure&gt;
  &lt;h2&gt;Що далі?&lt;/h2&gt;
  &lt;p&gt;Вітаємо, ви закінчили роботу з кодовою лабораторією! Якщо ви хочете дізнатись більше, ось кілька підказок, куди йти далі:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;Експериментуйте у &lt;a href=&quot;https://translate.google.com/website?sl=en&amp;tl=uk&amp;ajax=1&amp;u=https://dartpad.dev&quot; target=&quot;_blank&quot;&gt;DartPad.&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;Спробуйте іншу &lt;a href=&quot;https://dart.dev/codelabs&quot; target=&quot;_blank&quot;&gt;кодову лабораторію [англ.]&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;Дізнайтеся більше про Future та асинхронність:&lt;/li&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://dart.dev/tutorials/language/streams&quot; target=&quot;_blank&quot;&gt;Ознайомлення з Stream [англ.]&lt;/a&gt;: Дізнайтеся, як працювати з послідовністю асинхронних подій.&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/playlist?list=PLjxrf2q8roU0Net_g1NT5_vOO3s_FR02J&quot; target=&quot;_blank&quot;&gt;Відео про Dart від Google [англ.]&lt;/a&gt;: перегляньте одне або кілька відео про асинхронне кодування. Або, якщо хочете, прочитайте статті, основані на цих відео. (Почніть із &lt;a href=&quot;https://medium.com/dartlang/dart-asynchronous-programming-isolates-and-event-loops-bffc3e296a6a&quot; target=&quot;_blank&quot;&gt;ізоляцій та циклів подій [англ.]&lt;/a&gt;).&lt;/li&gt;
    &lt;/ul&gt;
    &lt;li&gt;&lt;a href=&quot;https://dart.dev/get-dart&quot; target=&quot;_blank&quot;&gt;Завантажте Dart SDK&lt;/a&gt;.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p&gt;Переклад виконано спеціально для &lt;a href=&quot;https://t.me/dart_itkpi&quot; target=&quot;_blank&quot;&gt;@dart_itkpi&lt;/a&gt;.&lt;/p&gt;

</content></entry><entry><id>itkpi_dart:Icon-widget</id><link rel="alternate" type="text/html" href="https://teletype.in/@itkpi_dart/Icon-widget?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=itkpi_dart"></link><title>Приборкуємо Icon: як він працює зсередини?</title><published>2021-04-30T09:42:46.996Z</published><updated>2021-05-06T09:06:35.890Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://teletype.in/files/85/eb/85eb37b6-b665-4a4d-8595-8ff3104a5a89.png"></media:thumbnail><category term="flutter" label="Flutter"></category><summary type="html">&lt;img src=&quot;https://teletype.in/files/b3/98/b3986097-0bf3-4f8c-a2f4-cc22a8ffd7ad.png&quot;&gt;У даній статті ми розглянемо віджет Icon, а також розкажемо про лайфхак, завдяки якому значення для піктограми можна задавати динамічно: наприклад, передавати з сервера.</summary><content type="html">
  &lt;p&gt;У даній статті ми розглянемо віджет Icon, а також розкажемо про лайфхак, завдяки якому значення для піктограми можна задавати динамічно: наприклад, передавати з сервера.&lt;/p&gt;
  &lt;blockquote&gt;Також ми переклали цю статтю &lt;a href=&quot;https://teletype.in/@itkpi_dart_en/Icon-widget&quot; target=&quot;_blank&quot;&gt;англійською&lt;/a&gt;&lt;/blockquote&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/b3/98/b3986097-0bf3-4f8c-a2f4-cc22a8ffd7ad.png&quot; width=&quot;1200&quot; /&gt;
  &lt;/figure&gt;
  &lt;h3&gt;Віджет Icon&lt;/h3&gt;
  &lt;p&gt;У Material Framework, що входить до Flutter, є віджет Icon. Даний віджет використовується, щоб відобразити яку-небудь піктограму. Його відмінність від віджету Image у тому, що даний віджет підпорядковується встановленій темі та може бути інтегрований з віджетом, у якому він використовується (наприклад, піктограма відповідно змінює свій колір у віджеті IconButton).&lt;/p&gt;
  &lt;p&gt;Розглянемо, які параметри приймає даний віджет:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://telegra.ph/file/1a2a609f85b683058e7e8.png&quot; width=&quot;515&quot; /&gt;
    &lt;figcaption&gt;Приклад роботи віджету Icon&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p&gt;Наведений приклад виводить піктограму з колекції Material Icons. Розберемо параметри:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Перший позиційний параметр&lt;/strong&gt;: значення типу IconData. Його ми розглянемо нижче&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Непозиційний параметр color&lt;/strong&gt;: значення типу Color щоб, відповідно, перефарбувати піктограму&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Непозиційний параметр size&lt;/strong&gt;: дійсне число, розмір піктограми у пікселях&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h3&gt;Віджет ImageIcon&lt;/h3&gt;
  &lt;p&gt;Даний віджет дозволяє використовувати растрові зображення для простого створення власних піктограм:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://telegra.ph/file/588379883c9b8e3b06df2.png&quot; width=&quot;831&quot; /&gt;
    &lt;figcaption&gt;Приклад роботи віджету ImageIcon&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p&gt;Синтаксис аналогічний до Icon, окрім того, що у перший позиційний параметр передається об&amp;#x27;єкт типу ImageProvider. ImageProvider це об&amp;#x27;єкт у Flutter, що відповідає за отримання зображення іншими віджетами. Наприклад, віджетом Image.&lt;/p&gt;
  &lt;p&gt;Скоріш за все, ви ніколи не працюватимете з ImageProvider напряму, натомість, ви будете використовувати одну з готових реалізацій:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;AssetImage — отримання зображення з асетів Flutter (&lt;a href=&quot;https://api.flutter.dev/flutter/painting/AssetImage-class.html&quot; target=&quot;_blank&quot;&gt;див. документацію щодо того, як додати зображення до списку асетів&lt;/a&gt;)&lt;/li&gt;
    &lt;li&gt;NetworkImage — завантаження зображення по HTTP&lt;/li&gt;
    &lt;li&gt;FileImage — отримання зображення з об&amp;#x27;єкту File&lt;/li&gt;
    &lt;li&gt;MemoryImage — отримання зображення з масиву байтів у оперативній пам&amp;#x27;яті&lt;/li&gt;
    &lt;li&gt;ExactAssetImage — аналог AssetImage, &lt;a href=&quot;https://api.flutter.dev/flutter/painting/ExactAssetImage-class.html&quot; target=&quot;_blank&quot;&gt;див. документацію щодо особливостей&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p&gt;У даному прикладі використовується зображення-асет.&lt;/p&gt;
  &lt;p&gt;Слід зазначити, що кольорові піктограми будуть приведені до монохромного вигляду за каналом прозорості:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://telegra.ph/file/4f718759c7e7a1545f657.png&quot; width=&quot;330&quot; /&gt;
    &lt;figcaption&gt;Зміна кольору у ImageIcon&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;h3&gt;Колекція Icons та CupertinoIcons&lt;/h3&gt;
  &lt;p&gt;Дані класи містять у собі колекції піктограм для Material Design та iOS відповідно. Слід зазначити, що між собою дані колекції не сумісні: якщо ви розробляєте такий застосунок, що використовує Material Icons на Android, а Cupertino — на iOS, вам доведеться власноруч вказувати відповідну піктограму, або, наприклад, скористатись бібліотекою &lt;a href=&quot;https://pub.dev/packages/flutter_platform_widgets&quot; target=&quot;_blank&quot;&gt;flutter_platform_widgets&lt;/a&gt;, яка пропонує обмежений список піктограм, що відповідають одна одній.&lt;/p&gt;
  &lt;p&gt;Список усіх піктограм для Material Design знаходиться на сторінці &lt;a href=&quot;https://fonts.google.com/icons&quot; target=&quot;_blank&quot;&gt;Material Icons&lt;/a&gt;. Кожна піктограма представлена у чотирьох стилях: звичайний, Rounded, Sharp та Outline. Щоб звернутися до необхідного стилю, слід додати до назви піктограми відповідне закінчення:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://telegra.ph/file/82e157dd459fbe3e631b8.png&quot; width=&quot;470&quot; /&gt;
    &lt;figcaption&gt;Використання різних стилів для піктограм&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p&gt;Список усіх наявних Cupertino Icons наведено на &lt;a href=&quot;https://flutter.github.io/cupertino_icons/&quot; target=&quot;_blank&quot;&gt;сторінці бібліотеки&lt;/a&gt;.&lt;/p&gt;
  &lt;h3&gt;Піктограми та теми&lt;/h3&gt;
  &lt;p&gt;Усі компоненти Material Framework підпорядковуються заданій темі. Тема задається у віджеті MaterialApp, завдяки об&amp;#x27;єкту ThemeData та властивості theme.&lt;/p&gt;
  &lt;p&gt;Ви можете глобально змінити стиль за замовчуванням для усіх піктограм у вашому застосунку, використовуючи властивість iconTheme у ThemeData:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/c8/33/c833f421-1609-48f1-806c-335af36a6683.png&quot; width=&quot;537&quot; /&gt;
    &lt;figcaption&gt;Приклад використання теми для задання стилю піктограм&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p&gt;Але крім цього, тему можна перезаписати безпосередньо у дереві віджетів, використовуючи віджет IconTheme (варто помітити, при цьому на перезаписану піктограму властивості теми діяти припиняють):&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://telegra.ph/file/54b6e251a9ba96c44659a.png&quot; width=&quot;545&quot; /&gt;
    &lt;figcaption&gt;Приклад локального перезапису теми піктограми&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;h3&gt;Як працюють векторні піктограми&lt;/h3&gt;
  &lt;p&gt;У Flutter для роботи векторних піктограм використовуються так звані &lt;em&gt;icon fonts&lt;/em&gt;, тобто шрифти, що використовують символи для виводу піктограм. Насправді, коли ви використовуєте віджет Icon, Flutter створює віджет Text з спеціально заданим шрифтом. Ми переконаємося у цьому далі.&lt;/p&gt;
  &lt;h3&gt;Тип IconData&lt;/h3&gt;
  &lt;p&gt;IconData виступає контейнером для даних піктограми, що має бути виведено. Саме об&amp;#x27;єкти цього типу зберігаються у колекції Icons: наприклад, Icons.people є об&amp;#x27;єктом IconData. Давайте розглянемо конструктор цього об&amp;#x27;єкта:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://telegra.ph/file/affb39feeb60f37987ce3.png&quot; width=&quot;487&quot; /&gt;
    &lt;figcaption&gt;Конструктор IconData — саме так зберігаються піктограми у колекції Icons&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p&gt;&lt;strong&gt;Першим позиційним параметром&lt;/strong&gt; є ціле число, що позначає номер символу, який буде використано для поточної піктограми.&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;Параметр fontFamily&lt;/strong&gt; містить назву шрифта, що використовується для виводу піктограми.&lt;/p&gt;
  &lt;p&gt;Таким чином, вивід піктограми зводиться до віджету Text (\ue8f5 - позначення символу під номером e8f5):&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://telegra.ph/file/e14aff67f0c19f209b4fb.png&quot; width=&quot;756&quot; /&gt;
    &lt;figcaption&gt;Аналог віджету Icon, реалізований на Text&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p&gt;Крім того ж, у поле тексту можна помістити читабельну назву піктограми:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://telegra.ph/file/b5727c84e9d418b3a45e5.png&quot; width=&quot;753&quot; /&gt;
    &lt;figcaption&gt;Замінюємо &amp;#x27;\ue8f5&amp;#x27; на &amp;#x27;people&amp;#x27; — віджет продовжує функціонувати&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p&gt;Слід зауважити, що конструктор IconData є константним, що дозволяє компілятору виключити невикористані піктограми з програми.&lt;/p&gt;
  &lt;h3&gt;Динамічні піктограми й інтеграція з Material Framework&lt;/h3&gt;
  &lt;blockquote&gt;❗ Робіть зазначене нижче тільки якщо вам дійсно це треба. Дана практика шкодить розміру застосунку. Замість цього, ви можете, наприклад, завантажувати піктограму з мережі через NetworkImage та використовувати ImageIcon.&lt;/blockquote&gt;
  &lt;p&gt;Таким чином можна зробити висновок, що використовуючи віджет Text можна відобразити піктограму за її кодом або навіть назвою. Проте, у такого підходу є суттєвий недолік: відсутність інтеграції з темою та Material Framework у цілому, адже Flutter сприймає таку піктограму як звичайний текст. Тому, ми напишемо власний об&amp;#x27;єкт Icon, що не є константою, та який приймає назву піктограми для її відображення (див. кінець статті).&lt;/p&gt;
  &lt;p&gt;Використання:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://telegra.ph/file/ad7317c106c711b91638e.png&quot; width=&quot;517&quot; /&gt;
    &lt;figcaption&gt;Приклад використання DynamicIcon&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p&gt;Увага, через те, що конструктор не константний, tree shaking не працюватиме, що призведе до помилки компіляції, але дану поведінку можна виключили прапорцем збирання &lt;code&gt;--no-tree-shake-icons&lt;/code&gt;.&lt;/p&gt;
  &lt;p&gt;Приєднуйтесь до нашого &lt;a href=&quot;https://t.me/itkpi_dart&quot; target=&quot;_blank&quot;&gt;чату&lt;/a&gt;.&lt;/p&gt;
  &lt;h3&gt;Віджет DynamicIcon&lt;/h3&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;iframe src=&quot;https://dartpad.dev/embed-flutter.html?id=cff07341614d667d7ef9e482524c5df4&amp;split=75&amp;null_safety=true&quot;&gt;&lt;/iframe&gt;
  &lt;/figure&gt;

</content></entry></feed>