Перейти к основному содержимому

Простые приложения на Dart

Разработчику Архитектору

Простые приложения на Dart

Dart знаком по Flutter, но консольные скрипты (dart run) полезны для утилит и прототипов API. Стандартные библиотеки: dart:io, dart:convert.


Как запускать примеры из главы

  1. Файл: bin/main.dart в проекте dart create cli или один файл script.dart.
  2. Команда: dart run script.dart или dart run bin/main.dart.
  3. Нужно: Dart SDK 3+ (dart --version).

Генератор паролей

Как запустить

  • Файл: password.dart
  • Команда: dart run password.dart
  • Результат: пароль в консоли.

import 'dart:math';

String generatePassword([int length = 16]) {
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#\$%';
final rand = Random.secure();
return List.generate(length, (_) => chars[rand.nextInt(chars.length)]).join();
}

void main() => print(generatePassword());

Разбор:

  • import 'dart:math'; подключает генератор случайных чисел Random.
  • generatePassword([int length = 16]) объявляет функцию с необязательным параметром длины и значением по умолчанию 16.
  • const chars = ... задаёт фиксированный набор символов, из которых собирается пароль.
  • Random.secure() использует криптографически более стойкий источник случайности по сравнению с обычным Random().
  • List.generate(...) создаёт список символов по одному: rand.nextInt(chars.length) выбирает случайный индекс, затем берётся символ из chars.
  • .join() склеивает список символов в одну строку и возвращает готовый пароль.

Сортировщик файла


import 'dart:io';

Future<void> sortFile(String input, String output) async {
final lines = await File(input).readAsLines();
final cleaned = lines.map((l) => l.trim()).where((l) => l.isNotEmpty).toList()..sort();
await File(output).writeAsString('${cleaned.join('\n')}\n');
}

Разбор:

  • readAsLines() читает файл построчно и возвращает List<String>.
  • map((l) => l.trim()) убирает лишние пробелы в начале и конце каждой строки.
  • where((l) => l.isNotEmpty) фильтрует пустые строки, чтобы они не попали в итог.
  • toList()..sort() превращает поток в список и сразу сортирует его через cascade-оператор ...
  • writeAsString('${cleaned.join('\n')}\n') записывает отсортированные строки обратно в файл, разделяя их переводами строки.
  • Функция асинхронная (Future<void>) и должна вызываться с await, иначе запись может не успеть завершиться.

Калькулятор

double? calculate(double a, double b, String op) {
switch (op) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': return b == 0 ? null : a / b;
default: return null;
}
}

Разбор:

  • double? в сигнатуре означает, что функция может вернуть число или null, если операция неизвестна/недопустима.
  • switch (op) выбирает ветку по строковому оператору (+, -, *, /).
  • Ветвь case '/': return b == 0 ? null : a / b; защищает от деления на ноль через тернарный оператор.
  • default: return null; обрабатывает неподдерживаемые символы операции и даёт безопасный результат вместо падения.
  • Такая модель возвращает "нет результата" через null; в production-логике часто добавляют текст ошибки или Result-тип.

Трекер задач (jsonEncode)

Код ITЗагрузка примера кода…

Разбор:

  • Класс Task описывает модель задачи — id, title, done.
  • toJson() превращает объект в Map<String, dynamic>, пригодный для jsonEncode.
  • factory Task.fromJson(...) выполняет обратное преобразование из JSON-словаря в объект Task.
  • В load(...) сначала проверяется наличие файла exists(), чтобы безопасно вернуть пустой список, если файла ещё нет.
  • jsonDecode(...) as List парсит JSON-массив, после чего каждый элемент приводится к Map<String, dynamic> и маппится в Task.
  • Шаблон toJson/fromJson — базовая схема сериализации для локального хранилища и HTTP API.

HTTP-сервер (HttpServer)


import 'dart:convert';
import 'dart:io';

Future<void> main() async {
final server = await HttpServer.bind('127.0.0.1', 8080);
await for (final req in server) {
req.response
..headers.contentType = ContentType.json
..write(jsonEncode({'ok': true, 'time': DateTime.now().toIso8601String()}))
..close();
}
}

Разбор:

  • HttpServer.bind('127.0.0.1', 8080) поднимает локальный сервер на порту 8080.
  • await for (final req in server) запускает бесконечный асинхронный цикл обработки входящих HTTP-запросов.
  • req.response..headers.contentType = ContentType.json выставляет тип ответа application/json.
  • jsonEncode({...}) сериализует словарь с полями ok и текущим временем в строку JSON.
  • ..close() закрывает ответ и отправляет его клиенту; без закрытия соединение может зависнуть.
  • Это минимальный "health/info" endpoint, который полезен для локальной проверки сервера.

Характерный пример — Future и async/await

Future<int> fetchLength(Uri url) async {
final client = HttpClient();
try {
final req = await client.getUrl(url);
final res = await req.close();
final body = await res.transform(utf8.decoder).join();
return body.length;
} finally {
client.close();
}
}

Разбор:

  • Future<int> показывает, что результат появится позже и будет числом (int).
  • HttpClient создаёт низкоуровневый HTTP-клиент из dart:io для запроса по URL.
  • await client.getUrl(url) и await req.close() по шагам отправляют запрос и получают ответ.
  • res.transform(utf8.decoder).join() конвертирует байты ответа в строку и собирает полный body.
  • return body.length; возвращает длину текста ответа как метрику.
  • Блок finally { client.close(); } гарантирует закрытие сокетов даже при ошибке, что защищает от утечек ресурсов.

Асинхронность встроена в язык — основа Flutter и серверного Dart.


См. также: Flutter в разделе 4-code-dev

Основа по протоколу

Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.