Matias
Matias

Reputation: 738

Calling scopedModel from initState in flutter

I have a scopedModel class where I fetch data into it. The thing is that I'm not being able to render this data from InitState method using my scoped model where I have all my api requests. The method is being called but the inside callings are not, so my initial state of the page is not properly shown.

void initState() {
    print("Check initState");
    super.initState();
    ScopedModelDescendant<MainModel>(
        builder: (BuildContext context, Widget child, MainModel model) {
      print("Get into the scoped model");
      model.fecthCars();
      model.fecthCities();
      model.fecthBuys(model.getUserDto.token);
      print(model.getBuys().length);
      return;
    });
  }

None of the fetches(Api requests) get called. And the scopedModel returns a widget. I need this to be updated the first time I get into the manager and that's it. No need to call it again. Is this possible? or should I hardcode my api requests in each file I need?

UPDATE

If you have your scoped model class set up already you can set a Future like this inside of it

mixin MyModel on Model {
    Future<TypeToReturn> methodName(String param) async {
    Uri uri = new Uri.http('serverUrl', 'api/call');

    return await http.get(uri).then((http.Response response) {
      final List<dynamic> myResponse= json.decode(response.body);

      return myResponse;
    }).catchError((error) {
      print(error);
    });
  }
}

Aftermards you can set up your FutureBuilder

Widget _buildBody(BuildContext context, MainModel model) {

    return FutureBuilder(
      future:  model.methodName(someString), //This is the method name above
      builder: (context, AsyncSnapshot<TypeToReturn> snapshot) { //type u return
        if (!snapshot.hasData) {
          return Center(
            child: CircularProgressIndicator(),
          );
        } else {
          if (snapshot.data.length == 0)
            return Center(
              child: Text(
                "No Data Found",
                textAlign: TextAlign.center,
                style: TextStyle(
                  fontSize: 16.0,
                ),
              ),
            );

          return (create your own widget with the data inside the snapshot)
        }
      },
    );
  }

Hope this clarify things a little bit more on how I did it.

Upvotes: 9

Views: 5205

Answers (3)

Rafael santana
Rafael santana

Reputation: 51

this is my solution i hope it help

@override
  void initState() {
    super.initState();
    User user = ScopedModel.of(this.context);
    _controllerFirstName.text = user.userData['first_name'];
    _controllerLastName.text = user.userData['last_name'];
    }

Upvotes: 2

Ivan
Ivan

Reputation: 61

I stumbled upon the following solution:

In the State Class of my StatefulWidget I do:

@override
void initState() {
  super.initState();
  // and here...
  MyModel model = ScopedModel.of(context);
  // now I can do with the model whatever I need to do:
  Text someVar = model.initialText;
  model.getValuesFromSomewhere();
  // and so on
}

This, in my opinion, is the easiest way of solving the problem as stated by the original Question.

Upvotes: 6

rmtmckenzie
rmtmckenzie

Reputation: 40493

I think you've a slight misunderstanding about the point of ScopedModel and ScopedModelDescendant. The basic idea of how these should work is that the ScopedModel is created with a valid model which can then be used in other parts of the app.

However, the ScopedModelDescendant should be used within the build() function of a one of your widgets and be part of the widget tree as well. The reason your fetch methods aren't being called is that it isn't in the widget tree so the build function will never be called.

I would recommend instead moving the fetch methods out of the model and into some other class (maybe call it a communicator or controller or something). Next, I'd make it so that the model is instantiated as the result of an asynchronous call from that controller.

And finally, rather than instantiating an invalid model then changing the model once the data has been fetched, I'd recommend using a FutureBuilder - this way you have control over what to build based on whether the future is underway, successful, or failed.

So that will look something like this (pseudo-code).

StatefulWidget (MyApp or whatever you call it)
  build =>
     FutureBuilder(<fetch model data>, ...)
       (if done)
          ScopedModel<MainModel>
             .... (whatever your code has here)
               ScopedModelDescendant<MainModel>
                  (build using the model)             
       (if not done)
          Loading.... (if needed)

If you absolutely want your model to always be there, I'd still recommend doing the fetching in the top stateful widget and simply changing which model you pass in below it rather than modifying the existing model once the data is loaded.

Upvotes: 3

Related Questions