How to display something while the value is null/loading in Flutter? - flutter

I've created a real-time object detection with Tiny YOLOv2 using Flutter app. So far the app managed to detect the object and display its bounding box with the detectedClass and confidence. Then I pulled the detectedClass (the name of the object) and assigned it into my String _result variable because I need it to fetch data from Firebase later.
The main issue is when the app is not detecting anything I want to display something like maybe 'Loading...' until the _result return the name of the object bcus u see my custom Tiny YOLOv2 takes a lil bit of time before it detect the object. Then, I want to fetch data based on _result from the Firebase. So far, I've managed to fetch the data from the Firebase BUT ONLY if I hardcoded the name of the object detected. Otherwise the app would return null error if I'm fetching using _result variable.
Below is my attempt of displaying the the name of the food and its calorie (which is fetched from the Firebase) based on the _result variable but FAILED:
return MaterialApp(
debugShowCheckedModeBanner: false,
home: SafeArea(
child: Scaffold(
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image:AssetImage('assets/back.jpg'), fit: BoxFit.fill),
),
child: Column(
children: [
Stack(
children: [
Center(
child: Container(
margin: EdgeInsets.only(top: 10),
// child: Icon(Icons.photo_camera, color: Colors.orange, size: 40),
child: Text('Press on the camera icon',
style: TextStyle(
fontSize: 16.0,
color: Colors.orangeAccent,
fontWeight: FontWeight.bold
),
textAlign: TextAlign.center,
),
),
),
Center(
child: FlatButton(
onPressed: ()
{
initCamera();
},
child: Container(
margin: EdgeInsets.only(top: 35),
height: 270,
width: 360,
color: Colors.orange,
child: Stack(
children: list,
),
),
),
),
],
),
Center(
child: Container(
margin: EdgeInsets.only(top: 45.0),
child: SingleChildScrollView(
child: FutureBuilder(
future: dbRef.orderByChild("food_name").equalTo(_result).once(),
builder: (context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(
child: Text("Loading..."),
);
} else {
lists.clear();
Map<dynamic, dynamic> values = snapshot.data.value;
values.forEach((key, values) {
lists.add(values);
});
return ListView.builder(
shrinkWrap: true,
itemCount: lists.length,
itemBuilder: (BuildContext context,
int index) {
return
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment
.start,
children: <Widget>[
Text("Name: " + lists[index]["food_name"]),
Text("Calorie: " + lists[index]["calorie"]),
],
),
);
});
}
})
),
),
),
],
),
),
),
),
);
}
}
The error from the above is:
Exception has occurred.
NoSuchMethodError (NoSuchMethodError: The method 'forEach' was called on null.
Receiver: null
Tried calling: forEach(Closure: (String, dynamic) => Null))
My issue is kinda similar with this user and the solution in there is not working in my case.
I dont know whether its possible to fetch the data from firebase based on the real time input? Otherwise how do I save the name of the object somewhere to make it static(?) I'm really new to Flutter so some guidance on how to code it is very much appreciated. Thank you in advance.
Edited Btw just want to add it here. This is how I declared my _result:
_recognitions.forEach((response)
{
_result = "${response["detectedClass"]}" + "\n\n";
});
Basically _result is just the name of the object detected.

Just wanna share how I resolved this in case anyone's having the same issue. All I did is just insert a while (values == null) inside my else statement like this:
else {
values = snapshot.data.value;
while (values == null){
return Center(
child: CircularProgressIndicator(color: Colors.orange),
);
}
lists.clear();
values.forEach((key, values){
lists.add(values);
});
}
and then followed by the ListView.builder() to display them.
Btw, thank you so much for those who responded!

Related

How to show insert data into list view builder without reload application in flutter

