I think there is a better way to manage your app state in an easy way and I agree that using provider could be effective.
Provide the model to all widgets within the app. We're using
ChangeNotifierProvider
because that's a simple way to rebuild
widgets when a model changes. We could also just use Provider, but
then we would have to listen to Counter ourselves.
Read Provider's docs to learn about all the available providers.
Initialize the model in the builder. That way, Provider can own
Counter's lifecycle, making sure to call dispose
when not needed
anymore.
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => Counter(),
child: MyApp(),
),
);
}
Simplest possible model, with just one field. ChangeNotifier
is a
class in flutter:foundation
. Counter
does not depend on Provider.
class Counter with ChangeNotifier {
int count = 1;
void clickIncrease() {
count += 1;
notifyListeners();
}
}
Consumer
looks for an ancestor Provider widget and retrieves its
model (Counter, in this case). Then it uses that model to build
widgets, and will trigger rebuilds if the model is updated.
You can access your providers anywhere you have access to the context.
One way is to use Provider<Counter>.of(context)
.
The provider package also defines extension methods on context itself.
You can call context.watch<Counter>()
in a build method of any
widget to access the current state of Counter, and to ask Flutter to
rebuild your widget anytime Counter changes.
You can't use context.watch()
outside build methods, because that
often leads to subtle bugs. Instead, you should use
context.read<Counter>()
, which gets the current state but doesn't
ask Flutter for future rebuilds.
Since we're in a callback that will be called whenever the user taps
the FloatingActionButton
, we are not in the build method here. We
should use context.read()
.
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Scaffold is a layout for the major Material Components.
return Scaffold(
appBar: AppBar(
title: Text('Flutter Demo Home Page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Count:'),
Consumer<Counter>(
builder: (context, counter, child) => Text(
'${counter.value}',
style: Theme.of(context).textTheme.headline4,
),
),
],
),
),
// I've change the button to `FloatingActionButton` for better ui experience.
floatingActionButton: FloatingActionButton(
// Here is the implementation that you are looking for.
onPressed: () {
var counter = context.read<Counter>();
counter.increment();
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Complete code:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => Counter(),
child: MyApp(),
),
);
}
class Counter with ChangeNotifier {
int count = 1;
void clickIncrease() {
count += 1;
notifyListeners();
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Demo Home Page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Count:'),
Consumer<Counter>(
builder: (context, counter, child) => Text(
'${counter.count}',
style: Theme.of(context).textTheme.headline4,
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
var counter = context.read<Counter>();
counter.clickIncrease();
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Actual app:

For more information on the provider package (where Provider comes from), please see the package documentation.
For more information on state management in Flutter, and a list of other approaches, head over to the State management page at flutter.dev.