This has been a question of mine for over a year. I resorted to having a factory class that has methods to instantiate various view models (and pass in dependencies) and return the pointer to that view model. This factory would be exposed to QML via singleton or contextProperty at the start of my program. Any QML view can decide which view models it wants and doesn't deal with dependency injection.
Here is an example:
class ViewModelFactory : public QObject { Q_OBJECT QML_ELEMENT QML_SINGLETON public: Q_INVOKABLE HomeViewModel* createHomeViewModel(QObject* parent = nullptr) { // Inject dependencies from ApplicationModels auto& appModels = ApplicationModels::instance(); return new HomeViewModel(appModels.smartHomeService(), appModels.userService(), parent); } };Now, the above example makes ApplicationModels a singleton and passes it to the view model, but you could imagine a world where the factory can manage the dependencies without them being singletons.
It took a lot of thinking and I gathered this approach by looking at how QtWidgets does things: a very hierarchical object structure where the MainWindow class would contain everything within it and proctor instantiation, DI, and more.
QML just has access to what is publicly exposed by the ApplicationModel/factory, and no longer needs access to dependencies for ViewModels to use their desired view model.
Note: I have not tested this yet (my fear being that I do NOT know the impact of calling factory methods on init of a Qml component)