Консоль, файлы и HTTP в Dart
Консоль, файлы и HTTP в Dart
Dart часто воспринимают только через Flutter, но тот же язык подходит для консольных утилит, скриптов автоматизации и лёгких серверов. Для этого используют библиотеку dart:io (доступна в Dart VM на десктопе и сервере; в браузерной сборке её нет — там другие API).
Связанные темы: первая программа, асинхронность, архитектура и dart:io.
Консольный ввод и вывод
Вывод в терминал — функция print из dart:core (уже в первой программе). Для потоков без буферизации и stderr:
import 'dart:io';
void main() {
stdout.writeln('Введите имя:');
final name = stdin.readLineSync()?.trim() ?? 'Гость';
stderr.writeln('(отладка: прочитано ${name.length} символов)');
print('Привет, $name!');
}
Разбор:
stdout.writeln(...)пишет подсказку в стандартный поток вывода с переводом строки.stdin.readLineSync()читает ввод пользователя синхронно;?.trim()убирает лишние пробелы.?? 'Гость'подставляет значение по умолчанию, если ввод пустой илиnull.stderr.writeln(...)выводит диагностическое сообщение в поток ошибок, что полезно для логирования.print('Привет, $name!')показывает итоговую строку с интерполяцией имени.
| API | Назначение |
|---|---|
stdout | Обычный вывод |
stderr | Сообщения об ошибках и диагностика |
stdin.readLineSync() | Строка до Enter (синхронно) |
В интерактивных CLI предпочтительнее асинхронное чтение, чтобы не блокировать event loop при сложной логике:
Future<void> main() async {
stdout.write('Число: ');
final line = await stdin.readLine();
final n = int.tryParse(line?.trim() ?? '');
if (n == null) {
print('Некорректный ввод');
return;
}
print('Квадрат: ${n * n}');
}
Разбор:
- Асинхронный
main(Future<void> main() async) позволяет использоватьawaitпрямо в точке входа. stdout.write('Число: ')печатает приглашение без перевода строки.await stdin.readLine()читает строку без блокировки event loop.int.tryParse(...)безопасно парсит число и возвращаетnull, если формат неверный.- Ветка
if (n == null)обрабатывает ошибку ввода и завершает функцию черезreturn. - При корректном вводе вычисляется квадрат
n * nи выводится пользователю.
Шаблон проекта: dart create -t console my_tool — см. установку и запуск.
Работа с файлами и каталогами
Классы File, Directory из dart:io описывают пути в файловой системе.
Текст:
import 'dart:io';
Future<void> copyText(String from, String to) async {
final source = File(from);
if (!await source.exists()) {
throw FileSystemException('файл не найден', from);
}
final text = await source.readAsString();
await File(to).writeAsString(text);
}
Разбор:
File(from)создаёт объект пути к исходному файлу.await source.exists()проверяет наличие файла до чтения.throw FileSystemException(...)явно сообщает вызывающему коду о проблеме пути.readAsString()загружает весь текст в память, аwriteAsString(text)записывает его в новый файл.- Функция асинхронная, поэтому операции I/O выполняются через
awaitбез блокировки.
Построчное чтение (логи, CSV):
Future<int> countLines(String path) async {
var count = 0;
await for (final _ in File(path).openRead().transform(utf8.decoder).transform(const LineSplitter())) {
count++;
}
return count;
}
Разбор:
openRead()открывает поток байтов для файла, что подходит для больших данных.transform(utf8.decoder)превращает байты в текст.transform(const LineSplitter())разбивает поток на отдельные строки.await for (...)перебирает события потока асинхронно и увеличивает счётчик.- В итоге функция возвращает общее количество строк без чтения всего файла целиком в память.
Для utf8 и LineSplitter нужен import 'dart:convert';.
Каталоги:
Future<void> ensureDataDir() async {
final dir = Directory('data');
if (!await dir.exists()) {
await dir.create(recursive: true);
}
await for (final entity in dir.list()) {
print(entity.path);
}
}
Разбор:
Directory('data')создаёт ссылку на каталог относительно текущей рабочей директории.exists()проверяет, есть ли папка, аcreate(recursive: true)создаёт её вместе с отсутствующими родителями.await for (final entity in dir.list())асинхронно перечисляет содержимое каталога.print(entity.path)выводит путь каждого найденного файла/подкаталога.- Такой шаблон полезен для подготовительного шага утилит, которые работают с локальными данными.
Бинарные данные — readAsBytes / writeAsBytes; для больших файлов — потоки openRead() / openWrite() без загрузки всего файла в память.
JSON — сохранение и загрузка
Сериализация через dart:convert не требует сторонних пакетов для простых структур:
Код ITЗагрузка примера кода…
Разбор:
jsonEncode(config)сериализует словарь конфигурации в JSON-строку.writeAsString(json)сохраняет JSON в файлconfig.json.- В
loadConfig()проверкаexists()предотвращает падение при первом запуске и возвращает{}. jsonDecode(raw)парсит JSON обратно в объект, затем выполняется приведение кMap<String, dynamic>.- Это минимальный цикл "сохранить/загрузить" для настроек CLI-приложения.
Для сложных моделей в production-проектах генерируют код (json_serializable, freezed); на этапе обучения достаточно ручного Map или record с явным маппингом полей.
HTTP-клиент
Встроенный HttpClient (dart:io) подходит для скриптов и сервисов без лишних зависимостей:
Код ITЗагрузка примера кода…
Разбор:
HttpClient()создаёт HTTP-клиент низкого уровня изdart:io.getUrl(Uri.parse(...))формирует GET-запрос,request.close()отправляет его и получаетHttpClientResponse.- Проверка
statusCode != 200обрабатывает неуспешные ответы как исключениеHttpException. response.transform(utf8.decoder).join()собирает тело ответа в строку.jsonDecode(body)разбирает JSON и даёт доступ к полям (data['origin']).finally { client.close(force: true); }гарантирует освобождение сетевых ресурсов.
В приложениях на Flutter чаще берут пакет http или dio — они проще в тестах и повторном использовании. Пример GET и списка на экране — галерея Lab; общий контекст запросов — Fetch / axios (Lab). Для консольного Dart достаточно HttpClient или package:http.
Всегда оборачивайте сетевые вызовы в try/catch и закрывайте клиент в finally, как в обработке ошибок.
Минимальный HTTP-сервер
Тот же dart:io позволяет поднять лёгкий сервер для учебных задач и локальных API:
Код ITЗагрузка примера кода…
Разбор:
HttpServer.bind(InternetAddress.loopbackIPv4, 8080)поднимает локальный сервер на127.0.0.1:8080.await for (final request in server)организует бесконечный цикл обработки входящих запросов.- Условие
request.uri.path == '/health'реализует endpoint проверки состояния. - В успешной ветке выставляется
HttpStatus.okи тело ответа'ok', иначеHttpStatus.notFound. await request.response.close()обязательно завершает отправку ответа клиенту.
Для реального бэкенда обычно выбирают фреймворки уровня Shelf, Serverpod или отдельный стек (Node, Go и т.д.); встроенный HttpServer полезен, чтобы понять цикл "запрос → ответ" без лишних слоёв. Общие принципы HTTP — в справочнике по HTTP энциклопедии.
Асинхронность и изоляты
Чтение файлов и сеть — операции ввода-вывода. Их выполняют через Future и async/await (функции и асинхронность), чтобы не блокировать единственный поток изолята. Тяжёный разбор большого файла можно вынести в Isolate.run, если утилита начинает "подвисать" на больших объёмах данных.
Ограничения платформ
| Среда | dart:io | Примечание |
|---|---|---|
| Консоль, сервер, desktop VM | Да | Основной сценарий статьи |
| Flutter mobile/desktop | Да | Плюс доступ к нативным API через плагины |
| Flutter Web / dart2js | Нет | Файлы и сокеты — через браузерные API и пакеты |
Перед переносом консольного скрипта в веб-проект проверьте, что код не импортирует dart:io без условной компиляции.
Типичные ошибки
- Забыли
awaitприreadAsString— получилиFuture, а не текст. - Путь к файлу без
Directory.current/Platform.pathSeparator— ломается при смене ОС. - Не закрыли
HttpClient— утечка сокетов при многократных запросах. - Синхронное
readLineSyncв UI-изоляте Flutter — риск подвисания интерфейса (там только асинхронные API).
Что изучить дальше
После консольного Dart логично углубить паттерны и switch для разбора ответов API и перейти к Flutter, если цель — пользовательские приложения. Готовые экраны с http и FutureBuilder — Flutter — готовые виджеты. Для серверной разработки в экосистеме Dart смотрите также разделы энциклопедии про сети и бэкенд.
Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.