How to fetch and display an array of network images? - amazon-web-services

I am fetching an array of images from AWS in a List and I am displaying it using .map. I don't know if I am doing it right. I am having an error 'package:flutter/src/painting/_network_image_io.dart': Failed assertion: line 25 pos 14: 'url != null': is not true. But works perfectly fine if I manually display it like this, Image.network('https://thisIsJustSampleLink.s3.us-east-2.amazonaws.com/id/sampleOnly/bb.jpg')
Here is my code. Fetching data from the database. The babyimage[] is an array of images link from AWS.
List <String> _babyList = List();
Future<void> getApplicantInfo() async {
AuthService().getRequestorApplicants().then((val) async {
requestorApplicants.clear();
_babyList.clear();
for (var i = 0; i < val.data.length; i++) {
var temp = val.data[i];
setState(() {
requestorApplicants.add(new RequestorApplicantsList(
parentname: temp['parentname'],
babyname: temp['babyname'],
));
_babyList.add(temp['babyimage[]']);
});
}
});
}
Displaying the list using Image.network
Container(
height: MediaQuery.of(context).size.height,
child: ListView(
shrinkWrap: true,
scrollDirection: Axis.vertical,
children: <Widget>[
Column(
children: _babyList.map((e) => Image.network(e)).toList(),
),
],
)),

in this code _babyList.map((e) => Image.network(e)).toList() some of urls (or maybe all) are null. Add filter to remove null elements.
_babyList.where((e) => e != null).map((e) => Image.network(e)).toList(growable:false)
if they can't be null check your code, maybe there is a bug somwhere

Related

Flutter property values of a Map all treated as strings

