dart_macros 1.0.2 copy "dart_macros: ^1.0.2" to clipboard
dart_macros: ^1.0.2 copied to clipboard

A powerful Dart package that brings C-style macro preprocessing capabilities to Dart.

๐Ÿš€ dart_macros pub package License: MIT #

A powerful Dart package that brings C-style macro preprocessing capabilities to Dart, enabling compile-time code generation and manipulation.

๐Ÿ“ฆ Installation #

Add this to your package's pubspec.yaml file:

dependencies:
  dart_macros: ^1.0.2

Install it:

dart pub get

๐ŸŒŸ Overview #

dart_macros provides a familiar C-like macro system for Dart developers, offering features such as:

  • โœ… Object-like macros for constant definitions
  • โœ… Function-like macros for code generation
  • โœ… Token concatenation operations
  • โœ… Conditional compilation directives
  • โœ… Macro expansion and evaluation
  • โœ… Built-in predefined macros
  • โœ… Cross-platform support (Flutter/iOS/Android)

๐Ÿ“‹ Use Cases #

  • Code generation without external build tools
  • Platform-specific code branching
  • Debug and release mode configurations
  • Compile-time constants and computations
  • Code reusability through macro templates
  • Meta-programming capabilities
  • White-label applications with client-specific configurations

โœจ Features #

  • ๐Ÿ” Clean, lightweight syntax that feels natural in Dart
  • ๐Ÿ”’ Type-safe macro expansions
  • ๐Ÿ”ฅ Detailed error reporting and debugging support
  • ๐Ÿ”„ Integration with existing Dart tooling
  • โšก Performance optimization through compile-time evaluation
  • ๐Ÿงฉ Support for nested macro definitions
  • ๐Ÿ“ฑ Full support for Flutter on all platforms

๐Ÿš€ Usage #

Object-like Macros #

Simple macros that define constants or expressions:

import 'package:dart_macros/dart_macros.dart';

// Definition
@MacroFile()
@Define('MAX_SIZE', 100)
@Define('PI', 3.14159)
@Define('DEBUG', true)
void main() async {
  await initializeDartMacros(); // This is optional but won't hurt

  // Usage
  var array = List<int>.filled(Macros.get<int>('MAX_SIZE'), 0);
  var circleArea = Macros.get<double>('PI') * radius * radius;
  
  if (Macros.get<bool>('DEBUG')) {
    print('Debug mode enabled');
  }
}

Function-like Macros #

Macros that take parameters and expand to code:

import 'package:dart_macros/dart_macros.dart';

@MacroFile()
@DefineMacro(
  'SQUARE',
  'x * x',
  parameters: ['x'],
)
@DefineMacro(
  'MIN',
  'a < b ? a : b',
  parameters: ['a', 'b'],
)
@DefineMacro(
  'VALIDATE',
  'x >= 0 && x <= max',
  parameters: ['x', 'max'],
)
void main() async {
  await initializeDartMacros();

  // Usage
  var squared = MacroFunctions.SQUARE(5);  // Evaluates to 25
  var minimum = MacroFunctions.MIN(x, y);  // Returns the smaller of x and y
  var isValid = MacroFunctions.VALIDATE(value, 100);  // Checks if value is in range
}

Flutter Support #

Use dart_macros in Flutter applications on all platforms:

import 'package:flutter/material.dart';
import 'package:dart_macros/dart_macros.dart';

void main() {
  // Initialize Flutter macros
  FlutterMacros.initialize();
  
  // Register macros for Flutter apps
  FlutterMacros.registerFromAnnotations([
    Define('APP_NAME', 'My Flutter App'),
    Define('API_ENDPOINT', 'https://api.example.com'),
    Define('DEBUG', true),
    DefineMacro('FORMAT_CURRENCY', '"\$" + amount.toStringAsFixed(2)', parameters: ['amount']),
  ]);
  
  // Configure platform-specific settings
  FlutterMacros.configurePlatform(
    platform: 'android',
    debug: true,
    additionalValues: {
      'MIN_SDK_VERSION': 21,
      'TARGET_SDK_VERSION': 33,
    },
  );
  
  runApp(MyApp());
}

// Use macros just like on other platforms
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: Macros.get<String>('APP_NAME'),
      theme: ThemeData(
        primarySwatch: Colors.blue,
        brightness: Macros.get<bool>('DEBUG') ? Brightness.light : Brightness.dark,
      ),
      home: HomeScreen(),
    );
  }
}

Stringizing #

Convert macro arguments to string literals:

@MacroFile()
@DefineMacro(
  'STRINGIFY',
  '"x"',
  parameters: ['x'],
)
@DefineMacro(
  'REPORT_VAR',
  '"Variable " + "var" + " = " + var.toString()',
  parameters: ['var'],
)
void main() async {
  await initializeDartMacros();

  // Usage
  var name = MacroFunctions.STRINGIFY(user);  // Evaluates to "user"
  MacroFunctions.REPORT_VAR(count);  // Prints: Variable count = 5
}

Concatenation #

Join tokens together:

@MacroFile()
@DefineMacro(
  'CONCAT',
  'a + b',
  parameters: ['a', 'b'],
)
void main() async {
  await initializeDartMacros();

  // Usage
  var fullName = MacroFunctions.CONCAT("John", "Doe");  // Evaluates to "JohnDoe"
}

Debug Operations #

Special macros for debugging:

