Ответ
Ключевое слово external в Dart используется для объявления функции или метода, реализация которого предоставляется вне Dart-кода — обычно в нативной платформе (через dart:ffi) или в платформо-специфичных имплементациях Dart SDK.
Основные сценарии использования в моей практике с Flutter:
- Интеграция с нативным кодом через dart:ffi
import 'dart:ffi'; import 'package:ffi/ffi.dart';
// Объявление external функции из нативной библиотеки final DynamicLibrary nativeLib = DynamicLibrary.open('libnative_audio.dylib');
// External функция, реализованная в C/C++
external int processAudioFrame(
Pointer
// Использование в Dart-коде
void processAudio(List
for (var i = 0; i < input.length; i++) { inputPtr[i] = Float(input[i]); }
final result = processAudioFrame(inputPtr, outputPtr, input.length);
// Обработка результата... calloc.free(inputPtr); calloc.free(outputPtr); }
2. **Платформо-специфичные реализации в Dart SDK**
При изучении исходного кода Dart SDK можно встретить такие конструкции:
```dart
// В dart:io
abstract class File {
external factory File(String path);
external Future<RandomAccessFile> open({FileMode mode = FileMode.read});
// Реализация предоставляется отдельно для каждой платформы
// через аннотацию @patch в patch-файлах
}
-
Генерация кода через аннотации
// В библиотеках для кодогенерации @JsonSerializable() class User { final String name; final int age; User(this.name, this.age); // External factory, реализация генерируется build_runner external factory User.fromJson(Map<String, dynamic> json); external Map<String, dynamic> toJson(); }
Ограничения и особенности:
externalчлены не могут бытьasync,sync*, илиasync*- Реализация должна быть предоставлена во время выполнения
- Часто используется с аннотацией
@patchв исходном коде Dart SDK - Требует точного соответствия сигнатур между объявлением и реализацией
Практический пример из Flutter-разработки: При создании плагина для Flutter, который использует нативные API iOS/Android, external методы позволяют объявить интерфейс в Dart, в то время как реализация предоставляется в Objective-C/Swift или Java/Kotlin через механизм платформенных каналов (Platform Channels) или dart:ffi.
Ответ 18+ 🔞
А, ну вот, про external в Dart! Это же, блядь, такая штука, которая как будто говорит: «Слушай, дружок, реализация этой функции где-то там, за горами, за лесами, в нативном коде, а я тут просто красивая вывеска». Ёпта, прям как обещание политика перед выборами — звучит здорово, а где конкретика — хуй его знает.
Основные сценарии, где эта мартышлюшка вылезает:
-
Когда надо пообщаться с нативным кодом через
dart:ffiТут вообще история отдельная. Представь: у тебя есть какая-нибудь библиотека на C, которая делает что-то офигенное, например, аудио обрабатывает со скоростью света. А ты в своём уютном Dart-мирке сидишь. Как до неё достучаться? Вот тут и появляется нашexternal, как волшебная дверь в другой мир.import 'dart:ffi'; import 'package:ffi/ffi.dart'; // Открываем нативную библиотеку, как банку пива после работы final DynamicLibrary nativeLib = DynamicLibrary.open('libnative_audio.dylib'); // А вот и наша загадочная external-функция. Где её тело? Да в той самой C-шной библиотеке! external int processAudioFrame( Pointer<Float> inputBuffer, Pointer<Float> outputBuffer, int frameSize, ); // Использование в Dart-коде void processAudio(List<double> input) { final inputPtr = calloc<Float>(input.length); final outputPtr = calloc<Float>(input.length); for (var i = 0; i < input.length; i++) { inputPtr[i] = Float(input[i]); } // И тут мы вызываем эту магию. Вжух — и выполнение уходит в нативный код. final result = processAudioFrame(inputPtr, outputPtr, input.length); // Обработка результата... calloc.free(inputPtr); calloc.free(outputPtr); }Чувствуешь? Мы как бы говорим Dart: «Поверь мне на слово, такая функция есть. Когда дойдёт до дела — я тебе её подсуну». Доверия ебать ноль, но система работает.
-
Внутри самого Dart SDK, для платформо-специфичных вещей Если когда-нибудь полезешь в исходники самого Dart (что само по себе приключение), увидишь такое. Вот, например, класс
Fileизdart:io. Он же на разных операционках по-разному работает! Ну не может один и тот же код и на Windows, и на Linux, и на macOS файлы одинаково открывать. Поэтому там внутри делают так:abstract class File { // Смотри-ка, external factory! То есть сама логика создания объекта File — где-то снаружи. external factory File(String path); // И открытие файла — тоже external. Реализация спрятана в «патчах» для каждой ОС. external Future<RandomAccessFile> open({FileMode mode = FileMode.read}); }Это гениально и немного по-пидорски. Вместо того чтобы городить
if (windows) {...} else if (linux) {...}, они просто объявляют интерфейс, а потом в отдельных файлах-заплатках (@patch) подсовывают правильную реализацию под каждую платформу. Хитрая жопа, не спорю. -
В кодогенерации, когда реализацию напишет за тебя машинка Любишь аннотации вроде
@JsonSerializable()? Так вот, под капотом там может использоватьсяexternalкак раз для того, чтобы сказать: «Эй, кодогенератор, вот тут будет методfromJson, но я его писать не буду — ты его сгенерируй сам, умница».@JsonSerializable() class User { final String name; final int age; User(this.name, this.age); // External factory. Когда запустишь build_runner, он увидит эту аннотацию // и нагенерирует тебе реальную реализацию этого метода где-то в глубинах .g.dart файла. external factory User.fromJson(Map<String, dynamic> json); external Map<String, dynamic> toJson(); }Удобно, правда? Написал заголовок, а всю грязную работу за тебя делает автоматика. Лень — двигатель прогресса, ёпта.
Что нельзя и на что обратить внимание (чтоб не обосраться):
- Функция с
externalне может бытьasync,sync*илиasync*. Она должна быть прямой и честной, без этих ваших потоковых извращений. - Это не магия. Реализация обязательно должна быть предоставлена где-то в другом месте, иначе в рантайме получишь ядрёную вошь в виде ошибки.
- Сигнатура (типы аргументов и возвращаемого значения) должна идеально совпадать между объявлением в Dart и реальной реализацией в нативном коде. Одно несовпадение — и пиzдец, приехали.
Из практики Flutter-разработки:
Допустим, пишешь ты плагин, которому нужно зажечь вспышку на камере. API для этого — сугубо нативное, на Java/Kotlin для Android и на Obj-C/Swift для iOS. Ты в Dart-части плагина объявляешь external-метод, который будет вызывать нативную функцию через dart:ffi или через старые добрые Platform Channels. Получается такой мостик между мирами. Dart кричит: «Эй, Андроид, включи вспышку!», а external — это та самая труба, по которой этот крик доходит.
Короче, external — это как дипломатический паспорт для функций. Сама функция живёт в другой стране (в нативном коде или в сгенерированном коде), но у неё есть права приходить и работать на территории Dart. Главное — чтобы паспорт был настоящий, а не хуй в пальто.