Information from api doesn't show - Flutter - android

I have a problem with Future builder in Flutter. It gets the info from api successfully but doesn't show it. When I put print and print the info from api, it is ok and it shows the movies name without any problems. here is my code:
class Search extends StatefulWidget {
final String value;
Search({Key key, String this.value}) : super(key: key);
#override
_SearchState createState() => _SearchState();
}
class _SearchState extends State<Search> {
var title;
Future getSearch({index}) async {
http.Response response = await http.get(
'https://api.themoviedb.org/3/search/company?api_key=6d6f3a650f56fd6b3347428018a20a73&query=' +
widget.value);
var results = json.decode(response.body);
setState(() {
this.title = results['results'];
});
return title[index]['name'];
}
getName(index) {
return title[index]['name'];
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: Color(0xff1d1d27),
body: Column(
children: [
Expanded(
child: FutureBuilder(
initialData: [],
future: getSearch(),
builder: (context, snapshot) {
return ListView.builder(itemBuilder: (context, index) {
Padding(
padding:
EdgeInsets.symmetric(horizontal: 30, vertical: 20),
child: Container(
color: Colors.white,
child: Text(getName(index).toString()),
),
);
});
},
))
],
)),
);
}
}

Please Use this code, It works fine to fetch the names and show them on the list,
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class Search extends StatefulWidget {
final String value;
Search({Key key, String this.value}) : super(key: key);
#override
_SearchState createState() => _SearchState();
}
class _SearchState extends State<Search> {
var title;
var results;
getSearch() async {
http.Response response = await http.get(
'https://api.themoviedb.org/3/search/company?api_key=6d6f3a650f56fd6b3347428018a20a73&query=' +
widget.value);
results = json.decode(
response.body); //make it global variable to fetch it everywhere we need
return results['results'][0]['name'];
}
getName(index) {
return results['results'][index]['name'];
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: Color(0xff1d1d27),
body: Column(
children: [
Expanded(
child: FutureBuilder(
// initialData: [],
future: getSearch(),
builder: (context, snapshot) {
String name =
snapshot.data; // to get the data from the getSearch
print(name);
if (snapshot.hasData) {
// if there is data then show the list
return ListView.builder(
itemCount: results['results']
?.length, // to get the list length of results
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.symmetric(
horizontal: 30, vertical: 20),
child: Container(
color: Colors.white,
child: Text(getName(index)
.toString()), // pass the index in the getName to get the name
),
);
});
} else {
// if there is no data or data is not loaded then show the text loading...
return new Text("Loading...",
style: TextStyle(fontSize: 42, color: Colors.white));
}
},
))
],
)),
);
}
}
P.S
To Learn the basics of Futurebuilder You can see this article For more learning
I have commented the code to explain more to you.

Related

flutter How to add loop in final list Widget according to the data from database?

