Skip to main content

Code Generation Reference

The fluttron generate services command generates type-safe Host/Client code from Dart service contracts.

Quick Start

# Generate from a service contract
fluttron generate services --contract lib/service_contract.dart

# Preview generated files without writing
fluttron generate services --contract lib/service_contract.dart --dry-run

# Generate into specific directories
fluttron generate services \
--contract lib/service_contract.dart \
--host-output my_service_host/lib/src/ \
--client-output my_service_client/lib/src/ \
--shared-output my_service_shared/lib/src/

Command Options

OptionDescription
--contract, -cPath to the service contract Dart file (required)
--host-outputOutput directory for host-side generated code
--client-outputOutput directory for client-side generated code
--shared-outputOutput directory for shared model generated code
--dry-runPreview generated files without writing to disk

Contract Structure

A service contract is a Dart file containing:

  1. One or more abstract classes annotated with @FluttronServiceContract
  2. Zero or more model classes annotated with @FluttronModel
import 'package:fluttron_shared/fluttron_shared.dart';

(namespace: 'weather')
abstract class WeatherService {
Future<WeatherInfo> getCurrentWeather(String city);
Future<List<WeatherForecast>> getForecast(String city, {int days = 5});
Future<bool> isAvailable();
}

()
class WeatherInfo {
final String city;
final double temperature;
final String condition;
final DateTime timestamp;

const WeatherInfo({
required this.city,
required this.temperature,
required this.condition,
required this.timestamp,
});
}

Generated Files

Host-Side Code

For each contract class, generates {class_name}_generated.dart:

// GENERATED CODE — DO NOT MODIFY BY HAND
// Generated by: fluttron generate services
// Source: service_contract.dart

import 'package:fluttron_host/fluttron_host.dart';
import 'package:fluttron_shared/fluttron_shared.dart';

abstract class WeatherServiceBase extends FluttronService {

String get namespace => 'weather';


Future<dynamic> handle(String method, Map<String, dynamic> params) async {
switch (method) {
case 'getCurrentWeather':
final city = _requireString(params, 'city');
final result = await getCurrentWeather(city);
return result.toMap();
case 'getForecast':
final city = _requireString(params, 'city');
final days = params['days'] == null ? 5 : params['days'] as int;
final result = await getForecast(city, days: days);
return result.map((e) => e.toMap()).toList();
case 'isAvailable':
return {'result': await isAvailable()};
default:
throw FluttronError(
'METHOD_NOT_FOUND',
'weather.$method not implemented',
);
}
}

Future<WeatherInfo> getCurrentWeather(String city);
Future<List<WeatherForecast>> getForecast(String city, {int days = 5});
Future<bool> isAvailable();

// Helper methods for parameter validation...
}

Usage: Extend the base class and implement the abstract methods:

import 'weather_service_generated.dart';

class WeatherServiceImpl extends WeatherServiceBase {

Future<WeatherInfo> getCurrentWeather(String city) async {
// Real implementation
return WeatherInfo(city: city, temperature: 22.5, ...);
}


Future<List<WeatherForecast>> getForecast(String city, {int days = 5}) async {
// Real implementation
}


Future<bool> isAvailable() async => true;
}

Client-Side Code

Generates {class_name}_client_generated.dart:

// GENERATED CODE — DO NOT MODIFY BY HAND

import 'package:fluttron_ui/fluttron_ui.dart';

class WeatherServiceClient {
WeatherServiceClient(this._client);

final FluttronClient _client;

Future<WeatherInfo> getCurrentWeather(String city) async {
final result = await _client.invoke(
'weather.getCurrentWeather',
{'city': city},
);
return WeatherInfo.fromMap(Map<String, dynamic>.from(result as Map));
}

Future<List<WeatherForecast>> getForecast(String city, {int days = 5}) async {
final result = await _client.invoke(
'weather.getForecast',
{'city': city, 'days': days},
);
return (result as List)
.map((e) => WeatherForecast.fromMap(
Map<String, dynamic>.from(e as Map)))
.toList();
}

Future<bool> isAvailable() async {
final result = await _client.invoke('weather.isAvailable', {});
return result['result'] as bool;
}
}

