The argument type 'Stream<List<ObjectModel>>' can't be assigned to the parameter type 'List<ObjectModel>' - flutter

I am using BLoC pattern (with rxdart package) to read a list of "EmpresaDatosModel" and when trying to include the sink it throws me the following error:
The argument type 'Stream <List < EmpresaDatosModel >>' can't be
assigned to the parameter type 'List < EmpresaDatosModel >'.
In the BLoC pattern I am using the following code:
class EmpresaDatosBloc {
final _empresaDatosController = new BehaviorSubject<List<EmpresaDatosModel>>();
Stream <List<EmpresaDatosModel>> get empresaDatosStream => _empresaDatosController.stream;
Stream<List<EmpresaDatosModel>> cargarEmpresasStream() {
final empresasList = _empresaDatosProvider.cargarEmpresasStream();
_empresaDatosController.sink.add(empresasList); //THE ERROR THROWS HERE
return empresasList;
}
dispose() {
_empresaDatosController?.close();
}
}
The provider where the query is made from Firebase RTDB has the following:
Stream<List<EmpresaDatosModel>> cargarEmpresasStream() {
Query resp = db.child('admon');
final empStream = resp.onValue;
final publicarStream = empStream.map((event) {
final empMap = Map<String, dynamic>.from(event.snapshot.value);
final empList = empMap.entries.map((e) {
return EmpresaDatosModel.fromJson(Map<String,dynamic>.from(e.value));
}).toList();
return empList;
});
return publicarStream;
}
And the display widget looks like this:
final empresaDatosBloc = Provider.empresaDatosBloc(context);
empresaDatosBloc.cargarEmpresasStream();
//---
return StreamBuilder(
stream: empresaDatosBloc.empresaDatosStream,
builder: (BuildContext context, snapshot){
final empresasList = [];
if (snapshot.hasData) {
final myList = snapshot.data as List<EmpresaDatosModel>;
myList.forEach((element) {
empresasList.add(element);
});
}
How can I assign a List<EmpresaDatosModel> to the sink in order to fix the error?

You can call .addStream on the _empresaDatosController BehaviorSubject .
This forwards data and error events to the controller's stream.
_empresaDatosController.addStream(empresasList);

Hope can help you.
Just keep a reference to StreamSubscription and use Stream.listen() method
class EmpresaDatosBloc {
StreamSubscription<void>? _subscription;
final _empresaDatosController = new BehaviorSubject<List<EmpresaDatosModel>>();
Stream <List<EmpresaDatosModel>> get empresaDatosStream => _empresaDatosController.stream;
void cargarEmpresasStream() {
_subscription?.cancel();
_subscription = _empresaDatosProvider.cargarEmpresasStream()
.listen(_empresaDatosController.add, onError: _empresaDatosController.addError);
}
dispose() {
_subscription?.cancel();
_empresaDatosController?.close();
}
}

Related

LateInitializationError: Field 'check' has not been initialized

I'm trying to Use data that I fetched from database and i got an error : "LateInitializationError: Field 'check' has not been initialized. "
, i tried to remove the late word and adding " ? " and it gives another error "Expected a value of type 'num', but got one of type 'Null'
"
class _letterssState extends State<letterss> {
late var check;
Future getData() async{
var url = 'http://ip/getSpell.php';
http.Response response = await http.get(Uri.parse(url));
var data = jsonDecode(response.body);
check=data;
print(data.toString());
}
bool searchRes (String s){
int x=0;
for ( var i=0 ; i<check.length;i++ )
{
if (check[i]['letter']==s){
x=i;
}
}
if (check[x]['result']=='true')
{
return true;
}
else
{
return true;
}
}
initState()
{
getData();
}
It will take some frame to get data from getData future method and assigning on check.
It would better to use FutureBuilder for future methods. Follow this doc example
Future<List<yourDataType>?> getData() async {
var url = 'http://ip/getSpell.php';
http.Response response = await http.get(Uri.parse(url));
var data = jsonDecode(response.body);
return data;
}
late final future = getData();
#override
Widget build(BuildContext context) {
return FutureBuilder<List<YourDataType>?>(
future: future,
builder: (context, snapshot) {
if (snapshot.hasData) {
//todo:
}
return CircularProgressIndicator();
},
);
}

How to solve value of type 'Map<String, dynamic>', but got one of type 'List<dynamic>'

I want to get an image from an api and I get the error mentioned in the title.
class _ApiState extends State<Api> {
Future<CatData> fetchcat() async {
final response =
await http.get(Uri.parse('https://api.thecatapi.com/v1/images/search'));
// Appropriate action depending upon the
// server response
if (response.statusCode == 200) {
return CatData.fromJson(json.decode(response.body));
//return CatData.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
} else {
throw Exception('Failed to load album');
}
}
late Future<CatData> futureAlbum;
#override
void initState() {
super.initState();
futureAlbum = fetchcat();
}
#override
Widget build(BuildContext context) {
return FutureBuilder<CatData>(
future: fetchcat(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Image.network(snapshot.data!.imagen);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return CircularProgressIndicator();
},
);
}
}
here the class model:
class CatData {
String imagen;
CatData({required this.imagen});
factory CatData.fromJson(Map<String, dynamic> json) {
return CatData(
imagen: json['url'],
);
}
}
If I get an answer please, I would like you to explain to me the reason for the problem. because I always get this kind of errors when I consume API's.
"receives one value but expects another"
https://api.thecatapi.com/v1/images/search
Well, json.decode(response.body) gives you back a List<dynamic>, but you declared the method fromJson to accept one argument of type Map<String, dynamic>, thus the incompatibility.
You can change the signature of the method fromJson and set it to List<dynamic>. Then you could access it with json[0].url, json[0]['url'] or {url} = json[0].
I tested the following code in https://dartpad.dev and works like a charm now.
import 'dart:convert';
import 'package:http/http.dart' as http;
Future<CatData> fetchcat() async {
final response =
await http.get(Uri.parse('https://api.thecatapi.com/v1/images/search'));
// Appropriate action depending upon the
// server response
if (response.statusCode == 200) {
return CatData.fromJson(json.decode(response.body));
//return CatData.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
} else {
throw Exception('Failed to load album');
}
}
class CatData {
String imagen;
CatData({required this.imagen});
factory CatData.fromJson(List<dynamic> json) {
return CatData(
imagen: json[0]['url']
);
}
}
void main() async {
CatData catData = await fetchcat();
print(catData.imagen);
}
You probably making mistake on casting. first make sure what kind of data you are retrieving means is it key-value pair { "url" : "www...." } or List [{"url" :"www...} , { "url": " www..."}]
if its key-value pairs then decode it as follows:
final decoded = json.decode(response.body) as Map<String, dynamic>;
final _catData = CataData.fromJson(decoded);
or if its list of urls then do it as follows:
final _decoded = json.decode(response.body) as List<dynamic>;
final _catsData = _decoded.map((e) => CatData.fromJson(e as Map<String, dynamic>)).toList();

When I am using the provider package in Flutter to load data from an API into a list it repeatedly calls the API, how do I fix it?

I am trying to lode data from an api call that retrieves a map, I am able to get the map from the api to display how I want it to, however it repeatedly calls the api meaning the list keeps on refreshing. Even though I have tried setting the listener to false, it works but I have to manually refresh the app for it to work?
Additional Info: Assigning and Retrieving Data
import 'package:http/http.dart' as http;
class Stores with ChangeNotifier {
var s_length;
Future<List<Store>> getStores(String storeCatName) async {
final queryParameters = {
"store_category_name": storeCatName,
};
try {
//TODO this is the issue - must fix.
final uri = Uri.http("url", 'url', queryParameters);
//print(uri);
final response = await http.get(uri);
//print(response.statusCode);
//print(response.body);
if (response.statusCode == 200) {
final List<Store> stores = storeFromJson(response.body);
_stores = stores;
//print(_stores);
print("lenght: ${_stores.length}");
Store store;
for(store in _stores) {
store.products = Products().products(store.storeId);
}
//check if this is correct
notifyListeners();
//return stores;
} else {
print("error1");
return List<Store>();
}
} catch (e) {
print(e.toString());
return List<Store>();
}
//notifyListeners();
print(_stores);
}
List<Store> get favoriteItems {
//return _stores.where((storeItem) => storeItem.isFavorite).toList();
}
bool isNotFull(){
if (_stores.isEmpty){
return true;
} else {
return false;
}
}
int get numberOfStores{
return s_length;
}
List<Store> _stores = [];
List<Store> stores (String storeCatName){
getStores(storeCatName);
//print("cpp; + $s_length");
//notifyListeners();
return _stores;
}
}
final storesProvider = Provider.of<Stores>(
context, listen: false
);
storesProvider.getStores(categoryName);
final providerStoreList = storesProvider.stores(category.storeCategoryName);
Additional Info: Builder for List:
child: ListView.builder(
itemCount: providerStoreList.length,
itemBuilder: (context, index) => ChangeNotifierProvider.value(
value: providerStoreList[index],
child: StoreItem(),
)));
If any additional information is required just let me know. Any help would be greatly appreciated.
Thanks
Use
listen: false;
var ourClient = Provider.of<CartBlock>(context, listen: false);
Setting the listener to false means that your widget won't build again when notifyListeners() is called.
So, that might not be the issue.
The only reason I can think of is calling the API again from the build method,
which might happen if you are using a ListView builder.
So, every time you might be scrolling the ListView your API would call again.

Flutter Riverpod : How to Implement FutureProvider?

I using Flutter Riverpod package to handling http request. I have simple Http get request to show all user from server, and i using manage it using FutureProvider from Flutter Riverpod package.
API
class UserGoogleApi {
Future<List<UserGoogleModel>> getAllUser() async {
final result = await reusableRequestServer.requestServer(() async {
final response =
await http.get('${appConfig.baseApiUrl}/${appConfig.userGoogleController}/getAllUser');
final Map<String, dynamic> responseJson = json.decode(response.body);
if (responseJson['status'] == 'ok') {
final List list = responseJson['data'];
final listUser = list.map((e) => UserGoogleModel.fromJson(e)).toList();
return listUser;
} else {
throw responseJson['message'];
}
});
return result;
}
}
User Provider
class UserProvider extends StateNotifier<UserGoogleModel> {
UserProvider([UserGoogleModel state]) : super(UserGoogleModel());
Future<UserGoogleModel> searchUserByIdOrEmail({
String idUser,
String emailuser,
String idOrEmail = 'email_user',
}) async {
final result = await _userGoogleApi.getUserByIdOrEmail(
idUser: idUser,
emailUser: emailuser,
idOrEmail: idOrEmail,
);
UserGoogleModel temp;
for (var item in result) {
temp = item;
}
state = UserGoogleModel(
idUser: temp.idUser,
createdDate: temp.createdDate,
emailUser: temp.emailUser,
imageUser: temp.emailUser,
nameUser: temp.nameUser,
tokenFcm: temp.tokenFcm,
listUser: state.listUser,
);
return temp;
}
Future<List<UserGoogleModel>> showAllUser() async {
final result = await _userGoogleApi.getAllUser();
state.listUser = result;
return result;
}
}
final userProvider = StateNotifierProvider((ref) => UserProvider());
final showAllUser = FutureProvider.autoDispose((ref) async {
final usrProvider = ref.read(userProvider);
final result = await usrProvider.showAllUser();
return result;
});
After that setup, i simply can call showAllUser like this :
Consumer((ctx, read) {
final provider = read(showAllUser);
return provider.when(
data: (value) {
return ListView.builder(
itemCount: value.length,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
final result = value[index];
return Text(result.nameUser);
},
);
},
loading: () => const CircularProgressIndicator(),
error: (error, stackTrace) => Text('Error $error'),
);
}),
it's no problem if http request don't have required parameter, but i got problem if my http request required parameter. I don't know how to handle this.
Let's say , i have another http get to show specific user from id user or email user. Then API look like :
API
Future<List<UserGoogleModel>> getUserByIdOrEmail({
#required String idUser,
#required String emailUser,
#required String idOrEmail,
}) async {
final result = await reusableRequestServer.requestServer(() async {
final baseUrl =
'${appConfig.baseApiUrl}/${appConfig.userGoogleController}/getUserByIdOrEmail';
final chooseURL = idOrEmail == 'id_user'
? '$baseUrl?id_or_email=$idOrEmail&id_user=$idUser'
: '$baseUrl?id_or_email=$idOrEmail&email_user=$emailUser';
final response = await http.get(chooseURL);
final Map<String, dynamic> responseJson = json.decode(response.body);
if (responseJson['status'] == 'ok') {
final List list = responseJson['data'];
final listUser = list.map((e) => UserGoogleModel.fromJson(e)).toList();
return listUser;
} else {
throw responseJson['message'];
}
});
return result;
}
User Provider
final showSpecificUser = FutureProvider.autoDispose((ref) async {
final usrProvider = ref.read(userProvider);
final result = await usrProvider.searchUserByIdOrEmail(
idOrEmail: 'id_user',
idUser: usrProvider.state.idUser, // => warning on "state"
);
return result;
});
When i access idUser from userProvider using usrProvider.state.idUser , i got this warning.
The member 'state' can only be used within instance members of subclasses of 'package:state_notifier/state_notifier.dart'.
It's similiar problem with my question on this, but on that problem i already know to solved using read(userProvider.state) , but in FutureProvider i can't achieved same result using ref(userProvider).
I missed something ?
Warning: This is not a long-term solution
Assuming that your FutureProvider is being properly disposed after each use that should be a suitable workaround until the new changes to Riverpod are live. I did a quick test to see and it does work. Make sure you define a getter like this and don't override the default defined by StateNotifier.
class A extends StateNotifier<B> {
...
static final provider = StateNotifierProvider((ref) => A());
getState() => state;
...
}
final provider = FutureProvider.autoDispose((ref) async {
final a = ref.read(A.provider);
final t = a.getState();
print(t);
});
Not ideal but seems like a fine workaround. I believe the intention of state being inaccessible outside is to ensure state manipulations are handled by the StateNotifier itself, so using a getter in the meantime wouldn't be the end of the world.

Flutter - Widget that no longer appears in the widget tree or this error might indicate a memory leak Warning

I have 3 page I check transitions with bottomNavigationBar first page is Soclose in this page im gettting information from the database and print it on the screen.
I'm getting information from the database smoothly but when i switch screens my console gives warning messages. An error appears in the console, but the application is working properly. When changing screens and returning to the old page(Soclose page), an error page appears and disappears within milliseconds.
I cant find similar questions and i tried to make suggestions in the warnings but either I couldn't do it or the solutions don't work.
Related soclose dart file:
class _Closesevents extends State<Soclose> {
List<Event> eventList;
int eventListLen;
#override
void initState() {
try{
final Future<Database> dbFuture = DbHelper.initializeDatabase();
dbFuture.then((database) {
Future<List<Event>> eventListFuture = DbHelper().getEventList();
eventListFuture.then((eventList) {
setState(() {
this.eventList = eventList;
this.eventListLen = eventList.length;
});
});
});}
catch (e,s)
{
print("[ERROR] $e");
print("[ERROR TREE]\n$s");
}
super.initState();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
child: new ListView.builder(
itemCount: eventListLen,
itemBuilder: (BuildContext context, int index) =>
buildTripCard(context, index)),
);
}
Widget buildTripCard(BuildContext context, int index)
...
Databasehelper file
import ...
class DbHelper {
static DbHelper _databaseHelper; // Singleton DatabaseHelper
static Database _database;
static final String _tablename = EventConstants.TABLE_NAME;
static final String _columnId = EventConstants.COLUMN_ID;
static final String _columnTitle = EventConstants.COLUMN_TITLE;
static final String _columnDate = EventConstants.COLUMN_DATE;
static final String _columnStartTime = EventConstants.COLUMN_STARTTIME;
static final String _columnFinishTime = EventConstants.COLUMUN_FINISHTIME;
static final String _columnDesc = EventConstants.COLUMN_DESCRIPTION;
static final String _columnIsActive = EventConstants.COLUMN_ISACTIVE;
DbHelper._createInstance(); // Named constructor to create instance of DatabaseHelper
factory DbHelper() {
if (_databaseHelper == null) {
_databaseHelper = DbHelper._createInstance(); // This is executed only once, singleton object
}
return _databaseHelper;
}
Future<Database> get database async {
if (_database == null) {
_database = await initializeDatabase();
}
return _database;
}
static Future<Database> initializeDatabase() async {
Directory directory = await getApplicationDocumentsDirectory();
String path = directory.path + 'takvimapp.db';
// Open/create the database at a given path
var notesDatabase = await openDatabase(path, version: 1, onCreate: _createDb);
return notesDatabase;
}
static void _createDb(Database db, int newVersion) async {
await db.execute('CREATE TABLE $_tablename ( $_columnId INTEGER PRIMARY KEY NOT NULL,$_columnTitle TEXT ,$_columnDate TEXT,$_columnStartTime TEXT,$_columnFinishTime TEXT,$_columnDesc TEXT,$_columnIsActive INTEGER);');
}
// Get all events --map
Future<List<Map<String, dynamic>>> getEventMapList() async {
Database db = await this.database;
var result = await db.query(_tablename, orderBy: '$_columnTitle ASC');
return result;
}
// Insert Operation: Insert a Event object to database
Future<int> insertEvent(Event event) async {
Database db = await this.database;
var result = await db.insert(_tablename, event.toMap());
return result;
}
// Update Operation: Update a Event object and save it to database
Future<int> updateEvent(Event event) async {
var db = await this.database;
var result = await db.update(_tablename, event.toMap(), where: '$_columnId = ?', whereArgs: [event.id]);
return result;
}
// Delete Operation: Delete a Event object from database
Future<int> deleteEvent(int id) async {
var db = await this.database;
int result = await db.rawDelete('DELETE FROM $_tablename WHERE $_columnId = $id');
return result;
}
// Get number of Event objects in database
Future<int> getCount() async {
Database db = await this.database;
List<Map<String, dynamic>> x = await db.rawQuery('SELECT COUNT (*) from $_tablename');
int result = Sqflite.firstIntValue(x);
return result;
}
// Convert map to list
Future<List<Event>> getEventList() async {
var eventMapList = await getEventMapList(); // Get 'Map List' from database
int count = eventMapList.length; // Count the number of map entries in db table
List<Event> eventList = List<Event>();
// For loop to create a 'Event List' from a 'Event List'
for (int i = 0; i < count; i++) {
eventList.add(Event.fromMap(eventMapList[i]));
}
return eventList;
}
static Future closeDb() => _database.close();
}
The error warning is constantly written to the console in an infinite loop.
To get rid of the warning, I need to close the app and restart the emulator.
Warning message:
E/flutter (30455): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: setState() >called after dispose(): _CountDownItemState#2bbc3(lifecycle state: defunct, not mounted)
E/flutter (30455): This error happens if you call setState() on a State object for a widget that no >longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its >build). This error can occur when code calls setState() from a timer or an animation callback.
E/flutter (30455): The preferred solution is to cancel the timer or stop listening to the animation >in the dispose() callback.
Another solution is to check the "mounted" property of this object >before calling setState() to ensure the object is still in the tree.
E/flutter (30455): This error might indicate a memory leak if setState() is being called because >another object is retaining a reference to this State object after it has been removed from the >tree. To avoid memory leaks, consider breaking the reference to this object during dispose().
Solution:
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: _db.getEventList(),
builder: (context, snapshot) {
if (snapshot.data == null) {
return Container(
child: Text("Loading....."),
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(snapshot.data[index].title),
);
});
}
}),
);
}
The issue is with your initState function override. It's good practice to also call the super of initState, super.initState, before all other logic. Your Futures may be completing too quickly, and calling setState before the state is even initialized. Simply move super.initState(); as the first statement in the override. Ex.
#override
void initState() {
super.initState();//Always call this first
try{
final Future<Database> dbFuture = DbHelper.initializeDatabase();
dbFuture.then((database) {
Future<List<Event>> eventListFuture = DbHelper().getEventList();
eventListFuture.then((eventList) {
setState(() {
this.eventList = eventList;
this.eventListLen = eventList.length;
});
});
});}
catch (e,s)
{
print("[ERROR] $e");
print("[ERROR TREE]\n$s");
}
}
Edit: However, this can still lead to errors as setState could still be called before the widget is mounted. This is why the FutureBuilder widget exists. Wrap the widget that needs this Future data in your build method, pass the Future to the future parameter of the FutureBuilder and access the data with the AsyncSnapshot that the builder provides. See more about FutureBuilder.