If a name and mileage live in separate variables, their association can become unclear. A class describes a custom type with named fields. An object is a particular value of that type. A constructor defines how an object is created. For two vehicles, we create two separate objects.
01Define a model
Vehicle has name and mileage fields. final prevents replacing their values after construction. The model contains no screen or database details.
class Vehicle {
const Vehicle({required this.name, required this.mileage});
final String name;
final int mileage;
Vehicle withMileage(int value) {
return Vehicle(name: name, mileage: value);
}
}
void main() {
final car = Vehicle(name: 'Family car', mileage: 64820);
final updated = car.withMileage(64920);
print('${updated.name}: ${updated.mileage}');
print(car.mileage);
}
Run / check
dart run models.dart02Create objects
required this.name is shorthand for a named parameter that directly fills the matching field. Calling Vehicle(name: ..., mileage: ...) creates an object. car.name then reads its name.
03Model changes deliberately
withMileage creates a new Vehicle. The old object stays unchanged. This style makes state transitions easier to follow. The real app adds IDs, timestamps and further fields. This example shows only the basic idea.
Output: Family car: 64920. The old object still holds 64820.
Your exercise
Add an optional plate field and preserve it in withMileage.
Show the solution and explanation
Declare final String? plate, add this.plate to the constructor and pass plate: plate when creating the new Vehicle. The question mark permits a missing plate.
Project context and sources
The example is simplified for learning. The linked source version shows its use in the project and the checks applied there.