When is a ViewModel not a ViewModel?
In “What even are Flutter widgets?”, Matt makes the case that a Flutter widget tree is equivalent to a ViewModel.
Why? Well, primarily because Widgets aren’t actual “views” (like an iOS UIView). Instead they’re actually definitions that, eventually, will produce elements and render objects that actually draw the user interface.
I can see his point… but I have to respectfully disagree.
In fact, most of the Flutter code I’ve seen to date harkens back to the age of classic iOS MVC architecture. They’re “Massive” View Controllers in which user interface code, business logic, presentation logic, navigation, animation, and other code is combined wholesale.
Let’s look a at small example:
The above code takes a data set and converts it into a list for presentation to the user. The user can select one of the items whose key will be returned to the caller.
Of the 42 lines of code shown, and in my opinion, only eight lines lie in the realm of what I’d consider to be information traditionally manipulated and/or provided by the view model. Those lines are indicated by “// VM“ comments in the listing.
They hold the data (7,8), manipulate it into a list (11,12), provide the title (15), the number of elements (20), and the elements themselves (22,23).
The majority of the rest is primarily user interface code, which includes such arcana as background colors, whether or not to respect safe areas, “expanded” widgets used to get placement and sizing correct, and even padding (plus, “materialTapTargetSize”. Really?).
It even contains navigation logic (31) that in Swift I’d relegate to a Coordinator.
Let’s look at it from a different standpoint and say that in my iOS ViewController I created a method called “Row” that took a string and a flag and did all of the behind the scenes work of creating UIViews and UILabels and wiring everything together for me.
Or what if instead I made my Row some sort of class (widget?) that I could use to compose my interface?
In either case, is my UIViewController now suddenly a ViewModel just because I’m now constructing my user interface using larger pieces and parts?
To my mind, no. Construction of the actual user interface is the realm of the view controller. Providing the data the view controller needs to do the job is the realm of the view model.
And one is not the other simply because I’m using a bigger hammer.
Composing user interfaces using higher-level nested user interface elements is a wonderful idea (and one that at some later time I’ll dive into).
But Flutter makes it all to easy to mix business logic and interface logic into a MWT.
Otherwise known as a “Massive Widget Tree”.
Does it have to be that way? Of course not. But as mentioned, Flutter makes it very, very, very easy to add a bit of business code here, and a condition there, and suddenly you’re in the world of MWT’s.
Yes, you can test your widget tree “definitions”, but that’s akin to diving into a list of generated iOS UIViews and poking around. You can do it, but why do so?
A view model is intended to be a clear separation of church and state. Business logic and data manipulation here, user interface construction and management there.
In fact, a good view model is user-interface ignorant. If it’s meant to manage a list of data, it manages a list of data. It doesn’t know if the data is being presented in a table view, collection view, or even in a stack view, nor should it care. That’s not in it’s job description.
Is the data ready, clean, properly formatted, and ready for consumption? If so, then the view model did it’s job, and it did it well.