Dart : How to fetch data with no number id (string)? - dart

So I was able to fetch data for 'locations' in this json using the number (0,1,2,3,4). But I was not able to fetch data from 'prayer_times' string directly. Is there any way to solve this?
I have tried Text(data["date"] because it cannot start with string right away and will give error The argument type 'dart.core::String' can't be assigned to the parameter type
'dart.core::int'.
The api is working do check the link thanks.
Data fetch display code
Card(
child: Container(
padding: EdgeInsets.all(15.0),
child: Row(
children: <Widget>[
Text("Name: "),
Text(data[0]["date"],
style: TextStyle(
fontSize: 18.0, color: Colors.black87)),
],
)),
),
API URI code
final String url = "http://api.azanpro.com/times/today.json?zone=ngs02&format=12-hour";
List data;
Future<String> getSWData() async {
var res = await http
.get(Uri.encodeFull(url), headers: {"Accept": "application/json"});
setState(() {
var resBody = json.decode(res.body);
data = resBody["prayer_times"];
});

You just need to make two changes.
Change the type of data to a Map and depending on your use case, initialise it to a default value:
Map<String, dynamic> data = {'date': "-------"};
And then get the date field directly in data
Card(
child: Container(
padding: EdgeInsets.all(15.0),
child: Row(
children: <Widget>[
Text("Name: "),
Text(data["date"],
style: TextStyle(
fontSize: 18.0, color: Colors.black87)),
],
)),
),

Related

i have a case like this, how to display data based on the number/id in the API

I have a case where I want to filter data based on the semester the user chooses, for example, when selecting semester 2 it displays the data in semester 2.
but from BackEnd when you have to filter the data you have to add parameters as shown, how do you do that?
and when I want to filter by semester, I have to add a parameter to the endpoint, is there a solution?
and this is when i call the API
static Future<Map<String, DataKuliahModel>> getDataKuliah() async {
String url = Constant.baseURL;
String token = await UtilSharedPreferences.getToken();
await Future.delayed(const Duration(seconds: 4));
Map<String, DataKuliahModel> finalResult = {};
final response = await http.get(
Uri.parse(
'$url/auth/mhs_siakad/perwalian/get_paket',
),
headers: {
'Authorization': 'Bearer $token',
},
);
final result = jsonDecode(response.body)['data'] as Map<String, dynamic>;
result.forEach((key, value) {
DataKuliahModel dataKuliah = DataKuliahModel.fromMap(value);
finalResult.addAll({
key: dataKuliah,
});
});
return finalResult;
}
when it is displayed to the user
FutureBuilder(
future: Services.getDataKuliah(),
builder: (context, snapshot) {
if (snapshot.hasData) {
DataKuliahModel selectedData =
snapshot.data!['$semester'] ?? DataKuliahModel();
return Column(
children: [
Container(
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(
Radius.circular(8),
),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
spreadRadius: 1,
blurRadius: 9,
offset: const Offset(
1, 2), // changes position of shadow
),
],
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
selectedData.matakuliah,
style: bold6,
textAlign: TextAlign.center,
), ...
the result is like this in every card has the same value
If you want to add smt to param, try this :
Uri.parse(
'$url/auth/mhs_siakad/perwalian/get_paket?smt=${yourSmtValue}',
),

How do I prevent the infinite rendering of message tiles despite the number of chat rooms there are?

To whom it may concern,
I am working on a project in which I am required to work on functionality for chat rooms. I have the bulk of the code made and most of it runs just fine, but I am having issues rendering the user message tiles to the chats scroll list, or the tile the user clicks to enter a specific chat room. My problem is that I am trying to render only as many chat rooms as there are in the database for the user, and right now there is only 1 in the database, meaning only 1 message tile should be rendered. However, when the message tile is rendered, it is just rendered infinitely down the list, over and over. Even after adding a second user to the Added Users collection in Cloud Firestore, only the first user is ever rendered, and infinitely at that. How can I make it to where there are only as many message tiles as there are added users in the database and each user in the Added Users collection is only rendered once? I need this issue fixed in order to complete the functionality of chatting in my application, since the person the user chats with is dependent on which message tile they tap.
Please note that I am using Android Studio, Flutter/Dart, Firebase Authentication (though this is not very relevant in this case), and Firebase Cloud Firestore.
To be clear, I expect to see one message tile per Added Users in the Cloud Firestore database, and to have the ability to scroll through this list of users if there are more than the screen can hold. So far, I have tried debugging to see the issue. However, I believe that the infinite render issue is an issue with the logic of my code, which is why I am posting on Stack Overflow.
Below are pictures of the chats scroll page itself and the Cloud Firestore database for this project. Below these pictures is the code responsible for the chats scroll list page:
Pictures:
The chats scroll list. Message tiles can be seen here, being infinitely rendered
Picture of the Cloud Firestore database for this project. Note that Added Users is a subcollection of a user document within the collection Users
Code:
class ChatsPage extends StatelessWidget{
const ChatsPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context){
UserObject testReceiver = UserObject("test2#gmail.com", "MyTestUser2", "Test", "2", "456", "Test", "Test", "Test", "Test", 123456, "https://imgv3.fotor.com/images/blog-cover-image/10-profile-picture-ideas-to-make-you-stand-out.jpg");
// Create a custom scroll view to list user chats.
return CustomScrollView(
// Using slivers...
slivers: [
// Create a sliver app bar.
SliverAppBar(
// Set the color to whatever is necessary.
backgroundColor: Colors.black,
// Center the title text.
centerTitle: true,
// Set the title text.
title: Text("Chats Page"),
// Create an IconBackground object to display the back arrow icon.
leading: IconBackground(
// Set the icon itself to the back arrow icon.
icon: Icons.arrow_back,
// Upon pressing the icon...
onTap: (){
// Simply return to the previous screen.
Navigator.pop(context);
}),
actions: [
Padding(
padding: const EdgeInsets.only(right: 10.0),
// Add chat functionality should be implemented here.
child: IconBackground( icon: Icons.add, onTap: (){ print("Must complete add chat functionality"); })
),
Padding(
padding: const EdgeInsets.only(right: 15.0),
child: IconBackground(icon: Icons.search, onTap: (){ print("Must complete search functionality"); })
)
],
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection("Users").doc(FirebaseAuth.instance.currentUser?.uid.toString()).collection("Added Users").snapshots(),
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 SizedBox(
height: 75,
child: ListView(
physics: const NeverScrollableScrollPhysics(),
children: snapshot.data!.docs.map((DocumentSnapshot document){
UserObject docUser = UserObject(
document.get("email"),
document.get("userName"),
document.get("firstName"),
document.get("lastName"),
document.get("userID"),
document.get("birthday"),
document.get("university"),
document.get("city"),
document.get("state"),
document.get("zipCode"),
document.get("profilePictureURL")
);
return _MessageChatTile(user: docUser);
}).toList(),
),
);
},
);
}
),
)
],
);
}
}
class _MessageChatTile extends StatelessWidget{
const _MessageChatTile({
Key? key,
required this.user
}) : super(key: key);
final UserObject user;
// There are 19 children in this one function.
#override
Widget build(BuildContext context){
// log("Hello? Does this work?");
return Material(
color: Colors.black,
child: InkWell(
onTap: () {
Navigator.of(context).push(ChatRoom.routeMessage(user));
},
child: Row(
children: [
Padding(
padding: const EdgeInsets.all(11.0),
child: Avatar.medium(url: user.profilePictureURL),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DefaultTextStyle(
style: const TextStyle(
fontSize: 20,
color: Colors.white,
letterSpacing: 0.2,
wordSpacing: 1.5
// fontWeight: FontWeight.w900
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(user.userName, overflow: TextOverflow.ellipsis),
// TODO: Add functionality for seeing messages outside of chat room.
//Text(messageData.message, overflow: TextOverflow.ellipsis)
],
),
)
]
),
),
Padding(
padding: const EdgeInsets.only(right: 20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
DefaultTextStyle(
style: const TextStyle(
fontSize: 11,
letterSpacing: -0.2,
fontWeight: FontWeight.w600
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
const SizedBox(
height: 4,
),
// TODO: Add functionality for seeing message dates outside of chat room.
// Text(
// messageData.dateMessage.toUpperCase(),
// style: const TextStyle(
// fontSize: 11,
// letterSpacing: -0.2,
// fontWeight: FontWeight.w600,
// color: Colors.blueGrey
// ),
// ),
const SizedBox(
height: 8,
),
Container(
width: 18,
height: 18,
decoration: const BoxDecoration(
color: Colors.blueAccent,
shape: BoxShape.circle
),
child: const Center(
child: Text(
'1',
style: TextStyle(
fontSize: 10,
color: Colors.white
),
),
),
)
],
)
)
],
),
)
],
),
)
);
}
}

