Bsel
Bsel

Reputation: 135

Flutter Provider.of<> without a consumer don't change my state

I am trying to get into the provider topic, however calling a function only works if I put it into a consumer

@override
Widget build(BuildContext context) {
return MultiProvider(
    providers: [
      ChangeNotifierProvider(create: (_) => ClickerProvider()),
    ],
    child: Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Text("some text"),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => Provider.of<ClickerProvider>(context, listen: false)
                          .incrementCounter(),
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    ));
}

As in this example, my state is not updated. However, it already works with a consumer.

floatingActionButton: Consumer<ClickerProvider>(
builder: (context, value, child) {
  return FloatingActionButton(
    onPressed: Provider.of<ClickerProvider>(context, listen: false)
        .incrementCounter,
    tooltip: 'Increment',
    child: Icon(Icons.add),
  );
},
)

Is there an error in my code?

Upvotes: 0

Views: 1621

Answers (2)

chunhunghan
chunhunghan

Reputation: 54367

You can copy paste run two full code below
Reason : Can not find ClickerProvider
Solution 1: Move ClickerProvider to upper level such as MyApp

class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MultiProvider(
            providers: [
              ChangeNotifierProvider(create: (_) => ClickerProvider()),
            ],
            child: MaterialApp(

Solution 2: Use Builder

body: Center(child: Builder(builder: (BuildContext context) {
                return Text(context.watch<ClickerProvider>().getCounter.toString());
              })),
floatingActionButton: Builder(builder: (BuildContext context) {
    return FloatingActionButton(
      onPressed: () =>
          Provider.of<ClickerProvider>(context, listen: false)
              .incrementCounter(),
      tooltip: 'Increment',
      child: Icon(Icons.add),
    );

full code 1

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

class ClickerProvider extends ChangeNotifier {
  int _count = 0;

  int get getCounter {
    return _count;
  }

  void incrementCounter() {
    _count += 1;
    notifyListeners();
  }
}

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (_) => ClickerProvider()),
        ],
        child: MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(title: 'Flutter Demo Home Page'),
        ));
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Center(
          child: Text(context.watch<ClickerProvider>().getCounter.toString()),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () => Provider.of<ClickerProvider>(context, listen: false)
              .incrementCounter(),
          tooltip: 'Increment',
          child: Icon(Icons.add),
        ));
  }
}

full code 2

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

class ClickerProvider extends ChangeNotifier {
  int _count = 0;

  int get getCounter {
    return _count;
  }

  void incrementCounter() {
    _count += 1;
    notifyListeners();
  }
}

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (_) => ClickerProvider()),
        ],
        child: Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Center(child: Builder(builder: (BuildContext context) {
            return Text(context.watch<ClickerProvider>().getCounter.toString());
          })),
          floatingActionButton: Builder(builder: (BuildContext context) {
            return FloatingActionButton(
              onPressed: () =>
                  Provider.of<ClickerProvider>(context, listen: false)
                      .incrementCounter(),
              tooltip: 'Increment',
              child: Icon(Icons.add),
            );
          }),
        ));
  }
}

Upvotes: 1

Bach
Bach

Reputation: 3326

As you can refer in the source code of Consumer here:

Obtains [Provider] from its ancestors and passes its value to [builder].

The [Consumer] widget doesn't do any fancy work. It just calls [Provider.of] in a new widget, and delegates its build implementation to [builder].

Provider.of<X> depends on value of listen (true or false) to trigger new State.build() to widgets and State.didChangeDependencies() for StatefulWidget.

Consumer<X> always update UI, as it uses Provider.of<T>(context), where listen is true

In this case, since your listen is set as false, but you're putting it in the Consumer which make it true. That's why the UI will update with Consumer

Upvotes: 1

Related Questions