Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
HTTP клиенты в Dart/Flutter
HTTP клиент — это библиотека для выполнения HTTP запросов (GET, POST, PUT, DELETE и т.д.) к веб-сервисам и API. В Dart и Flutter существует несколько популярных вариантов, каждый со своими преимуществами.
1. http (официальный пакет Dart)
import 'package:http/http.dart' as http;
// GET запрос
Future<List<User>> fetchUsers() async {
final response = await http.get(
Uri.parse('https://api.example.com/users'),
);
if (response.statusCode == 200) {
final jsonData = jsonDecode(response.body) as List;
return jsonData.map((item) => User.fromJson(item)).toList();
} else {
throw Exception('Failed to load users: ${response.statusCode}');
}
}
// POST запрос
Future<User> createUser(String name, String email) async {
final response = await http.post(
Uri.parse('https://api.example.com/users'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({'name': name, 'email': email}),
);
if (response.statusCode == 201) {
return User.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to create user');
}
}
Преимущества:
- Официальный пакет от Dart команды
- Простой в использовании
- Минималистичный API
Недостатки:
- Нет встроенной поддержки interceptors
- Нет retry механизма
2. dio (самый популярный)
import 'package:dio/dio.dart';
class ApiClient {
late final Dio _dio;
ApiClient() {
_dio = Dio(BaseOptions(
baseUrl: 'https://api.example.com',
connectTimeout: Duration(seconds: 10),
receiveTimeout: Duration(seconds: 10),
));
_dio.interceptors.add(LoggingInterceptor());
_dio.interceptors.add(ErrorInterceptor());
}
Future<List<User>> fetchUsers() async {
try {
final response = await _dio.get('/users');
final data = response.data as List;
return data.map((item) => User.fromJson(item)).toList();
} on DioException catch (e) {
print('Error: ${e.message}');
rethrow;
}
}
Future<User> createUser(String name, String email) async {
final response = await _dio.post(
'/users',
data: {'name': name, 'email': email},
);
return User.fromJson(response.data);
}
Future<void> downloadFile(String fileUrl, String savePath) async {
await _dio.download(
fileUrl,
savePath,
onReceiveProgress: (received, total) {
print('Progress: ${(received / total * 100).toStringAsFixed(0)}%');
},
);
}
}
class LoggingInterceptor extends Interceptor {
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
print('REQUEST: ${options.method} ${options.path}');
handler.next(options);
}
@override
void onResponse(Response response, ResponseInterceptorHandler handler) {
print('RESPONSE: ${response.statusCode}');
handler.next(response);
}
}
Преимущества:
- Полная функциональность (timeout, retry, interceptors)
- Поддержка загрузки файлов
- Отличная документация
- Множество готовых интеграций
3. Chopper (генерация кода)
import 'package:chopper/chopper.dart';
part 'api_client.chopper.dart';
@ChopperApi(baseUrl: 'https://api.example.com')
abstract class ApiClient extends ChopperService {
@Get(path: '/users')
Future<Response<List<User>>> fetchUsers();
@Post(path: '/users')
Future<Response<User>> createUser(@Body() Map<String, dynamic> body);
@Put(path: '/users/{id}')
Future<Response<User>> updateUser(
@Path('id') String id,
@Body() Map<String, dynamic> body,
);
@Delete(path: '/users/{id}')
Future<Response<void>> deleteUser(@Path('id') String id);
}
Преимущества:
- Типизированный API через аннотации
- Генерация кода
- Меньше boilerplate кода
4. retrofit (для REST API)
import 'package:retrofit/retrofit.dart';
import 'package:dio/dio.dart';
part 'api_client.g.dart';
@RestApi(baseUrl: 'https://api.example.com')
abstract class ApiClient {
factory ApiClient(Dio dio, {String baseUrl}) = _ApiClient;
@GET('/users')
Future<List<User>> fetchUsers();
@POST('/users')
Future<User> createUser(@Body() User user);
@GET('/users/{id}')
Future<User> getUserById(@Path('id') String id);
@PUT('/users/{id}')
Future<User> updateUser(
@Path('id') String id,
@Body() User user,
);
@DELETE('/users/{id}')
Future<void> deleteUser(@Path('id') String id);
@GET('/search')
Future<List<User>> searchUsers(@Query('q') String query);
}
Преимущества:
- Декларативный синтаксис
- Автоматическое преобразование типов
- Очень похож на Retrofit в Android
5. graphql (для GraphQL)
import 'package:graphql/client.dart';
class GraphQLApiClient {
late GraphQLClient _client;
GraphQLApiClient() {
final httpLink = HttpLink('https://api.example.com/graphql');
final authLink = AuthLink(
getToken: () async => 'Bearer YOUR_TOKEN',
);
final link = authLink.concat(httpLink);
_client = GraphQLClient(
cache: GraphQLCache(),
link: link,
);
}
Future<Map<String, dynamic>> fetchUsers() async {
const String query = 'query GetUsers { users { id name email } }';
final result = await _client.query(
QueryOptions(document: gql(query)),
);
if (result.hasException) {
throw Exception(result.exception);
}
return result.data!;
}
Future<Map<String, dynamic>> createUser(String name, String email) async {
const String mutation = 'mutation CreateUser { createUser(name, email) { id } }';
final result = await _client.mutate(
MutationOptions(
document: gql(mutation),
variables: {'name': name, 'email': email},
),
);
if (result.hasException) {
throw Exception(result.exception);
}
return result.data!;
}
}
Сравнение HTTP клиентов
| Клиент | Сложность | Функции | GraphQL | Рекомендация |
|---|---|---|---|---|
| http | Простая | Базовые | Нет | Простые проекты |
| dio | Средняя | Полная | Нет | Большинство проектов |
| chopper | Средняя | Полная | Нет | REST API с кодогенерацией |
| retrofit | Средняя | Полная | Нет | REST API (Android-like) |
| graphql | Сложная | GraphQL | Да | GraphQL API |
Best Practices
// 1. Используй dio для большинства случаев
final dio = Dio(BaseOptions(
connectTimeout: Duration(seconds: 10),
receiveTimeout: Duration(seconds: 10),
headers: {'Authorization': 'Bearer token'},
));
// 2. Добавляй interceptors для обработки ошибок
dio.interceptors.add(ErrorInterceptor());
// 3. Используй типизацию
Future<User> getUser(String id) async {
final response = await dio.get('/users/$id');
return User.fromJson(response.data);
}
// 4. Обрабатывай ошибки правильно
try {
final users = await fetchUsers();
} on DioException catch (e) {
if (e.type == DioExceptionType.connectionTimeout) {
print('Connection timeout');
} else if (e.response?.statusCode == 404) {
print('Not found');
} else {
print('Error: ${e.message}');
}
}
Как выбрать клиент
http — если нужно минимально зависимостей и простота dio — стандартный выбор для большинства проектов retrofit — если работаешь с REST API и привык к Android graphql — если API построен на GraphQL chopper — если предпочитаешь кодогенерацию
Заключение
Большинство профессиональных Flutter приложений используют dio благодаря его мощности, гибкости и хорошей документации. Он позволяет работать с любыми API, имеет встроенную поддержку interceptors, обработку ошибок и загрузку файлов.