Flutter is an open-source UI software development kit (SDK) developed by Google, used to build high-performance UIs for mobile, web, and desktop applications. In this course, we will explain one of Flutter’s core concepts, ‘state management’, and take a closer look at Stateful widgets. The course includes the concept of Stateful widgets, how to use them, and practical examples utilizing them.
1. What is State Management?
State management refers to techniques that synchronize an application’s data and UI. The user interface (UI) often changes according to the application’s state, which can be altered by various factors such as user input, network requests, and timers. Flutter provides two main types of widgets to manage this state: Stateful widgets and Stateless widgets.
2. Stateless Widgets and Stateful Widgets
Stateless widgets have immutable states, thus they do not need to be refreshed every time the UI is drawn. In contrast, Stateful widgets can have internal states, and when this state changes, the widget needs to be rebuilt. Generally, Stateful widgets are used in UIs that involve interactions with the user or asynchronous operations.
2.1 Example of Stateless Widget
class MyStatelessWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text('Hello, world!');
}
}
2.2 Example of Stateful Widget
class MyStatefulWidget extends StatefulWidget {
@override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State {
int _count = 0;
void _incrementCounter() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('You have pushed the button this many times: $_count'),
ElevatedButton(
onPressed: _incrementCounter,
child: Text('Increment'),
),
],
);
}
}
3. Structure of Stateful Widgets
Stateful widgets consist of two classes: a widget class and a state class. The widget class defines the style of the UI that is displayed to the user, while the state class includes mutable data and the logic to change that data. These two classes are closely linked so that when the state in the state class changes, the build method is called to redraw the UI.
4. Lifecycle of Stateful Widgets
Stateful widgets have a specific lifecycle. This lifecycle consists of various methods related to the creation, updating, and deletion of the widget. These methods play an important role in ensuring the efficiency of state management. The main lifecycle methods are as follows:
- createState: Called when the widget is created. It should return a new state object.
- initState: Called for the first time after the state object is created. Suitable for performing initialization tasks.
- didChangeDependencies: Called when the widget’s dependencies change. Mainly used with InheritedWidget.
- build: Used for rendering the UI. The build method of the state object is called every time the state changes.
- setState: Method used to change the state and update the UI.
- dispose: Called when the object’s lifespan ends, used for cleaning up resources.
4.1 Example of State Class Lifecycle
class _MyStatefulWidgetState extends State {
@override
void initState() {
super.initState();
// Initialization code
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
// Dependency change code
}
@override
Widget build(BuildContext context) {
// UI construction code
}
@override
void dispose() {
// Resource cleanup code
super.dispose();
}
}
5. Real Example of Implementing Stateful Widget
Now let’s learn how to actually implement a Stateful widget. We will create a simple counter application. The functionality will increase the count every time the user clicks the button.
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 Widget Example'),
),
body: CounterWidget(),
),
);
}
}
class CounterWidget extends StatefulWidget {
@override
_CounterWidgetState createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State {
int _count = 0;
void _incrementCounter() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Press the button to increase the count:'),
Text('$_count', style: Theme.of(context).textTheme.headline4),
ElevatedButton(
onPressed: _incrementCounter,
child: Text('Increment'),
),
],
),
);
}
}
The above code is an implementation of a Stateful widget using the basic structure of Flutter. This application increases the count value every time the ‘Increment’ button is clicked and displays the incremented value on the screen.
6. State Management Patterns
Although Stateful widgets are designed to manage state, more efficient state management patterns may be required in complex applications. There are various state management patterns, and we will introduce the most commonly used patterns here.
6.1 Provider Pattern
Provider is one of the most widely used state management solutions in Flutter. The Provider pattern is based on InheritedWidget and is useful for simply managing and sharing state. Here is the code for the counter example modified to use Provider.
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(); // Notify state change
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Provider Example'),
),
body: CounterWidget(),
),
);
}
}
class CounterWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counter = Provider.of(context);
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Press the button to increase the count:'),
Text('${counter.count}', style: Theme.of(context).textTheme.headline4),
ElevatedButton(
onPressed: counter.increment,
child: Text('Increment'),
),
],
),
);
}
}
6.2 BLoC Pattern
The BLoC (Business Logic Component) pattern is an approach that separates the business logic of the app from the UI. It follows reactive programming and controls data flow using streams. We will cover more about the BLoC pattern in future lectures.
7. Conclusion
In this lecture and example, we explored the structure and lifecycle of Stateful widgets and practical use cases. We learned about the necessity of state management in Flutter and various state management patterns. Stateful widgets are essential elements for building UIs that change through user interactions, and effective use of them can lead to the development of more efficient and maintainable applications.
In future lectures, we will gradually learn about more state management patterns, UI components, application architectures, and more. Create your own amazing applications using Flutter!