Flutter Dismissible Unique Keys

I have a list of Dismissible widgets as follows:
Dismissible(
direction: DismissDirection.endToStart,
key: Key(widget.data[i]),
onDismissed: (direction) {
widget.onRemoveRequest(i, widget.data[i]);
},
background: Container(
color: Colors.red,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 20.0),
child: Text(
"Delete",
textAlign: TextAlign.right,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w500,
fontSize: 16.0,
),
),
),
],
),
),
child: CustomTextField(
padding: const EdgeInsets.only(left: 30.0, right: 30.0),
hintText: widget.newEntryHint,
text: widget.data[i],
keyboardType: TextInputType.multiline,
onChanged: (val) {
widget.onChanged(i, val);
},
),
)
It works as expected, except for when removing matching objects.
Note: widget.onRemoveRequest removes the object at specified index from the source data, widget.data.
widget.data is a List<String>. I provide these as the key, however whenever I have two matching strings and dismiss one, I get an error because the Dismissible isn't removed from the tree (understandable).
A dismissed Dismissible widget is still part of the tree.
So with a list of strings, how can I ensure each has a unique key, even if the actual strings are equal/match?
You need to assign each data to a unique identifier. Something unique enough for it to not contain any duplicates. Then you can associate that unique identifier to a Key.
This can't be done just with a primitive object such as String or Int. You'll need to map your data to a custom object.
The following class is a good example :
class Data {
final String id;
final String title;
Data({this.id, this.title});
}
This would allows you to then do the following :
Dismissible(
key: Key(widget.data[i].id),
...
)
You can generate a custom ID for your data using uuid package, or using a custom algorithm (such as an incremental index).
But be sure that your ID is unique for each item and stays the same for the whole lifetime of that item (even after updates).

