Building a Simple Counter App in Flutter: A Guide to Using Provider

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

pubspec.yaml
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.

main.dart
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.

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.

provider/counter_provider.dart
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.

main
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

screens/counter.dart
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.

Github Repository
Download

Counter App using provider

Dart 3