Displaying the data from my API based on the Dropdown selected value. I want to display on the same page. The data from the server(response) is displaying on the console. But still, this data is not displaying.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
//import 'package:json_parsing_example/model2.dart';
//import 'package:json_parsing_example/models.dart'
List<YouModel> youModelFromJson(String str) => List<YouModel>.from(json.decode(str).map((x) => YouModel.fromJson(x)));
String youModelToJson(List<YouModel> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class YouModel {
String columnName;
YouModel({
this.columnName,
});
factory YouModel.fromJson(Map<String, dynamic> json) => YouModel(
columnName: json["column_name"],
);
Map<String, dynamic> toJson() => {
"column_name": columnName,
};
}
UserModel userModelFromJson(String str) => UserModel.fromJson(json.decode(str));
String userModelToJson(UserModel data) => json.encode(data.toJson());
class UserModel {
String username;
String name;
UserModel({
this.username,
this.name,
});
factory UserModel.fromJson(Map<String, dynamic> json) => UserModel(
username: json["username"],
name: json["Name"],
);
Map<String, dynamic> toJson() => {
"username": username,
"Name": name,
};
}
class Addoffers2 extends StatefulWidget {
#override
State<StatefulWidget> createState() => _Addoffers2State();
}
class _Addoffers2State extends State<Addoffers2> {
List<String> _companies = [];
bool _isLoading = false;
String _selectedCompany;
#override
void initState() {
super.initState();
_selectedCompany=null;
_getcompanylist();
}
Future<String> loadFromAssets() async {
return await rootBundle.loadString('json/parse.json');
}
_getcompanylist() async {
setState(() {
_isLoading = true;
});
print("getting..");
final responseStr =
await http.get('http://10.0.2.2/Flutter/GetCompanieslist.php');
//String responseStr = await loadFromAssets();
final listData = youModelFromJson(responseStr.body);
for(int i=0;i<listData.length;i++)
{
print('this is the list :'+listData[i].columnName);
// _companies.add(listData[i].columnName);
}
// above method is the standard method to get creating a model class and then get the list of strings
// I have just shown you but example is according to you code .
// this above loadFromAssets is that you hit the api and you get the json string response
// i have created a dummy json file where i can the String.
// Else everything is the same as below you just have to pass the response.body to the json.decode method.
var jsonData = json.decode(responseStr.body);
for (var u in jsonData) {
_companies.add(u.toString().substring(14, u.toString().length - 1));
}
for (int i = 0; i < _companies.length; i++) {
print(_companies[i].toString());
}
setState(() {
_isLoading = false;
});
}
#override
Widget build(BuildContext context) {
//double width = MediaQuery.of(context).size.width;
//double height = MediaQuery.of(context).size.height;
return MaterialApp(
//color: Colors.red,
home: Scaffold(
backgroundColor: Colors.red,
appBar: AppBar(
backgroundColor: Theme.of(context).backgroundColor,
title: Text("Add.."),
),
body: Container(
color: Colors.blue,
// just put your height i have modified it replace it by height / 8
child: _isLoading
? CircularProgressIndicator()
: Center(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
//MainAxisAlignment: MainAxisAlignment.spaceBetween,
Text('Choose..'),
DropdownButtonHideUnderline(
child: DropdownButton(
// hint: Text('Choose Company'), // Not necessary for Option 1
value: _selectedCompany,
onChanged: (newValue) {
setState(() {
_selectedCompany = newValue;
// here i have taken the boolen variable to show and hide the list if you have not seleted the value from the dropdown the it will show the text and if selected the it will show you the list.
});
print(_selectedCompany);
},
items: _companies.map((company) {
return DropdownMenuItem(
child: new Text(company.toString()),
value: company,
);
}).toList(),
),
),
],
),
),
),
// this is to to check for the initial if string is null then show the text widget.
// else if the value is selected then it will show the listview
_selectedCompany == null
? Text('Select the dropdown value for list to appear.')// sample text you can modify
: Padding(
padding: const EdgeInsets.all(0.0),
child: Container(
height: 100,
color: Theme.of(context).backgroundColor,
child: new FutureBuilder(
future: _getUsers(
_selectedCompany), // a Future<String> or null
builder: (BuildContext context,
AsyncSnapshot snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Container(
child: Center(
child: new CircularProgressIndicator(
backgroundColor: Colors.white,
),
));
}
if (snapshot.hasError) {
return Center(
child: new Text(
'Error ${snapshot.error}'),
);
} else {
return Center(
child: Padding(
padding: const EdgeInsets.fromLTRB(
5.0, 8.0, 5.0, 8.0),
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder:
(BuildContext context,
int index) {
List<UserModel> user =
snapshot.data;
var username =
user[index].username;
var stuname =
user[index].name;
print(
'This is the user name :$username');
print(
'This is the name : $stuname');
//var title=snapshot.data[index]["Title"];
// new Text(parsedDate.toString());
return StudentList2(
regdNo: username,
name: stuname);
}),
),
);
}
}),
),
),
],
)),
)),
);
}
}
Future<String> loadFromAssets2() async {
return await rootBundle.loadString('json/parse2.json');
}
// the above method is just for the sample purpose where you get you json String after hitting the api call for _getUsers method
Future<List<UserModel>> _getUsers(String selectedcompany) async {
// here you call you api and you get the response
var url = 'https://10.0.2.2/Flutter/getstudentdata.php;
var data = { 'company': selectedcompany};
// Starting Web Call with data.
var response = await http.post(url, body: json.encode(data));
print(response.body);
//String responseStr = await loadFromAssets2();
final userModel = userModelFromJson(response.body);
// I have just made the model class for where fromt he below you get the complete object and then added to the list and returned.
List<UserModel> users = [];
users.add(userModel);
print('This is the name : ${users[0].name}'); // Even this also not getting printed
return users;
}
class StudentList2 extends StatefulWidget {
final regdNo;
final name;
const StudentList2({
Key key,
this.regdNo,
this.name,
}) : super(key: key);
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<StudentList2> {
bool visible = false;
#override
Widget build(BuildContext context) {
print(widget.regdNo.toString());
return Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0),
child: new Card(
color: Theme.of(context).primaryColor,
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 2.0),
child: Container(
child: new Text(
widget.regdNo.toUpperCase(),
style: TextStyle(
color: Colors.yellowAccent,
fontWeight: FontWeight.bold,
fontSize: 15.0,
),
),
),
),
ListTile(
title: new Text(
widget.regdNo,
style: TextStyle(
color: Colors.black,
fontSize: 14.0,
),
),
subtitle: new Text(
(widget.name),
style: TextStyle(
color: Colors.black,
fontSize: 15.0,
),
),
),
//
],
)),
);
}
}
I am able to retrieve the data from the server and print it on the console. Still, the data is not displaying. I do not know where I did the mistake.
So I have completely updated the answer and there are many things that you don't follow according to the global standard.
So I have listed some of the key things that you should follow :
Following is you company list json :
[
{
"column_name": "ABC"
},
{
"column_name": "XYZ"
}
]
Following is the get user json that you will get :
{"username":"1111","Name":"ABC" }
And Later the model class I have create accordingly to the json that you provided and then you can create your own based in the added json.
There are Two model classes that I have created :
First model class is for the company :
// To parse this JSON data, do
//
// final youModel = youModelFromJson(jsonString);
import 'dart:convert';
List<YouModel> youModelFromJson(String str) => List<YouModel>.from(json.decode(str).map((x) => YouModel.fromJson(x)));
String youModelToJson(List<YouModel> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class YouModel {
String columnName;
YouModel({
this.columnName,
});
factory YouModel.fromJson(Map<String, dynamic> json) => YouModel(
columnName: json["column_name"],
);
Map<String, dynamic> toJson() => {
"column_name": columnName,
};
}
second mode class is for the user :
// To parse this JSON data, do
//
// final userModel = userModelFromJson(jsonString);
import 'dart:convert';
UserModel userModelFromJson(String str) => UserModel.fromJson(json.decode(str));
String userModelToJson(UserModel data) => json.encode(data.toJson());
class UserModel {
String username;
String name;
UserModel({
this.username,
this.name,
});
factory UserModel.fromJson(Map<String, dynamic> json) => UserModel(
username: json["username"],
name: json["Name"],
);
Map<String, dynamic> toJson() => {
"username": username,
"Name": name,
};
}
Below is the main ui file just Check the comments that I have made so that it will be helpful for you .
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:json_parsing_example/model2.dart';
import 'package:json_parsing_example/models.dart';
void main() => runApp(Addoffers());
class Addoffers extends StatefulWidget {
#override
State<StatefulWidget> createState() => _AddoffersState();
}
class _AddoffersState extends State<Addoffers> {
List<String> _companies = [];
bool _isLoading = false;
String _selectedCompany;
#override
void initState() {
super.initState();
_selectedCompany=null;
_getcompanylist();
}
Future<String> loadFromAssets() async {
return await rootBundle.loadString('json/parse.json');
}
_getcompanylist() async {
setState(() {
_isLoading = true;
});
print("getting..");
/* final response =
await http.get('http://10.0.2.2/Flutter/GetCompanieslist.php'); */
String responseStr = await loadFromAssets();
final listData = youModelFromJson(responseStr);
for(int i=0;i<listData.length;i++)
{
print('this is the list :'+listData[i].columnName);
// _companies.add(listData[i].columnName);
}
// above method is the standard method to get creating a model class and then get the list of strings
// I have just shown you but example is according to you code .
// this above loadFromAssets is that you hit the api and you get the json string response
// i have created a dummy json file where i can the String.
// Else everything is the same as below you just have to pass the response.body to the json.decode method.
var jsonData = json.decode(responseStr);
for (var u in jsonData) {
_companies.add(u.toString().substring(14, u.toString().length - 1));
}
for (int i = 0; i < _companies.length; i++) {
print(_companies[i].toString());
}
setState(() {
_isLoading = false;
});
}
#override
Widget build(BuildContext context) {
//double width = MediaQuery.of(context).size.width;
//double height = MediaQuery.of(context).size.height;
return MaterialApp(
//color: Colors.red,
home: Scaffold(
backgroundColor: Colors.red,
appBar: AppBar(
backgroundColor: Theme.of(context).backgroundColor,
title: Text("Add.."),
),
body: Container(
color: Colors.blue,
// just put your height i have modified it replace it by height / 8
child: _isLoading
? CircularProgressIndicator()
: Center(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
//MainAxisAlignment: MainAxisAlignment.spaceBetween,
Text('Choose..'),
DropdownButtonHideUnderline(
child: DropdownButton(
// hint: Text('Choose Company'), // Not necessary for Option 1
value: _selectedCompany,
onChanged: (newValue) {
setState(() {
_selectedCompany = newValue;
// here i have taken the boolen variable to show and hide the list if you have not seleted the value from the dropdown the it will show the text and if selected the it will show you the list.
});
print(_selectedCompany);
},
items: _companies.map((company) {
return DropdownMenuItem(
child: new Text(company.toString()),
value: company,
);
}).toList(),
),
),
],
),
),
),
// this is to to check for the initial if string is null then show the text widget.
// else if the value is selected then it will show the listview
_selectedCompany == null
? Text('Select the dropdown value for list to appear.')// sample text you can modify
: Padding(
padding: const EdgeInsets.all(0.0),
child: Container(
height: 100,
color: Theme.of(context).backgroundColor,
child: new FutureBuilder(
future: _getUsers(
_selectedCompany), // a Future<String> or null
builder: (BuildContext context,
AsyncSnapshot snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Container(
child: Center(
child: new CircularProgressIndicator(
backgroundColor: Colors.white,
),
));
}
if (snapshot.hasError) {
return Center(
child: new Text(
'Error ${snapshot.error}'),
);
} else {
return Center(
child: Padding(
padding: const EdgeInsets.fromLTRB(
5.0, 8.0, 5.0, 8.0),
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder:
(BuildContext context,
int index) {
List<UserModel> user =
snapshot.data;
var username =
user[index].username;
var stuname =
user[index].name;
print(
'This is the user name :$username');
print(
'This is the name : $stuname');
//var title=snapshot.data[index]["Title"];
// new Text(parsedDate.toString());
return StudentList2(
regdNo: username,
name: stuname);
}),
),
);
}
}),
),
),
],
)),
)),
);
}
}
Future<String> loadFromAssets2() async {
return await rootBundle.loadString('json/parse2.json');
}
// the above method is just for the sample purpose where you get you json String after hitting the api call for _getUsers method
Future<List<UserModel>> _getUsers(String selectedcompany) async {
/* var data = await http.post("http://10.0.2.2/Flutter/getstdata.php", body: {
"company": selectedcompany,
//print(data.body);
}); */
// here you call you api and you get the response
String responseStr = await loadFromAssets2();
final userModel = userModelFromJson(responseStr);
// I have just made the model class for where fromt he below you get the complete object and then added to the list and returned.
List<UserModel> users = [];
users.add(userModel);
print('This is the name : ${users[0].name}');
//final x=users.length.toString();
//debugPrint("records:" + users.length.toString());
//debugPrint("kkk:" + absentees.length.toString());
return users;
}
class StudentList2 extends StatefulWidget {
//MyHomePage(String branch);
final regdNo;
final name;
const StudentList2({
Key key,
this.regdNo,
this.name,
}) : super(key: key);
//final String branch;
//const StudentList({Key key, this.branch}) : super(key: key);
//MyHomePage(String branch);
// final String title;
// final String branch="";
// MyHomePage(String branch, {Key key, this.title}) : super(key: key);
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<StudentList2> {
bool visible = false;
//bool _btnEnabled = false;
//bool _validate = false;
// var _firstPress = true ;
//Color _iconColor = Colors.yellow;
//Color _iconColor2 = Colors.white;
//var poll;
//DateTime parsedDate;
#override
Widget build(BuildContext context) {
print(widget.regdNo.toString());
return Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0),
child: new Card(
color: Theme.of(context).primaryColor,
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 2.0),
child: Container(
child: new Text(
widget.regdNo.toUpperCase(),
style: TextStyle(
color: Colors.yellowAccent,
fontWeight: FontWeight.bold,
fontSize: 15.0,
),
),
),
),
ListTile(
title: new Text(
widget.regdNo,
style: TextStyle(
color: Colors.black,
fontSize: 14.0,
),
),
subtitle: new Text(
(widget.name),
style: TextStyle(
color: Colors.black,
fontSize: 15.0,
),
),
),
//
],
)),
);
}
}
// This is not the good approach to create a model class just check the sample model class that i have created.
class User {
//final int index;
final String username;
final String name;
//final Float cgpa;
User(
this.username,
this.name,
);
}
And below is the sample Gif file for you :
As stated by #pskink the method _getcompanylist() is async. An async function runs asynchronously, which means that the rest of the program doesn't wait for it to complete. You can use a future builder to deal whit that or you can simply wait for it by using the await function. I believe that for your code snippet future builder is the better choice.
Related
I have a big Flutter project which is using my Woocommerce website as backend. Everything is working fine, but it was missing the search function on this recipes screen. I'm completly new to Flutter, but because I have Java experience with some luck and miracle I was able to create this function [the search] using the list view and call the dedicated endpoint if I enter the search term. This is working (hooray), but the problem is, that the list of elements are not refreshing if I call the search. It stays on the "all" view, only if I pull down the screen and make a refresh, only than I will see the search results... I tried with the suggested "key" for the widgets, but because I'm not really familiar with Flutter most likely I use is wrong or not on the right element... What is the best way to make this work? Can I call the refresh function somehow (I tried to find it, but failed) after calling the search or is it possible to force the widget re-draw in this case?
Thank you very much.
Edit3.:
This is the searchRecipeModel class:
import '../../../models/entities/blog.dart';
import '../../../models/paging_data_provider.dart';
import '../repositories/search_recipe_repository.dart';
export '../../../models/entities/blog.dart';
class SearchRecipeModel extends PagingDataProvider<Blog> {
SearchRecipeModel() : super(dataRepo: SearchRecipeRepository());
List<Blog> get recipes => data;
Future<void> searchRecipes() => getData();
}
This is the SearchRecipeRepository class:
import '../../../common/base/paging_repository.dart';
import '../../../models/entities/blog.dart';
import '../../../models/entities/paging_response.dart';
class SearchRecipeRepository extends PagingRepository<Blog> {
#override
Future<PagingResponse<Blog>> Function(dynamic) get requestApi =>
service.api.searchRecipes;
}
This is the Blog class, it's a Wordpress entity:
import 'dart:convert';
import 'package:html_unescape/html_unescape.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import '../../common/packages.dart';
import '../../services/index.dart';
import '../serializers/blog.dart';
class Blog {
final dynamic id;
final String title;
final String subTitle;
final String date;
final String content;
final String author;
final String imageFeature;
const Blog({
this.id,
this.title,
this.subTitle,
this.date,
this.content,
this.author,
this.imageFeature,
});
const Blog.empty(this.id)
: title = '',
subTitle = '',
date = '',
author = '',
content = '',
imageFeature = '';
factory Blog.fromJson(Map<String, dynamic> json) {
switch (Config().type) {
case ConfigType.woo:
return Blog._fromWooJson(json);
case ConfigType.shopify:
return Blog._fromShopifyJson(json);
case ConfigType.strapi:
return Blog._fromStrapiJson(json);
case ConfigType.mylisting:
case ConfigType.listeo:
case ConfigType.listpro:
return Blog._fromListingJson(json);
default:
return const Blog.empty(0);
}
}
Blog._fromShopifyJson(Map<String, dynamic> json)
: id = json['id'],
author = json['authorV2']['name'],
title = json['title'],
subTitle = null,
content = json['contentHtml'],
imageFeature = json['image']['transformedSrc'],
date = json['publishedAt'];
factory Blog._fromStrapiJson(Map<String, dynamic> json) {
var model = SerializerBlog.fromJson(json);
final id = model.id;
final author = model.user.displayName;
final title = model.title;
final subTitle = model.subTitle;
final content = model.content;
final imageFeature = Config().url + model.images.first.url;
final date = model.date;
return Blog(
author: author,
title: title,
subTitle: subTitle,
content: content,
id: id,
date: date,
imageFeature: imageFeature,
);
}
Blog._fromListingJson(Map<String, dynamic> json)
: id = json['id'],
author = json['author_name'],
title = HtmlUnescape().convert(json['title']['rendered']),
subTitle = HtmlUnescape().convert(json['excerpt']['rendered']),
content = json['content']['rendered'],
imageFeature = json['image_feature'],
date = DateFormat.yMMMMd('en_US').format(DateTime.parse(json['date']));
factory Blog._fromWooJson(Map<String, dynamic> json) {
String imageFeature;
var imgJson = json['better_featured_image'];
if (imgJson != null) {
if (imgJson['media_details']['sizes']['medium_large'] != null) {
imageFeature =
imgJson['media_details']['sizes']['medium_large']['source_url'];
}
}
if (imageFeature == null) {
var imgMedia = json['_embedded']['wp:featuredmedia'];
if (imgMedia != null &&
imgMedia[0]['media_details'] != null &&
imgMedia[0]['media_details']['sizes']['large'] != null) {
imageFeature =
imgMedia[0]['media_details']['sizes']['large']['source_url'];
}
/**
* Netbloom
* Featured image fix
*/
if(imageFeature == null &&
imgMedia[0]['media_details'] != null &&
imgMedia[0]['media_details']['sizes']['medium_large'] != null){
imageFeature =
imgMedia[0]['media_details']['sizes']['medium_large']['source_url'];
}
if(imageFeature == null &&
imgMedia[0]['media_details'] != null &&
imgMedia[0]['media_details']['file'] != null){
imageFeature =
"https://okosgrill.hu/wp-content/uploads/" + imgMedia[0]['media_details']['file'];
}
if(imageFeature == null && json['featured_image_urls'] != null && json['featured_image_urls']['medium_large'] != null){
imageFeature = json['featured_image_urls']['medium_large'];
}
if(imageFeature == null && json['featured_image_urls'] != null && json['featured_image_urls']['medium'] != null){
imageFeature = json['featured_image_urls']['medium'];
}
//Fallback
if(imageFeature == null){
imageFeature =
"https://okosgrill.hu/wp-content/uploads/okosgrill-tippek.jpg";
}
}
final author = json['_embedded']['author'] != null
? json['_embedded']['author'][0]['name']
: '';
final date =
DateFormat.yMMMMd('hu_HU').format(DateTime.parse(json['date']));
final id = json['id'];
final title = HtmlUnescape().convert(json['title']['rendered']);
final subTitle = json['excerpt']!= null ? HtmlUnescape().convert(json['excerpt']['rendered']) : '';
final content = json['content']['rendered'];
return Blog(
author: author,
title: title,
subTitle: subTitle,
content: content,
id: id,
date: date,
imageFeature: imageFeature,
);
}
static Future getBlogs({String url, categories, page = 1}) async {
try {
var param = '_embed&page=$page';
if (categories != null) {
param += '&categories=$categories';
}
final response =
await http.get('$url/wp-json/wp/v2/posts?$param'.toUri());
if (response.statusCode != 200) {
return [];
}
return jsonDecode(response.body);
} on Exception catch (_) {
return [];
}
}
static Future<dynamic> getBlog({url, id}) async {
final response =
await http.get('$url/wp-json/wp/v2/posts/$id?_embed'.toUri());
return jsonDecode(response.body);
}
#override
String toString() => 'Blog { id: $id title: $title}';
}
This is the BlogListItem class:
import 'package:flutter/material.dart';
import 'package:html/parser.dart';
import '../../../../common/constants.dart' show RouteList;
import '../../../../common/tools.dart' show Tools, kSize;
import '../../../../models/entities/blog.dart';
import '../../../../routes/flux_navigate.dart';
class BlogListItem extends StatelessWidget {
final Blog blog;
const BlogListItem({#required this.blog});
#override
Widget build(BuildContext context) {
var screenWidth = MediaQuery.of(context).size.width;
if (blog.id == null) return const SizedBox();
return InkWell(
onTap: () => FluxNavigate.pushNamed(
RouteList.detailBlog,
arguments: blog,
),
child: Container(
padding: const EdgeInsets.only(right: 15, left: 15),
child: Column(
children: <Widget>[
const SizedBox(height: 20.0),
ClipRRect(
borderRadius: BorderRadius.circular(3.0),
child: Tools.image(
url: blog.imageFeature,
width: screenWidth,
height: screenWidth * 0.5,
fit: BoxFit.fitWidth,
size: kSize.medium,
),
),
SizedBox(
height: 30,
width: screenWidth,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Text(
blog.date ?? '',
style: TextStyle(
fontSize: 14,
color: Theme.of(context).accentColor.withOpacity(0.5),
),
maxLines: 2,
),
const SizedBox(width: 20.0),
if (blog.author != null)
Text(
blog.author.toUpperCase(),
style: const TextStyle(
fontSize: 11,
height: 2,
fontWeight: FontWeight.bold,
),
maxLines: 2,
),
],
),
),
const SizedBox(height: 20.0),
Text(
blog.title ?? '',
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
maxLines: 2,
),
const SizedBox(height: 10.0),
Text(
blog.subTitle != null
? parse(blog.subTitle).documentElement.text
: '',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
height: 1.3,
color: Theme.of(context).accentColor.withOpacity(0.8),
),
maxLines: 2,
),
const SizedBox(height: 20.0),
],
),
),
);
}
}
Edit2.:
This is the recipe_helper global class:
library globals;
String recipeSerachTerm = "";
Edit.:
This is the class of the BaseScreen:
import 'package:flutter/material.dart';
abstract class BaseScreen<T extends StatefulWidget> extends State<T> {
#override
void initState() {
super.initState();
WidgetsBinding.instance
.addPostFrameCallback((_) => afterFirstLayout(context));
}
void afterFirstLayout(BuildContext context) {}
/// Get size screen
Size get screenSize => MediaQuery.of(context).size;
}
This is class of this screen:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import '../../../common/constants.dart';
import '../../../generated/l10n.dart';
import '../../../models/entities/blog.dart';
import '../../../widgets/common/skeleton.dart';
import '../../../widgets/paging_list.dart';
import '../../base.dart';
import '../models/list_recipe_model.dart';
import '../models/search_recipe_model.dart';
import '../helpers/recipe_helper.dart' as globals;
import 'widgets/blog_list_item.dart';
class ListRecipeScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() => _ListRecipeScreenState();
}
class _ListRecipeScreenState extends BaseScreen<ListRecipeScreen> {
#override
Widget build(BuildContext context) {
key: UniqueKey();
return Scaffold(
appBar: !kIsWeb
? AppBar(
elevation: 0.1,
title: Text(
S.of(context).recipe,
style: const TextStyle(color: Colors.white),
),
leading: Center(
child: GestureDetector(
onTap: () => Navigator.pop(context),
child: const Icon(
Icons.arrow_back_ios,
color: Colors.white,
),
),
),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
color: Colors.white,
onPressed: () {
showSearch(
context: context,
delegate: CustomSearchDelegate(),
);
},
),
],
)
: null,
body: PagingList<ListRecipeModel, Blog>(
itemBuilder: (context, blog) => BlogListItem(blog: blog),
loadingWidget: _buildSkeleton(),
lengthLoadingWidget: 3
),
);
}
Widget _buildSkeleton() {
key: UniqueKey();
return Padding(
padding: const EdgeInsets.only(
left: 16.0,
right: 16.0,
bottom: 24.0,
top: 12.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Skeleton(height: 200),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const Skeleton(width: 120),
const Skeleton(width: 80),
],
),
const SizedBox(height: 16),
const Skeleton(),
],
),
);
}
}
class CustomSearchDelegate extends SearchDelegate {
#override
List<Widget> buildActions(BuildContext context) {
return [
IconButton(
icon: Icon(Icons.clear),
onPressed: () {
query = '';
},
),
];
}
#override
Widget buildLeading(BuildContext context) {
return IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
close(context, null);
},
);
}
#override
Widget buildResults(BuildContext context) {
if (query.length < 4) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(
child: Text(
"Search term must be longer than three letters.",
),
),
],
);
}else{
globals.recipeSerachTerm = query;
}
return Scaffold(
appBar: !kIsWeb
? AppBar(
elevation: 0.1,
title: Text(
S.of(context).recipe,
style: const TextStyle(color: Colors.white),
),
leading: Center(
child: GestureDetector(
onTap: () => Navigator.pop(context),
child: const Icon(
Icons.arrow_back_ios,
color: Colors.white,
),
),
),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
color: Colors.white,
onPressed: () {
showSearch(
context: context,
delegate: CustomSearchDelegate(),
);
},
),
],
)
: null,
body: PagingList<SearchRecipeModel, Blog>(
itemBuilder: (context, blog) => BlogListItem(blog: blog),
loadingWidget: _buildSkeleton(),
lengthLoadingWidget: 3,
),
);
}
Widget _buildSkeleton() {
key: UniqueKey();
return Padding(
padding: const EdgeInsets.only(
left: 16.0,
right: 16.0,
bottom: 24.0,
top: 12.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Skeleton(height: 200),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const Skeleton(width: 120),
const Skeleton(width: 80),
],
),
const SizedBox(height: 16),
const Skeleton(),
],
),
);
}
#override
Widget buildSuggestions(BuildContext context) {
// This method is called everytime the search term changes.
// If you want to add search suggestions as the user enters their search term, this is the place to do that.
return Column();
}
}
Solution
#VORiAND is using the Library Provider.
The value watched in the Consumer is a List of Objects.
To 'force' the re-draw of the view, he had to either
Set his list of Objects to null, notify the listeners, update his list, notify the listeners.
_list = null;
notifyListeners();
_list = await fetchDatasFromService();
notifyListeners();
or
Re-create a new List Object and notify the Listeners
final datasFromService = await fetchDatasFromService();
_list = List.from(datasFromService);
notifyListeners();
Original Answer:
There are multiple ways to refresh a view after some data manipulation.
Without any State Management library :
If you're developing in 'vanilla' : you'll have to execute your data operations and then 'force' a refresh of the UI once it's done.
The method to use in order to refresh the UI is setState((){});
Note : For this to work, you HAVE to be in a StatefulWidget
Here is a fully working example :
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
#override
void initState() {
super.initState();
//Triggering my async loading of datas
calculateCounter().then((updatedCounter){
//The `then` is Triggered once the Future completes without errors
//And here I can update my var _counter.
//The setState method forces a rebuild of the Widget tree
//Which will update the view with the new value of `_counter`
setState((){
_counter = updatedCounter;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Current counter value:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
);
}
Future<int> calculateCounter() async {
//Demo purpose : it'll emulate a query toward a Server for example
await Future.delayed(const Duration(seconds: 3));
return _counter + 1;
}
}
Important note : Consider triggering your async requests in the initState or in your afterFirstLayout methods.
If you trigger it in the build method you'll end up with unwanted loops.
The above solution will work as long as you want to update the Widget which triggered the request.
If you want to update the ListRecipeScreen widget after some data manipulation in your CustomSearchDelegate, you'll have to call the setState method IN the ListRecipeScreen.
To trigger this setState in the parent Widget, you could use a Callback method.
In the following example, MyHomePage would be your ListRecipeScreen and OtherWidget would be your CustomSearchDelegate
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Current counter value:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
OtherWidget(callback: (counterValue) {
//This callback can be called any time by the OtherWidget widget
//Once it's trigger, the method I'm writing in will be triggered.
//Since I want to update my Widget MyHomePage, I call setState here.
setState(() {
_counter = counterValue;
});
})
],
),
),
);
}
}
class OtherWidget extends StatefulWidget {
const OtherWidget({required this.callback, Key? key}) : super(key: key);
final Function(int counter) callback;
#override
State<OtherWidget> createState() => _OtherWidgetState();
}
class _OtherWidgetState extends State<OtherWidget> {
#override
void initState() {
super.initState();
//Triggering my async loading of datas
calculateCounter().then((updatedCounter) {
//The `then` is Triggered once the Future completes without errors
//And here I can trigger the Callback Method.
//You can call here the Callback method passed as parameter,
//Which will trigger the method written in the parent widget
widget.callback(updatedCounter);
});
}
#override
Widget build(BuildContext context) {
return Container();
}
Future<int> calculateCounter() async {
//Demo purpose : it'll emulate a query toward a Server for example
await Future.delayed(const Duration(seconds: 3));
return 12;
}
}
Note: It looks like your delegate is updating a value stored as a Global variable.
In this case, you don't even need to create a Callback method with a parameter (like I did in the OtherWidget : you could simply use a Function without any params, or a VoidCallback
With a State Management Library
As you can see with my answer above, it's not that hard to refresh a view after some data manipulations.
But what if you have to refresh a Widget which isn't a direct parent of the Widget manipulating the datas ?
You could use a cascade of Callbacks (don't do that please) or an InheritedWidget, but those two solutions will get harder to maintain as your project grows.
For this reason, there are a lot of State Management libraries which were developed.
The following example showcases how it'd work with the Library Provider :
I create a Controller for my page which will manipulate my datas.
This controller extends ChangeNotifier so I can notify when the manipulation is done.
class HomePageController extends ChangeNotifier {
// I exported your global var in this Controller
String _searchTerms = '';
String get searchTerms => _searchTerms;
Future<void> calculateCounter() async {
//Demo purpose : it'll emulate a query toward a Server for example
await Future.delayed(const Duration(seconds: 3));
//Updating the class variable
_searchTerms = 'New value entered by the user';
//Method provided by the ChangeNotifier extension
//It'll notify all the Consumers that a value has been changed
notifyListeners();
}
}
Injection of the Controller in the Widgets Tree and Consuming of the value it holds.
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
//Injecting our HomePageController in the tree, and listening to it's changes
body: ChangeNotifierProvider<HomePageController>(
create: (_) => HomePageController(),
builder: (context, _) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Current counter value:',
),
//The Consumer listens to every changes in the HomePageController
//It means that every time the notifyListeners() is called
//In the HomePageController, the cildren of the Consumer
//Will check if they have to be re-drawn
Consumer<HomePageController>(
builder: ((_, controller, __) {
return Text(
controller.searchTerms,
style: Theme.of(context).textTheme.headline4,
);
}),
),
const OtherWidget()
],
),
);
},
),
);
}
}
In the child widget, I retrieve a reference to my HomePageController and trigger the async request.
Once the data manipulation is done, the notifyListeners() method will trigger every Consumer<HomePageController>
class OtherWidget extends StatefulWidget {
const OtherWidget({Key? key}) : super(key: key);
#override
State<OtherWidget> createState() => _OtherWidgetState();
}
class _OtherWidgetState extends State<OtherWidget> {
#override
void initState() {
super.initState();
//Getting the instance of the HomePageController defined in the parent widget
final parentController = Provider.of<HomePageController>(context, listen: false);
//Triggering the data manipulation
parentController.calculateCounter();
}
#override
Widget build(BuildContext context) {
return Container();
}
}
The code above is specific to the Provider lib, but the logic is similar in every State Management library :)
To make the widget "redraw", you need to call the setState() method like this:
setState(() {
// Here you can fix widget vars values;
});
For that you just need to call setState((){}), this will notify the framework that the internal state of the object has changed and will redraw the widget.
Documentation SetState
So your listing inside any dialog box because haven't run anything but i guess i know the answer please let me know are your doing in any dialog or in main screen.
So if you are showing into the any dialog then i have added a code for example like you need to statefulbuilder which is comes with it's own setState for inner rebuilt the inner UI
int i = 0;
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Center(
child: GestureDetector(
onTap: () {
showDialog(
context: context,
builder: (c) {
return StatefulBuilder(builder: (context, setStateInner) {
return Dialog(
backgroundColor: Colors.transparent,
elevation: 0,
child: InkWell(
onTap: () {
setStateInner(() {
++i;
print("$i");
});
},
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
//width: 100,
color: Theme.of(context)
.dialogBackgroundColor,
padding: const EdgeInsets.all(15),
child: Column(
children: <Widget>[Text("$i")]))
],
),
)));
});
});
},
child: Text("Tap me"),
),
),
),
);
}
I was trying to update data (add, edit, delete) from my list using API, when I clicked something to update the list e.g deleting an item it showed a successfully updated. but in the Debug Console, there's an error that says
The following assertion was thrown while finalizing the widget tree:
Duplicate GlobalKey detected in the widget tree. The following GlobalKey
was specified multiple times in the widget tree. This will lead to
parts of the widget tree being truncated unexpectedly, because the
second time a key is seen, the previous instance is moved to the new
location. The key was:
[LabeledGlobalKey #47c37] This was determined by noticing that after the widget with the above global key was moved out
of its previous parent, that previous parent never updated during this
frame, meaning that it either did not update at all or updated before
the widget was moved, in either case implying that it still thinks
that it should have a child with that global key.
And when I tried going back to the Previous screen it just showed Blank Screen
Here's the code
Model
import 'dart:convert';
class Nasabah {
int id;
String nama_debitur;
String alamat;
String no_telp;
String no_ktp;
String no_selular;
Nasabah({
this.id = 0,
this.nama_debitur,
this.alamat,
this.no_telp,
this.no_ktp,
this.no_selular,
});
factory Nasabah.fromJson(Map<String, dynamic> json) => Nasabah(
id: json["id"],
nama_debitur: json["nama_debitur"],
alamat: json["alamat"],
no_telp: json["no_telp"],
no_ktp: json["no_ktp"],
no_selular: json["no_selular"],
);
factory Nasabah.fromMap(Map<String, dynamic> map) => Nasabah(
id: map["id"],
nama_debitur: map["nama_debitur"],
alamat: map["alamat"],
no_telp: map["no_telp"],
no_ktp: map["no_ktp"],
no_selular: map["no_selular"],
);
Map<String, dynamic> toJson() => {
"id": id,
"nama_debitur": nama_debitur,
"alamat": alamat,
"no_telp": no_telp,
"no_ktp": no_ktp,
"no_selular": no_selular,
};
#override
String toString() {
return 'Nasabah{id: $id, nama_debitur: $nama_debitur, alamat: $alamat, no_telp: $no_telp, no_ktp: $no_ktp, no_selular: $no_selular}';
}
}
class NasabahResult {
String status;
List<Nasabah> data = new List<Nasabah>();
NasabahResult({
this.status,
this.data,
});
factory NasabahResult.fromJson(Map<String, dynamic> data) => NasabahResult(
status: data["status"],
data: List<Nasabah>.from(
data["data"].map((item) => Nasabah.fromJson(item))),
);
}
NasabahResult nasabahResultFromJson(String jsonData) {
final data = json.decode(jsonData);
return NasabahResult.fromJson(data);
}
String nasabahToJson(Nasabah nasabah) {
final jsonData = nasabah.toJson();
return json.encode(jsonData);
}
Service
import 'package:flutter_auth/Models/nasabah.dart';
import 'dart:convert';
import 'package:http/http.dart' show Client;
class ApiService {
final String baseUrl = '192.168.100.242:8080';
Client client = Client();
Future<List<Nasabah>> getNasabah() async {
final response = await client.get('http://$baseUrl/api/mstdebitur');
print(response.body);
if (response.statusCode == 200) {
final nasabah = (json.decode(response.body) as List)
.map((e) => Nasabah.fromMap(e))
.toList();
return nasabah;
} else {
throw Exception('Failed to load post');
}
}
Future<bool> createNasabah(Nasabah data) async {
String url = new Uri.http("$baseUrl", "/api/mstdebitur/").toString();
final response = await client.post(url,
headers: {"Content-Type": "application/json"},
body: nasabahToJson(data));
if (response.statusCode == 201) {
return true;
} else {
return false;
}
}
Future<bool> updateNasabah(Nasabah data) async {
String url =
new Uri.http("$baseUrl", "/api/mstdebitur/${data.id}").toString();
final response = await client.put(url,
headers: {"Content-Type": "application/json"},
body: nasabahToJson(data));
if (response.statusCode == 200) {
return true;
} else {
return false;
}
}
Future<bool> deleteNasabah(int id) async {
String url = new Uri.http("$baseUrl", "/api/mstdebitur/$id").toString();
final response = await client.delete(url);
if (response.statusCode == 200) {
return true;
} else {
return false;
}
}
}
Home
import 'package:flutter/material.dart';
import 'package:flutter_auth/Models/nasabah.dart';
import 'package:flutter_auth/network/nasabah_service.dart';
import 'FormAddNasabah.dart';
import 'ListNasabah.dart';
GlobalKey<ScaffoldState> _scaffoldState = GlobalKey<ScaffoldState>();
// ignore: must_be_immutable
class DataNasabah extends StatefulWidget {
DataNasabah({Key key}) : super(key: key);
String title;
#override
_DataNasabahState createState() => _DataNasabahState();
}
class _DataNasabahState extends State<DataNasabah> {
ApiService apiService;
ListNasabah _listNasabah;
List<Nasabah> data;
#override
void initState() {
super.initState();
apiService = ApiService();
_listNasabah = new ListNasabah(apiService: apiService);
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldState,
appBar: AppBar(
title: Text(
'Data Nasabah',
style: TextStyle(color: Colors.white),
),
actions: <Widget>[
GestureDetector(
onTap: () async {
var result = await Navigator.push(
_scaffoldState.currentContext,
MaterialPageRoute(builder: (BuildContext context) {
return FormAddNasabah(nasabah: null);
}),
);
if (result != null) {
setState(() {});
}
},
child: Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Icon(
Icons.add,
color: Colors.white,
),
),
)
],
),
body: _listNasabah.createViewList(),
);
}
}
List
import 'package:flutter/material.dart';
import 'package:flutter_auth/Models/nasabah.dart';
import 'package:flutter_auth/network/nasabah_service.dart';
import 'package:flutter_auth/screens/Menu/DataNasabah/FormAddNasabah.dart';
import 'package:flutter_auth/screens/Menu/DataNasabah/NasabahHome.dart';
import 'package:rflutter_alert/rflutter_alert.dart';
class ListNasabah {
ApiService apiService;
ListNasabah({this.apiService});
Widget createViewList() {
return SafeArea(
child: FutureBuilder(
future: apiService.getNasabah(),
builder: (BuildContext context, AsyncSnapshot<List<Nasabah>> snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(
'Something wrong with message: ${snapshot.error.toString()}',
textAlign: TextAlign.center,
),
);
} else if (snapshot.connectionState == ConnectionState.done) {
List<Nasabah> nasabah = snapshot.data;
return nasabahListView(nasabah);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
Widget nasabahListView(List<Nasabah> listnasabah) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
child: ListView.builder(
shrinkWrap: true,
itemBuilder: (context, index) {
Nasabah nasabah = listnasabah[index];
return Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
nasabah.nama_debitur,
style: Theme.of(context).textTheme.bodyText1,
),
Text(nasabah.alamat),
Text(nasabah.no_ktp),
Text(nasabah.no_telp),
Text(nasabah.no_selular),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FlatButton(
onPressed: () {
apiService.deleteNasabah(nasabah.id).then((value) =>
Navigator.of(context).pushNamed('start'));
},
child: Text(
"Hapus",
style: TextStyle(color: Colors.red),
),
),
FlatButton(
onPressed: () async {
var result = await Navigator.push(context,
MaterialPageRoute(builder: (context) {
return FormAddNasabah(
nasabah: nasabah,
);
}));
},
child: Text(
'Edit',
style: TextStyle(color: Colors.blue),
),
),
],
)
],
),
),
),
);
},
itemCount: listnasabah.length,
),
);
}
}
Form
import 'package:flutter/material.dart';
import 'package:flutter_auth/Models/nasabah.dart';
import 'package:flutter_auth/network/nasabah_service.dart';
import 'package:flutter_auth/screens/Menu/DataNasabah/NasabahHome.dart';
import 'package:rflutter_alert/rflutter_alert.dart';
class FormAddNasabah extends StatefulWidget {
Nasabah nasabah;
FormAddNasabah({this.nasabah});
#override
_FormAddNasabahState createState() => _FormAddNasabahState();
}
class _FormAddNasabahState extends State<FormAddNasabah> {
ApiService apiService;
TextEditingController _contNama = TextEditingController();
TextEditingController _contAlamat = TextEditingController();
TextEditingController _contNoTelp = TextEditingController();
TextEditingController _contNoKtp = TextEditingController();
TextEditingController _contNoSelular = TextEditingController();
#override
void initState() {
super.initState();
apiService = ApiService();
if (widget.nasabah != null) {
_contNama.text = widget.nasabah.nama_debitur;
_contAlamat.text = widget.nasabah.alamat;
_contNoTelp.text = widget.nasabah.no_telp;
_contNoKtp.text = widget.nasabah.no_ktp;
_contNoSelular.text = widget.nasabah.no_selular;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
iconTheme: IconThemeData(color: Colors.white),
title: Text(
widget.nasabah == null ? "Tambah Nasabah" : "Edit Nasabah",
style: TextStyle(color: Colors.white),
),
),
body: Stack(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
_buildTextField(_contNama, "Nama Nasabah"),
_buildTextField(_contAlamat, "Alamat"),
_buildTextField(_contNoTelp, "No Telp"),
_buildTextField(_contNoKtp, "No KTP"),
_buildTextField(_contNoSelular, "No Selular"),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: RaisedButton(
child: Text(widget.nasabah == null ? "Tambah" : "Edit"),
color: Colors.blue,
onPressed: () {
int id = 0;
if (widget.nasabah != null) {
id = widget.nasabah.id;
}
Nasabah nasabah = Nasabah(
id: id,
nama_debitur: _contNama.text,
alamat: _contAlamat.text,
no_telp: _contNoTelp.text,
no_ktp: _contNoKtp.text,
no_selular: _contNoSelular.text);
if (widget.nasabah == null) {
apiService.createNasabah(nasabah);
} else {
apiService.updateNasabah(nasabah);
}
Navigator.of(context)
.pushNamed('start')
.whenComplete(() => Navigator.pop(context));
},
),
)
],
),
)
],
),
);
}
Widget _buildTextField(TextEditingController _cont, String label) {
return TextField(
controller: _cont,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
labelText: label,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
);
}
}
Thank you for your attention!
hi guys I have an error I don't know how to solve it please reply as fast as u can please
this is a data file:
import 'dart:convert';
import 'package:newspaper/models/article.dart';
import 'package:http/http.dart' as http;
class News {
List<ArticleModel> news = [];
Future<void> getNews() async {
var url = Uri.parse(
"https://newsapi.org/v2/top-headlines?country=in&category=business&apiKey=5c2be84a9b8548ab8dde4cfa1eaa1023");
var response = await http.get(url);
var jsonData = jsonDecode(response.body);
if (jsonData['status'] == "ok") {
jsonData["articles"].forEach((element) {
if (element["urlToImage"] != null && element['description'] != null) {
ArticleModel articleModel = ArticleModel(
title: element["title"],
author: element["author"],**--> facing error here the error is(Exception has occurred.
_TypeError (type 'Null' is not a subtype of type 'String'))**
description: element["description"],
url: element["url"],
urlToImage: element["urlToImage"],
content: element["context"],
);
news.add(articleModel);
}
});
}
}
}
the modulo for this file is as bellow:
class ArticleModel {
String author;
String title;
String description;
String url;
String urlToImage;
String content;
ArticleModel({
required this.author,
required this.title,
required this.description,
required this.url,
required this.urlToImage,
required this.content,
});
}
these are the code for data processing
and in the bellow code I call the data I received
// ignore_for_file: prefer_typing_uninitialized_variables
import 'package:flutter/material.dart';
import 'package:newspaper/helper/data.dart';
import 'package:newspaper/helper/news.dart';
import 'package:newspaper/models/article.dart';
import 'package:newspaper/models/categorymodel.dart';
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
List<CategoryModel> categories = <CategoryModel>[];
List<ArticleModel> articles = <ArticleModel>[];
// ignore: non_constant_identifier_names
bool _Loading = true;
#override
void initState() {
super.initState();
categories = getCategories();
getNews();
}
getNews() async {
News newsClass = News();
await newsClass.getNews();
articles = newsClass.news;
setState(() {
_Loading = false;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0.0,
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text(
"News",
style: TextStyle(color: Colors.black),
),
Text(
"Paper",
style: TextStyle(color: Colors.blue),
),
],
),
),
// ignore: avoid_unnecessary_containers
body: _Loading
? const Center(child: CircularProgressIndicator())
: SingleChildScrollView(
child: Column(
children: [
///categories
// ignore: sized_box_for_whitespace
Container(
height: 70,
padding: const EdgeInsets.symmetric(horizontal: 16),
child: ListView.builder(
itemCount: categories.length,
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return CategoryTile(
imageUrl: categories[index].imageUrl,
categoryName: categories[index].categoryName,
);
}),
),
///blogs
// ignore: avoid_unnecessary_containers
Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: articles.length,
itemBuilder: (context, index) {
return BlogTile(
imageUrl: articles[index].urlToImage,
title: articles[index].title,
desc: articles[index].description,
);
},
),
)
],
),
),
);
}
}
class CategoryTile extends StatelessWidget {
final imageUrl, categoryName;
// ignore: use_key_in_widget_constructors
const CategoryTile({this.imageUrl, this.categoryName});
#override
Widget build(BuildContext context) {
// ignore: avoid_unnecessary_containers
return GestureDetector(
onTap: () {},
child: Container(
margin: const EdgeInsets.only(right: 16),
child: Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(6),
child: Image.network(imageUrl,
width: 120, height: 60, fit: BoxFit.cover),
),
Container(
alignment: Alignment.center,
width: 120,
height: 60,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
color: Colors.black26,
),
child: Text(
categoryName,
style: const TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
),
],
),
),
);
}
}
class BlogTile extends StatelessWidget {
final String imageUrl, title, desc;
// ignore: use_key_in_widget_constructors
const BlogTile(
{required this.imageUrl, required this.title, required this.desc});
#override
Widget build(BuildContext context) {
// ignore: avoid_unnecessary_containers
return Container(
child: Column(
children: [
Image.network(imageUrl),
Text(title),
Text(desc),
],
),
);
}
}
I am facing this error
_TypeError (type 'Null' is not a subtype of type 'String')
in the first code I have mentioned it with an arrow in which line I face please help as quickly as possible, please
The error means that element["author"] can be null, however your author variable can't be null. Try null checking it :
author: element["author"] ?? ''
Based on the api response, author can be null, but your ArticleModel passes is non-nullable i.e. String. To fix this, make it nullable and handle the case for which it is null.
import 'package:newspaper/models/article.dart';
import 'package:http/http.dart' as http;
class News {
List<ArticleModel> news = [];
Future<void> getNews() async {
var url = Uri.parse(
"https://newsapi.org/v2/top-headlines?country=in&category=business&apiKey=5c2be84a9b8548ab8dde4cfa1eaa1023");
var response = await http.get(url);
var jsonData = jsonDecode(response.body);
if (jsonData['status'] == "ok") {
jsonData["articles"].forEach((element) {
if (element["urlToImage"] != null && element['description'] != null) {
ArticleModel articleModel = ArticleModel(
title: element["title"],
author: element["author"] ?? 'unknown', **--> give default value when null where
`??` means value to assign is value on left is null
description: element["description"],
url: element["url"],
urlToImage: element["urlToImage"],
content: element["context"],
);
news.add(articleModel);
}
});
}
}
}
I think that this error comes from parameter urlToImage. Try below
class ArticleModel {
String author;
String title;
String description;
String url;
String? urlToImage;
String content;
ArticleModel({
required this.author,
required this.title,
required this.description,
required this.url,
required this.urlToImage,
required this.content,
});
}
Watch this video to understand the problem properly.
When I build the project, the widgets are not showing, but when I re-save the project, the widgets are showing. I'm giving my code below.
main.dart :-
(this is the main file which holds routes to each and every page)
import 'package:flutter/material.dart';
import 'package:player_profile/first.dart';
import 'package:player_profile/second.dart';
import 'package:player_profile/third.dart';
void main() {
runApp(MaterialApp(
initialRoute: '/first',
routes: {
'/first': (context) => First(),
'/second': (context) => Second(),
'/third': (context) => Third()
},
));
}
first.dart :-
(this is the first page of the app which contains a list of widgets of all team data. after clicking on a widget of team it'll go to the second page)
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:player_profile/teamData.dart';
import 'dart:convert' as convert;
class First extends StatefulWidget {
#override
_FirstState createState() => _FirstState();
}
class _FirstState extends State<First> {
List<Widget> teamList = [];
void getTeamList() async {
var url = Uri.parse('https://www.balldontlie.io/api/v1/teams');
http.Response response = await http.get(url);
if (response.statusCode == 200) {
var jsonData = convert.jsonDecode(response.body) as Map<String, dynamic>;
if (jsonData['data'] != null) {
jsonData['data'].forEach((v) {
// teamData.add(new TeamData.fromJson(v));
teamList.add(Padding(
padding: const EdgeInsets.all(8.0),
// ignore: deprecated_member_use
child: RaisedButton(
onPressed: () {
Navigator.pushNamed(context, '/second');
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'Name : ${TeamData.fromJson(v).name}',
style: TextStyle(
color: Colors.red,
fontSize: 15.0,
),
),
Text(
'Abbreviation : ${TeamData.fromJson(v).abbreviation}',
style: TextStyle(
color: Colors.blue,
fontSize: 15.0,
),
),
Text(
'City : ${TeamData.fromJson(v).city}',
style: TextStyle(
color: Colors.blue,
fontSize: 15.0,
),
),
Text(
'Conference : ${TeamData.fromJson(v).conference}',
style: TextStyle(
color: Colors.blue,
fontSize: 15.0,
),
),
Text(
'Division : ${TeamData.fromJson(v).division}',
style: TextStyle(
color: Colors.blue,
fontSize: 15.0,
),
)
],
)),
));
});
}
}
}
#override
void initState() {
super.initState();
getTeamList();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
title: Text('Team'),
),
body: GridView.count(
crossAxisCount: 2,
children: teamList,
));
}
}
second.dart :-
(this is the second page of the app which contains all the player details widgets received from http call. it receives data from http but widgets are not showing. widgets are showing only after resaving the project. after setState() it's not re-building the page.)
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert' as convert;
import 'package:player_profile/singlePlayer.dart';
class Second extends StatefulWidget {
#override
_SecondState createState() => _SecondState();
}
class _SecondState extends State<Second> {
List<SinglePlayerData> playersList = [];
List<Widget> showPlayers = [];
getAllPlayers() async {
var url = Uri.parse('https://www.balldontlie.io/api/v1/players');
http.Response response = await http.get(url);
if (response.statusCode == 200) {
var jsonData = convert.jsonDecode(response.body) as Map<String, dynamic>;
jsonData['data'].forEach((v) {
SinglePlayerData playerData = SinglePlayerData(
v['id'],
v['first_name'],
v['height_feet'],
v['height_inches'],
v['last_name'],
v['position'],
v['weight_pounds'],
v['team']['id'],
v['team']['abbreviation'],
v['team']['city'],
v['team']['conference'],
v['team']['division'],
v['team']['full_name'],
v['team']['name']);
playersList.add(playerData);
setState(() {
print('setState');
showPlayers.add(Padding(
padding: const EdgeInsets.all(8.0),
// ignore: deprecated_member_use
child: RaisedButton(
onPressed: () {
Navigator.pushNamed(context, '/third',
arguments: {'data': playerData});
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
height: 75.0,
width: 75.0,
child: Image(image: AssetImage('images/player2.png'))),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(
'Name : ${playerData.firstName} ${playerData.lastName}',
style: TextStyle(fontSize: 15.0, color: Colors.red),
),
),
Text(
'Team : ${playerData.name}',
style: TextStyle(fontSize: 15.0, color: Colors.blue),
),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(
'Position : ${playerData.position}',
style: TextStyle(fontSize: 15.0, color: Colors.purple),
),
)
],
)),
));
});
});
print(playersList[1].firstName);
}
}
#override
void initState() {
print('initState');
super.initState();
}
#override
Widget build(BuildContext context) {
print('build');
getAllPlayers();
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
title: Text('Players'),
),
body: GridView.count(
crossAxisCount: 2,
children: showPlayers,
));
}
}
teamData.dart (Model class) :-
(this is the model class for team data)
class TeamData {
int? id;
String? abbreviation;
String? city;
String? conference;
String? division;
String? fullName;
String? name;
TeamData(this.id, this.abbreviation, this.city, this.conference,
this.division, this.fullName, this.name);
TeamData.fromJson(Map<String, dynamic> json)
: id = json['id'],
abbreviation = json['abbreviation'],
city = json['city'],
conference = json['conference'],
division = json['division'],
fullName = json['full_name'],
name = json['name'];
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['abbreviation'] = this.abbreviation;
data['city'] = this.city;
data['conference'] = this.conference;
data['division'] = this.division;
data['full_name'] = this.fullName;
data['name'] = this.name;
return data;
}
}
singlePlayer.dart (Model class) :-
(this is the model class for players details data)
class TeamData {
int? id;
String? abbreviation;
String? city;
String? conference;
String? division;
String? fullName;
String? name;
TeamData(this.id, this.abbreviation, this.city, this.conference,
this.division, this.fullName, this.name);
TeamData.fromJson(Map<String, dynamic> json)
: id = json['id'],
abbreviation = json['abbreviation'],
city = json['city'],
conference = json['conference'],
division = json['division'],
fullName = json['full_name'],
name = json['name'];
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['abbreviation'] = this.abbreviation;
data['city'] = this.city;
data['conference'] = this.conference;
data['division'] = this.division;
data['full_name'] = this.fullName;
data['name'] = this.name;
return data;
}
}
Use FutureBuilder when ever you wanna fetch something for future.
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.cyan,
body: FutureBuilder<Transaction>(
future: getAllPlayers(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting)
return CircularProgressIndicator();
if (snapshot.hasError)
return Column(
children: [
Text(
snapshot.error.toString(),
),
],
);
if (snapshot.hasData) {
var data = snapshot.data!;
///todo:: add you design model
return GridView.count(
crossAxisCount: 2,
children: showPlayers,
);
}
return Text('Something was wrong!');
},
),
);
}
let me know if it solve your problem.
I think you just need to call setState((){}); at the end of getTeamList()
I need to shape myUrl inside Future related to username which I got from myfirstpage, I can get the name and use it in my homepage title but I couldn't figured out how can I use it in myUrl(instead of "$_name"),
Probably I made a mistake with point to data with "pushNamed(MyHomePage.routeName);" actually I don't need that value in MyHomePage, I just need it for shape myUrl line, also I tried to make "_name" value as a global value etc just not couldn't succeed..
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
import 'dart:convert';
import 'dart:async';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
new GlobalKey<RefreshIndicatorState>();
Future<bool> saveNamedPreference(String name) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString("name", name);
return prefs.commit();
}
Future<String> getNamePreferences() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String name = prefs.getString("name");
return name;
}
Payload payloadFromJson(String str) {
return Payload.fromJson(json.decode(str));
}
String payloadToJson(Payload data) {
return json.encode(data.toJson());
}
Future<Payload> getData() async{
String myUrl = 'http://lunedor.pythonanywhere.com/query?username=$_name';
http.Response response = await http.get(myUrl);
print(myUrl);
return response == null ? getData() : payloadFromJson(response.body);
}
class Payload {
String moviecast;
String moviedirectors;
String moviegenre;
String movieposterurl;
String movierating;
String movieruntime;
String moviesummary;
String movietitle;
String moviewriters;
String movieyear;
Payload({
this.moviecast,
this.moviedirectors,
this.moviegenre,
this.movieposterurl,
this.movierating,
this.movieruntime,
this.moviesummary,
this.movietitle,
this.moviewriters,
this.movieyear,
});
factory Payload.fromJson(Map<String, dynamic> json) => Payload(
moviecast: json["Actors"],
moviedirectors: json["Director"],
moviegenre: json["Genre"],
movieposterurl: json["Poster"],
movierating: json["imdbRating"],
movieruntime: json["Runtime"],
moviesummary: json["Plot"],
movietitle: json["Title"],
moviewriters: json["Writer"],
movieyear: json["Year"],
);
Map<String, dynamic> toJson() => {
"moviecast": moviecast,
"moviedirectors": moviedirectors,
"moviegenre": moviegenre,
"movieposterurl": movieposterurl.replaceAll('300.jpg', '900.jpg'),
"movierating": movierating,
"movieruntime": movieruntime,
"moviesummary": moviesummary,
"movietitle": movietitle,
"moviewriters": moviewriters,
"movieyear": movieyear,
};
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Random Movie',
theme: new ThemeData(
primarySwatch: Colors.grey,
),
home: new MyFirstPage(),
routes: <String, WidgetBuilder>{
MyHomePage.routeName: (context) => new MyHomePage(),
},
);
}
}
class MyFirstPage extends StatefulWidget {
#override
_MyFirstPageState createState() => new _MyFirstPageState();
}
class _MyFirstPageState extends State<MyFirstPage>{
var _controller = new TextEditingController();
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
backgroundColor: Colors.blueGrey,
centerTitle: true,
title: new Text("Please enter your Trakt username",
style: new TextStyle(
fontSize: 18.0,
color: Colors.white,
),
),
),
body: new ListView(
children: <Widget>[
new ListTile(
title: new TextField(
controller: _controller,
),
),
new ListTile(
title: new RaisedButton(
child: new Text("Submit"),
onPressed:(){setState(() {
saveName();
});
}),
)
],
),
);
}
void saveName() {
String name = _controller.text;
saveNamedPreference(name).then((bool committed) {
Navigator.of(context).pushNamed(MyHomePage.routeName);
});
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
static String routeName = "/myHomePage";
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
Payload payload;
class _MyHomePageState extends State<MyHomePage> {
Modal modal = Modal();
bool isLoading = true;
String _name = "";
#override
void initState() {
// TODO: implement initState
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {loadData();
WidgetsBinding.instance.addPostFrameCallback((_) => _refreshIndicatorKey.currentState.show());
getNamePreferences().then(updateName);
});
}
void loadData() async {
payload = await getData();
isLoading = false;
setState(() {});
print('${payload.movieposterurl.replaceAll('300.jpg', '900.jpg')}');
}
void updateName(String name) {
setState(() {
this._name = name;
ValueKey(_name);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar:
AppBar(
backgroundColor: Colors.blueGrey,
title:
isLoading ? Center(child: CircularProgressIndicator()):Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Center(
child: Text('${payload.movietitle}', maxLines: 2, textAlign: TextAlign.center,
style: new TextStyle(
fontSize: 18.0,
color: Colors.white,
),
),
),
Text('${payload.movieyear}' + " - " + _name + " - " + '${payload.movierating}',
style: new TextStyle(
fontSize: 14.0,
color: Colors.black,
fontStyle: FontStyle.italic,),
),
]
),
),
),
body:
isLoading ? Center(child: CircularProgressIndicator()):
RefreshIndicator(
key: _refreshIndicatorKey,
onRefresh: () async{payload = await getData();
isLoading = false;
setState(() {});
},
child: Center(
child:ListView(
shrinkWrap: true,
children: [
FittedBox(
alignment: Alignment.center,
child:
Image.network('${payload.movieposterurl.replaceAll('300.jpg', '900.jpg')}'),
),
]
)
)
),
bottomNavigationBar: isLoading ? Center(child: CircularProgressIndicator()):
BottomAppBar(
child: Container(
color: Colors.grey,
child: SizedBox(
width: double.infinity,
child:
FlatButton(
color: Colors.grey,
textColor: Colors.black,
onPressed: () {
modal.mainBottomSheet(context);
},
child: Text("Details",
style: TextStyle(fontSize: 16.0),),
),
),
),
),
);
}
}
class Modal {
mainBottomSheet(BuildContext context) {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
_createTile(
context, "Genre: " + payload.moviegenre + "\n" + "Runtime: " + payload.movieruntime, Icons.local_movies,
_action),
_createTile(
context, "Director: " + payload.moviedirectors,
Icons.movie_creation,
_action),
_createTile(
context, "Writer: " + payload.moviewriters,
Icons.movie,
_action),
_createTile(
context, "Cast: " + payload.moviecast, Icons.chrome_reader_mode,
_action),
_createTile(
context, "Summary: " + payload.moviesummary, Icons.recent_actors, _action),
],
),
);
}
);
}
ListTile _createTile(BuildContext context, String name, IconData icon,
Function action) {
return ListTile(
leading: Icon(icon),
title: Text(name),
onTap: () {
Navigator.pop(context);
action();
},
);
}
_action() {
print('action');
}
}
Not completely sure I got your question, but it should be enough to modify your getData() method to accept a String in parameters, at the moment getData() is a top-level function that doesn't know the _name value because is a private instance variable of _MyHomePageState
Future<Payload> getData(String name) async{
String myUrl = 'http://lunedor.pythonanywhere.com/query?username=$name';
http.Response response = await http.get(myUrl);
print(myUrl);
return response == null ? getData() : payloadFromJson(response.body);
}
And then in your loadData() method pass the correct value
void loadData() async {
payload = await getData(_name);
isLoading = false;
setState(() {});
print('${payload.movieposterurl.replaceAll('300.jpg', '900.jpg')}');
}
One last thing, you should add the "reload" logic when you change the name
void updateName(String name) {
setState(() {
isLoading = true;
this._name = name;
ValueKey(_name);
loadData();
});
}
Personally I think that the Payload variable should stay inside your _MyHomePageState class