How to turn off CircularProgressIndicator flutter - flutter

There is a list that displays data from the database that comes to it at the time the data is fetched and until the data appears, the CircularProgressIndicator () appears so that the user knows that there is a process happening in the background.
Excellent but there is a problem with this CircularProgressIndicator () continues to work non-stop if there is no data in the database. Herein lies the problem.
It is supposed to work for a specified time and if there is no data in the database it will stop working and disappear.
Is there a way to do this? So that if there is no data that can be fetched it stops working?
My code:
class MainListView extends StatefulWidget {
MainListViewState createState() => MainListViewState();
}
class MainListViewState extends State {
final String apiURL = 'http://====================/getStudentInfo.php';
Future<List<Studentdata>> fetchStudents() async {
var response = await http.get(apiURL);
if (response.statusCode == 200) {
final items = json.decode(response.body).cast<Map<String, dynamic>>();
List<Studentdata> studentList = items.map<Studentdata>((json) {
return Studentdata.fromJson(json);
}).toList();
return studentList;
}
else {
throw Exception('Failed to load data from Server.');
}
}
#override
Widget build(BuildContext context) {
return FutureBuilder<List<Studentdata>>(
future: fetchStudents(),
builder: (context, snapshot) {
if (!snapshot.hasData)
return Center(
child: CircularProgressIndicator()
);
return ListView(
children: snapshot.data
.map((data) => Column(children: <Widget>[
GestureDetector(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.fromLTRB(20, 5, 0, 5),
child: Text(data.studentName,
style: TextStyle(fontSize: 21),
textAlign: TextAlign.left))
]),),
Divider(color: Colors.black),
],))
.toList(),
);
},
);
}
}

You can try with the below lines
return FutureBuilder<List<Studentdata>>(
future: fetchStudents(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView(
children: snapshot.data
.map((data) =>
Column(children: <Widget>[
GestureDetector(
child: Row(
crossAxisAlignment: CrossAxisAlignment
.start,
children: [
Padding(
padding: EdgeInsets.fromLTRB(
20, 5, 0, 5),
child: Text(data.studentName,
style: TextStyle(fontSize: 21),
textAlign: TextAlign.left))
]),),
Divider(color: Colors.black),
],))
.toList(),
);
}
else if (!snapshot.hasData) {
return Text("No data Available");
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return Center(
child: CircularProgressIndicator()
);
},
);

You can also set a value to CircularProgressIndicator() to stop at full circle indicator.

Just do this:
if (!snapshot.hasData && !snapshot.hasError) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Center(
child: Text("No data Found"),
);
} else {
// do something here
}
It means that if status code is 200 and there is no data then you are going to get an error.

Related

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
}
}
);

how to return a form widget in futureBuild in flutter

I have this code as am trying to code something to update data in firestore.
#override
Widget build(BuildContext context) {
// Use the Todo to create the UI.
return Scaffold(
appBar: AppBar(
title: Text(mid.toString()),
),
body: FutureBuilder<Member?>(
future: readMember(mid),
builder: (context, snapshot) {
if (snapshot.hasData) {
final member = snapshot.data;
/// return a form
} else {
return const Center(child: CircularProgressIndicator());
}
},
),
);
}
if snapshot hasData I want to return a form like this
Card(
child: Row(
children: <Widget>[
TextField(
controller: controllerName,
decoration: decoration('name'),
),
const SizedBox(height: 24),
TextField(
controller: controllerAge,
decoration: decoration('Age'),
keyboardType: TextInputType.number,
),
const SizedBox(height: 24),
ElevatedButton(
child: const Text('Create'),
onPressed: () {},
),
],
));
All my attempt yield no success please I need help.
Check others state like error or if the data is null or not
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text("Got Error");
}
if (snapshot.data == null) {
return Text("No data");
}
if (snapshot.hasData) {
final member = snapshot.data;
return Card( ///here form
child: Row(
children: <Widget>[],
));
} else {
return const Center(child: CircularProgressIndicator());
}
},
And provide width on TextFiled to fix overflow, TextFiled and row are trying to get infinite with.
just wrap with Expanded
Expanded(child: TextField(...)),
You can find more about unbound height& width

FutureBuilder has empty snapshot despite Future returns proper data

I have FutureBuilder widget as Scaffold's body and I want to display a list of widgets when database returns required info but while database is properly returning data the FutureBuilder does not show any widgets(like CircularProgressIndicator or required List as it finishes its job). Code shown below:
Future<List<Widget>> getList() async{
List<Widget> list = [];
var db = dbMethods();
db.getConnection().then((conn) async {
await Future.delayed(const Duration(seconds: 1));
var result = await conn.query("select * from info", []);
for(var row in result) {
var item = BilbordItem(
firm: row[2],
constructionType: row[3],
demonstrationType: row[4],
lat: double.parse(row[5]),
long: double.parse(row[6]),
sizeX: double.parse(row[7]),
sizeY: double.parse(row[8]),
address: row[9],
positionRate: int.parse(row[10]),
colorsRate: int.parse(row[11]),
passabilityRate: int.parse(row[12]),
typeRate: int.parse(row[13])
);
print(item.address); //here i was checking if database fetch is working and it does print required info
list.add(item);
}
conn.close();
});
return list;
}
class _ListPage extends State<ListPage>{
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Список билбордов"),
centerTitle: true,
),
body: FutureBuilder(
future: getList(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if(snapshot.connectionState == ConnectionState.done){
if(snapshot.hasData && !snapshot.hasError){
return Center(
child: Column(
children: snapshot.data,
),
);
}
else if(snapshot.hasError){
return SingleChildScrollView(
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Icon(Icons.error_outline, color: Colors.red,),
Text(snapshot.error.toString()),
],
),
),
);
}
}else {
return Center(child: CircularProgressIndicator());
}
return Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
Text("Empty"),
],
),
);
},
),
);
}
}