Flutter List View error in building list view

I get an error while I'm building the ListView, At first I thought the API wasn't returning anything but I printed the movies variable in flutter and it consoled log the values.
Btw I'm trying to recreate this project:
https://github.com/mlabouardy/flutter-watchnow
The error I get is:
RangeError (index): Invalid value: Valid value range is empty: 0
This is the list view builder:
class TopMoviesState extends State<TopMovies> {
List<Movie> _movies = new List();
final _apiGatewayURL = "https://gfioehu47k.execute-api.us-west-1.amazonaws.com/staging/main";
Widget _fetchMovies() {
return new ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: (context, i){
return new Card(
child: new Container(
height: 250.0,
child: new Padding(
padding: new EdgeInsets.all(2.0),
child: new Row(
children: <Widget>[
new Align(
child: new Hero(
child: new Image.network("https://image.tmdb.org/t/p/w500"+this._movies[i].getPoster()),
tag: this._movies[i].getTitle()
),
alignment: Alignment.center,
),
new Expanded(
child: new Stack(
children: <Widget>[
new Align(
child: new Text(
this._movies[i].getTitle(),
style: new TextStyle(fontSize: 11.0, fontWeight: FontWeight.bold),
),
alignment: Alignment.topCenter,
),
new Align(
child: new Padding(
padding: new EdgeInsets.all(4.0),
child: new Text(
this._movies[i].getOverview(),
maxLines: 8,
overflow: TextOverflow.ellipsis,
style: new TextStyle(fontSize: 12.0, fontStyle: FontStyle.italic)
)
),
alignment: Alignment.centerRight,
),
new Align(
child: new Text(
this._movies[i].getReleaseDate(),
style: new TextStyle(fontSize: 11.0, fontWeight: FontWeight.bold),
),
alignment: Alignment.bottomRight,
),
]
)
)
]
)
)
)
);
}
);
}
This is how I'm running through each value I'm getting from the API:
void _addMovie(dynamic movie){
this._movies.add(new Movie(
title: movie["title"],
overview: movie["overview"],
poster: movie["poster_path"],
releaseDate: movie["release_date"]
));
}
#override
void initState() {
super.initState();
http.get(this._apiGatewayURL)
.then((response) => response.body)
.then(json.decode)
.then((movies) {
movies.forEach(_addMovie);
});
}
You are not specifying an itemCount to ListView. So if you ever have enough space to display more then the number of items available, you'll try to access _movies at an invalid index. Resulting in this error.
Try to add
new ListView(
...
itemCount: _movie.length
you need to add item count
new ListView(
...
itemCount: _movie.length
without it ListView.Builder not know the position of list and and we are not showing the list according to position
Try to add the index of listview.builder,
Listview.builder is used for a very long or unknown set of lists, which you are doing correct.
Just count the number of values you are getting from the API response and add the index as a parameter.
For more reference, check this URL:
https://docs.flutter.io/flutter/widgets/ListView/ListView.builder.html

Dart / Flutter: building widget from data obtained using async method

I want to use a LietView.build populated by Widgets that obtain the data from an async method, before the widget is built. Here is my function that collects the data from a website:
fetchBasicData(String URL) async {
final response = await http.get(URL);
var document = parse(response.body);
var result = new List<dom.Node>();
result = document.getElementsByClassName('datas-nev');
var dogName = result[0].nodes[1].toString();
result = document.getElementsByClassName('datas-tipus');
var dogBreed = result[0].nodes[1].toString();
result = document.getElementsByClassName('datas-nem');
var dogGender = result[0].nodes[1].toString();
result = document.getElementsByClassName('datas-szin');
var dogColor = result[0].nodes[1].toString();
result = document.getElementsByClassName('datas-kor');
var dogAge = result[0].nodes[1].toString();
result = document.getElementsByClassName('pirobox_gall');
String imageLink;
imageLink = urlPrefix + result[0].nodes[0].attributes.values.first;
return new Dog.basic(
URL,
dogName,
dogBreed,
dogGender,
dogColor,
dogAge,
imageLink);
}
The function is executed and gathers the data, but the widget building fails with type '_Future' is not a subtype of type 'Widget' of 'child' where
Here is the function that is supposed to build the widget:
buildBasicWidget(String URL) async {
Dog myDog = await fetchBasicData(URL);
return new SizedBox(
width: 500.0,
height: 400.0,
child: new Card(
child: new Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
//Header image row
new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Expanded(
child: new FutureBuilder(
future: fetchPortrait(myDog.imageLink),
builder: (context, snapshot) {
if (snapshot.hasData) {
return new Image.network(
snapshot.data,
fit: BoxFit.scaleDown,
);
} else {
if (snapshot.hasError) {
return new Text('Hiba');
}
}
return new Center(
child: new CircularProgressIndicator(),
);
}))
],
), //Header image row
new Row(
children: <Widget>[
new Expanded(
child: new Text(
dogName,
style: new TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0,
color: Colors.black),
))
],
),
new Row(
children: <Widget>[
new Expanded(
child: new Text(myDog.dogColor +
'színű, ' +
myDog.dogBreed +
' ' +
myDog.dogGender))
],
),
new Row(
children: <Widget>[
new Expanded(child: new Text('Kora: kb. ' + myDog.dogAge))
],
)
],
),
),
);
}
I tried making this function async as well, and making it wait for the fetchBasicDetails() to finish, so the data is present when it would use it.
I even tried using dynamic fetchBasicData(String URL) async {...} but that didn't help either.
Using Future<Dog> instead of dynamic also causes errors.
How could I make the buildBasicWidget use the fetchBasicData result? Or should I just handle the widget building in the fetchBasicData as a simple workaround?
You need to use a FutureBuilder and add your async function in the future argument otherwise the build method gets called before the data are obtained.
Alternatively do your async request inside initState.

Resources