@MacroFile()
@Define('__DEBUG__', true)
@DefineMacro(
  'DEBUG_PRINT',
  '"Debug [" + __FILE__ + ":" + __LINE__ + "]: " + text',
  parameters: ['text'],
)
void main() async {
  await initializeDartMacros();

  // Usage
  MacroFunctions.DEBUG_PRINT("Starting initialization");
  // Prints: Debug [example.dart:15]: Starting initialization
}

Predefined Macros #

Built-in system macros:

void main() async {
  await initializeDartMacros();

  print(Macros.file);      // Current source file name
  print(Macros.line);      // Current line number
  print(Macros.date);      // Compilation date
  print(Macros.time);      // Compilation time
}

Conditional Compilation #

Control compilation based on conditions:

@MacroFile()
@Define('DEBUG', true)
@Define('PLATFORM', 'android')
@Define('API_VERSION', 2)
class App {
  void initialize() {
    if (MacroFunctions.IFDEF('DEBUG')) {
      print('Debug mode initialization');
    }

    if (MacroFunctions.IF_PLATFORM('android')) {
      print('Initializing Android platform');
    }

    if (MacroFunctions.IF('DEBUG && API_VERSION >= 2')) {
      print('Advanced debug features available');
    }
  }
}

๐Ÿ“ฑ Mobile-Specific Features #

Platform Detection #

dart_macros automatically selects the appropriate implementation based on platform capabilities:

// The same API works across all platforms
// On Flutter mobile, a non-reflection based implementation is used
// On Dart VM and web, a reflection-based implementation is used
final appName = Macros.get<String>('APP_NAME');

Flutter Configuration #

For Flutter applications, use the FlutterMacros class to set up platform-specific configurations:

// Initialize with base settings
FlutterMacros.initialize();

// Configure for a specific platform
FlutterMacros.configurePlatform(
  platform: defaultTargetPlatform.name.toLowerCase(),
  debug: kDebugMode,
  additionalValues: {
    'DEVICE_TYPE': MediaQuery.of(context).size.width > 600 ? 'tablet' : 'phone',
    'API_BASE_URL': _getApiUrl(),
    'TIMEOUT_MS': 5000,
  },
);

Registration Helpers #

Register macros from annotations for mobile platforms:

// Register multiple macros at once
FlutterMacros.registerFromAnnotations([
  Define('VERSION', '1.0.0'),
  Define('MAX_ITEMS', 100),
  Define('FEATURE_NEW_UI', false),
  DefineMacro('SQUARE', 'x * x', parameters: ['x']),
]);

๐Ÿ“ Best Practices #

  1. ๐Ÿ“‹ Document macro behavior and expansion
  2. ๐Ÿ”ค Use meaningful and clear macro names
  3. ๐Ÿšง Avoid side effects in macro arguments
  4. ๐Ÿงช Test macro expansion in different contexts
  5. ๐Ÿ” Consider using const or static final instead of simple object-like macros
  6. โš ๏ธ Be careful with token concatenation and stringizing operators
  7. ๐Ÿ“ฑ For mobile apps, centralize macro registration in a configuration class

โŒ Common Pitfalls #

Side Effects in Arguments #

// Bad
@DefineMacro(
  'SQUARE',
  'x * x',
  parameters: ['x'],
)
var result = MacroFunctions.SQUARE(i++);  // i gets incremented twice

// Good
@DefineMacro(
  'SQUARE',
  '(x) * (x)',
  parameters: ['x'],
)

Missing Parentheses #

// Bad
@DefineMacro(
  'DOUBLE',
  'x + x',
  parameters: ['x'],
)
var result = 10 * MacroFunctions.DOUBLE(5);  // Evaluates to 10 * 5 + 5 = 55

// Good
@DefineMacro(
  'DOUBLE',
  '(x) + (x)',
  parameters: ['x'],
)
// Evaluates to 10 * (5 + 5) = 100

๐Ÿ“š API Reference #

Core Classes #

Macros

The main class for accessing macro values:

// Get a macro value
var debug = Macros.get<bool>('DEBUG');

// Access predefined macros
var currentFile = Macros.file;
var currentLine = Macros.line;

MacroFunctions

For invoking function-like macros:

// Using a function-like macro
var squared = MacroFunctions.SQUARE(5);

// Using predefined function-like macros
MacroFunctions.DEBUG_PRINT("Error occurred");

FlutterMacros

For Flutter-specific macro operations:

// Initialize for Flutter
FlutterMacros.initialize();

// Register macros for Flutter
FlutterMacros.registerFromAnnotations([...]);

// Configure platform-specific settings
FlutterMacros.configurePlatform(...);

Annotations #

// Mark a file for macro processing
@MacroFile()

// Define a simple constant macro
@Define('VERSION', '1.0.0')

// Define a function-like macro
@DefineMacro(
  'MAX',
  'a > b ? a : b',
  parameters: ['a', 'b'],
)

// Platform-specific code
@Platform('android')

// Debug-only code
@Debug()

๐Ÿค Contributing #

Contributions are welcome! Please feel free to submit a Pull Request.

๐Ÿ“„ License #

This project is licensed under the MIT License - see the LICENSE file for details.

2
likes
130
points
67
downloads

Publisher

unverified uploader

Weekly Downloads

A powerful Dart package that brings C-style macro preprocessing capabilities to Dart.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

analyzer, build, collection, glob, meta, path, source_gen, stack_trace, yaml

More

Packages that depend on dart_macros