Putting it together
- Dart course
- Expert
- Lesson 47/47
- 6 min
Combine all expert topics into a working mini app.
Putting it together
In the final lesson we bring all parts together: the FFI service, the code generator, the repository and the DI container are gathered in a MathApp shell. The run method starts the application.
MathApp takes a MathRepository and a BindingGenerator. The run method does addition via the repository and produces binding code via the generator.
This structure provides a clean entry point: main only creates MathApp and calls run. All dependencies are injected via the constructor; testing and extending are easy.
The full flow
Full flow: NativeMathService (FFI) → ServiceContainer (DI) → MathRepository (data) → MathApp (shell) → run (startup). Each layer connects to the previous via an interface.
main wires all parts: create service, register in container, resolve repository, create app, call run. This is a concrete application of dependency injection.
This project combines three expert topics: FFI (native integration), codegen (code generation), architecture (layered design). Each was learned in its own lesson; here they meet in a real application.
Practice Add MathApp and run it
The ServiceContainer is already defined below. Now define a class MathApp with final MathRepository repository; final BindingGenerator generator; MathApp(this.repository, this.generator); void run() { print('app: ${repository.sum(7, 8)}'); print(generator.generate('MathApp')); }. In main create final app = MathApp(repo, gen); and call app.run();.
Starter code
import 'dart:ffi';
abstract class MathService {
int add(int a, int b);
}
typedef NativeAdd = Int32 Function(Int32 a, Int32 b);
typedef DartAdd = int Function(int a, int b);
class NativeMathService implements MathService {
final String bindingName;
NativeMathService(this.bindingName);
@override
int add(int a, int b) => a + b;
String describe() => 'native: $bindingName';
}
class NativeBinding {
final String name;
const NativeBinding(this.name);
}
class BindingGenerator {
String generate(String className) => '// generated binding for $className';
}
class MathRepository {
final MathService service;
MathRepository(this.service);
int sum(int a, int b) => service.add(a, b);
}
class ServiceContainer {
final Map<String, dynamic> _services = {};
void register(String key, dynamic service) => _services[key] = service;
T resolve<T>(String key) => _services[key] as T;
}
// define a MathApp class (repository and generator fields; run method)
void main() {
print('service ready');
final svc = NativeMathService('native_add');
print(svc.describe());
print('result: ${svc.add(2, 3)}');
final gen = BindingGenerator();
print(gen.generate('NativeMathService'));
final container = ServiceContainer();
container.register('math', NativeMathService('native_add'));
final repo = MathRepository(container.resolve<NativeMathService>('math'));
print('repo: ${repo.sum(4, 6)}');
// create a MathApp; start with run
}Sample solution
import 'dart:ffi';
abstract class MathService {
int add(int a, int b);
}
typedef NativeAdd = Int32 Function(Int32 a, Int32 b);
typedef DartAdd = int Function(int a, int b);
class NativeMathService implements MathService {
final String bindingName;
NativeMathService(this.bindingName);
@override
int add(int a, int b) => a + b;
String describe() => 'native: $bindingName';
}
class NativeBinding {
final String name;
const NativeBinding(this.name);
}
class BindingGenerator {
String generate(String className) => '// generated binding for $className';
}
class MathRepository {
final MathService service;
MathRepository(this.service);
int sum(int a, int b) => service.add(a, b);
}
class ServiceContainer {
final Map<String, dynamic> _services = {};
void register(String key, dynamic service) => _services[key] = service;
T resolve<T>(String key) => _services[key] as T;
}
class MathApp {
final MathRepository repository;
final BindingGenerator generator;
MathApp(this.repository, this.generator);
void run() {
print('app: ${repository.sum(7, 8)}');
print(generator.generate('MathApp'));
}
}
void main() {
print('service ready');
final svc = NativeMathService('native_add');
print(svc.describe());
print('result: ${svc.add(2, 3)}');
final gen = BindingGenerator();
print(gen.generate('NativeMathService'));
final container = ServiceContainer();
container.register('math', NativeMathService('native_add'));
final repo = MathRepository(container.resolve<NativeMathService>('math'));
print('repo: ${repo.sum(4, 6)}');
final app = MathApp(repo, gen);
app.run();
}Expected output
service ready native: native_add result: 5 // generated binding for NativeMathService repo: 10 app: 15 // generated binding for MathApp
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 The run method
app.____();
app.run();
Explanation: the run method starts the application.
2 What does MathApp combine?
- Only comments
- Only prints
- Only imports
- Repository and generator Correct answer
Explanation: MathApp combines repository and generator in a shell.
3 Match the integration parts
| Term | Meaning |
|---|---|
MathApp |
Application shell |
repository |
Data layer |
generator |
Generation layer |
run() |
Startup |