Widgets present outside the Consumer are also getting rebuilt while using ChangeNotifierProvider in Flutter

Issue

So I am just playing around the provider widgets. Below is my Entry main function.

void main() async {

  runApp(
      ChangeNotifierProvider<GameProvider>(
        create: (_) => GameProvider(),
          builder: (context,child) => MaterialApp(home: GameScene()),
      )
  );

}

As seen in the code above, I’ve added the provider widget above MaterialApp so that it can be available across whole app.

Below is my GameScene widget :

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:river_crossing_game/Providers/GameProvider.dart';

class GameScene extends StatefulWidget {
  @override
  _GameSceneState createState() => _GameSceneState();
}

class _GameSceneState extends State<GameScene> with SingleTickerProviderStateMixin{

  double height, width;

  bool pseudoInitState = true;

  AnimationController _animationController;
  Animation _animation;


  GameProvider _provider;
  

  @override
  Widget build(BuildContext context) {

    height = MediaQuery.of(context).size.height;
    width = MediaQuery.of(context).size.width;

    if(pseudoInitState){

      _provider = Provider.of<GameProvider>(context);              /// Added this just to use the methods present inside the provider class

      _animationController = AnimationController(vsync: this,duration: Duration(seconds: 5));
      _animation = Tween<double>(begin: 0,end: 60 * 5.0).animate(_animationController);

      _provider.initPlankPos(Offset(0,height * 0.4));     /// Ignore this method

      _animation.addListener(() {
        _provider.translatePlank(_animation.value, Offset(1,0));    /// This one is just updating the position of the widget inside the Consumer
      });

      _animationController.forward();

      pseudoInitState = false;
    }


    return Material(
      child: Stack(
        children: [
          Container(height: height, width: width, color: Colors.lightBlue,),
          Align(
            alignment: Alignment.bottomCenter,
            child: Builder(builder: (context) {print("Background Build ${Random.secure().nextDouble().toString()}");return Image.asset('assets/grass_sprite.png',height: 90,width: width,fit: BoxFit.cover);}),
          ),
          Consumer<GameProvider>(
            child: Image.asset('assets/wood_plank.png',height: width * 0.15,width: width * 0.25,fit: BoxFit.cover,),
                builder: (context,value, child) {
              return Transform.translate(
                  offset: Offset(value.plankPosX,value.plankPosY),
                  child: child,
              );
            }
          )
        ],
      ),
    );
  }

}

As seen in the above code I’ve wrapped on Image widget with Consumer because it’s the only widget that is going to be moved to a new position everytime the provider a new value and rest all the other widgets(grass sprite image and blue container above stack) will remain the same irrespective of the provider’s updates.

To check if it’s working just the way I said above, I wrapped the grass sprite image widget with a Builder (as seen in the above code) to check if it’s getting rebuilt even if it is outside the Consumer widget and sadly it is getting rebuilt on every update.

Till now I thought that only the widgets inside the builder function of Consumer will get rebuilt while new value is provided by the provider but here the widgets outside the Consumer are also getting rebuilt.

Am I not using the provider correctly or have I get the wrong information about working of providers ?

Thankyou in advance for the help.

Solution

 _provider = Provider.of<GameProvider>(context);    

Change this to this:

 _provider = Provider.of<GameProvider>(context,listen: false );    

Answered By – Tasnuva Tavasum oshin

Answer Checked By – Willingham (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *