Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Doubt with Provider.of #31

Closed
basketball-ico opened this issue Mar 14, 2019 · 15 comments
Closed

Doubt with Provider.of #31

basketball-ico opened this issue Mar 14, 2019 · 15 comments
Assignees
Labels
documentation An opportunity for improving the documentation

Comments

@basketball-ico
Copy link

Hello, nice library 馃榾.
can you explain me please what is the difference.

Provider.of<SomeClass>(context, listen: true)
Provider.of<SoomeClass>(context, listen: false)

I noticed that I can use in initState() of StatefulWidget when is false, and can't use when is true, but really I don't know why :(

Thanks

@rrousselGit
Copy link
Owner

listen determines if the widget that use the value wants to rebuild when the value change.

For example, if:

Provider<int>(
  value: 42,
 child: ...
)

is later replaced by:

Provider<int>(
  value: 24,
  child: ...

Then this will trigger a rebuild of all widgets that called Provider.of(context) with listen to true.

@rrousselGit rrousselGit added the documentation An opportunity for improving the documentation label Mar 14, 2019
@rrousselGit rrousselGit self-assigned this Mar 14, 2019
@wheel1992
Copy link

wheel1992 commented Mar 22, 2019

Hi @rrousselGit, is it the way to update the value in a stateful widget?
For example:

Provider<int>(
  value: _value,
  child: ...
)

...
// Some widget calls (e.g. onClick event)
setState({
  _value = 1234,
})

@rrousselGit
Copy link
Owner

Yes, that's how you should do it.

@wheel1992
Copy link

wheel1992 commented Mar 22, 2019

@rrousselGit thank you for your confirmation! However, I'm stuck with this for quite long, not sure which part of my codes are wrong.

For example:
App init provider value as 123.
In Screen A, a button update the provider value to 456.
However, in screen B, it return the provider value as 123 instead of 456.

App:

MultiProvider(
  providers: [
    Provider<int>(
      value: 123,
      child: MaterialApp(
      ...
      ),
    ),
    ... // other providers
  ],
)

Screen A:

... 
int _value = 0;
...

Widget build(BuildContext context) {
  return Provider.of<int>(
    value: _value,
    child: Scaffold(
      ...
      Button(
        onPressed: ()  {
          setState({
            _value = 456, // Set value to 456
          });
        }
      ),
    ),
  );
}

Screen B:

int _value;

Widget build(BuildContext context) {
  final _provider = Provider.of<int>(context);
  // when access _provider, the value still returns 123 instead of 456
}

@enetor
Copy link
Contributor

enetor commented Mar 22, 2019

It looks to me that you have two provides: one in the App and one in Screen A. The value you got in Screen B is from the App, which is a value literal of 123.

@wheel1992
Copy link

wheel1992 commented Mar 23, 2019

@enetor Initially, Screen A is supposed to change the value in Provider by doing setState({ value = 456 }). :(

@enetor @rrousselGit Do you have recommendation on how to update the value from Screen A?

@jasonlaw
Copy link

jasonlaw commented Apr 6, 2019

hi, what if we are using ChangedNotifierProvider? will it based on the notifiedListener, instead of value changed? and i suppose it will not rebuild if listen = false right?

@PandaGeek1024
Copy link

It seems listen parameter is not working in my example, whenever I call setState, it will just rebuild the child widget no matter listen is false or not. Am I doing something wrong? @rrousselGit

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
          // Center is a layout widget. It takes a single child and positions it
          // in the middle of the parent.
          child: Provider<int>.value(
              value: _counter,
              child: Column(
                children: <Widget>[TestStatelessWidget()],
              ))),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

class TestStatelessWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // result is updated anyway, listen = false does not work.
    int result = Provider.of<int>(context, listen: false);
    return Text("result: $result");
  }
}

@mono0926
Copy link
Sponsor

mono0926 commented Sep 10, 2019

@PandaGeek1024

This should work.
Without const, TestStatelessWidget's rebuild is triggered by State's setState even though listen = false.
listen = false just suppresses rebuild triggered by Provider not State's setState.

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
          // Center is a layout widget. It takes a single child and positions it
          // in the middle of the parent.
          child: Provider<int>.value(
              value: _counter,
              child: Column(
                // add `const`
                children: const <Widget>[TestStatelessWidget()],
              ))),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

class TestStatelessWidget extends StatelessWidget {
  // add `const` constructor
  const TestStatelessWidget();
  @override
  Widget build(BuildContext context) {
    // result is updated anyway, listen = false does not work.
    int result = Provider.of<int>(context, listen: false);
    return Text("result: $result");
  }
}

@rrousselGit
Copy link
Owner

Exactly

Which is also why provider love const constructors/statelesswidgets

@Wes1324
Copy link

Wes1324 commented Oct 3, 2019

Hey @rrousselGit and @mono0926,

I am having a similar issue to @PandaGeek1024 except that my app (in the following attached document) uses only StatelessWidgets so there is no call to setState anywhere.

Listen doesn't work.txt

For that reason, I am not sure what is causing my text widgets to rebuild themselves.

I have been trying to figure this out for 2 days now so any insight you could give would be much appreciated.

@mono0926
Copy link
Sponsor

mono0926 commented Oct 3, 2019

@Wes1324

This should work.

         TextField(
            onChanged: (newText) {
              Provider.of<Data>(context, listen: false).updateValue(newText);
            },
          )

@Wes1324
Copy link

Wes1324 commented Oct 4, 2019

@mono0926
Please can you explain why this works?
Thank you

@mono0926
Copy link
Sponsor

mono0926 commented Oct 5, 2019

@Wes1324
I wrote about that in this article. This is written in Japanese, sorry.
https://link.medium.com/7a5FrRXFw0

@rrousselGit
Copy link
Owner

I believe that the new syntax (context.read/watch) makes it clear enough for this issue to be closed.

Feel free to correct me if you disagree

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation An opportunity for improving the documentation
Projects
None yet
Development

No branches or pull requests

8 participants