ShadyBoukhary / flutter_clean_architecture

Clean architecture flutter: A Flutter package that makes it easy and intuitive to implement Uncle Bob's Clean Architecture in Flutter. This package provides basic classes that are tuned to work with Flutter and are designed according to the Clean Architecture.
https://pub.dartlang.org/packages/flutter_clean_architecture
MIT License
691 stars 172 forks source link

How to get controller in v4 #49

Closed gemilangpermata closed 4 years ago

gemilangpermata commented 4 years ago

Hi, nice work here.

One question, how to get controller getter in v4 since it was removed? I want to get the controller in initState of view.

RafaelPlantard commented 4 years ago

+1

ShadyBoukhary commented 4 years ago

@rafaelcmm can you answer this? Why was this removed?

rafaelcmm commented 4 years ago

Hi, nice work here.

One question, how to get controller getter in v4 since it was removed? I want to get the controller in initState of view.

Hi, I just submitted a pull request that will fix this request.

For resume, I'm adding a way to get access to controller on initState lifecycle. For that, just implement over initViewState and do what you want with the injected controller.

The main goal of the removal of controller from the ViewState was to prevent "bad" usage of the controller, and to be able to remove the full re-render of the widget tree. Now we can have three scenarios where the access of the controller is pertinent.

  1. On initViewState, where you can access the early initialization of the controller and perform actions you want on this state.

  2. On StatelessWidget through ControlledWidgetBuilder. You can use it to create your stateless widgets outside the ViewState (for example, on a widgets folder inside your page) and have access to the last state of the controller, ensuring that the widget will re-build only when refreshUI is triggered, and will rebuild only where the controller is used.

  3. On StatefulWidget through FlutterCleanArchitecture.getController.

With those changes we are able to optimize at maximum the re-build cycle of child widgets of the page, only re-rendering on places that directly depends on controllers variables.

Any questions, I'll be happy to answer or provide other ways to access the controller when necessary.

IMPORTANT

This PR opens a possibility for a bad practice:

class State extends ViewState {
    Con _controller;

    // THIS IS BAD. YOU WILL HAVE AN INSTANCE OF AN UN-UPDATED INSTANCE OF THE CONTROLLER AND CHANGES               ON IT WILL NOT REFLECT ON YOUR WIDGETS.
    void initViewState(Con controller) {
        _controller = controller;
    }
}

Thank you for your contribution

RafaelPlantard commented 4 years ago

@rafaelcmm is there a better place to put AnimationController? Like this example: https://github.com/ShadyBoukhary/Axion-Technologies-HnH/blob/7ebb03e9c0511f1ec2649d6bdd123318ada191d5/lib/app/pages/splash/splash_view.dart

For example, to avoid using initViewState how can I put it in my Controller?

rafaelcmm commented 4 years ago

You can try do something like this:

  1. Declare those inside splash_controller

    final AnimationController animationController = AnimationController(duration:Duration(seconds: 1));
    Animation<double> animation = CurvedAnimation(parent: animationController, curve: Curves.easeIn);
  2. Then on initViewState

    @override
    void initState(Con controller) {
    super.initViewState();
    controller.initAnimation();
    }
  3. Wrap your animated widget inside a ControllerWidgetBuilder

    ControlledWidgetBuilder(
    builder: (ctx, controller) {
        return FadeTransition(
                opacity: controller.animation,
                child: Image(
                  image: AssetImage(Resources.logo),
                  width: 200.0,
                ))
    }
    )
  4. Now, on your initAnimation

    void initAnimation() {
    animation.addStatusListener((status) {
      if (!isLoading) {
        animationController.stop(canceled: true);
      } else if (status == AnimationStatus.completed) {
        animationController.reverse();
      } else if (status == AnimationStatus.dismissed) {
        animationController.forward();
      }
    });
    animationController.forward();
    refreshUI();
    }

To resume what is being done:

This approach may be done to every other thing that depends on a controlled variable. Please try this approach and give me some feedback if you achieve what you want using this approach (please adapt it to your needs)

RafaelPlantard commented 4 years ago

I'll try it!

suchoX commented 3 years ago

@rafaelcmm AnimationController requires vsync though and we need a state with TickerProvider. How do we do that in controller?