Dart 3 Migration Guide
This guide covers the migration from older AngularDart versions to AngularDart Reborn (Dart 3). AngularDart Reborn requires Dart 3, so this migration is a prerequisite.
Overview of Changes
Dart 3 introduced significant changes:
- Null safety - Types are non-nullable by default
- Class modifiers -
sealed,final,interface,base,mixin - Records - Anonymous immutable data structures
- Patterns - Advanced pattern matching
- Switch expressions - Expressive switch statements
Step 1: Update SDK Constraint
environment:
sdk: '>=3.0.0 <4.0.0'
Step 2: Update Dependencies
Update all dependencies to versions compatible with Dart 3 and AngularDart 8:
dart pub upgrade
dart pub outdated
Step 3: Fix Type Errors
Implicit casts removed
Dart 3 requires explicit casts:
// Old
dynamic value = 'hello';
String s = value; // Implicit cast
// Dart 3
dynamic value = 'hello';
String s = value as String; // Explicit cast
// or
String s = value.toString();
Generic type inference
// Old
List list = []; // List<dynamic>
// Dart 3
List<String> list = []; // Must specify type
// or
var list = <String>[];
Step 4: Null Safety Migration
Making types nullable
Add ? to types that can be null:
// Before
String name;
List<User> users;
// After
String? name;
List<User>? users;
Required parameters
// Before
void greet(String name) { }
// After (if name can be null)
void greet(String? name) { }
// Or make it required with a default
void greet([String name = 'World']) { }
Late initialization
Use late for variables initialized after construction:
class MyComponent {
late final UserService _userService;
MyComponent(Injector injector) {
_userService = injector.get(UserService);
}
}
Null checks
Use ! to assert non-null:
String name = user!.name; // Assert user is not null
Use ?. for safe navigation:
String? name = user?.name; // Null if user is null
Step 5: Class Modifiers
Dart 3 introduced class modifiers. Update your classes:
// Abstract classes that were used as mixins
abstract mixin class MyMixin { }
// Classes that should not be extended or implemented
final class MyService { }
// Sealed class hierarchies
sealed class Result { }
class Success extends Result { }
class Failure extends Result { }
Step 6: Records and Patterns (New in Dart 3)
Records
Records are anonymous, immutable data structures:
// Define a record type
typedef Point = (double x, double y);
// Create a record
final p = (1.0, 2.0);
print(p.$1); // 1.0
print(p.$2); // 2.0
// Named fields
final person = (name: 'Alice', age: 30);
print(person.name); // Alice
Pattern Matching
// Switch with patterns
switch (shape) {
case Circle(radius: var r) when r > 0:
print('Valid circle with radius $r');
case Square(side: var s):
print('Square with side $s');
case _:
print('Unknown shape');
}
// If-case
if (result case Success(value: final v)) {
print('Got value: $v');
}
Step 7: Update AngularDart Code
Component changes
// Before (AngularDart 5/6/7)
@Component(
selector: 'my-app',
template: '<h1>{{title}}</h1>',
)
class AppComponent {
String title = 'Hello';
}
// After (Dart 3 / AngularDart 8)
@Component(
selector: 'my-app',
template: '<h1>{{title}}</h1>',
)
class AppComponent {
String title = 'Hello';
}
Directive changes
// Before
@Directive(selector: '[tooltip]')
class TooltipDirective {
String? text;
@Input()
set tooltip(String value) { text = value; }
}
// After
@Directive(selector: '[tooltip]')
class TooltipDirective {
String? text;
@Input()
set tooltip(String? value) { text = value; }
}
Service changes
// Before
@Injectable()
class DataService {
Future<List> getData() async {
return [];
}
}
// After
@Injectable()
class DataService {
Future<List<Item>> getData() async {
return <Item>[];
}
}
Step 7: Update Tests
// Before
test('should work', () {
var result = service.getData();
expect(result, isNotNull);
});
// After
test('should work', () {
var result = service.getData();
expect(result, isNotNull);
// Add explicit type checks if needed
expect(result, isA<List<Item>>());
});
Common Issues
undefined_class errors
Ensure all imports are present and types are correctly spelled.
missing_return errors
Add explicit return statements or => null for nullable returns.
invalid_override errors
Ensure override signatures match the parent class exactly.
unused_element warnings
Remove unused private members or add // ignore: unused_element.