Getting started
Setup (Google packages)
# pubspec.yaml - Original (archived)
dependencies:
angular: ^7.0.2
angular_components: ^1.0.4
angular_forms: ^4.0.1
angular_router: ^3.0.1
dev_dependencies:
angular_test: ^4.0.1
build_runner: ^2.0.0
build_web_compilers: ^3.0.0
Original Google packages (archived).
Setup (Community fork)
# pubspec.yaml - Community (active)
dependencies:
ngdart: ^7.1.1
ngforms: ^4.1.2
ngrouter: ^3.1.1
dev_dependencies:
ngtest: ^4.1.0
build_runner: ^2.0.0
build_web_compilers: ^4.0.0
Active community-maintained packages.
Build commands
# Development
webdev serve
webdev serve --auto restart
# Production build
webdev build
webdev build --output web:build
# Alternative (build_runner)
dart run build_runner serve
dart run build_runner build
Components
Basic component
import 'package:angular/angular.dart';
(
selector: 'my-app',
template: '''
<h1>{{title}}</h1>
<p>Welcome to AngularDart!</p>
''',
styles: ['h1 { color: blue; }'],
directives: [coreDirectives],
)
class AppComponent {
String title = 'My App';
}
Must import coreDirectives explicitly.
Input/Output
class HeroComponent {
()
Hero hero;
final _deleted = StreamController<Hero>();
()
Stream<Hero> get deleted => _deleted.stream;
void onDelete() => _deleted.add(hero);
}
Uses Dart Streams, not EventEmitter.
Lifecycle hooks
class HeroComponent implements OnInit, OnDestroy {
void ngOnInit() {
// Initialize component
}
void ngOnDestroy() {
// Cleanup
}
}
Template syntax
Bindings
| Syntax | Purpose | Example |
|---|---|---|
{{expr}} |
Interpolation | {{hero.name}} |
[property]="expr" |
Property binding | [disabled]="isDisabled" |
(event)="stmt" |
Event binding | (click)="onSave()" |
[(ngModel)]="prop" |
Two-way binding | [(ngModel)]="hero.name" |
Identical to Angular TypeScript.
Structural directives
<!-- Conditional rendering -->
<div *ngIf="hero != null">{{hero.name}}</div>
<!-- Loop -->
<li *ngFor="let hero of heroes">
{{hero.name}}
</li>
<!-- Switch -->
<div [ngSwitch]="hero.emotion">
<div *ngSwitchCase="'happy'">:)</div>
<div *ngSwitchCase="'sad'">:(</div>
<div *ngSwitchDefault>:|</div>
</div>
Template references
<input #heroInput type="text">
<button (click)="onSave(heroInput.value)">
Save
</button>
Use #var for template refs.
Dependency Injection
Injectable service
import 'package:angular/angular.dart';
()
class HeroService {
final Client _http;
HeroService(this._http);
Future<List<Hero>> getHeroes() async {
final response = await _http.get('/api/heroes');
return _parseHeroes(response.body);
}
}
Provider types
(
providers: [
ClassProvider(HeroService),
ValueProvider(apiUrl, 'https://api.example.com'),
FactoryProvider(HeroService, heroServiceFactory),
ExistingProvider(HeroService, SuperHeroService),
],
)
| Provider | Usage |
|---|---|
ClassProvider |
Class instance |
ValueProvider |
Static value |
FactoryProvider |
Factory function |
ExistingProvider |
Alias existing |
Routing
Route definitions
import 'package:angular_router/angular_router.dart';
class Routes {
static final heroes = RouteDefinition(
path: 'heroes',
component: ng.HeroListComponentNgFactory,
);
static final hero = RouteDefinition(
path: 'hero/:id',
component: ng.HeroDetailComponentNgFactory,
);
static final all = [heroes, hero];
}
Navigation
class AppComponent {
final Router _router;
AppComponent(this._router);
void goToHero(int id) {
_router.navigate(
Routes.hero.toUrl(parameters: {'id': '$id'})
);
}
}
Router directives
(
directives: [routerDirectives],
template: '''
<nav>
<a [routerLink]="Routes.heroes.toUrl()">Heroes</a>
</nav>
<router-outlet></router-outlet>
''',
)
Forms
Template-driven form
import 'package:angular_forms/angular_forms.dart';
(
directives: [formDirectives],
template: '''
<form (ngSubmit)="onSubmit()">
<input [(ngModel)]="model.name"
ngControl="name"
required>
<button type="submit">Submit</button>
</form>
''',
)
class HeroFormComponent {
Hero model = Hero();
void onSubmit() {
// Handle submission
}
}
Must import formDirectives.
HTTP
HTTP service
import 'package:http/http.dart';
import 'dart:convert';
class HeroService {
final Client _http;
HeroService(this._http);
Future<List<Hero>> getAll() async {
final response = await _http.get(
Uri.parse('/api/heroes')
);
final data = jsonDecode(response.body) as List;
return data.map((json) => Hero.fromJson(json)).toList();
}
Future<Hero> create(Hero hero) async {
final response = await _http.post(
Uri.parse('/api/heroes'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(hero.toJson()),
);
return Hero.fromJson(jsonDecode(response.body));
}
}
Uses Dart's http package.
Pipes
Custom pipe
('truncate')
class TruncatePipe implements PipeTransform {
String transform(String value, [int length = 10]) {
return value.length > length
? '${value.substring(0, length)}...'
: value;
}
}
Usage: {{ longText | truncate:20 }}
Built-in pipes
| Pipe | Usage |
|---|---|
DatePipe |
Date formatting |
UpperCasePipe |
Uppercase text |
LowerCasePipe |
Lowercase text |
CurrencyPipe |
Currency formatting |
PercentPipe |
Percentage formatting |
SlicePipe |
Array/string slice |
AsyncPipe |
Unwrap async values |
JsonPipe |
JSON stringify |
Lifecycle Hooks
Available hooks
| Hook | Interface | When Called |
|---|---|---|
ngOnInit |
OnInit |
After first change detection |
ngOnDestroy |
OnDestroy |
Before destroy |
ngOnChanges |
OnChanges |
Input property changes |
ngDoCheck |
DoCheck |
Custom change detection |
ngAfterContentInit |
AfterContentInit |
After content projection |
ngAfterViewInit |
AfterViewInit |
After view init |
Migration Guide
Package migration
| Google (archived) | Community (active) |
|---|---|
angular |
ngdart |
angular_forms |
ngforms |
angular_router |
ngrouter |
angular_test |
ngtest |
vs Angular (TypeScript)
| Angular (TS) | AngularDart |
|---|---|
| TypeScript | Dart |
| npm/yarn | pub |
ng serve |
webdev serve |
| RxJS Observables | Dart Streams |
| EventEmitter | StreamController |
| @angular/cli | Manual setup |
| Node.js runtime | Dart VM/dart2js |
| webpack | build_runner |
Gotchas
- Archived by Google (Nov 2025) — community continues at angulardart-community
- NOT Flutter — different framework, same language (Dart)
- Uses Dart Streams, not RxJS Observables
- Build system: webdev/build_runner, not Angular CLI
- Must explicitly import coreDirectives in every component
- Limited ecosystem compared to Angular TypeScript
- Package names changed:
angular→ngdart(community fork)
Also see
- AngularDart Community Docs - Community documentation
- ngdart package - Community fork on pub.dev
- angulardart-community - GitHub repository
- Dart for Web - Official Dart web development guide
- Angular 2 - TypeScript version of Angular