Add OnEmpty Widget to ListView.builder() inside FutureBuilder using flutter? - flutter

I am using Flutter to develop small application with floor for the database.
I am getting the data from the database using Future then listing all items in UI using FutureBuild.
This is my code
Getting the data from database:
#Query('SELECT * FROM Doctor')
Future<List<Doctor>> findAllDoctor();
Getting data to UI
Future<List<Doctor>> findAllDoctor() async {
return await database.doctorDao.findAllDoctor();
}
Setting data into FutureBuilder:
return FutureBuilder(
future: findAllDoctor(),
builder: (BuildContext context, AsyncSnapshot<List<Doctor>> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data?.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: ListTile(
contentPadding: const EdgeInsets.all(8.0),
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"${snapshot.data![index].firstName} ${snapshot
.data![index].lastName}"),
Text(
snapshot.data![index].phone,
style: const TextStyle(
fontSize: 14,
color: Colors.grey,
),
),
],
),
subtitle: Text(
"${snapshot.data![index].address} ${snapshot.data![index]
.nameOfTheClinic}"),
),
);
},
);
} else {
return const Center(child: CircularProgressIndicator());
}
},
);
I want to add new widget that tells me no data if there is no data in the table.

Related

How can I get data from three separate FutureBuilders widgets?

I am trying to display one SingleChildListView containing THREE seperate FutureBuilder with ListView.sperator. I am using Provider for fetching data from the SQFLite database.
Thi is my code:
class SelectCategoryPage extends StatefulWidget {
const SelectCategoryPage({Key? key}) : super(key: key);
#override
State<SelectCategoryPage> createState() => _SelectCategoryPageState();
}
class _SelectCategoryPageState extends State<SelectCategoryPage> {
//
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: kBackgroundColor,
appBar: _appBar(),
body: _body(context),
bottomNavigationBar: _bottomNavigationBar(),
);
}
AppBar _appBar() {
return AppBar(
elevation: 0,
backgroundColor: Colors.white,
title: Text(
'Select Category',
style: appBarHeaderTStyle,
),
iconTheme: const IconThemeData(color: Colors.black),
actions: [
Consumer<SelectCategoryViewModel>(
builder: (context, provider, child) {
return TextButton(
child: Text(
provider.getEditOptionData ? 'Save' : 'Edit',
style: textButtonTStyle,
),
onPressed: () {
provider.toggleEditButton();
},
);
},
),
],
);
}
Widget _body(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 20, left: 24, right: 24),
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
controller: null,
physics: const BouncingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 12),
Text(
'Expense',
style: selectCategoryHeaderTStyle,
),
const SizedBox(height: 16),
//
_expenseCategory(context),
//
const SizedBox(height: 24),
Text(
'Income',
style: selectCategoryHeaderTStyle,
),
const SizedBox(height: 16),
//
_incomeCategory(context),
//
const SizedBox(height: 24),
Text(
'Other',
style: selectCategoryHeaderTStyle,
),
const SizedBox(height: 16),
//
_otherCategory(context),
//
const SizedBox(height: 20),
],
),
),
),
],
),
);
}
FutureBuilder<void> _expenseCategory(BuildContext context) {
return FutureBuilder(
future: Provider.of<SelectCategoryViewModel>(
context,
listen: false,
).selectCategoryByExpenseType(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(
child: CircularProgressIndicator(),
);
case ConnectionState.done:
return Consumer<SelectCategoryViewModel>(
child: const Center(child: Text('No Data')),
builder: (context, expenseProvider, child) => expenseProvider
.data.isEmpty
? child!
: ListView.separated(
shrinkWrap: true,
itemCount: expenseProvider.data.length,
physics: const BouncingScrollPhysics(),
separatorBuilder: (BuildContext context, int index) {
return const SizedBox(height: 16);
},
itemBuilder: (BuildContext context, int index) {
return SelectCategoryCard(
id: expenseProvider.data[index].id,
coloredIcon: expenseProvider.data[index].categoryIcon,
title: expenseProvider.data[index].categoryName,
isOptionDotsVisitable:
expenseProvider.getEditOptionData,
);
},
),
);
default:
return Container();
}
},
);
}
FutureBuilder<void> _incomeCategory(BuildContext context) {
return FutureBuilder(
future: Provider.of<SelectCategoryViewModel>(
context,
listen: false,
).selectCategoryByIncomeType(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(
child: CircularProgressIndicator(),
);
case ConnectionState.done:
return Consumer<SelectCategoryViewModel>(
child: const Center(child: Text('No Data')),
builder: (context, incomeProvider, child) => incomeProvider
.data.isEmpty
? child!
: ListView.separated(
shrinkWrap: true,
itemCount: incomeProvider.data.length,
physics: const BouncingScrollPhysics(),
separatorBuilder: (BuildContext context, int index) {
return const SizedBox(height: 16);
},
itemBuilder: (BuildContext context, int index) {
return SelectCategoryCard(
id: incomeProvider.data[index].id,
coloredIcon: incomeProvider.data[index].categoryIcon,
title: incomeProvider.data[index].categoryName,
isOptionDotsVisitable:
incomeProvider.getEditOptionData,
);
},
),
);
default:
return Container();
}
},
);
}
FutureBuilder<void> _otherCategory(BuildContext context) {
return FutureBuilder(
future: Provider.of<SelectCategoryViewModel>(
context,
listen: false,
).selectCategoryByOtherType(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(
child: CircularProgressIndicator(),
);
case ConnectionState.done:
return Consumer<SelectCategoryViewModel>(
child: const Center(child: Text('No Data')),
builder: (context, otherProvider, child) => otherProvider
.data.isEmpty
? child!
: ListView.separated(
shrinkWrap: true,
itemCount: otherProvider.data.length,
physics: const BouncingScrollPhysics(),
separatorBuilder: (BuildContext context, int index) {
return const SizedBox(height: 16);
},
itemBuilder: (BuildContext context, int index) {
return SelectCategoryCard(
id: otherProvider.data[index].id,
coloredIcon: otherProvider.data[index].categoryIcon,
title: otherProvider.data[index].categoryName,
isOptionDotsVisitable:
otherProvider.getEditOptionData,
);
},
),
);
default:
return Container();
}
},
);
}
Widget _bottomNavigationBar() {
return CustomBottomAppBar(
buttonText: '+ Add New Category',
onTapEvent: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const CreateNewCategoryPage(),
),
);
},
);
}
}
Only the last FutureBuilder is working properly, and other builders are showing the exact output as the last (3rd) FutureBuilder.
Went through the same thing once. You need to have only one future builder and a Future that gets all the data and assigns it. Something like this:
class _DeviceInformationScreenState extends State<DeviceInformationScreen> {
late StaticConfiguration staticConfiguration;
late PackageInfo packageInfo;
late DeviceData deviceData;
late QuarkusHealthClient quarkusHealth;
LisaInfo lisaInfo = LisaInfo();
//TODO: add translations?
Future<void> _getAppInfo() async {
packageInfo = await PackageInfo.fromPlatform();
staticConfiguration = await config.readStaticConfiguration();
deviceData = await DeviceData.fromDevice();
quarkusHealth = await QuarkusHealthClient.checkHealth(staticConfiguration.grpcServerHost);
lisaInfo = await lisaInfo.getLisaInfo();
}
#override
Widget build(BuildContext context) {
return buildToolsScaffold(context,
body: FutureBuilder(
future: _getAppInfo(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 50.0),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
Text(
T.screens.healthInfo.configData,
style: const TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(
height: 20,
),
_buildConfigDataWidgets()
],
),
And _buildConfigDataWidgets() looks like this:
Widget _buildConfigDataWidgets() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(T.screens.healthInfo.data.configFile +
(staticConfiguration.configFileExists
? T.screens.healthInfo.data.exists
: T.screens.healthInfo.data.doesNotExist)),
const SizedBox(
height: 10,
),
Text(T.screens.healthInfo.data.grpcHost + ': ' + staticConfiguration.grpcServerHost),
const SizedBox(
height: 10,
),
Text(T.screens.healthInfo.data.grpcPort + ': ' + staticConfiguration.grpcServerPort.toString()),
const SizedBox(
height: 10,
),
if (staticConfiguration.locale != null) ...[
Text(T.screens.healthInfo.data.activeLanguageCode + ': ' + staticConfiguration.locale!.languageCode),
const SizedBox(
height: 10,
),
if (staticConfiguration.locale!.countryCode != null)
Text(T.screens.healthInfo.data.activeCountryCode + ': ' + staticConfiguration.locale!.countryCode!),
],
],
);
}

