Today we will build counter without using StatefulWidget
. Yes you heard right, without using StatefulWidget
and setState
. We will do using provider package.
If you like to learn by watching videos then there is good news for you. I made video for the same few days back.
Step 1
First of all we need to use the package called provider
. In order to use that you have to add the package name to the file pubspec.yaml
file
https://pub.dev/packages/provider
name: provider_example
description: A new Flutter project.
version: 1.0.0+1
environment:
sdk: ">=2.7.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.0
provider: ^6.0.3 # Added
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
Step 2
Updating main.dart
file.
We will try to keep this file as much as. This will help us in focusing more on concept rather other things.
import 'package:flutter/material.dart';
// at this location we will create a widget
import 'screens/counter.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
// CounterPage will be created in next step
home: CounterPage(),
);
}
}
You migh be wondering about all the files which I will created, here is is my files and folder structure. I am pretty sure this will help you alot to understand the folder structure.
├── android
├── ios
├── lib
│ ├── main.dart
│ ├── provider
│ │ └── counter_provider.dart
│ └── screens
│ └── counter.dart
|____main.dart
└── pubspec.yaml
Step 3
As you already have seen my folder structure in the above step. You already know what folder and file needs to be created in this step.
We will create CounterPage in this step, the filename will be counter.dart
and the path will be lib/screens/counter.dart
.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class CounterPage extends StatelessWidget {
const CounterPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final count = 0;
return Scaffold(
appBar: AppBar(
title: const Text('Counter Example'),
),
body: Center(
child: Text(
'Count is $count',
style: Theme.of(context).textTheme.headline5,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.plus_one),
),
);
}
}
Step 4
Let’s introduce the Provider now to the counter app. For this we will create a file for provider.
We will create a class which will store and update the count value. The count variable is private becuase this variable should not be updated directly.
import 'package:flutter/material.dart';
class CounterProvider extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count = _count + 1;
notifyListeners();
}
}
You might be having few Question about this class, we will talk about those at the end of the article.
Although, I said let’s introduce provider in this step but we haven’t introduced provider full in this step. As you can see there is no any import for the provider package.
Step 5
Let’s make the provider available for the app.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'provider/counter_provider.dart';
import 'screens/counter.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => CounterProvider(),
child: const MaterialApp(
home: CounterPage(),
),
);
}
}
Step 6
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../provider/counter_provider.dart';
class CounterPage extends StatelessWidget {
const CounterPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// Obtain the provider
final counter = Provider.of<CounterProvider>(context);
// getting the value from the provider instance
final count = counter.count;
return Scaffold(
appBar: AppBar(
title: const Text('Counter Example'),
),
body: Center(
child: Text(
'You pressed button\n $count times',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headline5,
),
),
floatingActionButton: FloatingActionButton(
// calling increment method to update the value in counter provider
onPressed: counter.increment,
child: const Icon(Icons.plus_one),
),
);
}
}
Provider Quick Question
- Why this variable is private
_count
?
We don’t want this variable should be updated directly becuase we want where-ever this variable is being used that value also should be updated.
- What is
ChangeNotifier
?
When we extend this class, we get access to the change notification API
which can tell the listener that some change had been made.
- why are we calling
notifyListeners()
?
notifyListeners()
comes from the class ChangeNotifier
and it tell the listener that this call value has been updated.
As I mentioned we tell the listener about the changes, then how does the listener get callback
Source Code
In code you would like to get the source code for this project. You can find it easy on the github at this given link.