Model Code

For each @FluttronModel class, generates {model_name}_generated.dart:

// GENERATED CODE — DO NOT MODIFY BY HAND

class WeatherInfo {
final String city;
final double temperature;
final String condition;
final DateTime timestamp;

const WeatherInfo({
required this.city,
required this.temperature,
required this.condition,
required this.timestamp,
});

factory WeatherInfo.fromMap(Map<String, dynamic> map) {
return WeatherInfo(
city: map['city'] as String,
temperature: (map['temperature'] as num).toDouble(),
condition: map['condition'] as String,
timestamp: DateTime.parse(map['timestamp'] as String),
);
}

Map<String, dynamic> toMap() {
return {
'city': city,
'temperature': temperature,
'condition': condition,
'timestamp': timestamp.toIso8601String(),
};
}
}

Supported Types

Basic Types

Dart TypeJSON RepresentationNotes
StringstringDirect mapping
intnumberDirect mapping
doublenumberUses (as num).toDouble() for safety
boolbooleanDirect mapping
DateTimestring (ISO 8601)Serialized via toIso8601String()

Collection Types

Dart TypeJSON RepresentationNotes
List<T>arrayNested types supported
Map<String, dynamic>objectDirect mapping

Nullable Types

All types support nullability with ? suffix:

  • String? → null or string
  • List<int>? → null or array
  • DateTime? → null or ISO 8601 string

Custom Model Types

Classes annotated with @FluttronModel are serialized via toMap() and deserialized via fromMap() factory.

Parameter Handling

Required Positional Parameters

Future<String> greet(String name);

Generated extraction:

final name = _requireString(params, 'name');

Throws FluttronError('BAD_PARAMS', ...) if missing or invalid.

Optional Named Parameters with Defaults

Future<String> greet({String name = 'World'});

Generated extraction:

final name = params['name'] == null ? 'World' : params['name'] as String;

Nullable Parameters

Future<void> update(String id, {String? value});

Generated extraction:

final id = _requireString(params, 'id');
final value = params['value'] == null ? null : params['value'] as String;

Regeneration Safety

  • Generated files use _generated.dart suffix
  • Header includes timestamp for tracking
  • Never modify generated files directly
  • Extend the *Base class for implementation

Error Handling

Generated code uses standard Fluttron errors:

Error CodeDescription
METHOD_NOT_FOUNDUnknown method name
BAD_PARAMSMissing or invalid parameter

Example:

throw FluttronError('BAD_PARAMS', 'Missing or invalid "city"');

Workflow

1. Define Contract

Create service_contract.dart with your service definition.

2. Generate Code

fluttron generate services --contract lib/service_contract.dart \
--host-output host/lib/src/ \
--client-output client/lib/src/ \
--shared-output shared/lib/src/

3. Implement Host

// host/lib/src/weather_service.dart
import 'weather_service_generated.dart';

class WeatherServiceImpl extends WeatherServiceBase {

Future<WeatherInfo> getCurrentWeather(String city) async {
// Your implementation
}
}

4. Register in Host App

// host/lib/main.dart
void main() {
final registry = ServiceRegistry()
..register(SystemService())
..register(StorageService())
..register(WeatherServiceImpl());

runFluttronHost(registry: registry);
}

5. Use in UI

// ui/lib/app.dart
import 'package:weather_service_client/weather_service_client.dart';

final client = FluttronClient();
final weatherService = WeatherServiceClient(client);

final weather = await weatherService.getCurrentWeather('London');
print('Temperature: ${weather.temperature}');

Limitations

  • Nesting depth: Deeply nested generic types (e.g., Map<String, List<Map<String, int>>>) may fall back to dynamic
  • No runtime discovery: Services must be manually registered
  • JSON transport only: Binary protocols not supported

Next Steps