Why StreamBuilder always has no data before hot reload?

I use firestore and streambuilder to read data in a list, when i run the application for the first time i get a message "Unexpected null value" and I realized that "snapshot.hasData" is always false and snapshot.ConnectionState.waiting is always true. But when i restart application with hot reload i can retrieve data.
This is my stream:
Stream<QuerySnapshot> _branchStream = FirebaseFirestore.instance.collection('Companies').doc(company_id).collection("Branch Offices").snapshots();
This is my StreamBuilder
StreamBuilder<QuerySnapshot>(
stream: _branchStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
/* if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading");
}*/
return ListView(
children: snapshot.data!.docs
.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return Padding(
padding: const EdgeInsets.all(22.0),
child: Card(
elevation: 8,
shadowColor: Colors.blueGrey,
shape: cardShape,
child: Row(
children: [
Expanded(
flex: 2,
child: Padding(
padding: const EdgeInsets.all(22.0),
child: CircleAvatar(
radius: 50,
backgroundImage:
NetworkImage(data['branch_image'],scale: 60),
),
)),
Expanded(
flex: 4,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(22.0),
child: Text(data['branch_name'], style: textBlackTitle, textAlign: TextAlign.center,),
),
Padding(
padding: const EdgeInsets.all(22.0),
child: Text("UbicaciĆ³n: "+data['branch_address'], style: textGraySubTitle, textAlign: TextAlign.center,),
),
],
)),
Expanded(
flex: 2,
child: IconButton(
// focusColor: Color(color1),
// color: Color(color1),
onPressed: (){
Navigator.push(context, MaterialPageRoute(builder: (context) => Home(branch_id : data['branch_id'], company_id : company_id, branch_name : data['branch_name'], branch_image : data['branch_image'])));
}, icon: Image.asset("assets/enter.png", fit: BoxFit.contain, height: 100,)))
],
),
),
);
})
.toList()
.cast(),
);
},
)
This is data that I want to get
This is what I get at the first time
This is what I get after hot reload (That I should have from the beginning).
Because your data is null at the beginning, it takes some time to load the data.
You actually already included a check, but commented it out again. Undo it and try again.
/* if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading");
}*/
It takes some time to load snapshot data. For better UX return specific widgets for each state of the snapshot.
Make sure you're using StreamBuilder inside StatefulWidget.
StreamBuilder<QuerySnapshot>(
stream: _branchStream,
builder: (BuildContext context, snapshot) {
if (snapshot.hasError) {
return //error widget
} else {
switch (snapshot.connectionState) {
case ConnectionState.none:
return //some widget
case ConnectionState.waiting:
return CircularProgressIndicator(),
case ConnectionState.active:
return ListView()
case ConnectionState.done:
return //some widget
}
}
);

flutter: how can I retrieve current user data from firebase firestore

am trying to retrieve current user data but it showing all the users data from firestore in app
here is my code
Widget build(BuildContext context){
CollectionReference users = FirebaseFirestore.instance.collection('users');
return FutureBuilder<DocumentSnapshot>(
future: users.doc(documentId).get(),
builder: ((context, snapshot){
if(snapshot.connectionState == ConnectionState.done){
Map<String, dynamic> data =
snapshot.data!.data() as Map<String, dynamic>;
return Container(
child: Column(
children: [
Text('Email : '"${data['Email name'].toString()}",
style: const TextStyle(fontWeight: FontWeight.bold,wordSpacing: 10),),
const SizedBox(
width: 10,
),
Text('Contact : '"${data['age'].toString()}",
style: const TextStyle(fontWeight: FontWeight.bold,wordSpacing: 10),),
const SizedBox(
width: 10,
),
Text('Name : '"${data['firstname'].toString()}",
style: const TextStyle(fontWeight: FontWeight.bold,wordSpacing: 10),),
const SizedBox(
width: 10,
),
Text('Last Name : '"${data['lastname'].toString()}",
style: const TextStyle(fontWeight: FontWeight.bold,wordSpacing: 10),),
const SizedBox(
width: 10,
),
],
),
);
}
return const Text('Loading');
}),
here is documentId code
List<String> docIDs = [];
Future getDocId() async{
await FirebaseFirestore.instance.collection('users').get().then(
(snapShot) => snapShot.docs.forEach(
(documant){
print(documant.reference);
docIDs.add(documant.reference.id);
}));
}
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// ignore: prefer_const_constructors
Text('SignUp as ${user!.email!}',style: TextStyle(
fontSize: 20,
),),
Expanded(child: FutureBuilder(
future: getDocId(),
builder: (context, snapshot){
return ListView.builder(
itemCount: docIDs.length,
itemBuilder: (context, index){
return ListTile(
title: GetuserData(documentId: docIDs[index]),
);
},
);
},
))
when ever user post the form it submitted successfully and generate a post like summited form history but it showing to others users to but i wont it for only current user how to do that please any one can help me
You can use firestore query to return the current user using where()
on getDocId():
await FirebaseFirestore.instance.collection('users').get()
change to
await FirebaseFirestore.instance.collection('users')
.where('Email name', isEqualTo: 'myEmail#mail.test').get()
you can replace the email with your or consider using parameter to pass user email dynamically.

I cannot get the data from the firestore into my application

I am new to programming and while i was trying to create a small TODO app, i was able to save the data into cloud firestore but the problems ocurred when i tried to retrieve the data using Streambuilder. I was following old tutorials before null safety , so i suspect that issues are regarding the null safety.
The code works without any errors in android studio but the data from firestore can't be retrieved.
The code is as follows:
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class AddTODOlist extends StatelessWidget {
static String id = 'add_todolist';
final TextEditingController _controller = TextEditingController();
void _addUser(){
FirebaseFirestore.instance.collection("Todos").add({"title" : _controller.text});
_controller.text = "";
}
Widget _buildList(QuerySnapshot? snapshot){
return ListView.builder(
itemCount: snapshot!.docs.length,
itemBuilder: (context, index){
final doc = snapshot.docs[index];
final map = (doc.data()as dynamic)['title'];
return ListTile(
title: Text(map,style: TextStyle(color: Colors.black),),
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Loan System',),
centerTitle: true,
),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: InputDecoration(
hintText: 'Add new user',
),
),
),
TextButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(Colors.blue),
),
onPressed:(){
_addUser();
},
child: Text('Add',
style: TextStyle(
color: Colors.white,
),),
),
],
),
StreamBuilder<QuerySnapshot?>(
stream: FirebaseFirestore.instance.collection('todos').snapshots(),
builder: (context, snapshot){
if(!snapshot.hasData) return LinearProgressIndicator();
else {
return Expanded(
child: _buildList(snapshot.data),
);
}
}
),
],
),
),
),
);
}
}
You have an error in the spelling of the collection name. Remember that Firestore is case sensitive
Todos vs todos
FirebaseFirestore.instance.collection("Todos").add({"title" : _controller.text});
and here:
stream: FirebaseFirestore.instance.collection('todos').snapshots(),
Let me know if this does not help

