Stateless widgets are immutable. Since their properties cannot change, all values are final. On the contrary, stateful widgets maintain state.
For that reason, widgets are rebuilt with the change of the state.
Does that affect flutter application?
Certainly, that rebuilds widgets and affects the performance.
Then what should we do to solve that problem?
When should I use provider Flutter?
We should use provider package to solve that problem.
Why should we use provider package?
The main reason is, provider package will rebuild only the widget that needs the value. The widget that needs the value is known as consumer.
As a result, that consumer widget only rebuilds itself. For example, consider a simple counter. As we press the button to change the state, each pressing changes the state and rebuilds the widget.
For a stateful widget, that rebuild affects the whole tree of widgets.
In Android Studio, we can watch the flutter performance.
Firstly, we see the counter example with the stateful widget.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@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, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
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(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Let us press the button for five times and side by side watch the flutter performance.
The screenshot looks like the following image.
![Widget rebuild with stateful widget](https://i0.wp.com/sanjibsinha.com/wp-content/uploads/2021/08/Widget-rebuild-with-stateful-widget.jpg?ssl=1)
Now, let us take a closer look and the screenshot looks like the following image.
![Widget rebuild with stateful widget affects every widget](https://i0.wp.com/sanjibsinha.com/wp-content/uploads/2021/08/Widget-rebuild-with-stateful-widget-affects-every-widget.jpg?resize=751%2C227&ssl=1)
It clearly shows that after pressing the button for five times, on the far right side of the screen each widget shows the number 6.
That means a stateful way always requires more memory than the stateless way. In fact that is clearly visible in the first image, because the red colored bar represents memory usage.
This is the reason why we should use a combination of Provider package and stateless widgets.
It makes our flutter app more performant.
How do you use Flutter provider package?
We use Flutter provider package like any other packages. Firstly, we add the provider package to the dependencies section in our pubspec.yaml file.
dependencies:
provider: ^6.0.0
Secondly, we import the provider package just like any other packages.
import 'package:provider/provider.dart';
Finally, we need to notify the listeners that we’re going to change the state.
We can define that method in our model folder, from where our data come.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
Next, to run the app we must place providers above the Counter App. Not only that, we need to use other methods so that we can read and watch the change.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shop_app/models/counter.dart';
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => Counter()),
],
child: const CounterApp(),
),
);
}
class CounterApp extends StatelessWidget {
const CounterApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: CounterHomePage(),
);
}
}
class CounterHomePage extends StatelessWidget {
const CounterHomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final subtree = ConstantWidget(
child: const Text("Hello World")
);
final anotherSubtree = _widget();
return Scaffold(
appBar: AppBar(
title: const Text('A Stateless Counter App'),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget> [
Text('You have pushed the button this many times:'),
Text(
'${context.watch<Counter>().count}',
key: const Key('counterState'),
style: Theme.of(context).textTheme.headline4,
),
SizedBox(width: 10.0,),
subtree,
SizedBox(width: 10.0,),
anotherSubtree,
],
),
),
floatingActionButton: FloatingActionButton(
key: const Key('increment_floatingActionButton'),
onPressed: () => context.read<Counter>().increment(),
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
/// Just to test rebuild
Widget _widget() => const Text('Hi');
ConstantWidget({required Text child}) {
/// Just to test rebuild
return Text('Hello');
}
}
In the above code, a few lines are extremely important.
Firstly, we’ve placed the Multi Provider above our counter app.
Secondly, when the context use read and watch methods, we’ve supplied the type Counter that we’ve defined in the model folder.
'${context.watch<Counter>().count}',
onPressed: () => context.read<Counter>().increment(),
What is the difference between context.read and context.watch?
It’s a key concept while we’re trying to understand the role of Provider package.
To display the count number, we use context.watch.
Why?
We call context.watch to make the count property of Type Counter rebuild when the counter changes.
However, inside the floating action button on press method we return context.read.
It stops the floating action button widget from rebuilding itself.
Now, as a result, we get a different screenshot when we press the button.
![Widget rebuild with stateless widget](https://i0.wp.com/sanjibsinha.com/wp-content/uploads/2021/08/Widget-rebuild-with-stateless-widget.jpg?ssl=1)
With the help of Provider package, we have kept the requirement of memory usage much lower than the stateful widget.
As we can see, there are no red bars.
Not only that, now we can take a closer look at the flutter performance.
![Widget rebuilds affects only one widget](https://i0.wp.com/sanjibsinha.com/wp-content/uploads/2021/08/Widget-rebuilds-affects-only-one-widget.jpg?resize=747%2C202&ssl=1)
As we keep on pressing the button, only two widgets rebuild themselves without affecting the others.
Moreover, now the state change centers on only two text widgets.
The parent tree remains unaffected.
Leave a Reply