Why isn't the Provider Package changing the background image?

Issue

I’ve added the provider package to my application where I have two screen. When the user clicks on a small image on the app it changes the main background image on the other screen. I’ve called the Provider and classes on both screens but it just isn’t returning the ‘myValue’ inside Positioned.fill.

Homepage screen with the background image that needs to change:

import 'package:flutter/material.dart';
import 'package:flutter_app_background/small_images.dart';
import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart';


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


class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<MyModel>(
      create: (context) => MyModel(),
      child: MaterialApp(
        title: 'Title',
        home: HomePage(),
      ),
    );
  }
}

class HomePage extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
      return Scaffold(
        extendBodyBehindAppBar: true,
        appBar: AppBar(
          title: Text('Background Image', style: TextStyle(
              color: Colors.black,
              fontSize: 16,
              fontWeight: FontWeight.bold),
          ),
          iconTheme: IconThemeData(color: Colors.white),
          actions: <Widget>[
            IconButton(
              icon: Icon(Icons.settings, color: Colors.black,),
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) => SmallImages()),
                );
              },
            ),
          ],
          backgroundColor: Colors.transparent,
          elevation: 0.0,
        ),
        body: Stack(
          children: <Widget>
          [
            Positioned.fill(
              child: GestureDetector(
                child: Consumer<MyModel>(
                  builder: (context, myModel, child) {
                    return myModel.bgImage;
                    // return myValue;
                  },
                ),
              ),
            ),
          ],
        ),
      );
    }
}

class MyModel extends ChangeNotifier {
  Image bgImage = Image.asset('images/background_image.jpeg', fit: BoxFit.fill);

}

Small Images screen where the user taps on small image to change the background in Homepage.

import 'package:flutter/material.dart';
import 'package:flutter_app_background/main.dart';
import 'package:provider/provider.dart';


class SmallImages extends StatefulWidget {
  static int tappedGestureDetector = 1;

  @override
  _SmallImagesState createState() => _SmallImagesState();
}

class _SmallImagesState extends State<SmallImages> {

  List<bool> isSelected;

