Отладка — важная часть разработки. Разберём инструменты и техники для эффективной отладки Flutter приложений.
Dart DevTools
Запуск DevTools
# Запуск через CLI
flutter pub global run devtools
# Или при запуске приложения
flutter run --profile
Основные функции DevTools
- Flutter Inspector — исследование виджетов
- Performance — анализ производительности
- Memory — профилирование памяти
- Network — анализ сетевых запросов
- Logging — просмотр логов
Flutter Inspector
Tree view
<MyApp>
<MaterialApp>
<Scaffold>
<CustomScrollView>
<SliverAppBar>
<ListView>
Widget select
// Используйте ключи для идентификации виджетов
Scaffold(
key: Key('home-scaffold'),
appBar: AppBar(title: Text('Home')),
)
Property Inspector
Просмотр свойств виджета в реальном времени.
Breakpoints
Установка breakpoints
В VS Code / Android Studio:
void main() {
runApp(MyApp()); // Кликните слева для breakpoint
}
Conditional breakpoints
int counter = 0;
for (int i = 0; i < 100; i++) {
counter += i; // Breakpoint когда i == 50
}
Logpoint
Вместо breakpoint добавьте лог:
counter += i; // Logpoint: print('Counter: $counter')
Debugging print statements
print('Simple debug message');
// С переменными
final user = User(name: 'John');
print('User: ${user.name}');
debugPrint
// Автоматически разбивает длинные строки
debugPrint('Very long string that will be split...');
Flutter DevTools logging
import 'package:flutter/foundation.dart';
void main() {
// Различные уровни логирования
debugPrint('Info message');
// Или использовать foundation
}
structured logging
Logger пакет
dependencies:
logger: ^2.0.0
import 'package:logger/logger.dart';
final logger = Logger();
void main() {
logger.d('Debug message');
logger.i('Info message');
logger.w('Warning message');
logger.e('Error message');
// С данными
logger.i({'user': 'John', 'action': 'login'});
// Исключения
try {
throw Exception('Something went wrong');
} catch (e) {
logger.e('Error occurred', e);
}
}
Custom logger
class AppLogger {
static const _isDebugMode = kDebugMode;
static void debug(String message) {
if (_isDebugMode) {
print('[DEBUG] $message');
}
}
static void info(String message) {
if (_isDebugMode) {
print('[INFO] $message');
}
}
static void warning(String message) {
print('[WARNING] $message');
}
static void error(String message, {Object? error, StackTrace? stackTrace}) {
print('[ERROR] $message');
if (error != null) {
print(error);
}
if (stackTrace != null) {
print(stackTrace);
}
}
}
Error handling
Try-catch
Future<void> fetchData() async {
try {
final response = await http.get(Uri.parse('https://api.example.com'));
// Обработка ответа
} on SocketException {
print('No internet connection');
} on HttpException {
print('HTTP error');
} catch (e, stackTrace) {
print('Unexpected error: $e');
print(stackTrace);
}
}
Custom exceptions
class AppException implements Exception {
final String message;
final int? code;
AppException(this.message, {this.code});
@override
String toString() => 'AppException: $message (code: $code)';
}
// Использование
try {
// dangerous operation
} on AppException catch (e) {
print('Handled app exception: $e');
} catch (e) {
print('Unexpected error: $e');
}
ErrorWidget
class MyErrorWidget extends StatelessWidget {
final FlutterErrorDetails details;
const MyErrorWidget(this.details, {super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error, size: 48, color: Colors.red),
SizedBox(height: 16),
Text('Error occurred'),
SizedBox(height: 8),
Text(
details.exception.toString(),
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
);
}
}
void main() {
ErrorWidget.builder = (details) {
return MyErrorWidget(details);
};
runApp(MyApp());
}
Crash reporting
Sentry
dependencies:
sentry_flutter: ^8.0.0
import 'package:sentry_flutter/sentry_flutter.dart';
Future<void> main() async {
await SentryFlutter.init(
(options) {
options.dsn = 'YOUR_SENTRY_DSN';
options.tracesSampleRate = 1.0;
},
);
runApp(MyApp());
}
// Отправка исключений
try {
// dangerous operation
} catch (exception, stackTrace) {
await Sentry.captureException(
exception,
stackTrace: stackTrace,
);
}
// Отправка сообщений
await Sentry.captureMessage('Something happened');
// Breadcrumbs
Sentry.addBreadcrumb(
Breadcrumb(
message: 'User clicked button',
category: 'user',
),
);
Firebase Crashlytics
dependencies:
firebase_crashlytics: ^4.0.0
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
Future<void> main() async {
await Firebase.initializeApp();
// Установка Crashlytics
FlutterError.onError = (errorDetails) {
FirebaseCrashlytics.instance.recordFlutterFatalError(errorDetails);
};
runApp(MyApp());
}
// Запись исключения
try {
// dangerous operation
} catch (e, stack) {
await FirebaseCrashlytics.instance.recordError(e, stack);
}
// Пользовательские ключи
await FirebaseCrashlytics.instance.setCustomKey('user_id', '123');
// Пользовательские логи
await FirebaseCrashlytics.instance.log('App started');
Performance profiling
Performance overlay
MaterialApp(
showPerformanceOverlay: true,
home: MyApp(),
)
Timeline
import 'package:flutter/foundation.dart';
void main() {
runApp(MyApp());
if (kDebugMode) {
// Профилирование включено
}
}
Dart DevTools Performance
- Запустите приложение в
profileрежиме - Откройте DevTools
- Перейдите в Performance tab
- Запишите профиль
- Проанализируйте результаты
Network debugging
Dio logging interceptor
dependencies:
pretty_dio_logger: ^1.3.0
final dio = Dio();
dio.interceptors.add(
PrettyDioLogger(
requestHeader: true,
requestBody: true,
responseBody: true,
error: true,
),
);
Charles Proxy
Для локального перехвата HTTPS запросов настройте Charles Proxy.
Remote debugging
Flutter Remote Debugging
flutter attach --debug-url=http://localhost:8080
Debugging в production
Remote logging
class RemoteLogger {
Future<void> log(String message) async {
await http.post(
Uri.parse('https://your-api.com/logs'),
body: {
'message': message,
'timestamp': DateTime.now().toIso8601String(),
'platform': Platform.operatingSystem,
},
);
}
}
Flag для отключения
class Config {
static const bool enableDebug = kDebugMode;
static const bool enableRemoteLogging = !kDebugMode;
}
Best Practices
1. Используйте structured logging
logger.i('User logged in', error: null, stackTrace: null);
2. Добавляйте контекст
try {
// operation
} catch (e, stackTrace) {
logger.e('Failed to load user data',
error: e,
stackTrace: stackTrace,
);
}
3. Используйте breadcrumbs
Sentry.addBreadcrumb(
Breadcrumb(message: 'Navigated to home screen'),
);
4. Логируйте важные события
await FirebaseCrashlytics.instance.log('Purchase completed: $orderId');
Заключение
Отладка Flutter приложений в 2025 — это мощный набор инструментов. Используйте DevTools для профилирования и Sentry/Crashlytics для мониторинга production.