Flutter State Management

Flutter Code Example

State management in Flutter

State management in Flutter is a fundamental concept that ensures the dynamic behavior of your application by managing the state of various widgets and data throughout its lifecycle. we focus on state management basics and an example of a shopping cart application to understand how state is managed efficiently.

there are two conceptual types of state in any Flutter app. Ephemeral state can be implemented using State and setState() , and is often local to a single widget. The rest is your app state.


State

State refers to the current data or configuration of an application or widget at a given point in time.

    Ephemeral State (Local State):

    Ephemeral state, also called local state, is temporary data that is used by a single widget and does not need to be shared across the application. This type of state is typically small and transient, such as whether a checkbox is checked, the current tab in a TabBar, or the value of a text input field.

  • Short-lived and confined to a single widget.
  • Example: The state of an animation, the selected tab in a navigation bar.
  • Managed using StatefulWidget.

    App State (Global State):

    App State, also known as application state, refers to the state that is shared across multiple parts of an application and persists throughout the app's lifecycle. It represents global data or configurations that influence the behavior and appearance of the entire app.

  • Long-lived and shared across multiple widgets or screens.
  • Example: User login data, shopping cart content.
  • Managed using tools like Provider, Riverpod, or Bloc.

A widget is either stateful or stateless. If a widget can change—when a user interacts with it, for example—it's stateful. A stateless widget never changes. Icon , IconButton , and Text are examples of stateless widgets.


Counter App with Ephemeral State


import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: CounterScreen(),
); } } class CounterScreen extends StatefulWidget {
@override
_CounterScreenState createState() => _CounterScreenState();
}
class _CounterScreenState extends State<CounterScreen> {
int _counter = 0; // Ephemeral state
void _incrementCounter() {
setState(() { _counter++; // Update state
});
}
@override Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Ephemeral State Example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('You have pressed the button this many times:'),
Text('$_counter', style: TextStyle(fontSize: 24)),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
child: Icon(Icons.add),
),
);
}
}

Flutter Provider Example


import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
// Define AppState class AppState extends ChangeNotifier {
String _userName = "Guest";
bool _isLoggedIn = false;
String get userName => _userName;
bool get isLoggedIn => _isLoggedIn;
void login(String name) {
_userName = name;
_isLoggedIn = true;
notifyListeners();
}
void logout() {
_userName = "Guest";
_isLoggedIn = false;
notifyListeners();
}
}
// Main App
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => AppState(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Consumer<AppState>(
builder: (context, appState, _) {
return appState.isLoggedIn ? HomePage() : LoginPage();
},
),
);
}
}
// Login Page class LoginPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final appState = Provider.of<AppState>(context, listen: false);
return Scaffold(
appBar: AppBar(title: Text("Login")),
body: Center(
child: ElevatedButton(
onPressed: () {
appState.login("John Doe");
},
child: Text("Log In"),
),
),
);
}
}
// Home Page class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final appState = Provider.of<AppState>(context);
return Scaffold(
appBar: AppBar(
title: Text("Welcome, ${appState.userName}"),
actions: [
IconButton(
icon: Icon(Icons.logout),
onPressed: () {
appState.logout();
},
)
],
),
body: Center(child: Text("You are logged in!")),
);
}
}

Navigation and routing in Flutter

navigation refers to the process of switching between different screens or pages within an application, while routing involves managing the flow and organization of these screens.

The method of routing involves determining which views or pages should be displayed in response to a specific URL or hash fragment request. On the other hand, the process of moving from one perspective to another while following established paths is known as navigation.

The way that the navigation of an application is handled is called Routing. Flutter provides a basic routing class – MaterialPageRoute and two methods - Navigator.push and Navigator.pop, to define the work flow of an application.

MaterialPageRoute

MaterialPageRoute is a widget used to render its UI by replacing the entire screen with a platform specific animation.



MaterialPageRoute(builder: (context) => Widget())
    

Navigation.push

In Flutter, Navigator.push is used to navigate to a new screen or page. When you use Navigator.push, the new screen is pushed onto the stack, and the current screen remains in the stack, which allows users to navigate back to the previous screen.


Navigator.push(
context,
MaterialPageRoute(builder: (context) => NewScreen()),
);

Navigator.pop

Navigator.pop is used to go back to the previous screen in the navigation stack.


Navigator.pop(
context,
);

To organize the product information efficiently, you can create a Product class in Flutter. This class will store the product details like name, price, image, etc., and can be used to pass the product information between different screens.



class Product {
final String name;
final double price;
final String image;
Product({
required this.name,
required this.price,
required this.image,
});
}


List<Product> products = [
Product(
name: 'Floppy Disk',
price: 10.99,
image: 'assets/appimages/floppy.png',
),
Product(
name: 'iPhone',
price: 999.99,
image: 'assets/appimages/iphone.png',
),
Product(
name: 'Laptop',
price: 799.99,
image: 'assets/appimages/laptop.png',
),
Product(
name: 'Pen Drive',
price: 19.99,
image: 'assets/appimages/pendrive.png',
),
Product(
name: 'Pixel Phone',
price: 899.99,
image: 'assets/appimages/pixel.png',
),
Product(
name: 'Tablet',
price: 349.99,
image: 'assets/appimages/tablet.png',
),
];


class StarRating extends StatefulWidget {
@override
_StarRatingState createState() => _StarRatingState();
}
class _StarRatingState extends State<StarRating> {
int _currentRating = 0;
void _rateOne() {
setState(() {
_currentRating = 1;
});
}
void _rateTwo() {
setState(() {
_currentRating = 2;
});
}
void _rateThree() {
setState(() {
_currentRating = 3;
});
}
@override
Widget build(BuildContext context) {
double starSize = 22;
print(_currentRating);
return Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Container(
padding: EdgeInsets.all(0),
child: IconButton(
icon: (_currentRating >= 1
? Icon(
Icons.star,
size: starSize,
)
: Icon(
Icons.star_border,
size: starSize,
)),
color: Colors.orange[600],
onPressed: _rateOne,
iconSize: starSize,
),
),
Container(
padding: EdgeInsets.all(0),
child: IconButton(
icon: (_currentRating >= 2
? Icon(
Icons.star,
size: starSize,
)
: Icon(
Icons.star_border,
size: starSize,
)),
color: Colors.orange[600],
onPressed: _rateTwo,
iconSize: starSize,
),
),
Container(
padding: EdgeInsets.all(0),
child: IconButton(
icon: (_currentRating >= 3
? Icon(
Icons.star,
size: starSize,
)
: Icon(
Icons.star_border,
size: starSize,
)),
color: Colors.orange[600],
onPressed: _rateThree,
iconSize: starSize,
),
),
],
);
}
}


class ProductCard extends StatelessWidget {
ProductCard({Key? key, required this.product}) : super(key: key);
final Product product;
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(4),
height: 150,
child: Card(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Image.asset("assets/appimages/" + this.product.image),
Expanded(
child: Container(
padding: EdgeInsets.all(8),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
this.product.name,
style: TextStyle(fontWeight: FontWeight.bold),
),
Text(this.product.description),
Text("Price: \$" + this.product.price.toString()),
StarRating(), // Using the previously defined Rating widget
],
),
),
)
],
),
),
);
}
}
أحدث أقدم