  void initState() {
    isSelected = [true, false, false, false, false, false, false, false, false];
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    final myModel = Provider.of<MyModel>(context,listen:true); //default for listen is `true`
    return Scaffold(
          appBar: AppBar(
            title: Text('Small Image', style: TextStyle(
                color: Colors.black, fontSize: 16, fontWeight: FontWeight.bold),
            ),
            iconTheme: IconThemeData(color: Colors.white),
            actions: <Widget>[
              IconButton(
                icon: Icon(Icons.arrow_left, color: Colors.black,),
                onPressed: () {
                  Navigator.pop(
                    context,
                    MaterialPageRoute(builder: (context) => HomePage()),
                  );
                },
              ),
            ],
            backgroundColor: Colors.transparent,
            elevation: 0.0,
          ),
          body: Material(
            child: GestureDetector(
              child: MaterialApp(
                  builder: (context, snapshot) {
                    return GridView.count(
                      crossAxisCount: 1,
                      childAspectRatio: 1.0,
                      padding: const EdgeInsets.all(4.0),
                      mainAxisSpacing: 0.0,
                      crossAxisSpacing: 0.0,
                      children: [
                        GridView(
                          gridDelegate:
                          SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3,
                            childAspectRatio: MediaQuery
                                .of(context)
                                .size
                                .width /
                                (MediaQuery
                                    .of(context)
                                    .size
                                    .height / 2),
                          ),
                          children: [
                              GestureDetector(
                                onTap: () {
                                  setState(() {
                                    SmallImages.tappedGestureDetector = 1;
                                  });
                                  myModel.bgImage = Image.asset('images/iceland_background.jpg');
                                },
                                child: Container(
                                  height: 100,
                                  width: 107,
                                  decoration: BoxDecoration(border: SmallImages
                                      .tappedGestureDetector == 1
                                      ? Border.all(
                                      color: Color(0xff2244C7), width: 1.0)
                                      : Border
                                      .all(color: Colors.transparent,),),
                                  child: Image.asset(
                                    'images/nightsky_image.png',
                                  ),
                                ),
                            ),
                            Consumer<MyModel>(
                              builder: (context, myModel, child) {
                                return GestureDetector(
                                  onTap: () {
                                    setState(() {
                                      SmallImages.tappedGestureDetector = 2;
                                    }); // <-- replaced 'tapped' and 'other'
                                  },
                                  child: Container(
                                    height: 100,
                                    width: 107,
                                    decoration: BoxDecoration(border: SmallImages
                                        .tappedGestureDetector == 2
                                        ? Border.all(
                                        color: Color(0xff2244C7), width: 1.0)
                                        : Border
                                        .all(color: Colors.transparent,),),
                                    child: Image.asset(
                                      'images/own_image.png',
                                    ),
                                  ),
                                );
                              },
                            ),
                            Consumer<MyModel>(
                              builder: (context, myModel, child) {
                                return GestureDetector(
                                  onTap: () {
                                    setState(() {
                                      SmallImages.tappedGestureDetector = 3;
                                    }); // <-- replaced 'tapped' and 'other'
                                  },
                                  child: Container(
                                    height: 100,
                                    width: 107,
                                    decoration: BoxDecoration(border: SmallImages
                                        .tappedGestureDetector == 3
                                        ? Border.all(
                                        color: Color(0xff2244C7), width: 1.0)
                                        : Border
                                        .all(color: Colors.transparent,),),
                                    child: Image.asset(
                                      'images/iceland_image.png',
                                    ),
                                  ),
                                );
                              },
                            ),
                          ].toList(),
                        ),
                      ],
                    );
                  }),
            ),
          ),
        );
  }
}

Solution

You should wrap material app with provider:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Provider<MyModel>(
      create: (context) => MyModel(),
      child: MaterialApp(
        title: 'Title',
        home: HomePage(),
      );
    )

  }
}

if you want it to rebuild some widget on change of the MyModel, you should extend MyModel with ChangeNotifer like this:

class MyModel extends ChangeNotifier{
final bgImage = //someimage

and instead of Provider around the MaterialApp you should use ChangeNotifierProvider like this:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<MyModel>(
      create: (context) => MyModel(),
      child: MaterialApp(
        title: 'Title',
        home: HomePage(),
      );
    )

  }
}

after that you should include provider inside the widget like this:

  @override
  Widget build(BuildContext context) {
    final mymodel = Provider.of<MyModel>(context); // injecting the provider
    return Container ( .....

and you can use background image inside the container like this,

color:mymodel.bgImage

as soon as you will change the mymodel.bgImage widget will be rebuilt automatically.

if you are planning to rebuild specific widget inside the widget tree, you can remove injection such as this in this case final mymodel = Provider.of<MyModel>(context); and just wrap that specific widget with Consumer<MyModel> like this:

Container(
  child:Consumer<MyModel>(
    builder: (context, myModel, child) {
    return Text("${myModel.text}"); // supposing that `text` is inside the `MyModel`
  },
)

if you don’t need child and context to use under the Container you can do it like this:

child:Consumer<MyModel>(
    builder: (_, myModel, _) {

example of changing the myModel.bgImage will be something like this:

FlatButton(
onPressed: (val){
  myModel.image = otherImage  // changeing the value of my model while pressing on the button

  }
)

changing the value of my model while pressing on the button will rebuild any widget which injects MyModel Provider and Provider listen property is set to true,(listen property is set to true by default)

example of listen property:

final mymodel = Provider.of<MyModel>(context,listen:false) //default for listen is `true`

Answered By – man of knowledge

Answer Checked By – Marie Seifert (FlutterFixes Admin)

Leave a Reply

Your email address will not be published.