Code is copied!
Stationary Shop Bill App Using Flutter
In this tutorial, we'll be creating a Stationary App application using Firebase in Flutter,
Stationary app are essential app for keeping Stationary and study materials organized.
Our app creates a scrollable Stationary list , allowing users to save Stationary via a text field. Stationary are dynamically
displayed within containers. Users can scroll through the list as needed. Users can edit , delete their Stationary details and can find the total also.
By the end of this tutorial, you'll have a functional Stationary List app that you can run on both Android and iOS
devices.
Source Code for the main.dart file
Add the following Code inside your main.dart file :
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
void main() async{
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home:HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State createState() => _HomePageState();
}
class _HomePageState extends State {
// text fields' controllers
final TextEditingController _nameController = TextEditingController();
final TextEditingController _priceController = TextEditingController();
final TextEditingController _nameController1 = TextEditingController();
final TextEditingController _priceController1 = TextEditingController();
final CollectionReference item =
FirebaseFirestore.instance.collection('items');
Future _calculateTotalPrice() async {
QuerySnapshot querySnapshot = await item.get();
int totalPrice = 0;
querySnapshot.docs.forEach((itprice) {
totalPrice = totalPrice + (itprice['price'] as num).toInt();
});
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Total Price'),
content: Text('\u{20B9}${totalPrice.toInt()}'), // Format total price with two decimal places
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('Close'),
),
],
);
},
);
}
Future< void> _create() async {
await showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (BuildContext ctx) {
return Padding(
padding: EdgeInsets.only(
top: 20,
left: 20,
right: 20,
bottom: MediaQuery.of(ctx).viewInsets.bottom + 20),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 20,
),
Text('Add Items',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 19,
fontWeight: FontWeight.w600
),),
SizedBox(
height: 20,
),
TextField(
controller: _nameController1,
decoration: const InputDecoration(labelText: 'Name'),
),
TextField(
controller: _priceController1,
decoration: const InputDecoration(labelText: 'Price'),
),
const SizedBox(
height: 20,
),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.deepPurpleAccent
),
child: const Text('Create'),
onPressed: () async
{
final String name = _nameController1.text;
final int? price = int.tryParse(_priceController1.text);
if(price!=null)
{
await item.add({"name": name, "price": price});
_nameController1.text = '';
_priceController1.text = '';
Navigator.of(context).pop();
}
},
)
],
),
);
});
}
Future< void> _update([DocumentSnapshot? documentSnapshot]) async {
if (documentSnapshot != null) {
_nameController.text = documentSnapshot['name'];
_priceController.text = documentSnapshot['price'].toString();
}
await showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (BuildContext ctx) {
return Padding(
padding: EdgeInsets.only(
top: 20,
left: 20,
right: 20,
bottom: MediaQuery.of(ctx).viewInsets.bottom + 20),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 20,
),
Text('Update Item',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 19,
fontWeight: FontWeight.w600,
),),
SizedBox(
height: 20,
),
TextField(
controller: _nameController,
decoration: const InputDecoration(labelText: 'Name'),
),
TextField(
controller: _priceController,
decoration: const InputDecoration(labelText: 'Price'),
),
const SizedBox(
height: 20,
),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.deepPurpleAccent
),
child: const Text( 'Update'),
onPressed: () async
{
final String name = _nameController.text;
final int? price = int.tryParse(_priceController.text);
if(price!=null)
{
await item.doc(documentSnapshot!.id).update({"name": name, "price": price});
_nameController.text = '';
_priceController.text = '';
Navigator.of(context).pop();
}
},
)
],
),
);
});
}
Future< void> _delete(String productId) async {
await item.doc(productId).delete();
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
backgroundColor: Colors.deepPurpleAccent,
content: Text('You have successfully deleted an item')));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.deepPurpleAccent,
title: Text('Stationary Items'),
centerTitle: true,
elevation: 0.0,
),
body:
Padding(
padding: const EdgeInsets.only(left: 10, right: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
flex: 8,
child: StreamBuilder(
stream: item.snapshots(),
builder: (context, AsyncSnapshot streamSnapshot) {
if (streamSnapshot.hasData) {
return ListView.builder(
itemCount: streamSnapshot.data!.docs.length,
itemBuilder: (context, index) {
final DocumentSnapshot documentSnapshot =
streamSnapshot.data!.docs[index];
return Card(
margin: const EdgeInsets.all(10),
child: ListTile(
title: Text(documentSnapshot['name'],style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 17
),),
// subtitle: Text((documentSnapshot['price']).toString()
subtitle: Text('\u{20B9}${(documentSnapshot['price']).toString()}',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500
),
),
trailing: SizedBox(
width: 100,
child: Row(
children: [
IconButton(
icon: const Icon(Icons.edit),
color: Colors.yellow[800],
onPressed: ()
{
_update(documentSnapshot);
}
),
IconButton(
icon: const Icon(Icons.delete),
color: Colors.red,
onPressed: ()
{
_delete(documentSnapshot.id);
}
),
],
),
),
),
);
},
);
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
),
Expanded(
child: Container(
child:
TextButton(
onPressed: ()
{
_calculateTotalPrice();
},
child: Text('Total',
style: TextStyle(
fontSize: 24,
color: Colors.deepPurpleAccent,
),),
)
// color: Colors.deepPurpleAccent,
)
)
],
),
),
// Add new product
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.deepPurpleAccent,
onPressed: (){
_create();
},
child: const Icon(Icons.add),
),
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
);
}
}
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
void main() async{
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home:HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State createState() => _HomePageState();
}
class _HomePageState extends State {
// text fields' controllers
final TextEditingController _nameController = TextEditingController();
final TextEditingController _priceController = TextEditingController();
final TextEditingController _nameController1 = TextEditingController();
final TextEditingController _priceController1 = TextEditingController();
final CollectionReference item =
FirebaseFirestore.instance.collection('items');
Future _calculateTotalPrice() async {
QuerySnapshot querySnapshot = await item.get();
int totalPrice = 0;
querySnapshot.docs.forEach((itprice) {
totalPrice = totalPrice + (itprice['price'] as num).toInt();
});
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Total Price'),
content: Text('\u{20B9}${totalPrice.toInt()}'), // Format total price with two decimal places
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('Close'),
),
],
);
},
);
}
Future< void> _create() async {
await showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (BuildContext ctx) {
return Padding(
padding: EdgeInsets.only(
top: 20,
left: 20,
right: 20,
bottom: MediaQuery.of(ctx).viewInsets.bottom + 20),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 20,
),
Text('Add Items',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 19,
fontWeight: FontWeight.w600
),),
SizedBox(
height: 20,
),
TextField(
controller: _nameController1,
decoration: const InputDecoration(labelText: 'Name'),
),
TextField(
controller: _priceController1,
decoration: const InputDecoration(labelText: 'Price'),
),
const SizedBox(
height: 20,
),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.deepPurpleAccent
),
child: const Text('Create'),
onPressed: () async
{
final String name = _nameController1.text;
final int? price = int.tryParse(_priceController1.text);
if(price!=null)
{
await item.add({"name": name, "price": price});
_nameController1.text = '';
_priceController1.text = '';
Navigator.of(context).pop();
}
},
)
],
),
);
});
}
Future< void> _update([DocumentSnapshot? documentSnapshot]) async {
if (documentSnapshot != null) {
_nameController.text = documentSnapshot['name'];
_priceController.text = documentSnapshot['price'].toString();
}
await showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (BuildContext ctx) {
return Padding(
padding: EdgeInsets.only(
top: 20,
left: 20,
right: 20,
bottom: MediaQuery.of(ctx).viewInsets.bottom + 20),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 20,
),
Text('Update Item',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 19,
fontWeight: FontWeight.w600,
),),
SizedBox(
height: 20,
),
TextField(
controller: _nameController,
decoration: const InputDecoration(labelText: 'Name'),
),
TextField(
controller: _priceController,
decoration: const InputDecoration(labelText: 'Price'),
),
const SizedBox(
height: 20,
),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.deepPurpleAccent
),
child: const Text( 'Update'),
onPressed: () async
{
final String name = _nameController.text;
final int? price = int.tryParse(_priceController.text);
if(price!=null)
{
await item.doc(documentSnapshot!.id).update({"name": name, "price": price});
_nameController.text = '';
_priceController.text = '';
Navigator.of(context).pop();
}
},
)
],
),
);
});
}
Future< void> _delete(String productId) async {
await item.doc(productId).delete();
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
backgroundColor: Colors.deepPurpleAccent,
content: Text('You have successfully deleted an item')));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.deepPurpleAccent,
title: Text('Stationary Items'),
centerTitle: true,
elevation: 0.0,
),
body:
Padding(
padding: const EdgeInsets.only(left: 10, right: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
flex: 8,
child: StreamBuilder(
stream: item.snapshots(),
builder: (context, AsyncSnapshot streamSnapshot) {
if (streamSnapshot.hasData) {
return ListView.builder(
itemCount: streamSnapshot.data!.docs.length,
itemBuilder: (context, index) {
final DocumentSnapshot documentSnapshot =
streamSnapshot.data!.docs[index];
return Card(
margin: const EdgeInsets.all(10),
child: ListTile(
title: Text(documentSnapshot['name'],style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 17
),),
// subtitle: Text((documentSnapshot['price']).toString()
subtitle: Text('\u{20B9}${(documentSnapshot['price']).toString()}',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500
),
),
trailing: SizedBox(
width: 100,
child: Row(
children: [
IconButton(
icon: const Icon(Icons.edit),
color: Colors.yellow[800],
onPressed: ()
{
_update(documentSnapshot);
}
),
IconButton(
icon: const Icon(Icons.delete),
color: Colors.red,
onPressed: ()
{
_delete(documentSnapshot.id);
}
),
],
),
),
),
);
},
);
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
),
Expanded(
child: Container(
child:
TextButton(
onPressed: ()
{
_calculateTotalPrice();
},
child: Text('Total',
style: TextStyle(
fontSize: 24,
color: Colors.deepPurpleAccent,
),),
)
// color: Colors.deepPurpleAccent,
)
)
],
),
),
// Add new product
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.deepPurpleAccent,
onPressed: (){
_create();
},
child: const Icon(Icons.add),
),
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
);
}
}