Search code examples
dartflutterarchitecturenavigationscoped-model

Flutter / Dart using Scoped Model with descendants and Navigator


I've been struggling with this one since yesterday - I'm creating new ScopedModel obj in tab controller (ScreenTabs), and then passing it to multiple tabs in BottomNavigationBar (1st level descendants, for example: ScreenBook) by wrapping each tab as ScopedModelDescendant. So far so good - everything's working as expected.

The problem arises, when in one of my tabs, I'd like to navigate to it's child (2nd level descendant, for example: ScreenBookDetails) and use ScopedModel there. I've already tried using ScopedModelDescendant and ScopedModel.of(context) with no luck - getting Error: Could not find the correct ScopedModel everytime.

My code (skipped some for brevity):

TabController

...
class ScreenTabsState extends State<ScreenTabs> {
ModelTabs modelTabs = ModelTabs(); //new Model object
var screens = [new ScreenHome(),new ScreenBook(),];

@override
Widget build(BuildContext context) {
return ScopedModel<ModelTabs>(
model: modelTabs, 
child: Scaffold(...)
body: Builder (builder: (context) => Container(child:  
screens.elementAt(selectedIndex),)),
bottomNavigationBar: BottomNavigationBar(
currentIndex: selectedIndex,
items: [...],
onTap: onTabTapped,
),)...}

ScreenBook (1st ddescendant - ScpodedModel works as expected)

...
class ScreenBookState extends State<ScreenBook> {

@override
Widget build(BuildContext context) {
return new Scaffold(
body: ScopedModelDescendant<ModelTabs>(builder: (context, child, model) {
print(TAG + "model status:" +  model.status.toString());
return model.status == Status.LOADING ? MyCircularProgressIndicator() :         
Builder (builder: (context) => 
Card(...
child: InkWell(
onTap: (){
Navigator.of(context).pushNamed("/ScreenBookDetails");},))}}

ScreenBookDetails (1st ddescendant - error)

...
class ScreenBookDetailsState extends State<ScreenBookDetails>{

@override
Widget build(BuildContext context) {
return Scaffold(
body: ScopedModelDescendant<ModelTabs>(builder: (context, child, model) {
print(TAG + "scoped model status: " + model.status.toString()); //Error: Could not find the correct ScopedModel.
return model.status == Status.LOADING ? MyCircularProgressIndicator() :     Builder(builder: (context) => Column(...)
...

can somebody point me the right way to fix thiis problem please? I'm new to Dart and Flutter, so there might be something that I did not understood clearly as I'm facing similar problem when creating ScopedModel obj in ScreenSplashScreen then navigating to ScreenLogIn and trying to use it there.

Thank you! :)


Solution

  • Finally found out how to handle ScopedModel including Navigator with help from ScopedModel's Github support - the key was to create a ScopeModel instance outside of build(BuildContext), then pass the instance through the constructor of next screen, like:

    class HomePage extends StatelessWidget {
     SampleScopedModel model = SampleScopedModel();
     @override
      Widget build(BuildContext context) {
        return ScopedModel<SampleScopedModel>(
          model: model,
          child: Scaffold(
            body: Container(),
            floatingActionButton: FloatingActionButton(onPressed: () {
              Navigator.push(context, 
               MaterialPageRoute(builder: (context) => NextPage(model))
              );
            }),
          ),
        );
      }
    }
    
    class NextPage extends StatelessWidget {
      final SampleScopedModel model;
      NextPage(this.model);
      @override
        Widget build(BuildContext context) {
          return ScopedModel<SampleScopedModel>(
            model: model,
            child: Scaffold(
              body: ScopedModelDescendant<SampleScopedModel>(
              builder: (context, child, model) {
                return Text(model.payload.toString());
              })
            ),
         );
        }
    }