Stateless widgets don’t change once built.
Stateful widgets can change — they have some state (data) which can change over time, such as a counter, user input, or animations.
1. initState()
- It is called exactly once when the state object is first created.
- It’s used to initialize data, set up listeners, or perform any setup work before the widget is displayed.
2. setState()
- setState() is a method you call when you want to update the UI because some state data has changed.
- It tells Flutter: "Hey, something changed in this widget’s state, please rebuild the UI!"
- It triggers the build() method to run again with the updated state.
5.a) Learn about stateful and stateless widgets.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Stateful vs Stateless')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
StatelessExample(),
StatefulExample(),
],
),
),
),
);
}
}
class StatelessExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text('I am a Stateless Widget');
}
}
class StatefulExample extends StatefulWidget {
@override
_StatefulExampleState createState() => _StatefulExampleState();
}
class _StatefulExampleState extends State<StatefulExample> {
int counter = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('I am a Stateful Widget: $counter'),
ElevatedButton(
onPressed: () {
setState(() {
counter++;
});
},
child: Text('Increment'),
),
],
);
}
}
When the button in the StatefulExample widget is pressed, the following happens step-by-step:
- The onPressed callback of the ElevatedButton is triggered.
- Inside this callback, setState() is called with a function that increments the counter variable by 1 (counter++).
- Calling setState() tells Flutter that the state of this widget has changed.
- Flutter schedules a rebuild of the _StatefulExampleState widget.
- The build() method runs again, now with the updated counter value.
- The UI updates to display the new counter value in the Text widget: 'I am a Stateful Widget: $counter'.
1. class StatefulExample extends StatefulWidget
"Here I am creating a widget that can change its appearance or behavior over time."
2. _StatefulExampleState createState() => _StatefulExampleState();
To manage the state of StatefulExample, create and use an instance of _StatefulExampleState
What is createState()?
It's a special method Flutter calls when it wants to build your widget.
The job of this method is to create and link a separate object responsible for holding the widget’s state.
_StatefulExampleState() is that state object. The method returns an instance of it.
The @override means this method replaces the default version in the parent class.
Think of StatefulExample as the blueprint or shell of your widget, and _StatefulExampleState as the brain or memory behind it that controls its behavior and holds the current data.
3. class _StatefulExampleState extends State<StatefulExample>
- This line defines a new class called _StatefulExampleState.
- It extends State<StatefulExample>, meaning it is a class that Holds state information for StatefulExample
- Controls how the UI looks and reacts when the state changes.
- The underscore (_) in front of the name means this class is private to the Dart file—no other file can see or use it.
5 b) Implement state management using set State and Provider.
It's commonly used to efficiently share and update data between different parts of your app, such as widgets, without the need for prop drilling (passing data through multiple widget layers).
Why is provider necessary in the first place? When one of the values on a view needs to change, its item needs to be rebuilt. It means that setState needs to be called in the widget or parent widget. If the parent widget is rebuilt all child widgets are rebuilt even if the rebuild is not necessary for some of them. It takes longer to complete to rebuild all widgets than to rebuild only one widget.
Look at the above image. All widgets are defined in the same class. If the blue button is clicked to update the value of Box1 which is highlighted with red color and setState is called on the event, all widgets listed here are rebuilt. It’s not nice.
Provider notifies the value change to the consumer when the target value changes. Consumer can recreate their own widgets by using the updated value. Provider must be placed higher place than Consumer.
Before using the provider state management in the Flutter app, we must first understand these basic concepts:
- ChangeNotifier
- ChangeNotifierProvider
- Consumer
1. ChangeNotifier
What it is:
ChangeNotifier is a class provided by Flutter that helps manage state in an object-oriented way. It allows an object to notify listeners when its internal state changes.
How it works:
When the data within your ChangeNotifier subclass changes, you simply call the notifyListeners() method to inform all the widgets that are listening to it. This triggers a rebuild of the dependent UI elements, updating them with the latest data.
2. ChangeNotifierProvider
What it is:
ChangeNotifierProvider is a widget from the Provider package that provides an instance of a ChangeNotifier to the widget tree.
How doesChangeNotifierProvider work?
ChangeNotifierProvider is responsible for creating and supplying an instance of a ChangeNotifier directly to the UI.
It ensures that when the ChangeNotifier emits a signal(due to changes).,dependent widgets are notified and can rebuild accordingly.
3. Consumer
Consumer is a widget provided by the Provider package that listens for changes in a specific ChangeNotifier and automatically rebuilds its child widget tree whenever the ChangeNotifier calls notifyListeners(). This ensures that only the affected parts of the UI are rebuilt, leading to more efficient performance compared to rebuilding the entire screen.
notifyListeners():
This is a method from ChangeNotifier.
It tells all widgets that are listening to this Counter object:
"Hey! My data changed, you should rebuild your UI!"
This triggers widgets that depend on the Counter to update and show the new value.
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 = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('State Management')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer<Counter>(
builder: (context, counter, child) => Text(
'Count: ${counter.count}',
style: TextStyle(fontSize: 24),
),
),
ElevatedButton(
onPressed: () => context.read<Counter>().increment(),
child: Text('Increment'),
),
],
),
),
),
);
}
}
1. void main() { ... }
This is the entry point of every Flutter app.
runApp() tells Flutter to start the app and show the widget passed to it.
Here, you wrap your app inside a ChangeNotifierProvider. This means you are providing a special object called Counter to the whole app so that any widget can access it and listen for changes.
2. ChangeNotifierProvider(create: (context) => Counter(), child: MyApp())
ChangeNotifierProvider comes from the Provider package.
It creates an instance of Counter (your app’s state) and makes it available to all widgets below it in the widget tree.
MyApp is the root widget of your app.
3. class Counter with ChangeNotifier { ... }
This class holds the data and logic for your counter.
_count is a private integer variable that stores the current count.
int get count => _count; is a getter method to read the count value.
increment() is a method that increases _count by 1.
notifyListeners(); tells all widgets that use this counter to rebuild because the data changed.
4. class MyApp extends StatelessWidget { ... }
This is the main widget of your app.
It builds a MaterialApp with a Scaffold that shows an app bar and a body.
The body centers a column with two widgets: a text showing the count, and a button to increment it.
5. Consumer<Counter>(builder: (context, counter, child) => Text('Count: ${counter.count}', ...))
Consumer listens to the Counter object provided above.
When Counter changes (like when increment() is called), this widget rebuilds.
It shows the current count value inside a Text widget.
6. ElevatedButton(onPressed: () => context.read<Counter>().increment(), child: Text('Increment'))
This is a button the user can press.
When pressed, it calls increment() on the Counter instance.
context.read<Counter>() gets the Counter object without listening to changes (just to call a method).
How everything works together:
Initially, the count is 0.
The UI shows "Count: 0".
When you press the button, increment() increases the count and calls notifyListeners().
Because the Consumer listens to the Counter, it rebuilds and shows the updated count.
This creates a reactive app where UI updates automatically when data changes.
Comments
Post a Comment