How to setState http url? - json

I want to create a news app and I've used newsapi.org as well but The
problem is actually that I want to make something like search feature in my
app and my idea is very simple.
I've created a TextField in the AppBar and my idea is to take the input from
user and setState with the new url of user input.
My Code:
import 'package:flutter/material.dart';
import 'package:newly/services/networking.dart';
import 'package:newly/widgets/article.dart';
class NewsScreen extends StatefulWidget {
#override
_NewsScreenState createState() => _NewsScreenState();
}
class _NewsScreenState extends State<NewsScreen> {
List<Article> articles = [];
String topic = 'google';
var newsData;
Future getNews(String topic) async {
NetworkHelper networkHelper = NetworkHelper(
url:
'https://newsapi.org/v2/everything?q=$topic&from=2019-11-04&to=2019-11-04&sortBy=popularity&apiKey=392495172bab4b3885ae93760df54b91',
);
newsData = await networkHelper.getData();
for (int i = 0; i < newsData['articles'].length; i++) {
var title = newsData['articles'][i]['title'];
var urlToImage = newsData['articles'][i]['urlToImage'];
var content = newsData['articles'][i]['content'];
var author = newsData['articles'][i]['author'];
var url = newsData['articles'][i]['url'];
setState(() {
articles.add(
Article(
author: author,
content: content,
title: title,
url: url,
urlToImage: urlToImage,
),
);
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.black,
centerTitle: true,
title: TextField(
textInputAction: TextInputAction.search,
onChanged: (String text) async {
setState(() {
topic = text;
});
await getNews(topic);
print(topic);
},
onSubmitted: (String text) async {
setState(() {
topic = text;
});
print(topic);
await getNews(topic);
},
decoration: InputDecoration(
suffixIcon: IconButton(
onPressed: () async {
await getNews(topic);
print(topic);
},
icon: Icon(
Icons.search,
),
),
hintText: 'Search',
filled: true,
fillColor: Colors.white,
),
),
),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(15.0),
child: FutureBuilder(
future: getNews(topic),
builder: (context, snapshot) {
return ListView.builder(
itemCount: newsData['articles'] == null
? 0
: newsData['articles'].length,
itemBuilder: (BuildContext ctxt, int index) {
return Article(
author: articles[index].author,
content: articles[index].content,
title: articles[index].title,
url: articles[index].url,
urlToImage: articles[index].urlToImage,
);
},
);
},
),
),
),
);
}
}
Network Helper :
import 'package:http/http.dart' as http;
import 'dart:convert';
class NetworkHelper {
NetworkHelper({this.url});
final String url;
Future getData() async {
http.Response response = await http.get(url);
if (response.statusCode == 200) {
String data = response.body;
return json.decode(data);
} else {
print('something wrong');
print(response.statusCode);
}
}
}

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());
} }

type 'List<dynamic>' is not a subtype of type 'List<MaintenanceInfo>' of 'function result'

