После освоения базового синтаксиса Dart и концепций объектно-ориентированного программирования пришло время взглянуть на то, как структурировать более крупные проекты и эффективно использовать существующие решения.
Dart предоставляет надежную экосистему управления пакетами и зависимостями, которая помогает разработчикам организовывать код, повторно использовать решения и интегрировать внешние библиотеки. Давайте разберемся в этой системе.
В Dart библиотека — это единица организации кода, которая предоставляет определенный набор функциональности.
Каждый файл Dart является библиотекой (даже если это явно не указано). Библиотеки позволяют разделить код на логические части, обеспечивая инкапсуляцию и возможность повторного использования.
// Пример объявления библиотеки
library my_utilities;
// Функции, классы и переменные библиотеки
int add(int a, int b) {
return a + b;
}
class Calculator {
double multiply(double a, double b) {
return a * b;
}
}
Чтобы использовать библиотеку в другом файле, мы используем директиву import
:
// Импорт локальной библиотеки
import 'my_utilities.dart';
void main() {
var result = add(5, 3);
print('5 + 3 = $result');
var calc = Calculator();
print('4 * 2.5 = ${calc.multiply(4, 2.5)}');
}
Dart позволяет контролировать, какие части библиотеки будут доступны другим:
// Файл: my_library.dart
library my_library;
// Публичный класс, доступен везде
class PublicClass {
void publicMethod() {
print('Публичный метод');
}
// Приватный метод, доступен только внутри библиотеки
void _privateMethod() {
print('Приватный метод');
}
}
// Приватная функция, доступна только внутри библиотеки
void _privateFunction() {
print('Приватная функция');
}
// Публичная функция, доступна везде
void publicFunction() {
print('Публичная функция');
}
В Dart соглашение о приватности основано на символе подчеркивания (_
):
_
, являются приватными и доступны только внутри библиотекиПакет — это набор библиотек и других ресурсов (например, изображений, файлов конфигурации), объединенных для распространения и повторного использования.
Пакеты в Dart бывают двух основных типов:
Базовая структура пакета Dart выглядит так:
my_package/
│
├── .dart_tool/ # Служебная директория для инструментов Dart
├── lib/ # Код библиотеки, который можно импортировать
│ ├── src/ # Внутренние реализации (обычно приватные)
│ │ └── utils.dart
│ └── my_package.dart # Основная точка входа в библиотеку
├── test/ # Тесты
├── example/ # Примеры использования
├── pubspec.yaml # Метаданные пакета и зависимости
└── README.md # Документация
Файл pubspec.yaml
играет особую роль — он описывает пакет, его метаданные и зависимости:
name: my_package
description: A sample package demonstrating Dart packaging.
version: 1.0.0
environment:
sdk: ">=3.1.0 <4.0.0"
dependencies:
http: ^1.1.0
path: ^1.8.3
dev_dependencies:
test: ^1.24.0
lints: ^2.1.1
Зависимости — это внешние пакеты, которые использует ваш проект. Dart использует инструмент pub
для управления зависимостями.
Чтобы добавить зависимость, нужно указать ее в pubspec.yaml
:
dependencies:
http: ^1.1.0 # Пакет для HTTP-запросов
После этого выполните команду для загрузки зависимостей:
dart pub get
или для Flutter проектов:
flutter pub get
После установки зависимость можно импортировать в код:
import 'package:http/http.dart' as http;
Future<void> fetchData() async {
final response = await http.get(Uri.parse('https://api.example.com/data'));
if (response.statusCode == 200) {
print('Получены данные: ${response.body}');
} else {
print('Ошибка: ${response.statusCode}');
}
}
В pubspec.yaml
можно указать различные типы зависимостей:
dependencies:
# Зависимость из pub.dev с указанием версии
http: ^1.1.0
# Локальная зависимость (путь к директории)
my_local_package:
path: ../my_local_package
# Зависимость из Git-репозитория
my_git_package:
git:
url: https://github.com/user/repo.git
ref: main # Ветка, тег или коммит
Dart использует семантическое версионирование (SemVer) для управления версиями пакетов:
MAJOR.MINOR.PATCH
В pubspec.yaml
можно указать различные ограничения версий:
dependencies:
package1: "1.0.0" # Точная версия
package2: ">=1.0.0 <2.0.0" # Диапазон версий
package3: "^1.2.3" # Совместимые версии (≥1.2.3 <2.0.0)
package4: "~1.2.3" # Близкие версии (≥1.2.3 <1.3.0)
Давайте создадим простую библиотеку, которую можно будет использовать в других проектах:
string_utils/
├── lib/
│ ├── src/
│ │ └── formatters.dart
│ └── string_utils.dart
└── pubspec.yaml
pubspec.yaml
:name: string_utils
description: Utilities for string manipulation in Dart.
version: 1.0.0
environment:
sdk: ">=3.1.0 <4.0.0"
lib/src/formatters.dart
:/// Преобразует строку в формат заголовка (первые буквы слов заглавные)
String toTitleCase(String text) {
if (text.isEmpty) return text;
return text.split(' ').map((word) {
if (word.isEmpty) return word;
return word[0].toUpperCase() + word.substring(1).toLowerCase();
}).join(' ');
}
/// Удаляет лишние пробелы из строки
String removeExtraSpaces(String text) {
return text.replaceAll(RegExp(r'\s+'), ' ').trim();
}
lib/string_utils.dart
:/// Библиотека для манипуляции строками
library string_utils;
// Экспортируем функции, которые должны быть доступны пользователям
export 'src/formatters.dart';
// Дополнительные публичные функции
String reverseString(String text) {
return String.fromCharCodes(text.runes.toList().reversed);
}
import 'package:string_utils/string_utils.dart';
void main() {
var text = 'hello WORLD';
print(toTitleCase(text)); // "Hello World"
print(removeExtraSpaces(text)); // "hello WORLD"
print(reverseString(text)); // "DLROW olleh"
}
pub.dev — официальный репозиторий пакетов для Dart и Flutter. Здесь разработчики могут:
dart pub search http
dart pub publish
Некоторые популярные пакеты для Dart:
Для эффективной организации крупных проектов рекомендуется:
Разделяйте код на библиотеки по функциональности:
lib/
├── src/
│ ├── api/
│ ├── models/
│ ├── services/
│ └── utils/
└── my_app.dart
Используйте экспорты для создания чистого API:
// lib/models.dart
export 'src/models/user.dart';
export 'src/models/product.dart';
Скрывайте внутренние реализации в директории src
:
// Публичный API
import 'package:my_app/models.dart';
// Внутренняя реализация, лучше не использовать напрямую
import 'package:my_app/src/models/user.dart';
Создавайте внутренние мини-пакеты для повторного использования кода:
my_project/
├── packages/
│ ├── auth_client/
│ └── api_models/
└── apps/
├── mobile_app/
└── web_app/
import 'package:http/http.dart' as http;
import 'dart:convert';
Future<List<User>> fetchUsers() async {
final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/users'));
if (response.statusCode == 200) {
List<dynamic> data = jsonDecode(response.body);
return data.map((json) => User.fromJson(json)).toList();
} else {
throw Exception('Failed to load users');
}
}
class User {
final int id;
final String name;
final String email;
User({required this.id, required this.name, required this.email});
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'],
name: json['name'],
email: json['email'],
);
}
}
import 'package:intl/intl.dart';
void showFormattedDates() {
final now = DateTime.now();
// Форматирование даты
final dateFormatter = DateFormat('dd.MM.yyyy');
print('Дата: ${dateFormatter.format(now)}'); // например: 10.05.2025
// Форматирование времени
final timeFormatter = DateFormat('HH:mm:ss');
print('Время: ${timeFormatter.format(now)}'); // например: 14:30:45
// Локализованные форматы
final russianFormatter = DateFormat.yMMMMd('ru');
print('По-русски: ${russianFormatter.format(now)}'); // например: 10 мая 2025 г.
// Форматирование чисел
final numberFormatter = NumberFormat('#,###.##', 'ru');
print('Число: ${numberFormatter.format(1234567.89)}'); // 1 234 567,89
}
Минимизируйте публичное API
src
Четко определяйте версии зависимостей
Документируйте ваш код
///
для документированияexample/
Тестируйте библиотеки
test/
Следуйте соглашениям по именованию
lower_case_with_underscores
lower_case_with_underscores.dart
Система пакетов и библиотек Dart предоставляет мощные инструменты для структурирования кода, повторного использования решений и интеграции внешних компонентов. Понимание этой системы значительно повышает продуктивность разработки.
В этой статье мы рассмотрели:
pubspec.yaml
Эффективное использование пакетов и библиотек — это навык, который развивается с опытом. Начните с малого, изучайте существующие пакеты и постепенно внедряйте лучшие практики в свои проекты.
Задача: Создать библиотеку для работы с математическими функциями и использовать ее в отдельном проекте.
// Создайте библиотеку с базовыми математическими операциями
// Включите функции для работы с векторами, матрицами, и т.д.
// Структурируйте код с использованием приватных и публичных компонентов
Задача: Создать приложение, которое использует несколько внешних пакетов для решения задачи.
// Создайте приложение, которое:
// 1. Получает данные по HTTP
// 2. Анализирует JSON
// 3. Форматирует результаты
// 4. Сохраняет данные локально
Задача: Разделить монолитное приложение на модули (библиотеки).
// Возьмите ваше существующее приложение и разделите его на:
// - Модуль работы с API
// - Модуль бизнес-логики
// - Модуль представления (UI для Flutter)
// - Модуль утилит
Задача: Создать пакет, готовый к публикации на pub.dev.
// Создайте пакет, который:
// - Решает конкретную задачу
// - Имеет полную документацию
// - Включает тесты
// - Содержит примеры использования
Задача: Создать проект с несколькими окружениями и разными зависимостями.
// Создайте проект с отдельными конфигурациями для:
// - Разработки
// - Тестирования
// - Производства
// Управляйте зависимостями для каждого окружения