FutureBuilder always refresh

i have a function for asking that u have token apikey or not. but when im trying to get some textfield there, i cant texting in it(always refresh causing that futurebuilder i think).
below is the function.
Future<String> getApiKey() async {
WidgetsFlutterBinding.ensureInitialized();
final SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getString("apikey");
}
FutureBuilder<String>(
future: getApiKey(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(
child: CupertinoActivityIndicator(),
);
default:
if (snapshot.hasError) {
print("has error");
return Center(child: Text('Error: ${snapshot.error}'));
} else if (snapshot.data == null) {
print("not login");
// return Container();
return Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset("assets/image/images/nodata.svg",
height: 150),
SizedBox(height: 15),
Text(
"no data",
style: TextStyle(
color: baseColor,
fontSize: 18,
fontWeight: FontWeight.w500,
fontFamily: "Sofia"),
),
],
),
);
} else {
return FutureBuilder<List<LineUpListModel>>(
future: prov.setLineUpList(prov),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(
child: CupertinoActivityIndicator(),
);
default:
if (snapshot.hasError)
return Center(
child: Text('Error: ${snapshot.error}'));
else if (snapshot.data == null)
// return Container();
return Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
"assets/image/images/nodata.svg",
height: 150),
SizedBox(height: 15),
Text(
"no data",
style: TextStyle(
color: baseColor,
fontSize: 18,
fontWeight: FontWeight.w500,
fontFamily: "Sofia"),
),
],
),
);
else {
print(prov.getMyProfileModel.apiKey.toString());
return TextField();
}
}
},
);
}
}
},
),
How can I type in the TextField without any refreshing interruptions from the Futurebuilder? what should I fix?
You can take this approach for all of your future builder.
Future _future;
#override
#initState() {
_future = getApiKey();
}
Now in your FutureBuilder use this _future variable.
FutureBuilder<String>(
future: _future,
The issue might come from your future: getApiKey() because you are not saving an instance of your future value so it will be reloaded each time the build() method is called (which will happen a lot). A solution might be to keep your future instance inside a variable and initializing it only one time (you will need a StatefulWidget):
Future<String> _futureString;
#override
void initState() {
super.initState();
_futureString = getApiKey();
}
#override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: _futureString,
);
}
By doing so the _futureString variable will keep the value of your asynchronous operation and won't reload every time.

Flutter Futurebuilder showing error when showing snapshot data value

I am using Future builder in app and its working fine but when data load and when i am showing it in Text widget its showing this error
Class '_InternalLinkedHashMap<String, dynamic>' has no instance getter 'approved_value'.
Receiver: _LinkedHashMap len:31
Tried calling: approved_value
My code
class _ClaimsScreenState extends State<ClaimsScreen> {
#override
initState() {
super.initState();
doSomeAsyncStuff();
}
Future<List> doSomeAsyncStuff() async {
final storage = new FlutterSecureStorage();
String value = await storage.read(key: 'health_card');
print(value);
String url2 =
'api.com';
final response2 = await http.get(url2);
var Data = json.decode(response2.body);
print(Data);
var DisplayData = Data["records"];
return DisplayData;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('IGI GENERAL INSURANCE'),
),
body: FutureBuilder<List>(
future: doSomeAsyncStuff(),
builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
if (snapshot.hasData) {
print('ss');
print(snapshot.data);
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Row(
children: <Widget>[
Text('Approved Value:'),
Text(snapshot.data[index].approved_value)
],
),
Row(
children: <Widget>[
Text('Patient Name:'),
Text(snapshot.data[index].patient_name)
],
)
],
),
Row(
children: <Widget>[
Row(
children: <Widget>[
Text('Claimed Value:'),
Text(snapshot.data[index].claimed_value)
],
),
Row(
children: <Widget>[
Text('status:'),
Text(snapshot.data[index].patient_name)
],
)
],
)
],
),
);
});
} else if (snapshot.hasError) {
return Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('Error: ${snapshot.error}'),
);
} else {
return Padding(
padding: EdgeInsets.only(top: 16),
child: Text('Awaiting result...'),
);
}
}),
);
}
}
I am using listview builder in future builder because the values are in array and need to show all in text widget
I am not sure why its showing this error if remove the value its working fine just when i show the values in Text widget then its showing error.
snapshot.data[index] returns a Map. The Map class does not have an approved_value getter. You likely intended to do snapshot.data[index]['approved_value'] instead.