В прошлый раз рассказала, как встроить расширение прямо в существующий pub-пакет. Сегодня разберем, как взаимодействовать с кодом приложения извне и динамически выполнять Dart-код. Поговорим об EvalOnDartLibrary.
EvalOnDartLibrary — это класс из пакета devtools_app_shared, который позволяет выполнять Dart-код прямо из вашего расширения DevTools.
Благодаря нему можно:
▫️управлять состоянием приложения
▫️вызывать методы
▫️получать значения из рантайма
Как это возможно?
1. Инициализация
Future<void> initEval() async {
await serviceManager.onServiceAvailable; // Убедимся, что vmService доступен
_controllerEval = EvalOnDartLibrary(
'package:some_package/src/controller.dart', // Путь к библиотеке, где находится нужный код
serviceManager.service!, // Передаем vmService
serviceManager: serviceManager,
);
evalDisposable = Disposable(); // Обязательно создаем Disposable
}
Пояснения:
◽️ 'package:...' — путь до нужного кода
◽️ serviceManager.service! — экземпляр
VmService◽️ Disposable — защищает от утечек памяти при закрытии DevTools
2. Выполнение кода
EvalOnDartLibrary предоставляет три метода:
▪️asyncEval — асинхронное выполнение кода
▪️ eval — синхронное выполнение
▪️ evalInstance — получение экземпляра объекта (для дальнейшей работы с его полями и методами)
▪️ safeEval — безопасная обертка над eval, которая дополнительно обрабатывает ошибки выполнения
Ограничение: у eval и asyncEval есть лимит на размер возвращаемых данных.
Рекомендуется:
1. Сначала вызвать метод через asyncEval или eval
2. Потом получить значение через evalInstance.
Future<String?> _getValue() async {
await _controllerEval.asyncEval(
'await SomeController.instance.calculateValue()', // Запуск асинхронной функции
isAlive: evalDisposable,
);
final result = await _controllerEval.evalInstance(
'SomeController.instance.sum.value', // Получение значения после выполнения
isAlive: evalDisposable,
); // Результатом является объект типа Instanse
return result?.valueAsString; // Возвращаем строковое значение
}
Не забывайте передавать isAlive: evalDisposable — без этого могут быть утечки памяти!
Что такое Instance?
Метод evalInstance возвращает Instance — ссылку на результат выполнения кода. Вы можете его преобразовать в строку через valueAsString. Но убедитесь, что значение действительно можно привести к строке (иначе может быть ошибка).
Пример:
final result = await _controllerEval.evalInstance(
'SomeController.instance.sum.value.toString()',
isAlive: evalDisposable,
);
final value = result?.valueAsString;
Мини-памятка
✔️Используйте Disposable для управления жизненным циклом
✔️ Следите за ограничением на размер результата
✔️ Передавайте только валидные Dart-выражения
✔️ При изменениях в проекте обновляйте пути в EvalOnDartLibrary
Для меня это был неплохой опыт с DevTools — я даже не знала, что так можно. А еще через serviceManager можно получить код приложения через id изолята (но там свои нюансы).
На этом мы заканчиваем нашу мини-серию по расширениям DevTools! Я рассказала далеко не все, но теперь вам будет гораздо проще ориентироваться🚀
Подробнее о методах EvalOnDartLibrary — в комментариях👇