Flutter how to save list data locally - json

I am building a to-do list app and I would like to store the data locally such that every time I open the app, I get all the tasks that I had created previously. I am new to flutter and this is my first app. I already tried saving the data to a file, creating a JSON file to save the data and tried using a database. Nothing seems to work. Can someone help me with this?
This is my code: -
import 'package:flutter/material.dart';
class toDoList extends StatefulWidget
{
bool data = false;
#override
createState()
{
return new toDoListState();
}
}
class toDoListState extends State<toDoList>
{
List<String> tasks = [];
List<bool> completedTasks = [];
List<String> descriptions = [];
List<bool> importance = [];
#override
Widget build(BuildContext context)
{
return new Scaffold
(
body: buildToDoList(),
floatingActionButton: new FloatingActionButton
(
onPressed: addToDoItemScreen,
tooltip: 'Add Task',
child: new Icon(Icons.add),
),
);
}
Widget buildToDoList()
{
return new ListView.builder
(
itemBuilder: (context, index)
{
if(index < tasks.length)
{
if(tasks[index] == "#45jiodg{}}{OHU&IEB")
{
tasks.removeAt(index);
descriptions.removeAt(index);
importance.removeAt(index);
}
return row(tasks[index], descriptions[index], index);
};
},
);
}
Widget row(String task, String description, int index)
{
return Dismissible(
key: UniqueKey(),
background: Container(color: Colors.red, child: Align(alignment: Alignment.center, child: Text('DELETE', textAlign: TextAlign.center, style: TextStyle(color: Colors.white, fontSize: 18),))),
direction: DismissDirection.horizontal,
onDismissed: (direction) {
setState(() {
tasks.removeAt(index);
if(completedTasks[index])
{
completedTasks.removeAt(index);
}
descriptions.removeAt(index);
importance.removeAt(index);
});
Scaffold.of(context).showSnackBar(SnackBar(content: Text(task+" dismissed")));
},
child: CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
title: Text(task, style: (completedTasks[index]) ? TextStyle(decoration: TextDecoration.lineThrough) : TextStyle(),),
subtitle: Text(descriptions[index], style: (completedTasks[index]) ? TextStyle(decoration: TextDecoration.lineThrough) : TextStyle(),),
isThreeLine: true,
secondary: (importance[index])? Icon(Icons.error, color: Colors.red,) : Text(''),
value: completedTasks[index],
onChanged: (bool value) {
setState(() {
if(completedTasks[index])
{
completedTasks[index] = false;
}
else
{
completedTasks[index] = true;
}
});
},
));
}
void addToDoItemScreen() {
int index = tasks.length;
while (importance.length > tasks.length) {
importance.removeLast();
}
importance.add(false);
tasks.add("#45jiodg{}}{OHU&IEB");
descriptions.add("No Description");
completedTasks.add(false);
Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
return StatefulBuilder(builder: (context, setState) { // this is new
return new Scaffold(
appBar: new AppBar(title: new Text('Add a new task')),
body: Form(
child: Column(
children: <Widget>[
TextField(
autofocus: true,
onSubmitted: (name) {
addToDoItem(name);
//Navigator.pop(context); // Close the add todo screen
},
decoration: new InputDecoration(
hintText: 'Enter something to do...',
contentPadding: const EdgeInsets.all(20.0),
border: OutlineInputBorder()),
),
TextField(
//autofocus: true,
//enabled: descriptions.length > desc,
onSubmitted: (val) {
addDescription(val, index);
},
decoration: new InputDecoration(
hintText: 'Enter a task decription...',
contentPadding: const EdgeInsets.all(20.0),
border: OutlineInputBorder()),
),
Row(
children: <Widget> [
Switch(
value: importance[index],
onChanged: (val) {
setState(() {
});
impTask(index);
},
),
Text('Important Task', style: TextStyle(fontSize: 18)),
],
),
RaisedButton(onPressed: () { Navigator.pop(context); }, child: Text('DONE', style: TextStyle(fontSize: 20)),)
],
),
));
});
}));
}
void addToDoItem(String task)
{
setState(() {
tasks.last = task;
});
}
void addDescription(String desc, int index)
{
setState(() {
descriptions.last = desc;
});
}
void impTask(int index)
{
setState(() {
if(importance[index])
{
importance[index] = false;
}
else
{
importance[index] = true;
}
});
}
}
I have 4 lists with the data. I need a simple way to save the lists such that the next time I open the app, the lists retain the data that was saved in them, the last time I had closed the app.