I want to show images in loop which are store in List widget but this are static data.
like this:-
final List imgs = [
"assets/images/people.png",
"assets/images/people.png",
"assets/images/people.png",
"assets/images/people.png",
"assets/images/people.png",
];
now I want to show this list with dynamic data and that's why I have an API that returns the images from the database. now I want to show that images in final List<String> imgs = [] widget. so how do I add the loop in this widget? please help me.
In images, I store the image URL which comes from a database.
I want to show the images in the List widget
here is my code:
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:io';
import 'package:mindmatch/utils/Auth.dart';
import 'package:http_parser/http_parser.dart';
class ProfileImages extends StatefulWidget {
ProfileImages({Key? key}) : super(key: key);
#override
_ProfileImages createState() => _ProfileImages();
}
class _ProfileImages extends State<ProfileImages>{
var UsrID = Auth.prefs?.getString('usrid');
var Imagedata;
var Images = "";
#override
void initState() {
super.initState();
getImageData();
}
getImageData() async{
var res = await http.get(Uri.https('www.*******.net', '/mm_api/index.php',{'act':'usrPhotos','UsrID': '${UsriD}'}));
var jsonData = json.decode(res.body);
var images = jsonData.map<String>((json) => json['image']).toList();
//print(images);
setState(() {
for(var img in images){
imgs.add(img);
}
});
}
final List<String> imgs = [];
#override
Widget build(BuildContext context) {
return
Imagedata != null?GFItemsCarousel(
rowCount: 3,
//itemHeight: MediaQuery.of(context).size.height / 1.5,
children: imgs.map(
(url) {
return Container(
child: ListView.builder(
itemCount: imgs.length,
itemBuilder: (context, index) {
//check the image if it is asset or network
return Container(
margin: EdgeInsets.all(5.0),
child: Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(8.0)),
child:
Image.network(imgs[index]),
),
Positioned(
top: 9,
right: 9,
child: SvgPicture.asset(
width: 30,
'assets/images/close.svg',
height: 30,
),
)
],
),
);
},
),
);
},
).toList(),
): const Center(
child: CircularProgressIndicator(),
);
}
}
at the Images variable, I store the full image URL from the database but how do I show this in List widget please help me with how I show these images in the loop.
here is my JSON data:-
[{"id":"8","image":"5e11b3c030f07b70ee982158bccff41f.jpeg"},{"id":"9","image":"158ad385dff9ec07a40d4401351de434.jpeg"},{"id":"10","image":"af8079a1832c0458b80ac17c865a840b.jpeg"}]
here is a new error please anyone helps me.
Please help with how I show these images.
Extract images from response and added it to the imgs list .
var res = await http.get(Uri.https('www.*******.net', '/mm_api/index.php',{'act':'usrPhotos','UsrID': '${UsrID}'}));
Imagedata = jsonDecode(res.body);
imgs.clear();
ImagesData.forEach((e){
imgs.add(e.image);
})
}```
Then after
ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(8.0)),
child:
//append with baseURL
Image.network(
"https://www.*******.net/files/profile/" + url,
fit: BoxFit.cover,
),
),
use ListView.builder like this:
for asset images
ListView.builder(
itemCount: img.length,
itemBuilder: (BuildContext context, int index) {
return new Image.asset(img[index], fit: BoxFit.cover);
},
);
for images from network
ListView.builder(
itemCount: urls.length,
itemBuilder: (BuildContext context, int index) {
return Image.network(urls[index]);
},
);
}
you can use cached_network_image to get an image from the network only once and then get it from the local cache
for your example after getting an images successfully from database merge them with imgs list
change getImageData to
getImageData() async{
var res = await http.get(Uri.https('www.*******.net', '/mm_api/index.php',{'act':'usrPhotos','UsrID': '${UsrID}'}));
var jsonData = json.decode(res.body);
var images = jsonData.map<String>((json) => json['image']).toList();
setState(() {
for(var img in images){
imgs.add(img);
}
});
}
change build to
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Profile Images"),
),
body: Container(
child: ListView.builder(
itemCount: imgs.length,
itemBuilder: (context, index) {
//check the image if it is asset or network
if(imgs[index].contains("assets")){
return Container(
margin: EdgeInsets.all(10),
child: Image.asset(imgs[index]),
);
}else{
return Container(
margin: EdgeInsets.all(10),
child: Image.network(imgs[index]),
);
}
},
),
),
);
}

How to assign value to variable from future before screen built?

In my application user can have multiple home and multiple rooms for each home. On top of my application I have dropdown box which im trying to set default value to selectedHome by user. Below that dropdown box I am showing the rooms in the home selected by user. In firebase I have rooms collection under each home. I'm getting the selected home data from firebase too. Also to show the rooms in selected home i need to query by home name. I have two FutureBuilder as you can see code below. One of them to get the selectedHome data from firebase and other for the getting the rooms in that home from firebase. As I said before to get the rooms in selected home I need to query by name of the home so I have a parameter which is the value of dropdownbox. In my code the problem is getting the rooms part is working before I get the selectedHome data from firebase and assign it to dropdown value. In this case I'm getting "Null check operator used on a null value".
Basicly the question is how can i assign value from future to variable before screen gets build.
Here you can see the code for getting selected home data from firebase;
Future<String> selectedHome() async {
return await database.selectedHome();
}
Future<String> selectedHome() async {
DocumentSnapshot docS =
await firestore.collection("users").doc(auth.currentUser()).get();
String selectedHome = (docS.data() as Map)["selectedHome"];
return selectedHome;
}
Here you can see the code for getting room data based on selectedHome from firebase;
Future<List<Map>> deviceAndRoomInfo() async {
return database.numberOfRooms(_dropdownValue!);
}
Future<List<Map>> numberOfRooms(String selectedHome) async {
List<Map> prodsList = [];
final snapshot = await firestore
.collection("users")
.doc(auth.currentUser())
.collection("homes")
.doc(selectedHome)
.collection("rooms")
.get();
List listOfRooms = snapshot.docs;
for (int a = 1; a <= listOfRooms.length; a++) {
var productsInRoom = await firestore
.collection("users")
.doc(auth.currentUser())
.collection("homes")
.doc(selectedHome)
.collection("rooms")
.doc(listOfRooms[a - 1]["roomName"])
.collection("products")
.get();
List prodList = productsInRoom.docs
.map((e) => DeviceModel.fromMap(e.data()))
.toList();
Map qq = {
"roomName": listOfRooms[a - 1]["roomName"],
"deviceInfo": prodList
};
prodsList.add(qq);
}
return prodsList;
}
Here you can see the code for screen contains 2 future builder that i told;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shelly_ess_production/constants.dart';
import 'package:shelly_ess_production/helper_widgets/loading_widget.dart';
import 'package:shelly_ess_production/screens/home_screen/components/circle_room_data.dart';
import 'package:shelly_ess_production/screens/home_screen/components/device_in_room_card.dart';
import 'package:shelly_ess_production/screens/home_screen/provider/home_screen_provider.dart';
import 'package:shelly_ess_production/screens/models/device_model.dart';
import 'package:shelly_ess_production/size_config.dart';
class Body extends StatefulWidget {
const Body({Key? key}) : super(key: key);
#override
State<Body> createState() => _BodyState();
}
class _BodyState extends State<Body> {
#override
Widget build(BuildContext context) {
var providerHelper =
Provider.of<HomeScreenProvider>(context, listen: false);
return SafeArea(
child: Padding(
padding:
EdgeInsets.symmetric(horizontal: getProportionateScreenWidth(0.07)),
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(
height: getProportionateScreenHeight(0.02),
),
Consumer<HomeScreenProvider>(builder: (context, data, child) {
return FutureBuilder<List<String>>(
future: data.getHomesAndSelected(),
builder: (context, snapshot) {
if (snapshot.hasData) {
data.setDropDownValue = snapshot.data![0];
return DropdownButtonHideUnderline(
child: DropdownButton(
iconEnabledColor: kPrimaryColor,
iconDisabledColor: kPrimaryColor,
style: TextStyle(
color: kPrimaryColor,
fontSize: getProportionateScreenHeight(0.05)),
menuMaxHeight: getProportionateScreenHeight(0.4),
borderRadius: BorderRadius.circular(15),
key: UniqueKey(),
value: data.dropdownValue,
isExpanded: true,
icon: const Icon(Icons.arrow_downward),
onChanged: (String? newValue) async {
data.setDropDownValue = newValue;
await data.changeSelectedHome();
},
items: snapshot.data!
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
alignment: Alignment.center,
value: value,
child: Text(value),
);
}).toList(),
),
);
} else {
return Transform.scale(
scale: 0.5,
child: const Center(
child: CircularProgressIndicator(),
),
);
}
});
}),
SizedBox(
height: getProportionateScreenHeight(0.02),
),
SizedBox(
height: getProportionateScreenHeight(0.14),
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: 5,
itemBuilder: (context, index) {
return CircleRoomData(
title: "Oda Sayısı",
icon: Icons.meeting_room,
content: "8",
);
}),
),
Consumer<HomeScreenProvider>(builder: (context, data, snapshot) {
return FutureBuilder<List<Map>>(
future: data.deviceAndRoomInfo(data.dropdownValue!),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
physics: const NeverScrollableScrollPhysics(),
itemCount: snapshot.data!.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return Column(
children: [
Divider(
thickness:
getProportionateScreenHeight(0.002),
),
Text(
snapshot.data![index]["roomName"],
style: TextStyle(
fontWeight: FontWeight.bold,
color: kSecondaryColor,
fontSize:
getProportionateScreenHeight(0.03)),
),
SizedBox(
height: getProportionateScreenHeight(0.01),
),
Text(
"${(snapshot.data![index]["deviceInfo"] as List).length.toString()} Cihaz",
style:
const TextStyle(color: kSecondaryColor),
),
SizedBox(
height: getProportionateScreenHeight(0.02),
),
GridView.builder(
shrinkWrap: true,
physics:
const NeverScrollableScrollPhysics(),
itemCount: (snapshot.data![index]
["deviceInfo"] as List)
.length,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
),
itemBuilder: (context, indexx) {
print(index);
return DeviceInRoom(
icon: Icons.light,
productName: ((snapshot.data![index]
["deviceInfo"]
as List)[indexx] as DeviceModel)
.deviceName,
);
})
],
);
});
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
});
}
)
],
)),
),
);
}
}
Am not certain where your error is coming from, but from what I see it maybe as a result of one of your functions returning null and a rendering of your content happens before the data is received.
You could try one of these:
You could declare the return type of your feature as being nullable for example you are expecting a value of type int:
Future<int?> xyz(){
......
return .....;
}
Now because your return type is nullable you wont have an issues as long as the receiving variable is also nullable.
Alternatively:
Future<int?> xyz(){
......
return ..... ?? 10 /*some default value*/;
}
because you know you result could be null you could also provide an optional default value incase your Future call returns a null value.

Searchable Dropdown with Flutter Rest Api Problem

I am Calling an Api to filter Countries list with Region. Whenever I Select the region it filter and show all countries of the selected region. I'm using searchable_dropdown package to filter.
My Searchable Dropdown Filter is Working Fine But Not getting All Data Without Selecting from Dropdown.
What i want :- i want to show all Available data from api when app runs or when no region is selected from dropdown as in Screenshot 1.
My Codes are Below
Main.dart
import 'package:flutter/material.dart';
import 'package:searchable_dropdown_flutter/searchable_dropdown1.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SearchableDropdownApp(),
);
}
}
searchable_dropdown.dart
import 'package:flutter/material.dart';
import 'package:searchable_dropdown/searchable_dropdown.dart';
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
class SearchableDropdownApp extends StatefulWidget {
#override
_AppState createState() => _AppState();
}
class _AppState extends State<SearchableDropdownApp> {
Map<String, String> selectedValueMap = Map();
// ignore: deprecated_member_use
List filteredData = List();
// ignore: deprecated_member_use
List filteredDataAll = List();
// ignore: deprecated_member_use
List alldata = List();
#override
void initState() {
selectedValueMap["server"] = null;
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: const Text('Searchable Dropdown Example App'),
),
body: FutureBuilder(
// get data from server and return a list of mapped 'name' fields
future:
getServerData(), //sets getServerData method as the expected Future
// ignore: missing_return
builder: (context, snapshot) {
if (snapshot.hasData) {
// ignore: deprecated_member_use
List<String> countries = new List();
for (int i = 0; i < snapshot.data.length; i++) {
if (!countries.contains(snapshot.data[i]['region']))
countries.add(snapshot.data[i]['region']);
}
alldata = snapshot.data;
//checks if response returned valid data
// use mapped 'name' fields for providing options and store selected value to the key "server"
return getSearchableDropdown(countries, "server", alldata);
} else if (snapshot.hasError) {
//checks if the response threw error
return Text("${snapshot.error}");
}
return Center(child: CircularProgressIndicator());
},
),
),
);
}
Widget getSearchableDropdown(List<String> listData, mapKey, alldata) {
List<DropdownMenuItem> items = [];
for (int i = 0; i < listData.length; i++) {
items.add(new DropdownMenuItem(
child: new Text(
listData[i],
),
value: listData[i],
));
}
return Scaffold(
body: Column(
children: [
SearchableDropdown(
isExpanded: true,
style: TextStyle(color: Colors.red),
items: items,
value: selectedValueMap[mapKey],
isCaseSensitiveSearch: false,
hint: new Text('Select Country'),
searchHint: new Text(
'Select Country',
style: new TextStyle(fontSize: 20),
),
onChanged: (value) {
setState(() {
selectedValueMap[mapKey] = value;
print('selectedValueMap[mapKey]');
print(selectedValueMap[mapKey]);
// ignore: deprecated_member_use
filteredData = new List();
for (int i = 0; i < alldata.length; i++) {
if (alldata[i]['region'] == selectedValueMap[mapKey])
filteredData.add(alldata[i]);
}
filteredDataAll = filteredData.toList();
print('filteredDataAll ka data');
});
},
),
Text(selectedValueMap[mapKey].toString()),
Text('data'),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: filteredDataAll.length,
itemBuilder: (context, index) {
return SingleChildScrollView(
child: Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Text(filteredDataAll[index].toString()),
],
),
)),
);
}),
),
),
],
),
);
}
Future<List> getServerData() async {
String url =
'https://restcountries.eu/rest/v2/all?fields=name;capital;alpha3Code;region;population;';
final response =
await http.get(url, headers: {"Accept": "application/json"});
if (response.statusCode == 200) {
List<dynamic> responseBody = json.decode(response.body);
return responseBody;
} else {
print("error from server : $response");
throw Exception('Failed to load post');
}
}
}
SecreenShots
When App Run for First Time
SecreenShots 1
After Selecting from Filter
SecreenShots 2
try this its works fine for me...
FutureBuilder<List<AllAccountModel>>(
future: accountData,
builder: (context, snapshot){
if(snapshot.hasData){
List<String> dropDown=List.filled(snapshot.data!.length, '',growable: true);
print(snapshot.data!.length);
for(int i=0;i<=snapshot.data!.length-1;i++){
dropDown[i]=snapshot.data![i].accountName+"\n"+snapshot.data![i].phoneNo;
}
return DropdownSearch<String>(
mode: Mode.MENU,
items: dropDown,
showSearchBox: true,
label: "Account Name",
onChanged: print,
);
}else{
DropdownSearch<String>(
mode: Mode.MENU,
items: [snapshot.error.toString()],
showSearchBox: true,
label: "Account Name",
onChanged: print,
);
}
return
const Padding(
padding: EdgeInsets.all(8.0),
child: Center(
child:
CircularProgressIndicator(),
),
);
})
call Api in initState
#override void initState() {
// TODO: implement initState
super.initState();
accountData = allAccounts();}
my Api Function
Future<List<AllAccountModel>> allAccounts() async {
return HttpService().getAllAccounts();}
End Result

Flutter Bloc with news api

I am stacked!! and i know i will find help here . I create a flutter application which fetches data from news.org API . Everything was working fine until i started implementing BLOC in the app . I have successfully implemented the first part with BLOC with fetches all the data from the API . the next thing to do is to fetch another data using the categories provided by the API in another page using BLOC .
For instance , there are categories like business , technology , finance etc . So main thing is when the user taps on any of the category the data show be fetched from the API using BLOC .
the following are the codes for the bloc ...
THIS IS THE ERROR I GET
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following assertion was thrown building BlocListener<ArticleBloc, ArticleState>(dirty, state: _BlocListenerBaseState<ArticleBloc, ArticleState>#aae07):
A build function returned null.
The offending widget is: BlocListener<ArticleBloc, ArticleState>
Build functions must never return null.
To return an empty space that causes the building widget to fill available room, return "Container()". To return an empty space that takes as little room as possible, return "Container(width: 0.0, height: 0.0)".
The relevant error-causing widget was:
BlocListener<ArticleBloc, ArticleState> file:///C:/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_bloc-6.1.1/lib/src/bloc_builder.dart:149:12
When the exception was thrown, this was the stack:
#0 debugWidgetBuilderValue. (package:flutter/src/widgets/debug.dart:302:7)
#1 debugWidgetBuilderValue (package:flutter/src/widgets/debug.dart:323:4)
#2 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4632:7)
#3 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4800:11)
#4 Element.rebuild (package:flutter/src/widgets/framework.dart:4343:5)
...
════════════════════════════════════════════════════════════════════════════════════════════════════
RepositoRy
abstract class CategoryRepository {
Future<List<Article>> getCategory(String category);
}
class CatService implements CategoryRepository {
#override
Future<List<Article>> getCategory(String category) async {
// List<Article> categoryNewsList = [];
String url =
"http://newsapi.org/v2/top-headlines?country=us&category=$category&apiKey=df74fc47f0dd401bb5e56c34893a7795";
return getData(url);
/*var response = await http.get(url);
//decode the response into a json object
var jsonData = jsonDecode(response.body);
//check if the status of the response is OK
if (jsonData["status"] == "ok") {
jsonData["articles"].forEach((item) {
//check if the imageUrl and description are not null
if (item["urlToImage"] != null && item["description"] != null) {
//create an object of type NewsArticles
Article newsArticleModel = new Article(
author: item["author"],
title: item["title"],
description: item["description"],
url: item["url"],
urlToImage: item["urlToImage"],
content: item["content"]);
//add data to news list
categoryNewsList.add(newsArticleModel);
}
});
}
return categoryNewsList;*/
}
}
Future<List<Article>> getData(String url) async {
List<Article> items = [];
var response = await http.get(url);
//decode the response into a json object
var jsonData = jsonDecode(response.body);
//check if the status of the response is OK
if (jsonData["status"] == "ok") {
jsonData["articles"].forEach((item) {
//check if the imageUrl and description are not null
if (item["urlToImage"] != null && item["description"] != null) {
//create an object of type NewsArticles
Article article = new Article(
author: item["author"],
title: item["title"],
description: item["description"],
url: item["url"],
urlToImage: item["urlToImage"],
content: item["content"]);
//add data to news list
items.add(article);
}
});
}
return items;
}
Bloc
class ArticleBloc extends Bloc<ArticleEvent, ArticleState> {
CategoryRepository categoryRepository;
ArticleBloc({this.categoryRepository}) : super(ArticleInitial());
#override
Stream<ArticleState> mapEventToState(
ArticleEvent event,
) async* {
if (event is GetArticle) {
try {
yield ArticleLoading();
final articleList =
await categoryRepository.getCategory(event.category);
yield ArticleLoaded(articleList);
} catch (e) {
print(e.message);
}
}
}
}
Event
class GetArticle extends ArticleEvent{
final String category;
GetArticle(this.category);
}
States
#immutable
abstract class ArticleState {
const ArticleState();
}
class ArticleInitial extends ArticleState {
const ArticleInitial();
}
class ArticleLoading extends ArticleState {
const ArticleLoading();
}
class ArticleLoaded extends ArticleState {
final List<Article> articleList;
ArticleLoaded(this.articleList);
}
class ArticleError extends ArticleState {
final String error;
ArticleError(this.error);
#override
bool operator ==(Object object) {
if (identical(this, object)) return true;
return object is ArticleError && object.error == error;
}
#override
int get hashCode => error.hashCode;
}
UI
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'News app',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: BlocProvider(
child: TestCat(),
create: (context) => ArticleBloc(categoryRepository : CatService()),
),
);
}
}
tEST category page
class TestCat extends StatefulWidget {
#override
_TestCatState createState() => _TestCatState();
}
class _TestCatState extends State<TestCat> {
bool isLoading = true;
List<String> categoryItems;
#override
void initState() {
super.initState();
categoryItems = getAllCategories();
// getCategory(categoryItems[0]);
// getCategoryNews();
}
getCategory(cat) async {
context.bloc<ArticleBloc>().add(GetArticle(cat));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: header(context, isAppTitle: false, title: "App"),
body: _newsBody(context),
);
}
_newsBody(context) {
return ListView(
children: [
//category list
Container(
padding:
EdgeInsets.symmetric(horizontal: NewsAppConstants().margin16),
height: NewsAppConstants().columnHeight70,
child: ListView.builder(
itemCount: categoryItems.length,
itemBuilder: (context, index) {
return TitleCategory(
title: categoryItems[index],
onTap: ()=> callCat(context, categoryItems[index]),
);
},
shrinkWrap: true,
scrollDirection: Axis.horizontal,
),
),
Divider(),
Container(
child: BlocBuilder<ArticleBloc, ArticleState>(
builder: (context, ArticleState articleState) {
//check states and update UI
if (articleState is ArticleInitial) {
return buildInput(context);
} else if (articleState is ArticleLoading) {
return Loading();
} else if (articleState is ArticleLoaded) {
List<Article> articles = articleState.articleList;
updateUI(articles);
} else if (articleState is ArticleError) {
// shows an error widget when something goes wrong
final error = articleState.error;
final errorMsg = "${error.toString()}\nTap to retry";
ShowErrorMessage(
errorMessage: errorMsg,
onTap: getCategory,
);
}
return buildInput(context);
}),
),
],
);
}
getAllCategories() {
List<String> categoryList = [
"Business",
"Entertainment",
"General",
"Sports",
"Technology",
"Health",
"Science"
];
return categoryList;
}
Widget updateUI(List<Article> newsList) {
return SingleChildScrollView(
child: Column(
children: [
Container(
child: ListView.builder(
physics: ClampingScrollPhysics(),
shrinkWrap: true,
itemCount: newsList.length,
itemBuilder: (context, index) {
return NewsBlogTile(
urlToImage: newsList[index].urlToImage,
title: newsList[index].title,
description: newsList[index].description,
url: newsList[index].url,
);
}),
),
Divider(),
],
));
}
buildInput(context) {
ListView.builder(
itemCount: categoryItems.length,
itemBuilder: (context, index) {
return TitleCategory(
title: categoryItems[index],
onTap: () {
print("tapped");
// callCat(context, categoryItems[index]);
},
);
},
shrinkWrap: true,
scrollDirection: Axis.horizontal,
);
}
callCat(BuildContext context, String cat) {
print(cat);
context.bloc<ArticleBloc>().add(GetArticle(cat));
}
}
//this displays the data fetched from the API
class NewsBlogTile extends StatelessWidget {
final urlToImage, title, description, url;
NewsBlogTile(
{#required this.urlToImage,
#required this.title,
#required this.description,
#required this.url});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {},
child: Expanded(
flex: 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
margin: EdgeInsets.all(NewsAppConstants().margin8),
child: Column(
children: <Widget>[
ClipRRect(
borderRadius:
BorderRadius.circular(NewsAppConstants().margin8),
child: Image.network(urlToImage)),
Text(
title,
style: TextStyle(
fontWeight: FontWeight.w600,
color: Colors.black,
fontSize: NewsAppConstants().margin16),
),
SizedBox(
height: NewsAppConstants().margin8,
),
Text(
description,
style: TextStyle(color: Colors.black54),
)
],
),
),
Divider(),
],
),
),
);
}
}
//news title category
class TitleCategory extends StatelessWidget {
final title;
final Function onTap;
TitleCategory({this.title, this.onTap});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => onTap,
child: Container(
margin: EdgeInsets.all(NewsAppConstants().margin8),
child: Stack(
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.circular(NewsAppConstants().margin8),
child: Container(
child: Text(
title,
style: TextStyle(
color: Colors.white,
fontSize: NewsAppConstants().font16,
fontWeight: FontWeight.w500),
),
alignment: Alignment.center,
width: NewsAppConstants().imageWidth120,
height: NewsAppConstants().imageHeight60,
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(NewsAppConstants().margin8),
color: Colors.black,
),
),
)
],
),
),
);
}
}
from what I see
you might try one of the below solutions :
try in bloc builder to return Container and inside it handle you states like this :
builder: (context, state) {
return Container(
child: Column(
children: [
if (state is Loading)
CircularProgressIndicator(),
if (state is Loaded)
CatsListView(data: state.data),
try to cover all your kind of states in if/else in your Bloc builder
so let's assume that you have 2 states (state1, state2) so your Bloc builder
would be something like this
builder: (context, state) {
if (state is state1) return Container();
else if (state is state2) return Container();
else return Container();
note that if you covered all states you don't have to do the last else

Flutter : Provider do not update data in the previous screen

In my Flutter application I am using Provider version 4.0.4 to manage the state of my app. In basic terms, my app will list down the nearby companies with their rating. users can select a organisation, open it and add their rating as well, so the final rating will be updated. I am using the Consumer concept in Provider to handle the tasks.
In NearByPlacesPage class I am listing down the companies around me with rating information. User can click on a company and they will be taken to OrganizationPage page.
In OrganizationPage class, the rating is displayed again. user can add their rating to the system. Then the rating information in both OrganizationPage page and NearByPlacesPage (back page) need to be updated.
The issue is, when the user update the rating, the rating in OrganizationPage get updated but not NearByPlacesPage in back stack. When we go back to NearByPlacesPage, we can clearly see the old rating values. The page need to be reloaded to get updated values.
Below are the important sections in my code
NearByPlacesPage
class NearByPlacesPage extends StatelessWidget {
int orgTypeID;
String orgTypeName;
NearByPlacesPage(this.orgTypeID, this.orgTypeName);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => RatingService()),
],
child: SingleChildScrollView(
child: _NearByPlacesPageUI(orgTypeID, orgTypeName),
),
),
appBar: AppBar(
title: Text(orgTypeName),
),
);
}
}
class _NearByPlacesPageUI extends StatefulWidget {
int orgTypeID;
String orgTypename;
_NearByPlacesPageUI(this.orgTypeID, this.orgTypename);
#override
State<StatefulWidget> createState() {
return _NearByPlacesPageState();
}
}
class _NearByPlacesPageState extends State<_NearByPlacesPageUI> {
#override
Widget build(BuildContext context) {
Consumer<RatingService>(builder: (context, data, child){
return Flexible(
child: ListView.builder(
itemCount: orgList.length,
itemBuilder:(BuildContext context, int index) {
Organization organization = orgList[index];
if (organization.isDisabled != true) {
RatingValue ratingValue = data.getData();
return Container(
margin: EdgeInsets.only(
top: 5, left: 5, right: 5),
child: _buildPlace(organization, ratingValue));
} else {
return Container();
}
},),
);
},);
}
}
OrganizationPage
class OrganizationPage extends StatelessWidget {
Organization organization;
String orgTypeName;
OrganizationPage(this.organization, this.orgTypeName);
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: _OrganizationPageUI(organization, orgTypeName),
),
backgroundColor: Colors.white,
appBar: AppBar(
title: Text(organization.name),
),
);
}
}
class _OrganizationPageUI extends StatefulWidget {
Organization organization;
String orgTypeName;
_OrganizationPageUI(this.organization, this.orgTypeName);
#override
State<StatefulWidget> createState() {
return _OrganizationPageState();
}
}
class _OrganizationPageState extends State<_OrganizationPageUI> {
#override
Widget build(BuildContext context) {
Consumer<RatingService>(
builder: (context, data, child) {
Consumer<RatingService>(
return Row(
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 10, left: 10),
child: Text(daa.getData()
style: Theme.of(context).textTheme.bodyText2.apply(color: Colors.grey),
),
),
],
);
),
}
}
}
In OrganizationPage there is a AlerDialog, which allows the user to rate and save. When saved, it will call another method which will reload the data.
Widget _ratingDialog(double _rating) {
RatingService _ratingService =
Provider.of<RatingService>(context, listen: false);
Rating _rating = _ratingService.returnRating();
double _ratingValue = _ratingService.returnRating().rating;
return AlertDialog(
title: const Text("Your Rating"),
actions: [
new FlatButton(
child: const Text("Save"),
//onPressed: () => Navigator.pop(context),
onPressed: () async {
Rating rating = Rating(
idrating:
_rating.idrating != null ? _rating.idrating : null,
user: _user,
organization: widget.organization,
rating: _ratingValue,
dateCreated: DateTime.now().millisecondsSinceEpoch,
lastUpdated: DateTime.now().millisecondsSinceEpoch);
await _ratingService.saveOrUpdateRating(rating, authToken);
_loadRatingByUserAndOrganization(authToken);
_loadRatingValueByOrganization(authToken);
Navigator.pop(context);
},
),
],
);
}
Future _loadRatingByUserAndOrganization(String authToken) {
RatingService _ratingService =Provider.of<RatingService>(context, listen: false);
return _ratingService.getRatingByUserAndOrganization(
_authService.getDatabaseUser().user.iduser,
widget.organization.idorganization,
authToken);
}
RatingService
This is the class which is responsible for calling notifyListeners(). It will be triggered by the above AlertDialog and the expected behaviour is to reload data in both OrganizationPage and NearByPlacesPage
class RatingService with ChangeNotifier {
List<RatingValue> _ratingValueList ;
List<RatingValue> getData()
{
return _ratingValueList;
}
//Load rating by user and Organization
Future<void> getRatingByUserAndOrganization(int idUser, int organizationID, String authToken) async {
try {
var data = await http.get(
_navLinks.getRatingByUserAndOrganization(idUser, organizationID),
headers: {HttpHeaders.authorizationHeader: "Bearer $authToken"},
);
print(data.body);
_rating = Rating.fromJson(convert.json.decode(data.body));
notifyListeners();
} catch (error) {
print(error);
throw error;
}
}
}
What I have I done wrong?

Categories

Resources