In my application I would like to display vaccines according to the species of the animal (If is a dog or cat). I'm experiencing an error: The method 'map' was called on null. Tried calling: map DropdownMenuItem. Why is this happening? I already put async and await in the methods, I don't understand why it is still null. Bellow my code:
1) This is where I call my DropdownContent class in init to prepare my DropdownMenuItem in the row inside the widget
class _VaccineDetailFormState extends State<VaccineDetailForm> {
final DataRepository repository = DataRepository();
String selectedVaccine = "Select";
List<String> vaccinesBySpecie;
initState() {
DropdownContent.getVaccines(widget.selectedPetID).then((value) => vaccinesBySpecie = value);
}
#override
Widget build(BuildContext context) {
return Scaffold(
[...]
new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new DropdownButton<String>(
value: widget.vaccine.name == null ? selectedVaccine: widget.vaccine.name,
underline: Container(
height: 2,
color: Colors.grey,
),
onChanged: (String newValue) {
setState(() {
selectedVaccine = newValue;
widget.vaccine.name = newValue;
});
},
items: vaccinesBySpecie.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
)
]),
[...]
2) Here is the DropdownContent class that, inside the getVaccines() method, searches the repository to find out the species of the current animal and then returns the appropriate vaccine list.
class Dropdown content
static Future<List<String>> getVaccines(String petId) async {
final DataRepository repository = DataRepository();
String currentSpecie = await repository.getSpecie(petId);
if (currentSpecie.contains('Dog')) {
return listOfVaccinesForDogs();
}
if (currentSpecie.contains('Cat')) {
return listOfVaccinesForCats();
}
}
3) Finally, the repository class that searches for the species of the animal
class Repository
Future<String> getSpecie(String petId) async {
DocumentReference documentReference = petCollection.document(petId);
await documentReference.get().then((snapshot) {
return snapshot.data['specie'].toString();
});
}
While your initState method may be asynchronous, your build method isn't. So at the time that the build method is called, your vaccinesBySpecie method is null.
The best way to fix this would be to initialize your List<String> vaccinesBySpecie like so List<String> vaccinesBySpecie = [];. This way it isn't null when the build method is called.
As a side note, I would suggest using a FutureBuilder or StreamBuilder if you can, that way you can handle when there isn't a value (i.e it is null) vs when there is a value(ie it is not null)
What Dean said illuminated my ideas. I managed to solve it by reaching the following answer:
1) DropdownMenuItem inside the widget
Container(
child: FutureBuilder <List<String>>(
future: DropdownContent.getVaccines(widget.selectedPetID),
builder: (context, AsyncSnapshot snapshot) {
if(snapshot.data == null) {
return CircularProgressIndicator();
}
else {
return DropdownButton<String>(
value: widget.vaccine.name == null? selectedVaccine: widget.vaccine.name,
underline: Container(
height: 2,
color: Colors.grey,
),
onChanged: (String newValue) {
setState(() {
selectedVaccine = newValue;
widget.vaccine.name = newValue;
});
},
items: snapshot.data.map<DropdownMenuItem<String>>((value) =>
new DropdownMenuItem<String>(
child: Text(value),
value: value,
))
.toList(),
);
}
})
)
2) DropdownContent class that searches the repository to find out the species of the current animal and then returns the appropriate vaccine list:
static Future<List<String>> getVaccines(String petId) async {
final DataRepository repository = DataRepository();
String currentSpecie;
await repository.getSpecie(petId).then((value) {
currentSpecie = value;
});
if (currentSpecie.contains('Dog')) {
return listOfVaccinesForDogs();
}
if (currentSpecie.contains('Cat')) {
return listOfVaccinesForCats();
}
}
3) The repository class that searches for the species of the animal
Future<String> getSpecie(String petId) async {
DocumentReference documentReference = petCollection.document(petId);
String specie;
await documentReference.get().then((snapshot) {
specie = snapshot.data['specie'].toString();
});
return specie;
}
Related
My problem is with Futures, because they should be obtained before build() method executed, as the documentation states:
The future must be obtained earlier, because if the future is created
at the same time as the FutureBuilder, then every time the
FutureBuilder's parent is rebuilt, the asynchronous task will be
restarted.
I know that Futures should be called in initstate() function before the build method executed, but my case is different.
I want to get data from api as a Future, but the request I am sending to the api needs some parameters that user should select inside the screen's build() method.
And I don't know what the parameter of the request will be until user selects in build() method, and I have to call the api in the build() method and use FutureBuilder there, but that makes FutureBuilder to get constantly called, and I don't want that.
basically, I don't want to call FutureBuilder indefinetely, and I can't put my Future inside initState() because the Future needs some parameters that user later selects when the screen is shown inside build() method.
inside the build method:
FutureBuilder<List<LatLng>>(
builder: (context, snapshot) {
if (snapshot.hasData) {
return PolylineLayer(
polylines: [
Polyline(
points: snapshot.data!,
strokeWidth: 4,
color: Colors.purple),
],
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
} else {
return Container();
}
},
future: Provider.of<NavigationProvider>(context)
.getNavigationPoints(pointToGoTo!),
),
now if you look at the code, at the final lines, I am sending the parameter pointToGoTo to the function which calls the backend.
simply, I want to get rid of calling api and getting data back as a Future inside build method, I want to do it in initState or somewhere else that prevents the build methods calling backend indefinitely.
is there any way to fix this problem?
Thanks in advance.
Firstly, create future state variable and a nullable params and use it with conditional if while using FutureBuilder.
I will recommend checking Fixing a common FutureBuilder and StreamBuilder problem
Now you can follow this example. It is missing progressBar on API recall, StreamBuilder might be better option in cases like this.
class Foo extends StatefulWidget {
const Foo({super.key});
#override
State<Foo> createState() => _FooState();
}
class _FooState extends State<Foo> {
int? params;
Future<int> fetch(int? data) async {
await Future.delayed(Duration(seconds: 1));
return (params ?? 0) * 2;
}
late Future<int> future = fetch(params);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
DropdownButton<int?>(
value: params,
items: List.generate(
12,
(index) => DropdownMenuItem(
value: index,
child: Text("$index"),
)).toList(),
onChanged: (value) {
future =
fetch(params); // this will only call api with update data
setState(() {
params = value;
});
},
),
if (params != null)
FutureBuilder<int>(
future: future,
builder: (context, snapshot) {
if (snapshot.hasData) return Text("${snapshot.data}");
return CircularProgressIndicator();
},
)
],
),
);
}
}
class Testing extends StatefulWidget {
const Testing({super.key});
#override
State<Testing> createState() => _TestingState();
}
class _TestingState extends State<Testing> {
bool isFetched = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Consumer<SomethingProvider>(
builder: (context, prov, child) {
if (!isFetched) {
prov.getData("a", "b");
Future.delayed(const Duration(milliseconds: 200), () {
isFetched = true;
});
}
if (prov.newData.isNotEmpty) {
return Column(
// make widget tree from here
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
}
class SomethingProvider extends ChangeNotifier {
List newData = [];
Future getData(param1, param2) async {
newData = ["testingdata"];
}
}
I'm having this super annoying issue of being unable to grab and display a table from my server hosted on PhpmyAdmin. (I've managed to grab the data and have it printed in the console, but now that I'm trying to display it in a table I can't seem to get it working)
I've tried nulling my variables but I'm not really sure what the main culprit for this error is. Any help would be greatly appreciated.
Image of Error
data.dart File
class dataListing extends StatefulWidget {
const dataListing({Key? key}) : super(key: key);
#override
State<dataListing> createState() => _dataListingState();
}
class _dataListingState extends State<dataListing> {
#override
Widget build(BuildContext context) {
return Container();
}
}
class listingData{
String? ListingID, listingName, listingDescription, address, suburbName, phoneNumber, openingHours, Email, Website;
listingData({
this.ListingID,
this.listingName,
this.listingDescription,
this.address,
this.suburbName,
this.phoneNumber,
this.openingHours,
this.Email,
this.Website,
});
//constructor
List<listingData> datalist = [];
factory listingData.fromJSON(Map<String, dynamic> json){
return listingData(
ListingID: json["ListingID"],
listingName: json["listingName"],
listingDescription: json["listingDescription"],
address: json["address"],
suburbName: json["suburbName"],
phoneNumber: json["phoneNumber"],
openingHours: json["openingHours"],
Email: json["Email"],
Website: json["Website"],
);
}
}
Directory.dart file
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:app/pages/data.dart';
class directoryPage extends StatefulWidget {
#override
State<directoryPage> createState() => _directoryPageState();
}
class _directoryPageState extends State<directoryPage> {
// List serviceListing = [];
//
// getAllListing()async{
// String url = "URL HERE";
// var response = await http.get(Uri.parse(url));
// if (response.statusCode == 200){
// setState (() {
// serviceListing = json.decode(response.body);
// });
// print (serviceListing);
// return serviceListing;
// }
// }
bool error = false, dataloaded = false;
var data;
String dataurl = "URL HERE";
#override
void initState (){
loaddata();
super.initState();
// getAllListing();
}
void loaddata() {
Future.delayed(Duration.zero,() async {
var res = await http.post(Uri.parse(dataurl));
if (res.statusCode == 200) {
setState(() {
data = json.decode(res.body);
dataloaded = true;
});
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Directory'),
centerTitle: true,
elevation: 0,
backgroundColor: Color(0xFFA30B32),
//WSU Appbar Icon
leading: Padding(
padding: const EdgeInsets.all(8.0),
child: Image.asset("assets/wsulogo.png", scale: 8.0),
),
),
body: Container(
padding: EdgeInsets.all(15),
child:dataloaded?datalist():
Center(
child:CircularProgressIndicator()
),
)
);
}
Widget datalist(){
if(data["error"]) {
return Text(data["errmsg"]);
}else{
List<listingData> datalist = List<listingData>.from(data["data"].map((i){
return listingData.fromJSON(i);
})
);
return Table( //if data is loaded then show table
border: TableBorder.all(width:1, color:Colors.black45),
children: datalist.map((listingdata){
return TableRow( //return table row in every loop
children: [
//table cells inside table row
TableCell(child: Padding(
padding: EdgeInsets.all(5),
child:Text(listingdata.ListingID!)
)
),
TableCell(child: Padding(
padding: EdgeInsets.all(5),
child:Text(listingdata.listingName!)
)
),
TableCell(child: Padding(
padding: EdgeInsets.all(5),
child:Text(listingdata.listingDescription!)
)
),
TableCell(child: Padding(
padding: EdgeInsets.all(5),
child:Text(listingdata.address!)
)
),
]
);
}).toList(),
);
}
}
}
Looks like the issue was actually unrelated to the dart side of things, the php code wasn't properly structuring the data. Cannot have underscores or spaces.
Correct-> $json["dballlisting"] = array (); (I renamed it to just "data" later)
Incorrect->$json["db_all_listing"] = array ();
The error seems to be originating from this line, the data['data'] is null which is expected to be an Array.
List<listingData> datalist = List<listingData>.from(data["data"].map((i){
return listingData.fromJSON(i);
})
You need to investigate your API call to make sure why it is happening. If the null value is expected then you need to add safeguards in your code to make sure it won't break when it encounter such scenarios. You can add null safety checks for that one way to do it would be to
List<listingData> datalist = List<listingData>.from((data["data"] ?? []).map((i){
return listingData.fromJSON(i);
})
I have a function that is supposed to fetch me a list of Restaurants objects from firestore based on location.
the function does its job perfectly when i first run the app but after using the app from another device and updating resturants data in firestore documents, i somehow get duplicates of the restaurants list items.
here is the code for the function that fetch the the restaurants objects list:
Future<void> fetchRestaurantsList() async {
try {
Position position = await Geolocator().getCurrentPosition(
desiredAccuracy:
Platform.isIOS ? LocationAccuracy.lowest : LocationAccuracy.high);
final dbRestaurant = firestore
.collection('testing')
.document('users')
.collection('restaurant');
geo.collection(collectionRef: dbRestaurant)
.within(
center: GeoFirePoint(
position.latitude,
position.longitude
),
radius: 45.0,
field: 'resturantLocation')
.listen((event) {
restaurantList.clear();
await event.forEach((element){
final distance = Distance.getDistanceFromLatLonInKm( // calculating distance for each restaurant
position.latitude,
position.longitude,
element.data['location']['geopoint'].latitude,
element.data['location']['geopoint'].longitude)
restaurantList.add(Restaurant(
id: element.documentID,
logo: element.data['logo'],
name: element.data['name'],
distance: distance ,
));
notifyListeners();
});
});
} catch (e) {
print(e.toString());
}
} finally {
notifyListeners();
}
}
and this is the page that contains the list: (its under a parent widget which contains other tabs)
class RestruntsListTab extends StatefulWidget {
final MainModel model;
RestruntsListTab({#required this.model});
#override
State<StatefulWidget> createState() {
return _RestruntsListTabState();
}
}
class _RestruntsListTabState extends State<RestruntsListTab>
#override
void initState() {
widget.model.fetchRestaurantsList();
widget.model.checkLocationService().then((isActive) {
if (isActive) {
} else {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(
language.enableLcation,
style: TextStyle(
fontFamily: 'eff', fontSize: 18, fontWeight: FontWeight.bold),
),
backgroundColor: Colors.grey,
));
}
});
super.initState();
}
#override
Widget build(BuildContext context) {
return ScopedModelDescendant<MainModel>(
builder: (context, child, model) {
return ListView.builder(
itemCount:model.restaurantList.length,
itemBuilder: (context,index) {
return Row(
children: <Widget>[
Text(model.restaurantList[index].name),
Text(model.restaurantList[index].distance),
],
)
}
);
})
}
}
this is a simplified code for demonstration but the actual code is pretty similar.
if you have encountered similar issues kindly share your experience.
thank you all.
check that fetchRestaurantsList() method is not called on widget build
or it is in StreamBuilder method...it's because .listen((event) { this method it is like a stream so you have to use flag like bool variable to run the code inside it
if(mybool==false){// the other code goes.... setStste({mybool=true;})}
in this way it only excute the code once
There might be something wrong with the code, but I don't see it. What you can try doing is wrapping the content of forEach with
if(restaurantList.where((item) => item.id == element.documentID).isEmpty){
}
That should filter out duplicates.
I'm building a selectable checkbox contact list in flutter but if a contact has only an email and no number, an error is thrown. I want to create a loop to add a number of '99999' to the contacts phone number if they don't have one. Please can someone guide me with an explanation of what I should change to make this work? I have had a go, but I am quite new to flutter so I'm not completely certain on syntax etc...
Here is the part of the code that I am trying to put the function into.
setMissingNo()async {
Iterable<Contact> contactsToLoop = (await ContactsService.getContacts()).toList();
contactsToLoop.forEach((Contact) { contactsToLoop = []..add(Item.fromMap({'label': 'work', 'value': 99999})); });
}
//fetch contacts from setMissingNo
getAllContacts() async{
Iterable<Contact> _contacts = (await ContactsService.getContacts()).toList();
setState(() {
contacts = _contacts;
}
);
}
Here is my whole code
import 'package:flutter/material.dart';
// TODO: make it ask for permissions otherwise the app crashes
import 'package:contacts_service/contacts_service.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
List<Contact> contacts = [];
List<Contact> contactsFiltered = [];
TextEditingController searchController = new TextEditingController();
#override
void initState() {
super.initState();
getAllContacts();
searchController.addListener(() => filterContacts());
}
//remove the +'s and spaces from a phone number before its searched
String flattenPhoneNumber(String phoneStr) {
return phoneStr.replaceAllMapped(RegExp(r'^(\+)|\D'), (Match m) {
return m[0] == "+" ? "+" : "";
});
}
//loop and set all contacts without numbers to 99999, pass new list to getAllContacts
setMissingNo()async {
Iterable<Contact> contactsToLoop = (await ContactsService.getContacts()).toList();
contactsToLoop.forEach((Contact) { contactsToLoop = []..add(Item.fromMap({'label': 'work', 'value': 99999})); });
}
//fetch contacts from setMissingNo
getAllContacts() async{
Iterable<Contact> _contacts = (await ContactsService.getContacts()).toList();
setState(() {
contacts = _contacts;
}
);
}
//filtering contacts function to match search term
filterContacts() {
List<Contact> _contacts = [];
_contacts.addAll(contacts);
if (searchController.text.isNotEmpty) {
_contacts.retainWhere((contact) {
String searchTerm = searchController.text.toLowerCase();
String searchTermFlatten = flattenPhoneNumber(searchTerm);
String contactName = contact.displayName.toLowerCase();
bool nameMatches = contactName.contains(searchTerm);
if (nameMatches == true) {
return true;
}
if (searchTermFlatten.isEmpty) {
return false;
}
var phone = contact.phones.firstWhere((phn) {
String phnFlattened = flattenPhoneNumber(phn.value);
return phnFlattened.contains(searchTermFlatten);
}, orElse: () => null);
return phone != null;
});
setState(() {
contactsFiltered = _contacts;
});
}
}
final selectedContacts = Set<Contact>();
#override
Widget build(BuildContext context) {
bool isSearching = searchController.text.isNotEmpty;
return Scaffold(
body: SafeArea(
child: Column(
children: <Widget>[
AppBar(
title: Text('Create Group'),
),
Container(
child: TextField(
controller: searchController,
decoration: InputDecoration(
labelText: 'Search Contacts',
border: OutlineInputBorder(
borderSide: new BorderSide(
color: Theme.of(context).primaryColor
)
),
prefixIcon: Icon(
Icons.search,
color: Theme.of(context).primaryColor
)
),
),
),
Expanded( child: ListView.builder(
shrinkWrap: true,
itemCount: isSearching == true ? contactsFiltered.length : contacts.length,
itemBuilder: (context, index) {
Contact contact = isSearching == true ? contactsFiltered[index] : contacts[index];
//TODO: make it so when you clear your search, all items appear again & when you search words it works
return CheckboxListTile(
title: Text(contact.displayName),
subtitle: Text(
contact.phones.elementAt(0).value
),
value: selectedContacts.contains(contact),
onChanged: (bool value) {
if (value) {
selectedContacts.add(contact);
} else {
selectedContacts.remove(contact);
}
setState((){});
// TODO: add in function to add contact ID to a list
});
},
),
/*new Expanded(
child: Align(
alignment: Alignment.bottomLeft,
child: BottomNavigationBar(
currentIndex: _currentIndex,
items: const <BottomNavigationBarItem>[
//TODO: create new contact functionality to add someone by name + email
BottomNavigationBarItem(
icon: Icon(Icons.add),
title: Text('Add Contact'),
),
BottomNavigationBarItem(
icon: Icon(Icons.create),
title: Text('Create Group'),
),
],
onTap: (index) {
setState(() {
_currentIndex = index;
});
}
)
)
)*/
)
],
)
),
);
}
}
Updating all contacts listed on the device might take a long time depending on the size of the device's contact list. Add that the task is being done on the UI thread. You may want to consider using Isolate for the task to move away the load from the UI thread. If you can also provide the errors that you're getting, it'll help us get a picture of the issue.
Another thing is, the way you're approaching the issue might be impractical. Is it really necessary to write a placeholder phone number to the contacts? The issue likely stems from trying to fetch the number from a contact but the Object returns null. Perhaps you may want to consider only updating the phone number when the contact is selected.
I am able to fetch and print the data in json format in console and I am able to display the entire body of data in list format in the flutter app. But I can't figure out how to display only a specific key and its value in list. Its a list of maps. I have removed the consumer key and secret from this code.
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main(){
runApp(MaterialApp(
home: CustomHome(),
));
}
class CustomHome extends StatefulWidget {
#override
_CustomHomeState createState() => _CustomHomeState();
}
class _CustomHomeState extends State<CustomHome> {
List data;
Future<String> getData() async{
var response = await http.get('https://jbaat.com/wp-json/wc/v3/products/?consumer_key=&consumer_secret=');
setState(() {
var converted = jsonDecode(response.body);
data = converted;
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
this.getData();
}
#override
Widget build(BuildContext context) {
print(data);
return Scaffold(
body: ListView.builder(
itemCount: data == null ? 0 : data.length,
itemBuilder: (BuildContext context, int index) {
return Container(
child: Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text('$data'),
),
),
);
}),
);
}
}
Below is the response
[{id: 493, name: Bas 5 Min White Half Sleeve T-Shirt, slug: bas-5-min-white-half-sleeve-t-shirt, permalink: https://www.jbaat.com/product/bas-5-min-white-half-sleeve-t-shirt/, date_created: 2019-12-14T23:39:08, date_created_gmt: 2019-12-14T18:09:08, date_modified: 2019-12-14T23:48:01, date_modified_gmt: 2019-12-14T18:18:01, type: variable, status: publish, featured: false, catalog_visibility: visible, description: , short_description: , sku: , price: 500.00, regular_price: , sale_price: , date_on_sale_from: null, date_on_sale_from_gmt: null, date_on_sale_to: null, date_on_sale_to_gmt: null, price_html: <span class="woocommerce-Price-amount amount"><span class="woocommerce-Price-currencySymbol">₹</span>500.00</span>, on_sale: false, purchasable: true, total_sales: 0, virtual: false, downloadable: false, downloads: [], download_limit: -1, download_expiry: -1, external_url: , button_text: , tax_status: taxable, tax_class: , manage_stock: false, stock_quantity: null, stock_status: instock, backorders: no,
Jbaat, I'd recommend creating a model for your response data and use the values accordingly from each item's instance. There are few online converters available which converts your json response to Dart models, here is one - https://javiercbk.github.io/json_to_dart/. Below is a quick example of what it would look like based on your response data,
class Items {
final List<Items> items;
Items({this.items});
factory Items.fromJson(List<dynamic> json) {
List<Items> itemList = json.map((i) => Items.fromJson(i)).toList();
return Items(
items: itemList
);
}
}
class Item {
final int id;
final String name;
......
Item({this.id, this.name,.....});
factory Item.fromJson(Map<String, dynamic> json) {
return Item(
id: json['id'],
name: json['name']
......
);
}
}
And then your getData() would be,
Future<Items> getData() async{
var response = await http.get('https://jbaat.com/wp-json/wc/v3/products/?consumer_key=&consumer_secret=');
return Items.fromJson(json.decode(response.body)).items;
}
You should now have Item list which can be used to get specific item info. You should also use FutureBuilder to call your getData() instead of calling it in initState to make sure data is available before building widgets like so,
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Scaffold(
body: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
Item item = snapshot.data[index]; //Your item
return Container(
child: Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(item.name),
),
),
);
}),
);
} else {
return Center(child: CircularProgressIndicator());
}
});
}
Hope this helps. Good luck!
Change the type for data to
List<Map<String,dynamic>>
A possible implementation for your use case:
Map y;
var keytobesearched='name';
List<Map<String,dynamic>> x= [{'id': 493, 'name': 'Bas 5 Min White Half Sleeve T-Shirt', 'slug': 'bas-5-min-white-half-sleeve-t-shirt'}];
x.forEach((Map<String,dynamic> ele){
if(ele.containsKey(keytobesearched))
y=Map.from(ele);
// display/alter y to your liking
});
If you would want a complete plug and play Woocommerce Sdk that handles authentication, products, customer, shipping etc for you, you can use the Woo Commerce SDK library for flutter at https://pub.dev/packages/woocommerce
Woocommerce myWoocommerce = WooCommerce(baseUrl: yourBaseUrl, consumerKey: yourConsumerKey, consumerSecret: consumerSecret);
Then simply get your lists eg:
WooProduct products = await myWocommerce.getProducts(); // returns the ist of products.
for (var product in products){ print(product.name)};