Search code examples
dartflutterscoped-model

Error: Could not find the correct ScopedModel when using ScopedModel.of<MyModel>(context)


As an outsider looking into flutter state management, I liked the idea of using scoped_model for state management as recommended in flutter.dev. I have a rewrite of the counter app running. I am able to access the model properties using ScopedModelDescendant<CounterModel>, but I am having troubles accessing the model properties using ScopedModel.of<CounterModel>(context). Could someone please advice what I might be doing wrong? I have a hunch that it could be where the ScopedModel is in my widget tree. My code and error message follows.

main.dart

import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
import 'package:second/model/counter_model.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Widget build(context) {
    return ScopedModel(
      model: new CounterModel(),
      child: Scaffold(
          appBar: AppBar(
            title: Text('ScopedModel'),
          ),
          body: ScopedModelDescendant<CounterModel>(
            builder: (context, child, value) {
              return Text("Pressed ${value.counter} times");
            },
          ),
          floatingActionButton: buildFab1()),
    );
  }

  Widget buildFab1() {
    return ScopedModelDescendant<CounterModel>(
      builder: (context, child, model) => FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: model.incrementCounter,
          ),
    );
  }

  Widget buildFab2(BuildContext context) {
    return FloatingActionButton(
      child: Icon(Icons.add),
      onPressed: ScopedModel.of<CounterModel>(context).incrementCounter,
    );
  }
}

model/counter_model.dart

import 'package:scoped_model/scoped_model.dart';

class CounterModel extends Model {
  int _counter = 0;

  int get counter => _counter;

  void incrementCounter() {
    _counter++;
    notifyListeners();
  }
}

In main.dart, if I use buildFab2(context) instead of buildFab1(), I get the following error


flutter: The following ScopedModelError was thrown building ScopedModelDescendant<Model>(dirty):
flutter: Error: Could not find the correct ScopedModel.
flutter:
flutter: To fix, please:
flutter:
flutter:   * Provide types to ScopedModel<MyModel>
flutter:   * Provide types to ScopedModelDescendant<MyModel>
flutter:   * Provide types to ScopedModel.of<MyModel>()
flutter:   * Always use package imports. Ex: `import 'package:my_app/my_model.dart';
flutter:

I took a look at a few SO questions, but none helped.


Solution

  • Yes, that because, the context you pass will not have a ScopedModel of CounterModel.

    What you can do is wrap your buildFab2 inside a Builder widget which will provide you with a context having ScopedModel with CounterModel as parent.

    Like:

    Builder(
        builder: (context){
          return buildFab2(context);
        },
      )
    

    You app will look like:

    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: MyHomePage(),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      Widget build(context) {
        return ScopedModel(
          model: new CounterModel(),
          child: Scaffold(
            appBar: AppBar(
              title: Text('ScopedModel'),
            ),
            body: ScopedModelDescendant<CounterModel>(
              builder: (context, child, value) {
                return Text("Pressed ${value.counter} times");
              },
            ),
            floatingActionButton: Builder(
              builder: (context) {
                // return buildFab1() if fab one required
                return buildFab2(context);
              },
            ),
          ),
        );
      }
    
      Widget buildFab1() {
        return ScopedModelDescendant<CounterModel>(
          builder: (context, child, model) => FloatingActionButton(
                child: Icon(Icons.add),
                onPressed: model.incrementCounter,
              ),
        );
      }
    
      Widget buildFab2(BuildContext context) {
        return FloatingActionButton(
          child: Icon(Icons.add),
          onPressed: ScopedModel.of<CounterModel>(context).incrementCounter,
        );
      }
    }
    

    Hope that helps!