Flutter dropdownbutton evaluting value from json - json

```{"Piece": "[{\"sno\":7,\"valueName\":\"3\",\"value\":\"200\"},{\"sno\":7,\"valueName\":\"3\",\"value\":\"200\"},{\"sno\":7,\"valueName\":\"3\",\"value\":\"200\"}]",
"Caret": "[{\"sno\":1,\"valueName\":\"18k\",\"value\":\"1000\"},{\"sno\":1,\"valueName\":\"18k\",\"value\":\"1000\"},{\"sno\":1,\"valueName\":\"18k\",\"value\":\"1000\"},{\"sno\":1,\"valueName\":\"18k\",\"value\":\"1000\"}]"
}```
I have a json response from server like above. and I want to create dropdownbuttons dynamically in a way that how many keys are available like 'Caret' and 'piece' in json, it will create the same no of dropdown and each key has its own list and I want to deploy that list inside the drop down. and when I select any valueName like '18k','22k', it will return value like '1000','2000'.
please help me.
I tried...
Container(
padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 0.0),
child: ListView.builder(
itemCount: data2.keys.length,
primary: false,
shrinkWrap: true,
itemBuilder: (context, i) {
List valueList;
List attributeNameList= List();
data2.forEach((key, value) {attributeNameList.add(key);valueList=jsonDecode(value);});
return DropdownButton<dynamic>(
items: valueList.map((map) {
return DropdownMenuItem(
child: Text(map['valueName']),
value: map['value'],
);
}).toList(),
onChanged: (value) {
print(value);
},
);
},
),
),```

Related

Flutter: How to display all columns in the same table

I am working on a test application in Dart. The application is used for displaying and inserting data into a database hosted with 000webhost.com
I am displaying my json data into a table in my app. I would like all the columns to be inside of one table. With my current code it is displaying every column inside of a brand new table
like shown here:
Below is the relevant code for my project:
class ViewData extends StatelessWidget{
final String url = 'https://fourieristic-thousa.000webhostapp.com/index.code.php?action=view';
Future<List<dynamic>> fetchData() async {
var result = await http.get(
Uri.parse(url),
);
print(json.decode(result.body));
return json.decode(result.body);
}
// First entry of each column.
String _test(dynamic test, int index){
return test[index]['testColumn'];
}
// Second entry of each column.
int _test2(dynamic test, int index){
return json.decode(test[index]['testColumn2']);
}
// Third entry of each column (Will some day be a delete function)
int _id(dynamic test, int index){
return json.decode(test[index]['ID']);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Test data table'),
),
body: Container(
child: FutureBuilder<List<dynamic>>(
future: fetchData(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if(snapshot.hasData){
return ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
columns: const <DataColumn>[
DataColumn(
label: Text(
'Test',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Test2',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Delete',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
],
rows: <DataRow>[
DataRow(
cells: <DataCell>[
DataCell(
Text(_test(snapshot.data, index).toString())
),
DataCell(
Text(_test2(snapshot.data, index).toString())
),
DataCell(
Text(_id(snapshot.data, index).toString())
)
],
),
],
),
);
}
);
} else {
return Center(child: CircularProgressIndicator());
}
},
),
),
);
}
}
I have tried looking for an answer online with no result. I have also tried rewriting my code to attempt to find anything wrong with it. I understand why the app is showing the data in individual tables, but I can not find a way to fix it.
You claim to know what the problem is so I won't go in depth, but the main issue is your ListView.builder; there is no reason to have it there. Instead, you should replace the code between if(snapshot.hasData){ and return SingleChildScrollView( with something like this:
List<DataRow> dataRows = [];
for (var index = 0; index < snapshot.data.length; index++) {
dataRows.add(
DataRow(
cells: <DataCell>[
DataCell(
Text(_test(snapshot.data, index).toString())
),
DataCell(
Text(_test2(snapshot.data, index).toString())
),
DataCell(
Text(_id(snapshot.data, index).toString())
),
],
),
);
}
and then put the dataRows variable in the rows: field of your DataTable. Your tree should end up being Scaffold -> Container -> FutureBuilder -> SingleChildScrollView -> DataTable. This way you won't be building a new table for every entry.

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"],
);

How to passing new items on top List view

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

RangeError (index): Invalid value: Not in range 0..7, inclusive: 8

I'm trying to display all the values from retrieved from an API request.
Here's what it's showing at the moment:
I'm using a listview builder for the page. The JSON can be retrieved from this link: Here
This is my entire code for the page: Code
The error is in your ListViewBuilder:
ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: _ListFamilyPageState.data.body.family.length,
itemBuilder: (context, index) {
return Container(
height: 74.0,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topRight: const Radius.circular(20.0),
bottomRight: const Radius.circular(20.0)),
),
width: MediaQuery.of(context).size.width - 50.0,
child: Center(
child: Text(
data.body.friends[index].id.toString(),
style:
TextStyle(fontSize: 24.0),
)));
},
)
You specified that there are family.length items (in your data: 15),
but you are pulling actual data from friends[index] (8 items in your data).
This gives you RangeError when rendering item at index 8.
On top of that: you use static data in your state:
class _ListFamilyPageState extends State<ListFamily> {
static Relations data;
// ...
}
Don't do that.
You've set the itemCount attribute of the ListView with _ListFamilyPageState.data.body.family.length and you've use the index of it's builder with another list data.body.friends[index].id.toString()
I don't think both have the same number of elements

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.