Search code examples
flutterinherited-widget

Flutter - InheritedModel still update child even if it does not match an aspect


Good day. I've watched a video about Flutter's InheritedModel and got interested on it. Unfortunately, I can't seems to make it work properly.


Summary: Need help how to properly implement InheritedModel.

Expected Code Output: Widget CountText should not be updated when updating count parameter in CountModel.

Actual Code Output: CountText still updates (I think this is due to that the parent widget is a StatefulWidget)


Details

I am trying to implement a Counter app using InheritedModel. Code below is my code

import 'package:flutter/material.dart';

class CountModel extends InheritedModel<String> {

  final int count;  

  CountModel({ this.count, child }) : super(child: child);

  @override
  bool updateShouldNotify(CountModel oldWidget) {
    if (oldWidget.count != count) {
      return true;
    }
    return false;
  }

  @override
  bool updateShouldNotifyDependent(InheritedModel<String> oldWidget, Set<String> dependencies) {
    if (dependencies.contains('counter')) {
      return true;
    }
    return false;
  }

  static CountModel of(BuildContext context, String aspect) {
    return InheritedModel.inheritFrom<CountModel>(context, aspect: aspect);
  }

}

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Counter',
      theme: Theme.of(context),
      home: Counter(),
    );
  }
}

class Counter extends StatefulWidget {
  @override
  CounterState createState() => CounterState();
}

class CounterState extends State<Counter> {

  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        // title: Text("Counter"),
      ),
      body: CountModel(
        count: count,
        child: CounterText()
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            ++count;          
          });
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

class CounterText extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    CountModel model = CountModel.of(context, 'test');
    return Text('Count: ${model.count}');
  }  

}

I have a CountModel as InheritedModel and a CountText widget which consumes the data from the CountModel. As you can see in the implementation of the CountText, it pass test when getting the CountModel. In my understanding, it should not be updated when the count value is updated in the CountModel. Unfortunately, this does not happen.


Solution

  • In short, you should use const.

    Add const to CounterText constructor

    class CounterText extends StatelessWidget {
      const CounterText();
      ...
    }
    

    and use const when you create instance of CounterText() (const CounterText())

    class CounterState extends State<Counter> {
      ...
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          ...
          body: CountModel(..., child: const CounterText()),
          ...
        );
      }
    }
    

    And voila 🎉

    I have described why this is happening here in details