I have a list which is fetched from the server but when we get outside of the fetching method the list is initialized to default. I have noticed that the problem is with the notifyListeners() as here:
Does notifyListeners() sometimes complete asynchronously?
Interestingly, inside the fetching method the the list is ok.
class Products with ChangeNotifier {
List<Product> _loadedProducts = [];
Future<void> fetchAndSetProducts() async {
try {
var response =
await http.get(StoreServer.serverAddress + '/products.json');
final extractedData = json.decode(response.body) as Map<String, dynamic>;
List<Product> extractedList = [];
extractedData.forEach((key, value) {
_loadedProducts.add(Product(
id: key,
imageUrl: value['imageUrl'],
title: value['title'],
price: value['price'],
description: value['description'],
isFavorite: value['isFavorite']));
});
_loadedProducts = extractedList;
notifyListeners();
await Future.delayed(Duration(seconds: 1));
} catch (error) {
throw error;
}
}
#override
void didChangeDependencies() {
if (!isInit) {
setState(() {
isLoading = true;
});
test = Provider.of<Products>(context);
test.fetchAndSetProducts().then((_) {
print("fetched");
setState(() {
isLoading = false;
isInit = true;
});
});
}
super.didChangeDependencies();
}
You have a logic error. Check my comments..!
class Products with ChangeNotifier {
List<Product> _loadedProducts = [];
Future<void> fetchAndSetProducts() async {
try {
var response =
await http.get(StoreServer.serverAddress + '/products.json');
final extractedData = json.decode(response.body) as Map<String, dynamic>;
List<Product> extractedList = []; // it is an empty list
extractedData.forEach((key, value) {
_loadedProducts.add(Product( // you add elements to _loadedProducts
id: key,
imageUrl: value['imageUrl'],
title: value['title'],
price: value['price'],
description: value['description'],
isFavorite: value['isFavorite']));
});
_loadedProducts = extractedList; // you reassign _loadedProducts to the empty list "extractedList"
notifyListeners();
await Future.delayed(Duration(seconds: 1));
} catch (error) {
throw error;
}
}
Am I right? I think you have that error!
Related
I am working with GraphQL. I need to pass parameters to Http headers. However, I see an option to pass only normal parameters (code below). PLEASE tell me how to do it!!
Query(
options: QueryOptions(
document: gql(userGraphQL),
variables: {
"..." : "..."
}
),
builder: ...
)
I have created this file to related the GraphQL
gql_utils.dart file
class GraphQLUtils {
static final GraphQLUtils _instance = GraphQLUtils._internal();
GraphQLClient? _client;
factory GraphQLUtils() {
return _instance;
}
GraphQLUtils._internal() {
final httpLink = HttpLink(
'your base url',
);
final authLink = AuthLink(
ignore: undefined_identifier
getToken: () async => 'Bearer $YOUR_PERSONAL_ACCESS_TOKEN',
);
var link = authLink.concat(httpLink);
var link = httpLink;
var link = httpLink;
final policies = Policies(
fetch: FetchPolicy.networkOnly,
);
_client = GraphQLClient(
cache: GraphQLCache(),
link: link,
defaultPolicies: DefaultPolicies(
watchQuery: policies,
query: policies,
mutate: policies,
),
);
}
Future<Map<String, dynamic>> queryRepo(
DocumentNode readRepositories, map) async {
final options = WatchQueryOptions(
document: readRepositories,
variables: map,
pollInterval: const Duration(seconds: 4),
fetchResults: true,
);
QueryResult result = await _client!.query(options);
if (result.hasException) {
Map<String, dynamic> response = <String, dynamic>{};
response['success'] = false;
response['message'] = result.exception!.graphqlErrors[0].message;
return response;
} else {
Map<String, dynamic> response = <String, dynamic>{};
response['success'] = true;
response['data'] = result.data;
return response;
}
}
}
this is Example query class
class UserQueries {
static final userInsertQuery = gql(r'''
mutation Insert_users($objects: [users_insert_input!]!, $onConflict: users_on_conflict) {
insert_users(objects: $objects, on_conflict: $onConflict) {
returning {
id
name
timestamp
}
}
}
''');
}
and how to call api
Future insertUserApi(String name) async {
try {
Map<String, dynamic> variables = <String, dynamic>{};
variables = {
"objects": [
{"name": name}
],
"onConflict": {
"constraint": "users_pkey",
"update_columns": [
"id",
"name",
]
}
};
await GraphQLUtils()
.queryRepo(UserQueries.userInsertQuery, variables)
.then((response) async {
if (response["data"] != null) {
print("----Data---:${response["data"]}");
Get.back();
} else {
Get.snackbar(
"Error",
"Something Went wrong",
);
}
});
} catch (e, st) {
Get.snackbar(
"Error",
e.toString(),
);
}
}
I have a stream that apparently does not return a value. Instead of returning anything, the snapshot I use in my Streambuilder returns the yellow container (see code below) which is returned when my snapshot has no data. Any idea what causes this issue?
Below you will all functions, the stream as well as my Streambuilder.
Here is the updated stream. The otherUserId print statement is NOT printed. Maybe the error lies somewhere here.
Stream<List>? roomsListStream() async* {
try {
print("userId: $userId");
var rooms = FirebaseFirestore.instance
.collection("rooms")
.where("users", arrayContains: userId)
.orderBy("latestMessageTime", descending: true)
.snapshots();
rooms.map((QuerySnapshot query) {
List<RoomsListModel> retVal = [];
for (var element in query.docs) {
// get other user id
String otherUserId = element["users"][0] == userId
? element["users"][1]
: element["users"][0];
print("otherUserId: $otherUserId");
// get other user details
getOtherUser(otherUserId).then((value) {
retVal.add(RoomsListModel(
roomId: element.id,
otherUserId: otherUserId,
avatar: value["photoUrl"],
name: value["name"],
lastMessage: element["latestMessage"],
lastMessageTime: element["latestMessageTime"]));
});
}
print(retVal);
return retVal;
});
} catch (e) {
print("Error: $e");
}
}
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:blabber_tech/services/auth.dart';
import 'package:blabber_tech/services/chat_services.dart';
import 'package:rxdart/rxdart.dart';
import 'package:blabber_tech/models/room_model.dart';
// Rooms List Model
class RoomsListModel {
String roomId;
String otherUserId;
String avatar;
String name;
String lastMessage;
Timestamp lastMessageTime;
RoomsListModel(
{required this.roomId,
required this.otherUserId,
required this.avatar,
required this.name,
required this.lastMessage,
required this.lastMessageTime});
}
class MyChatsScreen3 extends StatefulWidget {
static const String id = "mychats3_screen";
#override
State<MyChatsScreen3> createState() => _MyChatsScreenState();
}
// get other user details
Future getOtherUser(String id) async {
// get other user profile
var user = await FirebaseFirestore.instance
.collection("users")
.doc(id)
.get()
.then((value) => value.data()) as Map<String, dynamic>;
// return other user profile
return user;
}
class _MyChatsScreenState extends State<MyChatsScreen3> {
// get current user id
String userId = AuthService().getUserId();
// get all active chats
**Stream<List>? roomsListStream() {**
try {
FirebaseFirestore.instance
.collection("rooms")
.where("users", arrayContains: userId)
.orderBy("latestMessageTime", descending: true)
.snapshots()
.map((QuerySnapshot query) {
List<RoomsListModel> retVal = [];
query.docs.forEach((element) {
retVal.add(RoomsListModel(
roomId: element.id,
otherUserId: element["users"][0] == userId
? element["users"][1]
: element["users"][0],
avatar: element["photoUrl"],
name: element["name"],
lastMessage: element["latestMessage"],
**lastMessageTime: element["latestMessageTime"]**));
});
return retVal;
});
} catch (e) {
print("Error: $e");
}
}
// List builder for mobile app
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
// create listview of all chats of current user and show last message and other user name and photo
child: **StreamBuilder(**
stream: roomsListStream(),
builder: (context, AsyncSnapshot<dynamic> snapshot) {
**if (snapshot.hasData) {**
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(
// show other user profile photo
//backgroundImage:
//NetworkImage(otherUser["profilePhotoUrl"]),
),
//title: Text(snapshot.data[index]["userName"]),
subtitle: Text(snapshot.data[index]["lastMessage"]),
);
},
);
} else {
return Container(
color: Colors.yellow,
);
}
},
),
),
);
}
}
You forget to await for FirebaseFirestore result:
Stream<List>? roomsListStream() async* {
try {
var rooms = await FirebaseFirestore.instance
.collection("rooms")
.where("users", arrayContains: userId)
.orderBy("latestMessageTime", descending: true)
.snapshots();
await rooms.map((QuerySnapshot query) async*{
List<RoomsListModel> retVal = [];
query.docs.forEach((element) {
retVal.add(RoomsListModel(
roomId: element.id,
otherUserId: element["users"][0] == userId
? element["users"][1]
: element["users"][0],
avatar: element["photoUrl"],
name: element["name"],
lastMessage: element["latestMessage"],
lastMessageTime: element["latestMessageTime"]));
});
yield retVal;
});
} catch (e) {
print("Error: $e");
}
}
and also change this:
if (snapshot.hasData) {
to this:
if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
i have been trying to solve this error , after i login i was getting type 'Null' is not a subtype of type 'String' for about 5 seconds and after that the app successfully login, i do not know why this happen, i already add null check to the User but i still get the error . Below is my code, tell me if you need more info, Thanks for helping
class _controState extends State<contro> {
_controState();
User? user = FirebaseAuth.instance.currentUser;
UserModel loggedInUser = UserModel();
var role;
var email;
var id;
#override
void initState() {
super.initState();
FirebaseFirestore.instance
.collection("users") //.where('uid', isEqualTo: user!.uid)
.doc(user!.uid)
.get()
.then((value) {
this.loggedInUser = UserModel.fromMap(value.data());
}).whenComplete(() {
CircularProgressIndicator();
setState(() {
email = loggedInUser.email.toString();
role = loggedInUser.role.toString();
id = loggedInUser.uid.toString();
});
});
}
routing() {
if (role == 'Freelancer') {
return JobScreen(
id: id,
);
} else {
return JobScreenClient(
id: id,
);
}
}
#override
Widget build(BuildContext context) {
CircularProgressIndicator();
return routing();
}
}
inside your routing, role might be null before FirebaseFirestore's result get ready, try this:
routing() {
if(role == null){
return Container(); // show empty widget like this or what widget you want
}else if (role == 'Freelancer') {
return JobScreen(
id: id,
);
} else {
return JobScreenClient(
id: id,
);
}
}
You have to add async/await to your code, because it's future functions..
void initState() async {
super.initState();
await FirebaseFirestore.instance
.collection("users") //.where('uid', isEqualTo: user!.uid)
.doc(user!.uid)
.get()
.then((value) {
this.loggedInUser = UserModel.fromMap(value.data());
}).whenComplete(() {
CircularProgressIndicator();
setState(() {
email = loggedInUser.email.toString();
role = loggedInUser.role.toString();
id = loggedInUser.uid.toString();
});
});
}
routing() {
if(role == null){
return const Center(child: CircularProgressIndicator());
}else if (role == 'Freelancer') {
return JobScreen(
id: id,
);
} else {
return JobScreenClient(
id: id,
);
}
}
There are four quotes in the firestore but in the output i am not been able to fetch those. Following is the quotesPage and below that quotesController.
fetchQuotes(String catId) {
print(catId);
try {
quotesList.bindStream(firebaseFirestore
.collection('quotes')
.where(
'category',
isEqualTo: "General",
)
.snapshots()
.map((QuerySnapshot query) {
List<Quotes> quotes = [];
for (var quote in query.docs) {
final quoteModel =
Quotes.fromDocumentSnapshot(documentSnapshot: quote);
quotes.add(quoteModel);
}
return quotes;
}));
} on Exception catch (e) {
print(e);
}
}
You should listen for the snapshot from Firestore.
Try this snippet:
fetchQuotes(String catId) {
print(catId);
try {
quotesList.bindStream(firebaseFirestore
.collection('quotes')
.where(
'category',
isEqualTo: "General",
)
.snapshots()
.listen((snapshot) {
snapshot.map((query) {
List<Quotes> quotes = [];
for (var quote in query.docs) {
final quoteModel =
Quotes.fromDocumentSnapshot(documentSnapshot: quote);
quotes.add(quoteModel);
}
return quotes;
});
}));
} on Exception catch (e) {
print(e);
}
}
infinite scrolling from flutter application that fetches data from WordPress rest API based on categories... I tried to fetch data based on categories but it display all data on all categories tabs ... I just want to know how to filter news in their category it was working fine before I try to add infinite scrolling
Categories List are loading in all the tabs but the problem is when changing tabs, previous tab category list data is loading on the top of the next tab. How to overcome this problem. First tab data is loading when changing to the second tab on the top and In third tab First tab and Second tab data is loading on the top.
this is config.dart
class Config {
static String? apiURL = "https://hageez.tech/wp-json/wp/v2/";
static String? categoryURl = "categories?per_page=100";
static String? postURL = "latest-posts/?page_size=3&category_id=";
static String? postDetailURL = 'post-details/?id=';
static String? dateFormat = "dd-MM-yyyy";
}
this is API fetch for post and categories
class APIService {
static var client = http.Client();
static Future<List<CategoryModel>?> fetchCategories() async {
var response =
await client.get(Uri.parse(Config.apiURL! +
Config.categoryURl!));
if (response.statusCode == 200) {
var jsonString = response.body;
return categoryFromJson(jsonString);
} else {
return null;
}
}
static Future<List<NewsModel>?> fetchPosts(
int categoryId,
int pageNumber,
) async {
var url = Config.apiURL! +
Config.postURL! +
categoryId.toString() +
"&page_no=" +
pageNumber.toString();
var response = await client.get(Uri.parse(url));
if (response.statusCode == 200) {
var jsonString = response.body;
return postsFromJson(jsonString);
}
return null;
}
this is controller part using getx for category and post
class CategoriesController extends GetxController
with GetSingleTickerProviderStateMixin {
var isloading = true.obs;
var categoriesList = <CategoryModel>[].obs;
#override
void onInit() {
// TODO: implement onInit
fetchCategories();
super.onInit();
}
Future<void> fetchCategories() async {
try {
isloading(true);
var categories = await APIService.fetchCategories();
if (categories!.length > 0) {
categoriesList.clear();
categoriesList.addAll(categories);
}
} finally {
isloading(false);
}
}
}
this is postcontroller
class postcontroller extends GetxController {
var isloading = true.obs;
var postList = <NewsModel>[].obs;
#override
void onInit() {
// TODO: implement onInit
fetchPosts();
super.onInit();
}
Future<void> fetchPosts(
{int categoryId = 1, int pageNumber = 0, int totalrecords = 0}) async {
try {
if (postList.length == 0 || pageNumber == 0) {
isloading(true);
postList.clear();
}
if (postList.length < totalrecords) {
var posts = await APIService.fetchPosts( categoryId,pageNumber,);
if (posts != null) {
postList.addAll(posts);
print(posts);
}
}
} finally {
isloading(false);
}
}
}
the following is postpage that have tabs based on categories and for the post inject data from newspage.dart
class trytab extends StatefulWidget {
trytab({Key? key}) : super(key: key);
#override
State<trytab> createState() => _trytabState();
}
class _trytabState extends State<trytab> with SingleTickerProviderStateMixin {
final CategoriesController categoriesController =
Get.put(CategoriesController());
// List<Widget> tabs =[];
#override
void initState() {
// TODO: implement initState
super.initState();
// tabs.add(tab("tabName"));
// tabs.add(tab(" hey brother "));
// tabs.add(tab("3"));
// tabs.add(tab("5"));
// tabs.add(tab("tab6Name"));
}
#override
Widget build(BuildContext context) {
return Center(
child: Obx(() {
return DefaultTabController(
length: categoriesController.categoriesList.length,
child: Scaffold(
appBar: AppBar(
title: Text("API TEST"),
bottom: TabBar(
isScrollable: true,
tabs:categoriesController.categoriesList
.map((model) => tab(model.categoryName))
.toList()),
),
body: TabBarView(
children: categoriesController.categoriesList.map((model) {
return newspage(
categoryID: model.categoryId,
isRelod: true,
totalRecords: model.count,
);
}).toList(),
),
),
);
}),
);
}
}
newspge.dart
class newspage extends StatefulWidget {
late int? categoryID;
late bool? isRelod;
late int? totalRecords;
newspage({this.categoryID, this.isRelod, this.totalRecords});
#override
State<newspage> createState() => _newspageState();
}
class _newspageState extends State<newspage> {
final postcontroller postController = Get.put(postcontroller());
var refreshkey = GlobalKey<RefreshIndicatorState>();
ScrollController _scrollController = new ScrollController();
int _page = 1;
#override
void initState() {
// TODO: implement initState
super.initState();
Future.delayed(Duration.zero, () async {
if (this.widget.isRelod!) {
await postController.fetchPosts(
categoryId: this.widget.categoryID!,
pageNumber: 1,
totalrecords: this.widget.totalRecords!);
}
});
_scrollController.addListener(() async {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
await postController.fetchPosts(
categoryId: this.widget.categoryID!,
pageNumber: ++_page,
totalrecords: this.widget.totalRecords!,
);
}
});
}
#override
Widget build(BuildContext context) {
return newslist();
}
Widget newslist() {
return Container(
child: Obx(() {
if (postController.isloading.value) {
return Center(
child: CircularProgressIndicator(),
);
}
// ignore: curly_braces_in_flow_control_structures
else
// ignore: curly_braces_in_flow_control_structures
return RefreshIndicator(
child: ListView.builder(
key: refreshkey,
physics: const AlwaysScrollableScrollPhysics(),
itemCount: postController.postList.length,
controller: _scrollController,
itemBuilder: (context, index) {
if ((index == postController.postList.length - 1) &&
postController.postList.length <
this.widget.totalRecords!) {
return Center(child: CircularProgressIndicator());
}
return newsbycategory(
model: postController.postList[index]);
}),
onRefresh: () => postController.fetchPosts(
categoryId: this.widget.categoryID!,
pageNumber: ++_page,
totalrecords: this.widget.totalRecords!));
}),
);
}
}