Introduction
Badges in a Flutter app are often used to visually represent notifications, alerts, or counts associated with specific UI elements. In this guide, we’ll demonstrate how to implement badges using the ‘badges’ package in Flutter.
Content:
Step 1: Create a New Flutter Project
Ensure that you have Flutter installed and set up on your machine. Create a new Flutter project using the following command in your terminal:
flutter create badges_app
Step 2: Add the ‘badges’ Dependency
Open the pubspec.yaml
file in your Flutter project and add the ‘badges’ dependency:
dependencies:
badges: ^2.0.1
Save the file and run flutter pub get
in your terminal.
Step 3: Implement Badges in Flutter
Open the lib/main.dart
file and replace its content with the following code:
import 'package:flutter/material.dart';
import 'package:badges/badges.dart';
void main() {
runApp(BadgesApp());
}
class BadgesApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("Badges Example"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Badge(
badgeContent: Text('5', style: TextStyle(color: Colors.white)),
child: Icon(Icons.notifications),
),
SizedBox(height: 20),
Badge(
badgeContent: Text('New', style: TextStyle(color: Colors.white)),
child: Image.asset('assets/icon.png', width: 50, height: 50),
),
],
),
),
),
);
}
}
Step 4: Run the App
Save your changes and run the app using the following command in your terminal:
flutter run
Sample Code:
// ignore_for_file: prefer_const_literals_to_create_immutables, prefer_const_constructors, use_key_in_widget_constructors, library_private_types_in_public_api
import 'package:badges/badges.dart' as badges;
import 'package:flutter/material.dart';
class BadgesPage extends StatefulWidget {
@override
_BadgesPageState createState() => _BadgesPageState();
}
class _BadgesPageState extends State<BadgesPage> {
int _cartBadgeAmount = 3;
late bool _showCartBadge;
Color color = Colors.red;
@override
Widget build(BuildContext context) {
_showCartBadge = _cartBadgeAmount > 0;
return DefaultTabController(
length: 2,
child: Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
leading: badges.Badge(
position: badges.BadgePosition.topEnd(top: 10, end: 10),
child: IconButton(
icon: Icon(Icons.menu),
onPressed: () {},
),
),
title: Text('Badges Demo'),
actions: <Widget>[
_shoppingCartBadge(),
],
bottom: _tabBar(),
),
body: Column(
children: <Widget>[
_addRemoveCartButtons(),
Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
InstagramVerifiedAccount(),
],
),
),
Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
AlarmApp(),
YakoApp(),
FlagApp(),
HumanAvatar(),
],
),
),
],
),
],
),
),
);
}
Widget _shoppingCartBadge() {
return badges.Badge(
position: badges.BadgePosition.topEnd(top: 0, end: 3),
badgeAnimation: badges.BadgeAnimation.slide(),
showBadge: _showCartBadge,
badgeStyle: badges.BadgeStyle(
badgeColor: color,
),
badgeContent: Text(
_cartBadgeAmount.toString(),
style: TextStyle(color: Colors.white),
),
child: IconButton(icon: Icon(Icons.shopping_cart), onPressed: () {}),
);
}
PreferredSizeWidget _tabBar() {
return TabBar(tabs: [
Tab(
icon: badges.Badge(
badgeStyle: badges.BadgeStyle(
badgeColor: Colors.blue,
),
position: badges.BadgePosition.topEnd(top: -14),
badgeContent: Text(
'3',
style: TextStyle(color: Colors.white),
),
child: Icon(
Icons.account_balance_wallet,
color: Colors.grey[800],
),
),
),
Tab(
child: badges.Badge(
badgeStyle: badges.BadgeStyle(
shape: badges.BadgeShape.square,
borderRadius: BorderRadius.circular(5),
padding: EdgeInsets.all(2),
badgeGradient: badges.BadgeGradient.linear(
colors: [
Colors.purple,
Colors.blue,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
position: badges.BadgePosition.topEnd(top: -12, end: -20),
badgeContent: Text(
'NEW',
style: TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold),
),
child: Text(
'music',
style: TextStyle(color: Colors.black),
),
),
),
]);
}
Widget _addRemoveCartButtons() {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
ElevatedButton.icon(
onPressed: () => setState(() {
_cartBadgeAmount++;
if (color == Colors.red) {
color = Colors.green;
}
}),
icon: Icon(Icons.add),
label: Text('Add to cart')),
ElevatedButton.icon(
onPressed: _showCartBadge
? () => setState(() {
_cartBadgeAmount--;
color = Colors.green;
})
: null,
icon: Icon(Icons.remove),
label: Text('Remove from cart')),
],
),
);
}
}
class AlarmApp extends StatefulWidget {
const AlarmApp({
Key? key,
}) : super(key: key);
@override
State<AlarmApp> createState() => _AlarmAppState();
}
class _AlarmAppState extends State<AlarmApp> {
bool _isLooped = true;
int counter = 1;
@override
Widget build(BuildContext context) {
return badges.Badge(
badgeStyle: badges.BadgeStyle(padding: EdgeInsets.all(7)),
badgeAnimation: badges.BadgeAnimation.fade(
animationDuration: Duration(seconds: 1),
loopAnimation: _isLooped,
),
// onTap: () {
// setState(() => _isLooped = !_isLooped);
// },
ignorePointer: false,
// toAnimate: false,
badgeContent: Text(counter.toString(), style: TextStyle(color: Colors.white)),
position: badges.BadgePosition.topEnd(top: -12),
child: GestureDetector(
onTap: () {
setState(() => counter += 10);
},
child: Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: Colors.amber,
borderRadius: BorderRadius.circular(12),
),
child: Icon(
Icons.access_alarm_outlined,
size: 34,
),
),
),
);
}
}
class YakoApp extends StatelessWidget {
const YakoApp({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return badges.Badge(
badgeStyle: badges.BadgeStyle(
shape: badges.BadgeShape.square,
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.white, width: 2),
badgeColor: Colors.black,
elevation: 0,
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 2),
),
badgeContent: Text('20', style: TextStyle(color: Colors.white)),
position: badges.BadgePosition.topEnd(top: -10),
badgeAnimation: badges.BadgeAnimation.size(toAnimate: true),
onTap: () {
print('asdfsadfs');
},
child: Container(
alignment: Alignment.center,
width: 60,
height: 60,
decoration: BoxDecoration(
color: Colors.blueAccent,
borderRadius: BorderRadius.circular(12),
),
child: Text(
'Yako',
style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold),
),
),
);
}
}
class FlagApp extends StatelessWidget {
const FlagApp({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return badges.Badge(
badgeStyle: badges.BadgeStyle(
badgeColor: Colors.white,
),
badgeContent: Text(
'😃',
style: TextStyle(fontSize: 20),
textAlign: TextAlign.center,
),
position: badges.BadgePosition.bottomEnd(bottom: -10),
child: Container(
alignment: Alignment.center,
width: 60,
height: 60,
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(12),
),
child: Icon(Icons.flag, color: Colors.white, size: 40),
),
);
}
}
class HumanAvatar extends StatefulWidget {
const HumanAvatar({
Key? key,
}) : super(key: key);
@override
State<HumanAvatar> createState() => _HumanAvatarState();
}
class _HumanAvatarState extends State<HumanAvatar> with SingleTickerProviderStateMixin {
late final AnimationController _controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
)..repeat(reverse: true);
late final Animation<Offset> _offsetAnimation = Tween<Offset>(
begin: const Offset(0, 0.2),
end: const Offset(0, -0.1),
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.ease,
));
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return badges.Badge(
badgeStyle: badges.BadgeStyle(
elevation: 0,
badgeColor: Colors.blue,
borderSide: BorderSide(color: Colors.white, width: 2),
),
badgeContent: SlideTransition(position: _offsetAnimation, child: Icon(Icons.arrow_upward, color: Colors.white, size: 16)),
position: badges.BadgePosition.bottomEnd(bottom: 0, end: -4),
child: CircleAvatar(
radius: 34,
child: CircleAvatar(
radius: 32,
backgroundColor: Colors.white,
child: CircleAvatar(
radius: 30,
backgroundImage: NetworkImage(
"https://images.rawpixel.com/image_png_800/cHJpdmF0ZS9sci9pbWFnZXMvd2Vic2l0ZS8yMDIzLTAxL3JtNjA5LXNvbGlkaWNvbi13LTAwMi1wLnBuZw.png")),
),
),
);
}
}
class InstagramVerifiedAccount extends StatelessWidget {
const InstagramVerifiedAccount({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircleAvatar(
backgroundColor: Colors.grey.withOpacity(0.2),
radius: 24,
child: CircleAvatar(
radius: 23,
backgroundColor: Colors.white,
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Image.network(
"https://thumbs.dreamstime.com/b/colored-instagram-logo-icon-high-resolution-colored-instagram-logo-white-background-vector-eps-file-available-additional-175710005.jpg",
width: 30,
//color: Colors.white,
),
),
),
),
),
SizedBox(width: 5),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
'instagram',
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(width: 5),
badges.Badge(
badgeAnimation: badges.BadgeAnimation.fade(toAnimate: false),
badgeContent: Icon(
Icons.check,
color: Colors.white,
size: 10,
),
badgeStyle: badges.BadgeStyle(
shape: badges.BadgeShape.instagram,
badgeColor: Colors.blue,
),
),
],
),
SizedBox(height: 5),
Text('Instagram', style: TextStyle(color: Colors.grey)),
],
),
],
);
}
}
Output:
Conclusion:
In this guide, we’ve demonstrated how to implement badges in a Flutter app using the ‘badges’ package. The Badge
widget from this package makes it easy to add badges to various UI elements, such as icons or images, and customize the badge content. You can further customize the appearance and behavior of badges based on your app’s design requirements.