I have created a sqlite crud application from flutter. my app data insert into the table successfully. but when i show that inserted date from listview need to reload app.in my app data insert from a separate screen. data have shown on home screen. i want show data without reload app in listview how can I slove my problem?
Here is my app code. this is my home screen. this screen show data from listview.
FutureBuilder<List<Student>>(
future: DatabaseHelper.instance.getStudentDetails(),
builder: (BuildContext context,
AsyncSnapshot<List<Student>> snapshot) {
if (!snapshot.hasData) {
return Center(child: Text('Loading...'));
}
return snapshot.data!.isEmpty
? Center(child: Text('No Student Details in List.'))
: ListView(
children: snapshot.data!.map((student) {
return Center(
child: Card(
color: selectedId == student.id
? Colors.white70
: Colors.white,
child: ListTile(
title: Text(
'Student Name : ${student.name}'),
subtitle:
Text('Course : ${student.course}'),
onTap: () {},
onLongPress: () {
setState(() {
DatabaseHelper.instance
.remove(student.id!);
});
},
),
),
);
}).toList(),
);
}),
This is my second screen.this screen I adding data insert into sqlite table.when I click floating action button I want to show insert data in home screen list view without reload.
Column(
children: [
SizedBox(
height: 10.0,
),
Text(
'Enter Student Details',
style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),
),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Divider(color: Colors.black),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
decoration: InputDecoration(
label: Text('Enter Name :'),
),
controller: nameController,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
decoration: InputDecoration(
label: Text('Enter Course :'),
),
controller: courseController,
),
),
],
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.save),
onPressed: () async {
await DatabaseHelper.instance.add(
Student(name: nameController.text, course: courseController.text),
);
setState(() {
nameController.clear();
courseController.clear();
selectedId = null;
});
},
),
future: DatabaseHelper.instance.getStudentDetails(), will get recall the api on every setState(build).
To avoid recalling the api, create a state variable for this future on state class (outside the build method).
late final future = DatabaseHelper.instance.getStudentDetails();
and use it on FutureBuilder
FutureBuilder<List<Student>>(
future: future,
builder: (BuildContext context,
You can check Fixing a common FutureBuilder and StreamBuilder problem by
Randal L. Schwartz.

Showing recently search in dropdown field

I have created an auto complete drop down field which shows data of api and when i search particular data i have saved data into sharedpreference also so that i can show few recently searched list so to achieve that i checked a condition saying if textformcontroller value is empty meaning nothing is types in textform field show recently search drop down and if someone type something in textform field then soo filtred data which come from api.
This is my code
optionsViewBuilder: (BuildContext context,
AutocompleteOnSelected<Result1> onSelected,
Iterable<Result1> options) {
if (fieldTextEditingController.value.text == "") {
return Align(
alignment: Alignment.topLeft,
child: Material(
child: Container(
width: 0.78.w(context),
color: Colors.white,
child: ListView.builder(
padding: const EdgeInsets.all(10.0),
itemCount: titles.length,
itemBuilder:
(BuildContext context, int index) {
final Result1 option =
options.elementAt(index);
return GestureDetector(
onTap: () {
onSelected(option);
},
child: ListTile(
title: Text(titles[index],
style: const TextStyle(
color: Colors.black87)),
trailing: InkWell(
onTap: () {
titles.removeAt(index);
},
child: Text("X")),
),
);
},
),
),
),
);
} else {
return Align(
alignment: Alignment.topLeft,
child: Material(
child: Container(
width: 0.78.w(context),
color: Colors.white,
child: ListView.builder(
padding: const EdgeInsets.all(10.0),
itemCount: options.length,
itemBuilder:
(BuildContext context, int index) {
final Result1 option =
options.elementAt(index);
return GestureDetector(
onTap: () {
onSelected(option);
},
child: ListTile(
title: Text(option.originalTitle!,
style: const TextStyle(
color: Colors.black87)),
),
);
},
),
),
),
);
}
},
inside optionviewbuilder i want am checking if fieldTextEditingController is empty then show data from sharedpreference if not show data from api but data only renders of sharedpreference not from api need some guidance here i am stuck
what we can do is declare an string variable and inside textformfield it has property saying onChanged which takes value just assign that value to your declared string variable and use that declared variable to check if that variable is empty show shared preference data else show api data
eg:- String? data;
onChanged:(value){
data = value
}
then,
if(data == null || data.isEmpty){
show shared preference data
}else{
show apidata
}

Type 'Future<dynamic>' is not subtype of type 'Widget'

I am showing markers from API on google maps. Here is my build method. When the program reaches the _widgetbuilder() method, it throws the specific error of type Future is not a subtype of the widget. If someone could please help to solve the problem and also tell me that what exactly this error means.....
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: FutureBuilder<List<MarkersOnMap>>(
future: future,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData)
return Container(
child: Center(
child: CircularProgressIndicator(),
),
);
if (snapshot.hasData && snapshot.data.isEmpty) {
return Center(
child: Container(
child: Column(
children: [
Text(
'No Properties Added Yet\nPlease Add Some!',
style:
TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
ElevatedButton.icon(
onPressed: () {
Navigator.push(
context,
PageTransition(
duration: Duration(microseconds: 500),
type: PageTransitionType.fade,
child: AddNewEproperty(
createEproperty: widget.createEproperty),
),
);
},
label: Text('Add'),
icon: Icon(Icons.add),
),
],
),
),
);
} else
_widgetbuilder();
if (snapshot.hasData) {
return ListView.builder(
itemCount: allWidgets.length + 1,
shrinkWrap: true,
padding: EdgeInsets.only(top: 16),
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, i) {
return Stack(
children: <Widget>[
Container(),],);},);},},),);}
This is the _widgetbuilder() method. When it reaches this return _widgetbuilder, throws _typeerror.
_widgetbuilder() async {
allWidgets = [];
widget.markersonmap = await future;
widget.markersonmap.forEach(
(element) {
print(element);
allWidgets.add(
Container(
height: 25,
width: 50,
child: new DecoratedBox(
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
borderRadius: BorderRadius.circular(5.0),
color: Colors.black54),
child: Text(
element.ePropertiesCardsList.price.toString(),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white),
),
),
),
);
},
);
}
You are getting this error because your function _widgetbuilder returns Future<dynamic> because the function is async.
Widget _widgetbuilder(){
// code here
}
The function should be in this structure to return of type Widget. The code that needs to be async should either be taken out of build function or use .then pattern for async code instead of async await if you really need to.
This short 9 min video will help you understand async better in flutter.
In here now the type error is solved but after reading 'future.then..... it does not goto the future and fetch data but jumps to the next foreach line and then calls it as null.
_widgetbuilder() {
allWidgets = [];
// widget.markersonmap = await future;
future.then((value) {
widget.markersonmap = value;
});
widget.markersonmap.forEach(
(element) {
print(element);
allWidgets.add(
Container(
// other code
}

What is the best practice to display data from API call to a Flutter DropdownButton?

Currently my app is making an http request to an API that gives me json with some items listed, this json get assigned to a List variable. I am already pulling in this data into my app but need to display it on a DropDownButton. To display this data in a dropdown button should I be using FutureBuilder or is there a best practice for something like this?
You can try something like this,
WalletRepo.getRestaurantBalance() fetches a bunch of restaurant with their current balance
FutureBuilder(
future: WalletRepo.getRestaurantBalance(),
builder: (_, AsyncSnapshot<GetRestaurantBalance> snapshot){
if(snapshot.hasData && snapshot.data != null){
return StatefulBuilder(builder: (BuildContext context, void Function(void Function()) nSetState) {
return Column(
children: [
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(5)
),
padding: EdgeInsets.symmetric(horizontal: 10,vertical: 5),
child: DropdownButtonHideUnderline(
child: DropdownButton(
onChanged: (RestaurantBalanceModel restaurant) {
if(restaurant.balance > 0.0){
print(chosenRestaurant);
nSetState(() => chosenRestaurant = restaurant);
}else{
Snack.bottom('Error', 'Restaurant has no balance to withdraw');
}
},
value: chosenRestaurant,
isExpanded: true,
hint: Text(
'Choose a restaurant'
),
items: snapshot.data.getAllRestaurantsByOwner.data.map((restaurant) => DropdownMenuItem(
value: restaurant,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
restaurant.name
),
Text(
'৳ ${restaurant.balance.toStringAsFixed(1)}'
)
],
),
)).toList(),
),
),
)

ListView is not showing the result if empty list

I am loading data from a remote api:
This is the dart file that provides the connection and download:
clinica-api.dart
import 'package:flutter_capenergy/modelos/clinica.dart';
import 'package:http/http.dart' as http;
Future<List<Clinica>> fetchClinicas(String idUsuario) async {
String url ="https://..flutter_api/get_clinicas.php";
final response = await http.get(url);
if (response.body == "[]"){
}
return clinicaFromJson(response.body);
}
And this is the piece of code from misclinicas.dart where I am showing the list:
Expanded(
child: Container(
child: FutureBuilder(
future: fetchClinicas(miId),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
shrinkWrap: true,
itemBuilder: (BuildContext context, index) {
print(index.toString());
Clinica clinica = snapshot.data[index];
return new GestureDetector(
onTap: () {
clinicaProvider.setClinica(clinica.nombreClinica);
clinicaProvider.setClinicaId(clinica.idClinica);
} ,
child: new Card(
elevation: 6,
child: new Column(
children: [
new Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment
.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment
.center,
mainAxisAlignment: MainAxisAlignment
.center,
children: <Widget>[
Image.network(
'https://.../${clinica
.logoClinica}',
height: 180,
alignment: Alignment.center,),
],
),
Text(
'${clinica.nombreClinica}',
style: TextStyle(fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.blue),
),
Text(
'${clinica.direccionClinica}',
style: TextStyle(fontSize: 14,
color: Colors.grey,
fontStyle: FontStyle.italic),
),
],
),
),
],
),
),
);
},
);
}
else {
Text ("NO HAY CLINICAS");
}
return Text("Cargando clínicas");
},
),
),
),
If there are items on the list, they are shown, but if the list is empty I would like to show a text with a message reporting that the list is empty.
I am trying to do it putting this text widget if snapshot.hasdata is false:
Text ("NO HAY CLINICAS");
but it is not shown, I am only getting a blank listView.
In the empty list case, snapshot.hasData will be true and snapshot.data.length will be 0.
snapshot.hasData == false means it's either loading or an error has happened.
in the api call return empty list if response.body ="[]"
if (response.body == "[]"){
List<Clinica> emptyClinica = [];
return emptyClinica;
}
in misclinicas.dart
snapshot.data.lenth > 0 ? your list work : Text('No Data Found')