Search code examples
flutterdartoopsolid-principles

How to apply the principle of dependency inversion in the provider?


I want my project to follow the SOLID principles, and for that I want to know if the coupling that is always seen between the UI and the providers in various tutorials is something that violates the principle of dependency inversion or not.

I remember the definition at the beginning:

A. High level classes should not depend on low level classes. Both should depend on abstractions.

B. Abstractions should not depend on details. The details should depend on the abstractions.

By this I mean this application that you see a lot in tutorials:

class MyProvider extends ChangeNotifier{
  //...
}

class UIClass extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
     MyProvider myProvider = Provider.of<MyProvider>(context);
     //...
  }
}

As can be seen, the UI class directly instantiates the provider class and not an interface, generating coupling between these two classes.

I understand that the "high level class" would be the provider and the "low level class" would be the UI. And that "details" in this case would be the UI class.

Would it be better to use abstractions and pass the provider through the constructor?

Clarification: I'm not asking for personal opinions (otherwise the question would be closed), I'm asking about applying SOLID principles to Flutter.


Solution

  • As you mention before your application should be based on abstraction instead of real implementations. Your current approach describes how to make it wrong in terms of this principle.

    To make it right your UIClass should depend on the abstraction class (because dart doesn't have interfaces) instead of the real class. First, create an abstraction

    class MyProvider implements ChangeNotifier {
      void method();
    }
    

    Than implementations

    class MyNetworkProvider extends ChangeNotifier implements MyProvider {
      @override
      void method() {...}
    }
    
    class MyDatabaseProvider extends ChangeNotifier implements MyProvider {
      @override
      void method() {...}
    }
    

    Initialise your provider with one of implementation

    runApp(ChangeNotifierProvider<MyProvider>(
      create: (_) => MyNetworkProvider(),
      child: ...
    ));
    

    And make dependency on the abstraction

    class UIClass extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
         MyProvider myProvider = Provider.of<MyProvider>(context);
         //...
      }
    }