I have a list like this a = [{'one': 'one', 'two': null, 'three': [{'four': 'four'}]}]
I send it to a function to use it in a post request which in the body should receive a Map, so what I did was this to a[0], the problem is that I get this error The getter 'length' was called on null
I start to review and it treats all the property values as if they were Strings, even the nested list 'three': [{'four': 'four'}], I have tried to send the post in this way http.post (url, body: (recurrence [0] as Map)) but it has not worked, it always gives me the same error, even if in the body I put the properties by hand in the body: {'new property': a [0] [' tres']}, how should one act to solve this problem? Thank you very much for your help
Code:
void _ordersGet() async {
await http.get(url).then((value) {
setState(() {
orders = jsonDecode(value.body);
}
}
orders is sent to a new widget: orderList(orders)
orderList is a listView
ListView.builder(
shrinkWrap: true,
primary: false,
itemCount: orders.length,
itemBuilder: (orders, index) {
return return Card(
elevation: 5,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(orders[index]['facts']),
SizedBox(
width: 4,
),
Text('Cantidad : '),
Text(orders[index]['ITEMS'][0]['jeans']),
SizedBox(
width: 4,
),
IconButton(
onPressed: () => _reorderData(context, orders[index]),
icon: Icon(
Icons.replay_outlined,
color: Theme.of(context).accentColor,
)),
],
),
);
},
);
_reorderData is a function that make a get request, the info in shipped to ReorderModal
ReorderModal it only shows the information and has a button
void _reorderData(BuildContext ctx, order) async {
var data;
var url = 'serverAddress/${order['facts']}';
await http.get(url).then((value) {
data = jsonDecode(value.body);
data[0]['CORPORATION'] = order['corporation'];
showModalBottomSheet(
context: ctx,
builder: (_) {
return ReorderModal(data);
});
}).catchError((onError) {});
}
class ReorderModal extends StatelessWidget {
final List data;
ReorderModal(this.data);
void orderSend(orderInfo) async {
var url = 'serverAddress';
await http.post(url, body: orderInfo[0]).then((value) {
print(jsonDecode(value.body));
}).catchError((onError) {
print(onError);
});
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(10),
child: Column(children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: Column(
children: [
ElevatedButton(
onPressed: () {
orderSend(data);
//print(data);
},
child: Text('ONE Click'))
]),
);
}
}
when i press the ONE Click button execute the function orderSend, orderSend make a post request and the problem described above
This is the simplified code, I know it must be something very simple, but it is giving me a lot of work to solve

How to ADD a List<String> to CheckboxListTile values?

On this example im trying to add to the CheckboxListTile the values i have saved on my List.
But for somereason im stuck here and i cant figure it out how to solve this problem.
Global List
List<String> _lstNomeData = new List<String>();
Values Map
Map<String, bool> values = {
'$_lstNomeData[index]': false,
};
Get selected checkbox values
var tmpArray = [];
getCheckboxItems() {
values.forEach((key, value) {
if (value == true) {
tmpArray.add(key);
}
});
print(tmpArray);
tmpArray.clear();
}
Body
body: Column(
children: <Widget>[
Expanded(
child: ListView(
children: values.keys.map((String key) {
return new CheckboxListTile(
title: new Text(key),
value: values[key],
activeColor: Colors.blue,
checkColor: Colors.white,
onChanged: (bool value) {
setState(() {
values[key] = value;
});
},
);
}).toList(),
),
),
],
)
Error
Print of the error displayed
You are on the right track. You can do two things here:
Prefill the map with false for every key (what you are trying to do)
Assume that if the map does not have a key, the answer is false (default)
Second approach is probably even better because by prefilling the map with false you could not distinct between false being actually answered by the user or if it was set by default. If a key is not in the map you know that the user has not answered the question so far. I will go and show how to work with the second approach:
Keep your global list as it is:
List<String> _lstNomeData = [];
Initialise the map (which represents the answers from the user for each question) with an empty Map:
Map<String, bool> answers = {};
Now correctly reference those answers in your Checkbox widget and make use of the onChanged property:
ListView(
children: _lstNomeData.map((key) =>
CheckboxListTile(
title: Text(key),
// Here we check if the answers map has this key and if it does, use the bool value, otherwise set it to false (user has not answered this so far)
value: answers[key] ?? false,
activeColor: Colors.blue,
checkColor: Colors.white,
onChanged: (answer) =>
setState(() => answers[key] = answer)
),
).toList(),
),

How to properly sort a List of object and clone it

so i was creating this app which is an item catalogue for an online shop using StaggeredGridView, here i add a function to sort the item from a list, so that i can sort it by price or make it back to default sorting, but i encounter a problem where the list cant go back to default sorting when i select it, here is the code :
class ItemCatalogState extends State<ItemCatalog>{
sortedBy sorted = sortedBy.init;
bool isInit = true;
var selectedList = [];
void sortItem(List<Item> initialList){
switch (sorted) {
case sortedBy.lowestPrice:
setState(() {
selectedList.sort((a,b){return a.price.compareTo(b.price);});
});
break;
case sortedBy.highestPrice:
setState(() {
selectedList.sort((a,b){return b.price.compareTo(a.price);});
});
break;
case sortedBy.init:
//NOT WORKING
setState(() {
selectedList = initialList;
});
break;
default:
}
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
List<Item> itemList = Provider.of<List<Item>>(context);
if(isInit&&itemList!=null){
selectedList = itemList.map((e) => Item(image: e.image,name: e.name,isReady: e.isReady,price: e.price,seller: e.seller)).toList();
isInit = false;
}
sortItem(itemList);
return Container(
child: DropdownButtonHideUnderline(
child: DropdownButton(
value: sorted,
items: [
DropdownMenuItem(
child: Text('Default',),
value: sortedBy.init,),
DropdownMenuItem(
child: Text('Lowest',),
value: sortedBy.lowestPrice,),
DropdownMenuItem(
child: Text('Highest'),
value: sortedBy.highestPrice,),
],
onChanged: (value){
setState(() {
sorted = value;
});
}),
),
),
Container(
decoration: BoxDecoration(
color: kGrayMainColor,
),
child: StaggeredGridView.countBuilder(
itemCount: selectedList!=null?selectedList.length:itemList.length,
crossAxisCount: 2,
itemBuilder: (context, index) {
Item currentItem = selectedList!=null?selectedList[index]:itemList[index];
return ItemTile(item: currentItem, size: size,);
},
),
),
),
]),]
);
}
It was supposed to call the original List when i click the default drop down, but nothing changed, could i possibly wrong at copying the list to selectedList? Thankyou and any advice regarding my other bad code practice is appreciated since im still learning.
Inside sortItems you set selectedList to be equal to initialList. From that point on, both variables are now pointing at the same collection of objects, which means anything you do to one, you will also do to the other. And since you are getting the collection through provider, these changes will also affect the original list that was provided.
Instead of a direct assignment, copy initialList again so that the two lists are never pointing to the same collection.
Incidentally, there's a much easier way to create copies of lists:
selectedList = List.from(initialList);
On another note, I'm assuming that the default value of sorted is sortedBy.init. That makes the initial copy within your build method redundant as the sortItems method immediately overwrites the value of selectedList. Instead, just depend on sortItems to generate your list without having to worry about initialization:
void sortItem(List<Item> initialList){
switch (sorted) {
case sortedBy.lowestPrice:
selectedList = List.from(initialList)
..sort((a,b) => a.price.compareTo(b.price));
break;
case sortedBy.highestPrice:
selectedList = List.from(initialList)
..sort((a,b) => b.price.compareTo(a.price));
break;
case sortedBy.init:
selectedList = List.from(initialList);
break;
}
setState(() {}); // Just call this once to reduce code duplication and verbosity
}
#override
Widget build(BuildContext context) {
...
List<Item> itemList = Provider.of<List<Item>>(context);
sortItems(itemList);
...
}
This is how I would solve it (I changed your List<Item> to a List<String> for simplicity, but you get the gist):
Load the original list into the _sortedList variable (initially in didChangeDependencies) via the Provider and repeat this whenever you are in need of the original list again.
(My Provider returns ['aa', 'dd', 'cc'], so I could get in that map() you do on the list as well :) )
enum SortOrder { ascending, descending, original }
class _TestState extends State<Test> {
var _sortedList = List<String>();
#override
void didChangeDependencies() {
super.didChangeDependencies();
_loadOriginalList();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
RaisedButton(
child: Text('Ascending'),
onPressed: () => setState(() => _sortList(SortOrder.ascending)),
),
RaisedButton(
child: Text('Descending'),
onPressed: () => setState(() => _sortList(SortOrder.descending)),
),
RaisedButton(
child: Text('Original'),
onPressed: () => setState(() => _sortList(SortOrder.original)),
),
],
),
ListView.builder(
shrinkWrap: true,
itemCount: _sortedList.length,
itemBuilder: (context, index) => Center(
child: Text(_sortedList[index]),
),
),
],
),
);
}
void _sortList(SortOrder sortOrder) {
switch (sortOrder) {
case SortOrder.ascending:
_sortedList.sort();
break;
case SortOrder.descending:
_sortedList.sort();
_sortedList = _sortedList.reversed.toList();
break;
case SortOrder.original:
_loadOriginalList();
break;
}
}
void _loadOriginalList() {
final originalList = Provider.of<List<String>>(context, listen: false);
_sortedList.clear();
_sortedList.addAll(originalList.map((e) => e.substring(0, 1)));
}
}