_FutureBuilderState<dynamic>The getter 'length' was called on null. Receiver: null Tried calling: length

My method getting the data from db and displaying on the console. I tried several hints given in the other posts as well with no luck.
_getUsers() async {
print("getting");
var data = await http.post("http://10.0.2.2/Flutter/getdata.php", body: {
"date": formattedDate,
});
var jsonData = json.decode(data.body);
print(jsonData);
}
However the future builder not able to display:
new FutureBuilder(
future: _getUsers(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasError) {
return Center(
child: new Text('Error ${snapshot.error}'),
);
} else {
return Center(
child: Padding(
padding: const EdgeInsets.fromLTRB(56.0, 8.0, 56.0, 8.0),
//Here I guarded against the null as well:
child: ListView.builder(
itemCount: snapshot.data.length == null // showing error here
? 0
: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: new Text(
'${snapshot.data[index]["branch"]}',
style: TextStyle(
color: Colors.white,
fontSize: 25.0,
),
),
trailing: new Text(
'${snapshot.data[index]["count(`branch`)".toString()]}',
style: TextStyle(
color: Colors.white,
fontSize: 25.0,
),
),
);
}),
),
);
}
}),
How can I solve the issue?
you should return jsonData in _getUser().
getUsers() async {
print("getting");
var data = await http.post("http://10.0.2.2/Flutter/getdata.php", body: {
"date": formattedDate,
});
var jsonData = json.decode(data.body);
return jsonData;
}
, and change this
itemCount: snapshot.data.length == null // showing error here
? 0
: snapshot.data.length,
to this
itemCount: snapshot.data?.length ?? 0,
snapshot.data? checks whether the data is null or not. ?? executes its successor when the predecessor is null.
Your function doesn't return any Future, therefore the FutureBuilder is unable to get a Future to run on.
_getUsers() {
print("getting");
return http.post("http://10.0.2.2/Flutter/getdata.php", body: {
"date": formattedDate,
});
}
It needs to return a Future, you shouldn't be using await because the FutureBuilder depends on an actual Future, not data. You obtain the data inside the builder and then decode it.
new FutureBuilder(
future: _getUsers(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasError) {
return Center(
child: new Text('Error ${snapshot.error}'),
);
} else if (snapshot.hasData) { // checking for data
return Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 56, vertical: 8),
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: new Text(
'${snapshot.data[index]["branch"]}',
style: TextStyle(
color: Colors.white,
fontSize: 25.0,
),
),
trailing: new Text(
'${snapshot.data[index]["count(`branch`)".toString()]}',
style: TextStyle(
color: Colors.white,
fontSize: 25.0,
),
),
);
}),
),
);
}
}),