How to passing new items on top List view - json

I am making an app and it depends to give data from API, so I have List view to show new news to the top list view the first when build List view it was showing new items to the end list view so I do Reverse :true after that it is showing new items to the top but when open page show me the end list ....
How can I Pass new items to the top list view and when open aloes show me the top list view like the list view news
This my function get data if you need
Future<List<Ravs>> fetchRav() async {
String token = await read();
final String url= 'listravs';
String FullURL = Serveurl+url;
var response =await http.post(FullURL,
headers: {HttpHeaders.contentTypeHeader: "application/json", HttpHeaders.authorizationHeader: "Bearer $token"});
print('Token : ${token}');
print(response);
if (response.statusCode==200){
final items =jsonDecode(response.body).cast<Map<String,dynamic>>();
List<Ravs> listrav =items.map<Ravs>((json){
return Ravs.fromjson(json);
}).toList();
return listrav;
}
else{
throw Exception('Failed to load data from Server.');
}
}
This my list
return Directionality(
textDirection: TextDirection.rtl,
child:Scaffold(
body:ListView(
reverse: true,
children: snapshot.data
.map((data) =>
Card(
child: InkWell(
onTap:(){
Navigator.push(context, MaterialPageRoute (builder:(context)=>Showpage(id:data.id.toString()),
),
);
},
child: Column(
children: <Widget>[
new Container(
padding: const EdgeInsets.only(top: 10),
width: 200,
alignment: Alignment.topLeft,
child: Image.network(data.image)
),
new Container(
padding: const EdgeInsets.all(10.0),
alignment: Alignment.topRight,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(data.nameravs,textDirection: TextDirection.rtl, style: Theme.of(context).textTheme.title),
Text(data.date_from,textAlign: TextAlign.right, style: TextStyle(color: Colors.black.withOpacity(0.5))),
Text(data.detalis , textAlign: TextAlign.right,),
],
)
)
],
),
),
)
)
.toList(),
)
)
);}, );

Since you are fetching news, I would recommend using ListView.builder. It is a more efficient way in showing a list of objects compared to ListView. The example below is how you can use it to display your data. Align is used to place your list items on top instead of in the bottom because setting the ListView.builder's reverse to true will make the list start at the bottom as well as position that whole list at the bottom.
Align(
alignment: Alignment.topCenter,
child: ListView.builder(
reverse:true,
itemCount: news.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('${news[index]}'),
);
},
);
)
Here is the official docs on ListView.builder(): https://flutter.dev/docs/cookbook/lists/long-lists
Here is a great tutorial that can help you get started with ListView.builder(): https://www.youtube.com/watch?v=TdWhYERuv7g

Related

Expected a value of type 'int', but got one of type 'String' in Flutter