From Future<List<String>> to List<String> for API calls to use in regular functions/scaffold

I'm using the TMDb API to get image urls to display. I am able to get the image urls and display them on the console. But then when I try to create a method and loop through for each of the items in the list it keeps returning empty (i checked with print).
Edit: Both methods are inside the same class and literally right below each other.
Any help will be appreciated!
List<String> popularMovie = List<String>();
#override
void initState() {
super.initState();
this.getJsonData();
}
//GET POPULAR MOVIE INFO WITH API
Future<List> getJsonData() async {
http.Response response = await http.get(
Uri.encodeFull(popularURL),
headers: {
'Accept': 'application/json'
}
);
var popularMovieData = json.decode(response.body);
var placeholder = popularMovieData['results'];
//get a list of images only
for(var item in placeholder) {
popularMovie.add(item['poster_path']);
}
print(popularMovie.length); //20
print(popularMovie); //prints [imgURL, imageURL, ...]
return popularMovie;
} //end of popular movie api function
Below is the method that is suppose to loop through list items
//method to iterate through each item and get the image url to display image
List<Widget> popularMovies() {
List<Widget> popularMovieList = new List();
for(var item in popularMovie) { //this is empty
var popularMovieItem = Padding(
padding: EdgeInsets.all(10),
child: Container(
width: 250,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.4),
spreadRadius: 4,
blurRadius: 4,
),
]
),
child: Stack(
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height/2 - 20,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(15),),
image: DecorationImage(
image: NetworkImage('https://image.tmdb.org/t/p/w500${item}'),
fit: BoxFit.fill,
)
),
),
],
),
],
),
),
);
popularMovieList.add(popularMovieItem);
}
return popularMovieList;
}
And finally, I'd like to display all the movies on my page as
children:popularMovies(),
Since getJsonData is an async function, you need to use a FutureBuider or else instead of returning popularMovie you need to call setState
FutureBuilder<String>(
future: getJsonData,
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('Press button to start.');
case ConnectionState.active:
case ConnectionState.waiting:
return Text('Awaiting result...');
case ConnectionState.done:
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
return Text('Result: ${snapshot.data}');
}
return null; // unreachable
},
)
I finally figured it out! It is super easy with setState so if anyone needs a solution to convert a
Future<List<String>> to List<String> and then use it as a normal List inside the scaffold here's what I did:
First, declare your list variable:
List<String> popularMovie = List<String>(); //declare a list inside the class
Then do your http call and assign the 'list of items you want' to your previously declared list variable 'inside setState'
#override
void initState() {
super.initState();
this.getJsonData();
}
//GET POPULAR MOVIE INFO WITH API
Future<List<String>> getJsonData() async {
http.Response response = await http.get(
Uri.encodeFull(popularURL),
headers: {
'Accept': 'application/json'
}
);
setState(() { //only get the first 10 movie images
var popularMovieData = json.decode(response.body);
for (int i = 0; i < 10; i++) {
popularMovie.add(popularMovieData['results'][i]['poster_path']);
}
print(popularMovie);
});
} //end of popular movie api function
Then I created a method to create a widget to display each image which is the same as the method I created in my original post that starts like this:
List<Widget> popularMovies() {
And finally, I wanted to display it this way(which you can do however you want to):
Padding(
padding: EdgeInsets.only(left: 10, top: 10, bottom: 10),
child: Container(
height: MediaQuery.of(context).size.height/2,
child: ListView(
scrollDirection: Axis.horizontal,
children: popularMovies(), //display all images in a horizontal row
),
),
),
And you're done!! :)

Flutter return Firestore array values to a List

I am using Flutter table calendar plugin to make a calendar. In order to put events into the calendar, I have to add data to _events map. I want to get the data from Firestore document, and put the data into _events map. However, I don't know how to do it. I search everywhere but I can't get an answer.
This is my code
class _MemberEventsState extends State<MemberEvents>
with TickerProviderStateMixin {
Map<DateTime, List> _events;
List _selectedEvents;
AnimationController _animationController;
CalendarController _calendarController;
List<String> list = List();
#override
void initState() {
super.initState();
final _selectedDay = DateTime.now();
Firestore.instance
.collection('events')
.document('2019-07-30')
.get()
.then((DocumentSnapshot ds) {
list = List.from(ds['title']);
});
_events = {DateTime.parse("2019-08-01"): list};
_selectedEvents = _events[_selectedDay] ?? [];
_calendarController = CalendarController();
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 400),
);
_animationController.forward();
}
#override
void dispose() {
_animationController.dispose();
_calendarController.dispose();
super.dispose();
}
void _onDaySelected(DateTime day, List events) {
print('CALLBACK: _onDaySelected');
setState(() {
_selectedEvents = events;
});
}
void _onVisibleDaysChanged(
DateTime first, DateTime last, CalendarFormat format) {
print('CALLBACK: _onVisibleDaysChanged');
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
_buildTableCalendar(),
const SizedBox(height: 8.0),
const SizedBox(height: 8.0),
Expanded(child: _buildEventList()),
],
),
);
}
Widget _buildTableCalendar() {
return TableCalendar(
calendarController: _calendarController,
events: _events,
startingDayOfWeek: StartingDayOfWeek.sunday,
calendarStyle: CalendarStyle(
selectedColor: Colors.deepOrange[400],
todayColor: Colors.blueAccent[200],
markersColor: Colors.brown[700],
outsideDaysVisible: false,
),
headerStyle: HeaderStyle(
formatButtonTextStyle:
TextStyle().copyWith(color: Colors.white, fontSize: 15.0),
formatButtonDecoration: BoxDecoration(
color: Colors.deepOrange[400],
borderRadius: BorderRadius.circular(16.0),
),
),
onDaySelected: _onDaySelected,
onVisibleDaysChanged: _onVisibleDaysChanged,
);
}
Widget _buildEventList() {
return ListView(
children: _selectedEvents
.map((event) => Container(
decoration: BoxDecoration(
border: Border.all(width: 0.8),
borderRadius: BorderRadius.circular(12.0),
),
margin:
const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
child: ListTile(
title: Text(event.toString()),
),
))
.toList(),
);
}
}
So in the first step to achieve my goal, I made a document named 2019-07-30, then I made an array in it named title. Then I tried to get the values in the array to a List named list. However, list returned null.
I don't know where I went wrong.
I am new to Flutter, so the question might seem stupid.
Also, I am new to stackoverflow, so if I did any steps wrong on describing the question, please tell me so I can fix it.