I want to change List data; (List < dynamic> data) to List<MaintenanceInfo> data;
class MaintenanceInfo {
final String serial;
MaintenanceInfo({this.serial,});
factory MaintenanceInfo.fromJson(Map<String, dynamic> json) {
return new MaintenanceInfo(
serial: json['serial'], );}}
Or is there any way to extract JSON data as forEach?
I am trying to follow this example (https://stackoverflow.com/a/50569613/12908336) and am facing such a problem.
Check out this example that i have created for you :
import 'dart:async';
import 'package:flutter/material.dart';
import 'dart:convert';
//import 'package:http/http.dart' as http;
void main() => runApp(new MaterialApp(
home: new HomePage(),
debugShowCheckedModeBanner: false,
));
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => new _HomePageState();
}
class _HomePageState extends State<HomePage> {
TextEditingController controller = new TextEditingController();
String apiData =
'''{"ResultText": "Success","ResultStatus": 0,"data": [{"serial": "sample1"},{"serial": "sample2"},{"serial": "sample3"},{"serial": "sample4"},{"serial": "sample5"},{"serial": "sample6"}]} ''';
// Get json result and convert it to model. Then add
Future<Null> getUserDetails() async {
// final response = await http.get(url);
// final responseJson = json.decode(response.body);
// this is where you apis gets hit i have taken the sample json you provided.
final maintenanceInfo = maintenanceInfoFromJson(apiData);
setState(() {
/* for (Map user in responseJson) {
_userDetails.add(UserDetails.fromJson(user));
} */
maintenanceInfo.data.forEach((element) {
_userDetails.add(element);
});
});
}
#override
void initState() {
super.initState();
getUserDetails();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Home'),
elevation: 0.0,
),
body: new Column(
children: <Widget>[
new Container(
color: Theme.of(context).primaryColor,
child: new Padding(
padding: const EdgeInsets.all(8.0),
child: new Card(
child: new ListTile(
leading: new Icon(Icons.search),
title: new TextField(
controller: controller,
decoration: new InputDecoration(
hintText: 'Search', border: InputBorder.none),
onChanged: onSearchTextChanged,
),
trailing: new IconButton(
icon: new Icon(Icons.cancel),
onPressed: () {
controller.clear();
onSearchTextChanged('');
},
),
),
),
),
),
new Expanded(
child: _searchResult.length != 0 || controller.text.isNotEmpty
? new ListView.builder(
itemCount: _searchResult.length,
itemBuilder: (context, i) {
return new Card(
child: new ListTile(
title: new Text(_searchResult[i].serial),
),
margin: const EdgeInsets.all(0.0),
);
},
)
: new ListView.builder(
itemCount: _userDetails.length,
itemBuilder: (context, index) {
return new Card(
child: new ListTile(
title: new Text(_userDetails[index].serial),
),
margin: const EdgeInsets.all(0.0),
);
},
),
),
],
),
);
}
onSearchTextChanged(String text) async {
_searchResult.clear();
if (text.isEmpty) {
setState(() {});
return;
}
_userDetails.forEach((userDetail) {
if (userDetail.serial.contains(text)) _searchResult.add(userDetail);
});
setState(() {});
}
}
List<Serials> _searchResult = [];
List<Serials> _userDetails = [];
// To parse this JSON data, do
//
// final maintenanceInfo = maintenanceInfoFromJson(jsonString);
MaintenanceInfo maintenanceInfoFromJson(String str) =>
MaintenanceInfo.fromJson(json.decode(str));
String maintenanceInfoToJson(MaintenanceInfo data) =>
json.encode(data.toJson());
class MaintenanceInfo {
MaintenanceInfo({
this.resultText,
this.resultStatus,
this.data,
});
String resultText;
int resultStatus;
List<Serials> data;
factory MaintenanceInfo.fromJson(Map<String, dynamic> json) =>
MaintenanceInfo(
resultText: json["ResultText"],
resultStatus: json["ResultStatus"],
data: List<Serials>.from(json["data"].map((x) => Serials.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"ResultText": resultText,
"ResultStatus": resultStatus,
"data": List<dynamic>.from(data.map((x) => x.toJson())),
};
}
class Serials {
Serials({
this.serial,
});
String serial;
factory Serials.fromJson(Map<String, dynamic> json) => Serials(
serial: json["serial"],
);
Map<String, dynamic> toJson() => {
"serial": serial,
};
}
Let me know if it works.

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

Flutter: Loading json data within second page Flutter Class timing out without error

I've generated a listView from the Pokemon API of a list of Pokemon, I then have onTap events to trigger a new page/class, where I'm passing the name and URL from the API to the new secondPage Class/Screen.
I need to make a second request in this new page because the API Url needs to change to grab specific details but my request seems to be timing out..
Here is my code: If loaded into a new project the first screen should function fine, loading a bunch of Pokemon and their API specific URLs into a listView.
I can successfully pass the name and URL onto the second screen because they do appear in the Appbar.
However when loading the new json data it seems to be timing out without any error.
Does anyone have any advice for a newbie trying to get his footing?
import 'dart:async';
import 'dart:convert';
import 'package:basic_utils/basic_utils.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TextEditingController editingController = TextEditingController();
String url = 'https://pokeapi.co/api/v2/pokemon/?limit=151';
List data;
Future<String> makeRequest() async {
var response = await http
.get(Uri.encodeFull(url), headers: {"Accept": "application/json"});
setState(() {
var extractData = json.decode(response.body);
data = extractData["results"];
});
}
#override
void initState() {
this.makeRequest();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Pokemon List'),
),
body: Container(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(15.0),
child: new TextField(
onChanged: (value) {
},
decoration: InputDecoration(
labelText: "Search",
hintText: "Search",
contentPadding: const EdgeInsets.all(10.0),
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(6.0))
),
),
),
),
Expanded(
child: new ListView.builder(
itemCount: data == null ? 0 : data.length,
itemBuilder: (BuildContext context, i) {
return new ListTile(
title: new Text(StringUtils.capitalize(data[i]["name"])),
subtitle: new Text(data[i]["url"]),
// leading: new CircleAvatar(
// backgroundImage:
// new NetworkImage(data[i]["picture"]["thumbnail"]),
// ),
onTap: () {
Navigator.push(
context,
new MaterialPageRoute(
builder: (BuildContext context) =>
new SecondPage(data[i])
)
);
},
);
}
),
),
],
),
),
);
}
}
// Class for getting Specific Details on SecondPage
class Post {
final String name;
final int weight;
Post({this.name, this.weight});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
name: json['name'],
weight: json['weight'],
);
}
}
// New Request for Specific Details
class SecondPage extends StatelessWidget {
SecondPage(this.data);
final data;
Future<Post> fetchPost() async {
final response =
await http.get('https://pokeapi.co/api/v2/pokemon/' + data["name"]);
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON.
return Post.fromJson(json.decode(response.body));
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
Future<Post> post;
#override
void initState() {
fetchPost();
post = fetchPost();
}
#override
Widget build(BuildContext context) =>
new Scaffold(
appBar: new AppBar(
title: new Text(data["name"] + ' - ' + data["url"])),
body: new Center(
child: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.all(30.0),
child: FutureBuilder<Post>(
future: post,
builder: (context, snapshot) {
if (snapshot.hasData) {
Text(snapshot.data.name);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
),
],
),
)
);
}
Man, I rewrite your code as I like
import 'dart:async';
import 'dart:convert';
//import 'package:basic_utils/basic_utils.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TextEditingController editingController = TextEditingController();
String url = 'https://pokeapi.co/api/v2/pokemon/?limit=151';
List data;
Future<String> makeRequest() async {
var response = await http.get(Uri.encodeFull(url), headers: {"Accept": "application/json"});
setState(() {
var extractData = json.decode(response.body);
data = extractData["results"];
});
}
#override
void initState() {
super.initState();
makeRequest();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Pokemon List'),
),
body: Container(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(15.0),
child: TextField(
onChanged: (value) {},
decoration: InputDecoration(
labelText: "Search",
hintText: "Search",
contentPadding: const EdgeInsets.all(10.0),
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(6.0))),
),
),
),
Expanded(
child: ListView.builder(
itemCount: data == null ? 0 : data.length,
itemBuilder: (BuildContext context, i) {
return ListTile(
title: Text(data[i]["name"].toString().toUpperCase()),
subtitle: Text(data[i]["url"]),
// leading: CircleAvatar(
// backgroundImage:
// NetworkImage(data[i]["picture"]["thumbnail"]),
// ),
onTap: () {
Navigator.push(
context, MaterialPageRoute(builder: (BuildContext context) => SecondPage(data[i])));
},
);
}),
),
],
),
),
);
}
}
class SecondPage extends StatefulWidget {
Map data;
SecondPage(this.data);
_SecondState createState() => _SecondState();
}
class _SecondState extends State<SecondPage> {
#override
void initState() {
super.initState();
_fetchPost();
}
Map post;
bool isLoad = true;
_fetchPost() async {
setState(() {
isLoad = true;
});
var url = widget.data["url"];
debugPrint(url);
final response = await http.get(url);
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON.
post = json.decode(response.body.toString());
setState(() {
isLoad = false;
});
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.data["name"] + ' - ' + widget.data["url"])),
body: _buildPokemon(context),
);
}
Widget _buildPokemon(BuildContext context) {
if (isLoad) return Center(child: CircularProgressIndicator());
return Container(
padding: EdgeInsets.all(10),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(post['name']),
Text(post['weight'].toString()),
Text(post['height'].toString()),
Image.network(post['sprites']['front_default'])
],
),
);
}
}

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.