Skip to main content
An external function in Dart is a function declaration that asserts the existence of an implementation provided by the underlying platform, the Dart runtime, or a foreign function interface (FFI), rather than defining the logic within the Dart source code itself. It separates the function’s type signature from its execution logic.

Syntax and Declaration

The external modifier precedes the function signature. Unlike standard functions, external functions are terminated with a semicolon (;) and do not contain a function body { ... }.
// Top-level external function
external void clearSystemCache();

class FileHandler {
  // External instance method
  external String readNativeString(int address);

  // External static method
  external static int getProcessId();

  // External getter
  external int get memoryUsage;
}

Mechanism and Semantics

  1. Signature Definition: The Dart compiler uses the external declaration to enforce type safety (parameter types and return types) during static analysis, treating the function as if it were fully implemented.
  2. Concrete Status: Although external functions lack a body, they are considered concrete members. They are distinct from abstract methods, which are intended to be overridden by subclasses.
  3. Implementation Resolution: The actual implementation is bound at runtime or compile-time depending on the compilation target and annotations.
    • Dart Native (VM/AOT): The implementation is typically provided by C/C++ code via dart:ffi (specifically Native Assets) or internal VM patch files.
    • Dart Web: The implementation is mapped to JavaScript code via dart:js_interop.

Implementation Binding

Invoking an external function without a bound implementation results in a runtime error (typically a NoSuchMethodError). Binding is usually achieved through metadata annotations.

FFI Context (Native Assets)

In the context of dart:ffi, the external keyword is used in conjunction with the @Native annotation. This mechanism, known as Native Assets, binds the Dart signature directly to a native symbol (e.g., a C function) in a dynamic library.
import 'dart:ffi';

// The @Native annotation instructs the compiler to bind this external 
// function to the C symbol 'system_beep'.
@Native<Void Function(Int32)>()
external void system_beep(int frequency);

JavaScript Interop (Web)

In web compilation using dart:js_interop, the external keyword pairs with @JS annotations to proxy calls to the JavaScript engine. The function signature must use types compatible with the interop boundary.
import 'dart:js_interop';

// Binds to the global JavaScript function 'alert'
@JS()
external void alert(JSString message);

// To access the global 'Math' object, define an external getter
@JS()
external MathNamespace get Math;

// Define the shape of the Math object using an extension type
@JS()
extension type MathNamespace._(JSObject _) implements JSObject {
  external double pow(double base, double exponent);
}

Restrictions

  • No Body: An external function cannot define a block body.
  • Constructors: External generative constructors are permitted, provided they lack initializer lists and bodies. External factory constructors are also permitted.
  • Static Interop Type Strictness: When using dart:js_interop, parameters and return types are strictly validated:
    • Direct Primitives: Only int, double, num, bool, and void (as a return type) are permitted as direct Dart primitives in the signature.
    • Mapped Types: The Dart String type is not a valid type for external signatures; it must be explicitly mapped to JSString.
    • Collections: Standard Dart collections (e.g., List, Map) are not allowed and must be typed as their JS counterparts (e.g., JSArray, JSObject).
Master Dart with Deep Grasping Methodology!Learn More