In a Flutter app I'm developing, I have a class hierarchy of model objects of a certain base class. To take a typical example, the abstract base class could be Vehicle
. Then I have a (pretty much) parallel hierarchy of widget classes that show info about a particular subclass of Vehicle
: Car
objects are shown with CarWidget
, Truck
s with TruckWidget
, etc. These widgets all inherit from the ABC VehicleWidget
. This is because all of the widgets have some part in common, which is the part that corresponds to the base class Vehicle
, but each subclass of Vehicle
needs to show some specialized info as well. (If you don't like inheritance with Flutter widgets, then replace "inheritance" with "composition", what follows still holds.)
My app's global state holds a List<Vehicle>
, and for each vehicle in that list it needs to show the corresponding widget. In order to do this, the runtime type of the vehicle must be checked. The natural object-oriented approach that comes to mind is to use virtual methods: declare an abstract method makeWidget()
in the Vehicle
class, and then make each subclass select the appropriate widget subclass.
However, I don't like mixing UI with model.
I thought of using extensions to define the makeWidget()
method in a separate file, but extensions in Dart can't define virtual methods, they're essentially syntactic sugar for a regular function call (i.e. with static type checking).
What I ended up doing is switch
ing on the runtime type explicitly:
abstract class VehicleWidget extends StatelessWidget {
static VehicleWidget of(Vehicle vehicle) {
switch (vehicle.runtimeType) {
case Car:
// ...
case Truck:
// ...
}
}
// ...
}
// whenever I need to create the appropriate widget for a vehicle
VehicleWidget.of(vehicle)
Now the "UI logic" of selecting the appropriate widget is with UI code and separate from model code.
However, this has a big disadvantage.
The compiler won't be able to tell me if I've forgotten to implement the appropriate widget (and specify it as a case
in the switch
) when I create a new subclass of Vehicle
. Even worse, if there are multiple such parallel widget hierarchies (for example, VehicleCard
and VehicleDetails
), I need to mantain one switch
statement for each of those.
My question is: which of the two approaches is better (for which scenario)? Is there any advantage to separating the selection of the appropriate widget class from the model hierarchy, as I have done?