To do this you'll certainly have to use the path_provider package with this tutorial on the flutter.dev website. You should then be able to register a file and read it at the start of your application.
Once you have imported the path_provider and the dart:io packages, you can do something like this :
final directory = await getApplicationDocumentsDirectory();
final File file = File('${directory.path}/jsonObjects.json');
if (await file.exists()) {
json = await file.readAsString();
} else {
file.writeAsString(json);
}
First you get the application document directory ( the path ), then you create a File with the right path. Then if the file already exist, you read it, else you create it with the json you got and you should be good to go !

Related

Error in searching data using listview in flutter

I've tried this code for searching jobs in listview but the data is not shown in listview. I think JSON is not parsing properly for Jobs data.
Here is the code of the model:
import 'package:flutter/material.dart';
class JobItem {
final String title;
final String description;
JobItem(
{
required this.title,
required this.description,
});
factory JobItem.fromJson(Map<String, dynamic> json) {
return JobItem(
title: json['title'] as String,
description: json['description'] as String,
);
}
}
Here I've written code for the main file to search data from the listview.
List<JobItem> users = [];
List<JobItem> filteredUsers = [];
static String url = 'https://hospitality92.com/api/jobsbycategory/All';
static Future<List<JobItem>> getJobsData() async {
try {
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
List<JobItem> list = parseAgents(response.body);
return list;
} else {
throw Exception('Error');
}
} catch (e) {
throw Exception(e.toString());
}
}
static List<JobItem> parseAgents(String responseBody) {
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<JobItem>((json) => JobItem.fromJson(json)).toList();
}
#override
void initState() {
super.initState();
getJobsData().then((usersFromServer) {
setState(() {
users = usersFromServer;
filteredUsers = users;
});
});
}```
Try below code your problem has been solved :
//declare packages
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:http/http.dart' as http;
class Jobs extends StatefulWidget {
Jobs() : super();
#override
JobsState createState() => JobsState();
}
class Debouncer {
final int milliseconds;
VoidCallback action;
Timer _timer;
Debouncer({this.milliseconds});
run(VoidCallback action) {
if (null != _timer) {
_timer.cancel();
}
_timer = Timer(Duration(milliseconds: milliseconds), action);
}
}
class JobsState extends State<Jobs> {
final _debouncer = Debouncer(milliseconds: 500);
List<Subject> subjects = [];
List<Subject> filteredSubjects = [];
//API call for All Subject List
static String url = 'https://hospitality92.com/api/jobsbycategory/All';
static Future<List<Subject>> getAllSubjectsList() async {
try {
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
print(response.body);
List<Subject> list = parseAgents(response.body);
return list;
} else {
throw Exception('Error');
}
} catch (e) {
throw Exception(e.toString());
}
}
static List<Subject> parseAgents(String responseBody) {
final parsed =
json.decode(responseBody)['jobs'].cast<Map<String, dynamic>>();
return parsed.map<Subject>((json) => Subject.fromJson(json)).toList();
}
#override
void initState() {
super.initState();
getAllSubjectsList().then((subjectFromServer) {
setState(() {
subjects = subjectFromServer;
filteredSubjects = subjects;
});
});
}
//Main Widget
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'All Subjects',
style: TextStyle(fontSize: 25),
),
),
body: Column(
children: <Widget>[
//Search Bar to List of typed Subject
Container(
padding: EdgeInsets.all(15),
child: TextField(
textInputAction: TextInputAction.search,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(25.0),
borderSide: BorderSide(
color: Colors.grey,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(20.0),
borderSide: BorderSide(
color: Colors.blue,
),
),
suffixIcon: InkWell(
child: Icon(Icons.search),
),
contentPadding: EdgeInsets.all(15.0),
hintText: 'Search ',
),
onChanged: (string) {
_debouncer.run(() {
setState(() {
filteredSubjects = subjects
.where((u) => (u.title
.toLowerCase()
.contains(string.toLowerCase())))
.toList();
});
});
},
),
),
//Lists of Subjects
Expanded(
child: ListView.builder(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
padding: EdgeInsets.only(top: 20, left: 20, right: 20),
itemCount: filteredSubjects.length,
itemBuilder: (BuildContext context, int index) {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
side: BorderSide(
color: Colors.grey[300],
),
),
child: Padding(
padding: EdgeInsets.all(5.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ListTile(
leading: Text(
filteredSubjects[index].skills,
),
title: Text(
filteredSubjects[index].title,
style: TextStyle(fontSize: 16),
),
trailing: Text(filteredSubjects[index].position.toString()),
)
],
),
),
);
},
),
),
],
),
);
}
}
//Declare Subject class for json data or parameters of json string/data
//Class For Subject
class Subject {
String title;
int id;
String skills;
String position;
Subject({
this.id,
this.title,
this.skills,
this.position,
});
factory Subject.fromJson(Map<String, dynamic> json) {
return Subject(
title: json['title'] as String,
id: json['id'],
skills: json['skills'],
position: json['positions']);
}
}
Your screen before search:
Your Screen after search :
U have a little mistake in parsing. I added response.body['jobs'].
static Future<List<JobItem>> getJobsData() async {
try {
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
List<JobItem> list = parseAgents(Map<String, dynamic>.from(jsonDecode(response.body))['jobs']);
return list;
} else {
throw Exception('Error');
}
} catch (e) {
throw Exception(e.toString());
} }

RSS / XML parsing

Am stuck trying to parse and display rss feed data. How can I easily get these links to all work? What library should be used. Thanks!
Currently using https://pub.dev/packages/webfeed
DART /Flutter mobile application
Cannot seem to parse and render links below:
https://academic.oup.com/rss/site_5267/3133.xml (Returns 405)
https://journals.lww.com/co-anesthesiology/_layouts/15/OAKS.Journals/feed.aspx?FeedType=MostPopularArticles (won't load or import xml)
Have this link working ok:
http://anesthesiology.pubs.asahq.org/rss/site_157/173.xml
import 'dart:io';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:wcm_anes/common/circularIndicator.dart';
import 'package:wcm_anes/customIcons/custom_icons_icons.dart';
import 'package:wcm_anes/styles/colorAsset.dart';
import 'package:wcm_anes/styles/stringAssets.dart';
import 'package:wcm_anes/styles/textStyles.dart';
import 'package:webfeed/webfeed.dart';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
class RssUrlListScreen extends StatefulWidget {
String screenName;
String feedUrl;
String categoryId;
RssUrlListScreen({this.screenName,this.feedUrl,this.categoryId});
#override
_RssUrlListScreenState createState() => _RssUrlListScreenState();
}
class _RssUrlListScreenState extends State<RssUrlListScreen> {
static const String loadingMessage = 'Loading Feed...';
static const String feedLoadErrorMessage = 'Error Loading Feed.';
static const String feedOpenErrorMessage = 'Error Opening Feed.';
String _title;
RssFeed feed;
bool isLoading = false;
updateTitle(title) {
setState(() {
_title = title;
});
}
updateFeed(feed) {
setState(() {
feed = feed;
});
}
Future<void> openFeed(String url) async {
if (await canLaunch(url)) {
await launch(
url,
forceSafariVC: true,
forceWebView: false,
);
return;
}
updateTitle(feedOpenErrorMessage);
}
// Method to load the RSS data.
load() async {
updateTitle(loadingMessage);
loadFeed().then((result) {
if (result == null || result.toString().isEmpty) {
// Notify user of error.
updateTitle(feedLoadErrorMessage);
return;
}
setState(() {
feed = result;
isLoading = false;
});
});
}
// Method to get the RSS data
Future<RssFeed> loadFeed() async {
try {
final client = http.Client();
final response = await client.get(widget.feedUrl);
print("responssssurl>> ${response.body}");
return RssFeed.parse(utf8.decode(response.bodyBytes));
} catch (e) {
print("execption>> $e");
// handle any exceptions here
}
return null;
}
#override
void initState() {
updateTitle(widget.screenName);
isLoading = true;
load();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomPadding: false,
appBar: AppBar(
brightness: Platform.isIOS?Brightness.light:Brightness.dark,
title:feed != null?Text(feed.title,style:headerTextStyle):Container(),
leading: IconButton(
onPressed: (){
Navigator.pop(context);
},
icon:Icon(Icons.arrow_back_ios,color: ColorAsset.tabColorGray,size:20.0,)),
backgroundColor: Colors.white,
elevation: 0.0,
titleSpacing: 0.0,
centerTitle: true,
),
body: Stack(
children: <Widget>[
feed != null?
feed.items.length>0?ListView.builder(
physics: new ClampingScrollPhysics(),
itemCount: feed.items.length,
itemBuilder: (BuildContext context, int index){
final feedItems = feed.items[index];
print("rssTitle>>> ${feedItems.title}");
return Container(
margin: EdgeInsets.only(left:13.0,right:13.0),
child: Center(
child: Card(
elevation: 1.0,
child: ListTile(
onTap: (){
openFeed(feedItems.link);
},
title: Text(feedItems.title,style:listTextStyle),
subtitle: Text(feedItems.pubDate,style: subCategoroyTextStyle,),
contentPadding: EdgeInsets.all(5.0),
trailing: Padding(
padding: EdgeInsets.only(top:15.0,bottom:15.0),
child: Icon(CustomIcons.next,size:20.0,),
)
),
),
),
);
}
):Container(
margin: EdgeInsets.only(top:(MediaQuery.of(context).size.height/2)-110),
child: Text(StringAssets.noData,
style: listTextStyle,
),
)
:Container(),
Visibility(
visible: isLoading,
child: Container(
//margin: EdgeInsets.only(top:(MediaQuery.of(context).size.height/2)-110),
child: Center(
child: CircularIndiacator()
),
),
)
],
),
);
}
}

How can we use JSON with datatable?

I am new on flutter but I work a lot for learning all I need for my projects.
I have a JSON sent by a server using HTTP:
[{"equipe1":"PSG","equipe2":"DIJON","type_prono":"1N2"},{"equipe1":"MONACO","equipe2":"REIMS","type_prono":"1N2"},{"equipe1":"TOULOUSE","equipe2":"RENNES","type_prono":"1N2"},{"equipe1":"MONTPELLIER","equipe2":"STRASBOURG","type_prono":"1N2"},{"equipe1":"AMIENS","equipe2":"METZ","type_prono":"1N2"},{"equipe1":"BREST","equipe2":"ANGERS","type_prono":"1N2"},{"equipe1":"LORIENT","equipe2":"CHAMBLY","type_prono":"1N2"}]
And I try to set it to a datatable widget but it seems complicated to do.
Now here is my entire code :
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'dart:async';
// Create a Form widget.
class Affiche_grille extends StatefulWidget {
#override
Affiche_grille_State createState() {
return Affiche_grille_State();
}
}
// Create a corresponding State class.
// This class holds data related to the form.
class Affiche_grille_State extends State<Affiche_grille> {
#override
final _formKey = GlobalKey<FormState>();
Grille_display() async {
// SERVER LOGIN API URL
var url = 'http://www.axis-medias.fr/game_app/display_grid.php';
// Store all data with Param Name.
var data = {'id_grille': 1};
// Starting Web API Call.
var response = await http.post(url, body: json.encode(data));
// Getting Server response into variable.
var match = json.decode(response.body);
}
Widget build(BuildContext context) {
// Build a Form widget using the _formKey created above.
var listmatch = Grille_display();
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
DataTable(
columnSpacing: 20,
columns: [
DataColumn(
label: Text("Eq 1"),
numeric: false,
tooltip: "",
),
DataColumn(
label: Text("Eq 2"),
numeric: false,
tooltip: "",
),
DataColumn(
label: Text("Type pro"),
numeric: false,
tooltip: "",
),
],
rows: EquipeList.map((equipe_detail) => DataRow(
cells: [
DataCell(
Text(equipe_detail['equipe1'].toString()),
),
DataCell(
Text(equipe_detail['equipe2'].toString()),
),
DataCell(
Text(equipe_detail['type_prono'].toString()),
),
]),
).toList(),
)
],
)
);
}
}
class Match_detail {
String equipe1;
String equipe2;
String typeProno;
Match_detail({this.equipe1, this.equipe2, this.typeProno});
Match_detail.fromJson(Map<String, dynamic> json) {
equipe1 = json['equipe1'];
equipe2 = json['equipe2'];
typeProno = json['type_prono'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['equipe1'] = this.equipe1;
data['equipe2'] = this.equipe2;
data['type_prono'] = this.typeProno;
return data;
}
}
class EquipeList {
List<Match_detail> breeds;
EquipeList({this.breeds});
factory EquipeList.fromJson(List<dynamic> json) {
return EquipeList(
breeds: json
.map((e) => Match_detail.fromJson(e as Map<String, dynamic>))
.toList());
}
}
It doesn't work :( it says me : error: The method 'map' isn't defined for the class 'EquipeList'. (undefined_method at [flutter_app] lib
You can copy paste run full code below
You can use package https://pub.dev/packages/json_table
working demo
full code
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:json_table/json_table.dart';
class SimpleTable extends StatefulWidget {
#override
_SimpleTableState createState() => _SimpleTableState();
}
class _SimpleTableState extends State<SimpleTable> {
final String jsonSample =
'[{"equipe1":"PSG","equipe2":"DIJON","type_prono":"1N2"},{"equipe1":"MONACO","equipe2":"REIMS","type_prono":"1N2"},{"equipe1":"TOULOUSE","equipe2":"RENNES","type_prono":"1N2"},{"equipe1":"MONTPELLIER","equipe2":"STRASBOURG","type_prono":"1N2"},{"equipe1":"AMIENS","equipe2":"METZ","type_prono":"1N2"},{"equipe1":"BREST","equipe2":"ANGERS","type_prono":"1N2"},{"equipe1":"LORIENT","equipe2":"CHAMBLY","type_prono":"1N2"}]';
bool toggle = true;
#override
Widget build(BuildContext context) {
var json = jsonDecode(jsonSample);
return Scaffold(
body: Container(
padding: EdgeInsets.all(16.0),
child: toggle
? Column(
children: [
JsonTable(
json,
showColumnToggle: true,
tableHeaderBuilder: (String header) {
return Container(
padding: EdgeInsets.symmetric(
horizontal: 8.0, vertical: 4.0),
decoration: BoxDecoration(
border: Border.all(width: 0.5),
color: Colors.grey[300]),
child: Text(
header,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.display1.copyWith(
fontWeight: FontWeight.w700,
fontSize: 14.0,
color: Colors.black87),
),
);
},
tableCellBuilder: (value) {
return Container(
padding: EdgeInsets.symmetric(
horizontal: 4.0, vertical: 2.0),
decoration: BoxDecoration(
border: Border.all(
width: 0.5,
color: Colors.grey.withOpacity(0.5))),
child: Text(
value,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.display1.copyWith(
fontSize: 14.0, color: Colors.grey[900]),
),
);
},
allowRowHighlight: true,
rowHighlightColor: Colors.yellow[500].withOpacity(0.7),
paginationRowCount: 20,
),
SizedBox(
height: 20.0,
),
Text("Simple table which creates table direclty from json")
],
)
: Center(
child: Text(getPrettyJSONString(jsonSample)),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.grid_on),
onPressed: () {
setState(
() {
toggle = !toggle;
},
);
}),
);
}
String getPrettyJSONString(jsonObject) {
JsonEncoder encoder = new JsonEncoder.withIndent(' ');
String jsonString = encoder.convert(json.decode(jsonObject));
return jsonString;
}
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SimpleTable(),
);
}
}
I think you should first, convert this json into a real dart class so it can be easier to work with. You could create a class in dart/flutter called "Equipe" and run a map on the json. The [] means that you're dealing with a list of data.
But if you don't want to create a class, you could definitely work with with the json response, mapping over the list. I'm going to try to cook it up for you quickly. NB: Just remember to convert the json too, if it hasn't been done yet.
DataTable(
columnSpacing: 20,
columns: [
DataColumn(
label: Text("Eq 1"),
numeric: false,
tooltip: "",
),
DataColumn(
label: Text("Eq 2"),
numeric: false,
tooltip: "",
),
DataColumn(
label: Text("Type pro"),
numeric: false,
tooltip: "",
),
],
rows: equipeDetails.map((equipeDetail) => DataRow(
cells: [
DataCell(
Text(equipeDetail['equipe1'].toString()),
),
DataCell(
Text(equipeDetail['equipe2'].toString()),
),
DataCell(
Text(equipeDetail['type_prono'].toString()),
),
]),
).toList(),
)
I have done it:
Grille_display() async {
// SERVER LOGIN API URL
var url = 'http://www.axis-medias.fr/game_app/display_grid.php';
// Store all data with Param Name.
var data = {'id_grille': 1};
// Starting Web API Call.
var response = await http.post(url, body: json.encode(data));
// Getting Server response into variable.
var match = json.decode(response.body);
}
I think I need to create 2 class, instead of using equipeDetails and equipeDetail.
I need to display only equipe1 and equipe2 in table and use type prono for display radio button 1N2 or 12.
To populate data Table with json, create 2 methods .
One for populating the column headings.
Second one for populating the rows.
then pass the methods as value to datatable.
DataTable(
columnSpacing: 20,
columns:
dataTableColumnHeaderSetter(
dashBoardItems!
.oSsummary),
rows: dashBoardItems!.oSsummary
.mapIndexed(
(index, details) => DataRow(
cells:
dataTableColumnValueSetter(
dashBoardItems!
.oSsummary),
),
)
.toList()),
Method one.
List<DataColumn> dataTableColumnHeaderSetter(List<OSsummary> summary) {
return List.generate(summary.length, (i) {
return DataColumn(
label: Text(
summary[i].head,
textAlign: TextAlign.center,
),
numeric: true,
tooltip: "",
);
});
}
Method Two.
List<DataCell> dataTableColumnValueSetter(List<OSsummary> summary) {
return List.generate(summary.length, (i) {
return DataCell(
Text(
summary[i].value,
textAlign: TextAlign.center,
),
showEditIcon: false,
placeholder: false,
);
});
}
Do wrap the datatable in future builder and use snapshot.data for accessing the json data.

Autocomplete suggestion and search using json data

I want to display data from local json in list as suggestions when user types in a textfield. The suggestions displayed should be based on id that is associated with text to be displayed.
Somehow I am not able to achieve to display the data in UI and how to build the hierarchy of widgets that will display suggestions in list. Not sure what am I missing here. Looking for guidance. End result I am looking to achieve is:
Json snippet:
{
"data": [{
"serviceCategory": "ELECTRICAL",
"serviceCategoryDesc": "Electrical",
"serviceCategoryId": 3,
"autocompleteTerm": "Accent Lighting Installation",
"category": "IMPROVEMENT",
Ex : If user types electrical, then autocompleteterm value should be displayed in the list.
For this, I created model class and fetching it's data which is displayed in console properly.
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
);
}
}
Code :
// Get json result and convert it to model. Then add
Future<String> getUserDetails() async {
String jsonData = await DefaultAssetBundle.of(context).loadString('assets/services.json');
Map data = json.decode(jsonData);
print(data);
setState(() {
final List<Categories> items = (data['data'] as List).map((i) => new Categories.fromJson(i)).toList();
for (final item in items) {
print(item.autocompleteterm);
}
});
}
GlobalKey<AutoCompleteTextFieldState<Categories>> key = new GlobalKey();
get categories => List<Categories>();
AutoCompleteTextField textField;
String currentText = "";
List<Categories> added = [];
#override
void initState() {
textField = AutoCompleteTextField<Categories>
(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: EdgeInsets.fromLTRB(10.0, 30.0, 10.0, 20.0),
filled: true,
hintText: 'Search',
hintStyle: TextStyle(
color: Colors.white
)
),
itemSubmitted: null,
submitOnSuggestionTap: true,
clearOnSubmit: true,
textChanged: (item) {
currentText = item;
},
textSubmitted: (item) {
setState(() {
currentText = item;
added.add(widget.categories.firstWhere((i) => i.autocompleteterm.toLowerCase().contains(currentText)));
});
},
key: key,
suggestions: widget.categories,
itemBuilder: (context, item) {
return new Padding(
padding: EdgeInsets.all(8.0), child: new Text(item.autocompleteterm),
);
},
itemSorter: (a,b) {
return a.autocompleteterm.compareTo(b.autocompleteterm);
},
itemFilter: (item, query){
return item.autocompleteterm.toLowerCase().startsWith(query.toLowerCase());
});
super.initState();
_getUser();
getUserDetails();
}
#override
Widget build(BuildContext context) {
Column body = new Column(
children: <Widget>[
ListTile(
title: textField,
)
],
);
body.children.addAll(added.map((item) {
return ListTile(title: Text(item.autocompleteterm),
);
}
)
);
return Scaffold(
resizeToAvoidBottomPadding: false,
backgroundColor: Color(0xFF13212C),
appBar: AppBar(
title: Text('Demo'),
),
drawer: appDrawer(),
body: new Center(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new Column(
children: <Widget>[
textField,
]
),
The autocomplete_field package has been updated since this question was asked and now allows the use of objects other than Strings to work:
HomePage:
import 'package:flutter/material.dart';
import 'package:hello_world/category.dart';
import 'package:autocomplete_textfield/autocomplete_textfield.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => new _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<Category> added = [];
String currentText = "";
GlobalKey<AutoCompleteTextFieldState<Category>> key = new GlobalKey();
AutoCompleteTextField textField;
#override void initState() {
textField = new AutoCompleteTextField<Category>(
decoration: new InputDecoration(
hintText: "Search Item",
),
key: key,
submitOnSuggestionTap: true,
clearOnSubmit: true,
suggestions: CategoryViewModel.categories,
textInputAction: TextInputAction.go,
textChanged: (item) {
currentText = item;
},
itemSubmitted: (item) {
setState(() {
currentText = item.autocompleteterm;
added.add(item);
currentText = "";
});
},
itemBuilder: (context, item) {
return new Padding(
padding: EdgeInsets.all(8.0), child: new Text(item.autocompleteterm));
},
itemSorter: (a, b) {
return a.autocompleteterm.compareTo(b.autocompleteterm);
},
itemFilter: (item, query) {
return item.autocompleteterm.toLowerCase().startsWith(query.toLowerCase());
}
);
super.initState();
}
#override
Widget build(BuildContext context) {
Column body = new Column(children: [
new ListTile(
title: textField,
trailing: new IconButton(
icon: new Icon(Icons.add),
onPressed: () {
setState(() {
if (currentText != "") {
added.add(CategoryViewModel.categories.firstWhere((i) => i.autocompleteterm.toLowerCase().contains(currentText)));
textField.clear();
currentText = "";
}
});
}))
]);
body.children.addAll(added.map((item) {
return ListTile(title: Text(item.autocompleteterm), subtitle: Text(item.serviceCategory));
}));
return body;
}
}
Category classes:
import 'dart:convert';
import 'package:flutter/services.dart' show rootBundle;
class Category {
String serviceCategory;
String servCategoryDesc;
int id;
String autocompleteterm;
Category(
{this.serviceCategory,
this.servCategoryDesc,
this.id,
this.autocompleteterm});
factory Category.fromJson(Map<String, dynamic> parsedJson) {
return new Category(
serviceCategory: parsedJson['serviceCategory'],
servCategoryDesc: parsedJson['serviceCategoryDesc'],
id: parsedJson['serviceCategoryId'],
autocompleteterm: parsedJson['autocompleteTerm']);
}
}
class CategoryViewModel {
static List<Category> categories;
static Future loadCategories() async {
try {
categories = new List<Category>();
String jsonString = await rootBundle.loadString('assets/categories.json');
Map parsedJson = json.decode(jsonString);
var categoryJson = parsedJson['data'] as List;
for (int i = 0; i < categoryJson.length; i++) {
categories.add(new Category.fromJson(categoryJson[i]));
}
} catch (e) {
print(e);
}
}
}
Main with loading data:
void main() async {
await CategoryViewModel.loadCategories();
runApp(App());
}
Note, there are a few ways to load the data from the JSON but I find this way is easiest to do for a simple demo.

Flutter JSON duplicate index

I have a list of users that I am reading from JSON.
This is the JSON file:
{
"Dependents": [
{
"Name": "Kim",
"Relationship": "Parent"
},
{
"Name": "Tim",
"Relationship": "Spouse"
}
]
}
This is the model class:
new_fifth_model.dart
class NewFifthModel {
String name;
String relationship;
NewFifthModel(this.name, this.relationship);
}
And this is the class to bring out the users in a list.
NewFifth.dart
import 'package:flutter/material.dart';
import 'package:emas_app/model/new_fifth_model.dart';
import 'dart:convert';
import 'dart:async' show Future;
import 'package:http/http.dart' as http;
final String url = "http://crm.emastpa.com.my/MemberInfo.json";
final int page = 5;
//Future to get list of dependent names
Future<List<NewFifthModel>> fetchUserInfo() async{
var response = await http.get(url, headers: {"Accept": "application/json"});
List data = json.decode(response.body)["Dependents"];
var fifthmodel = <NewFifthModel>[];
data.forEach((f) => fifthmodel.add(new NewFifthModel(f["Name"], f["Relationship"])));
print(fifthmodel);
return fifthmodel;
}
class NewFifth extends StatefulWidget {
#override
_FifthState createState() => _FifthState();
}
class _FifthState extends State<NewFifth> {
List<NewFifthModel> fifthList;
#override
void initState() {
super.initState();
if (fifthList == null) {
fetchUserInfo().then((data) {
this.setState(() {
fifthList = data;
});
});
}
}
#override
Widget build(BuildContext context) {
//body widget
Widget _createBody() {
if(fifthList == null){
return new Center(
child: new CircularProgressIndicator(),
);
}
else{
return new ListView.builder(
shrinkWrap: true,
itemCount: fifthList.length,
itemBuilder: (context, index){
return new Column(
children: fifthList.map((f){
return new Card(
child: new ListTile(
title: new Text(f.name),
subtitle: new Text(f.relationship),
trailing: new Text(index.toString()),
onTap: (){
makeDialog(index.toString());
},
),
);
}).toList(),
);
});
}
}
return new Scaffold(
body: _createBody(),
);
}
}
This is the output on the screen.
The problem I am having (as you can see in the picture) is that the index number I put in the trailing part of the ListTile is duplicating and I really need the index number in order to proceed.
How do I rectify this problem?
Any help is very much appreciated.
you are creating 2 list here, you are recreating a Column with the entire list inside the item build, the ListView.builder is already taking care of iterating on your list using the itemCount.
itemBuilder: (context, index) {
final f = fifthList[index];
return Card(
child: new ListTile(
title: new Text(f.name),
subtitle: new Text(f.relationship),
trailing: new Text(index.toString()),
onTap: (){
makeDialog(index.toString());
},
),
);
}
Looks like you have only 2 items in the JSON object but you are showing 4.
I think you meant to only show 2? If so, in your itemBuilder function, you should do this:
return new ListView.builder(
shrinkWrap: true,
itemCount: fifthList.length,
itemBuilder: (context, index) {
var f = fifthList[index];
return new Card(
child: new ListTile(
title: new Text(f.name),
subtitle: new Text(f.relationship),
trailing: new Text(index.toString()),
onTap: () {
makeDialog(index.toString());
},
),
);
});
You were using .map() which looped through the list again on each item. You had 2 items, so you ended up with 4. If you had 3, it would show 6 items, and so on.