I've been working on a quotes app that fetches data from a rest api, and displays each quote at a time randomly in the center of the screen with a press of a button. But can't quite get it right
I have made a method which fetches the json data, which is fetchQuotesData(), and it stores the unprocessed json in QuotesData. This is later converted into a list as QuotesList.
class _MyAppState extends State<MyApp> {
List QuotesList = [];
var _data;
var c;
final url = "https://type.fit/api/quotes";
fetchQuoteData() async {
Response response = await get(Uri.parse(url));
final QuotesData = jsonDecode(response.body);
setState(() {
QuotesList = QuotesData;
});
}
#override
void initState() {
fetchQuoteData();
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/pic/image4.jpg'),
fit: BoxFit.cover,
colorFilter: ColorFilter.mode(
Colors.black.withOpacity(0.6), BlendMode.darken)
),
),
child: Scaffold(
backgroundColor: Colors.transparent,
body: Center(
// Use future builder and DefaultAssetBundle to load the local JSON file
child: FutureBuilder(
future: fetchQuoteData(),
builder: (context, snapshot) {
_data = snapshot.data.toString();
var range = new Random();
c = range.nextInt(_data.length);
return Ui_Card(c);
},
),
),
bottomNavigationBar: BottomAppBar(
color: Colors.indigo.shade900,
child: Container(
margin: const EdgeInsets.only(left: 40.0,right: 40.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
IconButton(
tooltip:'Random Quotes',
icon: Icon(Icons.format_quote_outlined) ,
iconSize: 40,
color: Colors.white,
onPressed: (){
HapticFeedback.heavyImpact();
setState(() {
});
},
),
],
),
),
),
),
);
}
Widget Ui_Card(index){
return new Container(
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(25.0),
child: Text(_data[c]['text'],
style: TextStyle(
fontWeight: FontWeight.w300,
fontSize: 22.0,
color: Colors.white,
fontFamily: 'Raleway-Italic',
fontStyle: FontStyle.italic,),
textScaleFactor: 2.0,)
),
Text(_data[c]['author'],
style:TextStyle(
fontWeight: FontWeight.w500,
color: Colors.white,
fontFamily: 'Raleway-Bold',
fontSize: 18.0
),
textAlign: TextAlign.left,
),
],
),
),
);
}
}
I am suspecting some errors in the builder, or snapshot data, but not sure where I'm stuck
As the comment on your question mentions, Dart is a strongly typed language. When you use 'var' you're relying on type inference to figure out the type you want for the variable. I've only been using Dart for about a year, but in my experience so far I try to never use 'var' because it can result in harder-to-debug error messages. Also if you set your variable types the linter seems to be better at picking up type mismatches.
var _data;
...
_data = snapshot.data.toString();
Above you set _data equal to a String.
child: Text(_data[c]['text'],
Here you are trying to access it as something else - maybe a List<Map<String,String>> or a Map<int, Map<String,String>>
My hunch is your error message is coming from ['text']. Maybe _data's inferred type can take a two-dimensional int index. The characters of a Dart string can be accessed with an int index - i.e. string[0] is the first character, but it returns an int, and int isn't an indexed type AFAIK, so I don't know what Dart is doing with your second index dimension that wants an int. I suspect if you change it to an int - i.e. _data[0][0] you'll get a different error message.
Try defining _data as the type you want it to be, then see if the linter shows the error in your source or you get a more descriptive error message.

How to keep precedent widgets after setState

I want to create a dynamic listview of cards I create with a title and a content. I'm decoding what I receive from the two textfields of the adding page but then when a card is created, it doesn't work well. For the first card, it works perfectly, but when a second one is created, the two cards are the same. I don't really know how to keep my precedent cards while adding new one. If you have any ideas or tips please tell me. Thank you in advance! Here's my code :
class MyPage extends StatefulWidget {
#override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
static List dreams = [];
static List contentCard = [];
String text;
String title;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: kEdgeColor,
appBar: AppBar(
backgroundColor: kEdgeColor,
elevation: 0,
title: Text('My dreams'),
),
body: Container(
decoration: BoxDecoration(
color: Colors.black),
child: ListView.builder(
itemCount: dreams.length ,
itemBuilder: (BuildContext ctxt, int index) {
return new DreamCard(
Content: text,
title: title,
);
}
)
),
bottomNavigationBar: BottomAppBar(
color: kEdgeColor,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
FlatButton(
onPressed: (){
Navigator.popAndPushNamed(context, '/public');
},
child: Icon(Icons.public),
color: Colors.black,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
),
FlatButton(
onPressed: (){
Navigator.popAndPushNamed(context, '/');
},
child: Icon(Icons.bedtime),
color: Colors.black,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
),
],
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: FloatingActionButton(
onPressed: () async {
final result = await Navigator.of(context).push(MaterialPageRoute(builder: (context){return WritingPage();}));
if (null != result) {
dreams.add(result);
Map<String, dynamic> resultat = jsonDecode(result);
setState(() {text = resultat['text'];
title = resultat['title'];});
}
},
child: Icon(Icons.add, size: 30,color: Colors.black,),
backgroundColor: Colors.white,
),
);
}
}
cause you're only update the title and text when press, and your DreamCard has the same title and text, since you'r not reference items in dreams list by index. so your DreamCard in list always look the same.
if (null != result) {
Map<String, dynamic> resultat = jsonDecode(result);
// use setState to update your dreams list to trigger UI change
setState(() {
dreams.add(resultat);
});
}
ListView.builder(
itemCount: dreams.length ,
itemBuilder: (BuildContext ctxt, int index) {
// access item from dreams list by index
return new DreamCard(
Content: dreams[index]['text'],
title: dreams[index]['title],
);
}
)
Your itemBuilder is not referencing index in:
child: ListView.builder(
itemCount: dreams.length ,
itemBuilder: (BuildContext ctxt, int index) {
return new DreamCard(
Content: text,
title: title,
);
}
so of course all cards will always has the same text and title as whatever is in scope. You need to put the data in dreams and reference dreams[index][?something?] to get the two values appropriate for this card.
You cannot see the correct text for the next DreamCard added because you are adding the result received from WritingPage page directly to the dreams list. You are then JSON decoding result and setting the values of variables text and title. These values of the latest WritingPage are sent to DreamCard therefore every DreamCard appears to be the same. Instead you should be JSON decoding the dreams list and using the text and title from dreams list. Please make the following changes.
return new DreamCard(
Content: jsonDecode(dreams[index])["text"],
title: jsonDecode(dreams[index])["title"],
);

Flutter: exclude one object from scrolling within a listview

I have a listview of objects that contains basically a form with textfields, a google maps object and then another form of checkboxes.
What I would like to do is have the google maps object not part of the scroll physics of the listview as a whole, because then you cannot actually move the map around at all.
Some code:
Constructor inside the build widget:
Flexible(
child: StreamBuilder(
stream: Firestore.instance.collection('users').document(uid).snapshots(),
builder: (BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
physics: ScrollPhysics(),
shrinkWrap: true,
itemCount: 1,
itemBuilder: (context, index) =>
_buildListRows(context, snapshot.data),
);
} else {
return LoadingAnimation();
}
},
),
),
The _buildListRows Widget, simplified:
Widget _buildListRows(BuildContext context, DocumentSnapshot document) {
return Container(
child: Form(
child: Column(
children: <Widget>[
Container(
child: TextFormField(),
),
Container(
child: SizedBox(
child: GoogleMap(),
),
),
Expanded(
Container(
ListView(
physics: ScrollPhysics(),
children: keys.map(key) {
return Checkbox(value: key);
}
),
),
],
),
),
);
}
So a quick recap, I need to be able to scroll the objects above and below the map, to move the whole screen up and down, but on the map itself I need to be able to control the map, so that touch actions apply to map functionality instead.
I have read a lot about this and tried some of the suggestions like having a singlechildscrollview and then listviews underneath that parent but it seems anything else I try just completely breaks the app and nothing will display at all.

Flutter: TypeError when I try to access elements of JSON data

I am trying to display individual elements of a JSON data on a dynamic listview. However, I keep getting "type 'int' is not a subtype of type 'String'" error and I have no idea why.
The code works if I include just the left() function in the widget located under the Row in the buildFlightsColumn function. But once I include the middle() and right() functions, I get the error.
Widget buildListView() {
print(data);
return ListView.builder(
itemCount: data == null ? 0 : data.length,
itemBuilder: (context, index) {
return buildFlightsColumn(data[index]);
}
);
}
Widget buildFlightsColumn(dynamic item) => Container(
height: 150.0,
decoration: BoxDecoration(
),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
left(item['PlaceId']),
middle(item['IataCode']),
right()
],
),
);
Container left(dynamic item) {
return new Container (
child: Text(
item,
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 25.0,
color: Colors.red,
)
),
);
}
Container middle(dynamic item) {
return new Container(
child: Text(
item,
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 25.0,
color: Colors.red,
)
),
);
}
Container right() {
return new Container(
child: RaisedButton(
onPressed: () {
},
child: Text('Book Flights'),
)
);
}
The data passed into the buildFlightsColumn function is JSON data returned by the API request:
[{PlaceId: 65368, IataCode: LAX, Name: Los Angeles International, Type: Station, SkyscannerCode: LAX, CityName: Los Angeles, CityId: LAXA, CountryName: United States}, {PlaceId: 81727, IataCode: SFO, Name: San Francisco International, Type: Station, SkyscannerCode: SFO, CityName: San Francisco, CityId: SFOA, CountryName: United States}]
Text widgets cannot display int s, they can only interpet strings , so your error is coming from this code
Widget buildFlightsColumn(dynamic item) => Container(
height: 150.0,
decoration: BoxDecoration(
),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
left(item['PlaceId']) // item['placeId'] is int,
middle(item['IataCode']),
right()
],
),
);
Container left(dynamic item) {
return new Container (
child: Text(
item, // here item is int, which is not allowed <-----------------------
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 25.0,
color: Colors.red,
)
),
);
}
you can change it number to string using the .toString() method, or string interpolation
Container left(dynamic item) {
return new Container (
child: Text(
item.toString(), // here item is String <-----------------------
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 25.0,
color: Colors.red,
)
),
);
}
I think the problem is that PlaceId is of type int but you try to use it as a String.
Change your code like this:
Container left(dynamic item) {
return new Container (
child: Text(
item.toString(), //change the type here
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 25.0,
color: Colors.red,
)
),
);
}
Or like this:
Widget buildFlightsColumn(dynamic item) => Container(
height: 150.0,
decoration: BoxDecoration(
),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
left(item['PlaceId'].toString()) // convert item['placeId'] to String
middle(item['IataCode']),
right()
],
),
);
Coming to your question, the Flutter Text widget accepts only the String data type.
Try using x.toString() to convert int to String or use "$x" or "${x}" in Text widget.
To be honest, it is actually a bad practice to use JSON objects as it is in the flutter code. You should consider doing serialization & deserialization as it increases robustness. In simple terms, deserialization means converting your JSON into class object and serialization is the reverse. Here is an example from flutter docs.
class User {
final String name;
final String email;
User(this.name, this.email);
User.fromJson(Map<String, dynamic> json)
: name = json['name'],
email = json['email'];
Map<String, dynamic> toJson() =>
{
'name': name,
'email': email,
};
}
It is strongly recommended to use this technique to have more control over data and their data-types.
Map userMap = jsonDecode(jsonString);
var user = User.fromJson(userMap); // convert json to obj
print('Howdy, ${user.name}!');
print('We sent the verification link to ${user.email}.');
String json = jsonEncode(user); // convert obj to json
More on this topic: https://flutter.dev/docs/development/data-and-backend/json

How to hook up data from local json to achieve search with autocomplete text in list?

I am trying to implement input search feature wherein typing a search text will display suggested text and user can select relevant text from list and hit search button to proceed to corresponding screen. The suggested text is in local json and I added it under under assets/ folder and in pubspec.yaml.
The search textfield is:
The code for above is:
new TextField(
style: new TextStyle(
color: Colors.white,
fontSize: 16.0),
cursorColor: Colors.green,
decoration: new InputDecoration(
suffixIcon: Container(
width: 85.0,
height: 60.0,
color: Colors.green,
child: new IconButton(
icon: new Image.asset('assets/search_icon_ivory.png',color: Colors.white, height: 18.0,),
onPressed: () {
},
),
),
fillColor: Colors.black,
contentPadding: new EdgeInsets.fromLTRB(10.0, 30.0, 10.0, 20.0),
filled: true,
hintText: 'What Do You Need Help With?',
hintStyle: new TextStyle(
color: Colors.white
)
)
)
The local json data sample is:
I want to achieve above using autocomplete_textfield package which I've installed and imported and referring this example.
I would like to know how to get started with this and integrate parsing from local json, hook that data using autocomplete_textfield package to achieve my goal. I haven't done parsing json in flutter yet so looking for guidance on how to do that.
The end result I am looking for is like this:
***************** Edit **************
I am now able to parse data from local json and display it in a listView using a demo app. For it, I created a new model class `services.dart' as below:
class Categories {
String serviceCategory;
String servCategoryDesc;
int id;
String autocompleteterm;
String category;
String desc;
Categories({
this.serviceCategory,
this.servCategoryDesc,
this.id,
this.autocompleteterm,
this.category,
this.desc
});
factory Categories.fromJson(Map<String, dynamic> parsedJson) {
return Categories(
serviceCategory:parsedJson['serviceCategory'] as String,
servCategoryDesc: parsedJson['serviceCategoryDesc'] as String,
id: parsedJson['serviceCategoryId'],
autocompleteterm: parsedJson['autocompleteTerm'] as String,
category: parsedJson['category'] as String,
desc: parsedJson['description'] as String
);
}
}
Used builder function to retrieve and display value in listview as below:
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Load local JSON file"),
),
body: new Container(
child: new Center(
// Use future builder and DefaultAssetBundle to load the local JSON file
child: new FutureBuilder(
future: DefaultAssetBundle
.of(context)
.loadString('assets/services.json'),
builder: (context, snapshot) {
// Decode the JSON
Map data = json.decode(snapshot.data
.toString());
print(data);
final List<Categories> items = (data['data'] as List).map((i) => new Categories.fromJson(i)).toList();
for (final item in items) {
print(item.category);
return new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new Card(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new Text('Service Category: ' + items[index].category),
new Text('Category' + items[index].categoryDesc),
new Text('Auto complete term' + items[index].autocompleteterm),
new Text('Desc' + items[index].desc)
],
),
);
},
);
}
}
)
)
)
);
}
}
In my target app, added required code that uses autocomplete_textfield package that shows a static list of suggestions as of now :
#override
Widget build(BuildContext context) {
textField = new AutoCompleteTextField<String>
(style: new TextStyle(
color: Colors.white,
fontSize: 16.0),
decoration: new InputDecoration(
suffixIcon: Container(
width: 85.0,
height: 60.0,
color: Colors.green,
child: new IconButton(
icon: new Image.asset('assets/search_icon_ivory.png',color: Colors.white,
height: 18.0,),
onPressed: (){},
),
),
fillColor: Colors.black,
contentPadding: new EdgeInsets.fromLTRB(10.0, 30.0, 10.0, 20.0),
filled: true,
hintText: 'What Do You Need Help With ?',
hintStyle: new TextStyle(
color: Colors.white
)
),
submitOnSuggestionTap: true,
clearOnSubmit: true,
textChanged: (item){
currentText = item;
},
textSubmitted: (item) {
setState(() {
currentText = item;
});
},
key: key,
suggestions: suggestions,
itemBuilder: (context, item) {
return new Padding(
padding: EdgeInsets.all(8.0), child: new Text(item));
},
itemSorter: (a, b) {
return a.compareTo(b);
},
itemFilter: (item, query) {
return item.toLowerCase().startsWith(query.toLowerCase());
});
Column body = new Column(children: [
new GestureDetector(
child: new ListTile(
title: textField,
onTap: () {
setState(() {
if (currentText != "") {
added.add(currentText);
textField.clear();
currentText = "";
}
});
}
)
)
]
);
body.children.addAll(added.map((item) {
return new ListTile(
title: new Text(item)
);
}));
return Scaffold(
resizeToAvoidBottomPadding: false,
backgroundColor: Color(0xFF13212C),
appBar: AppBar(
title: Text(''),
),
drawer: appDrawer(),
body: new Center(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new Column(
children: <Widget>[
textField
Above code shows in UI as below:
I now would like to know how to hook the builder function retrieving json data in my target app, so that instead of static list of strings, the dropdown would show suggestions from json (as posted in my original question's screenshot).
If you found doing this manually it too much, this is actually a flutter package that does this. There are two examples on the package site too.
Do be warned, this is currently a bug in the package (I have raised a PR to fix it but the package owner has been too busy to review any PR recently). Depending on how you use it, the bug may not affect you.