Im currently using https://pub.dev/packages/file_picker for this function
FilePickerResult result =
await FilePicker.platform.pickFiles();
if (result != null) {
setState(() {
filePicked = result;
});
for (int i = 0; i < result.files.length; i++) {
setState(() {
listBytes.add(result.files[i].bytes);
});
}
} else {
// User canceled the picker
}
when i test on web it work successfully, however the problem occur when running on web app (in mobile) after select image it got no respond. It's hard to debug since i don't really know on how to debug for web app. My question is, can i just use the function i use on web, or is there any specific function i need to use in order for it to work in web app.
You can try this for the web app (This method should only be used for the web platform):
import '../../models/html_nonweb.dart'
if (dart.library.js) 'dart:html' as html;
Future<WebFileModel> pickWebFileModel() {
final completer = Completer<WebFileModel>();
final html.InputElement input =
html.document.createElement('input') as html.InputElement;
input
..type = 'file'
..accept = 'image/*';
input.onChange.listen((e) async {
final List<html.File> files = input.files!;
final reader = html.FileReader();
reader.readAsDataUrl(files.first);
reader.onError.listen(completer.completeError);
final Future<WebFileModel> resultsFutures =
reader.onLoad.first.then((_) => WebFileModel(
path: reader.result! as String,
type: files.first.type as String,
createdAt: DateTime.fromMillisecondsSinceEpoch(
files.first.lastModified!,
),
htmlFile: files.first,
));
final results = await resultsFutures;
completer.complete(results);
});
input.click();
return completer.future;
}
To use this code, you need to create the html_nonweb.dart file:
const document = Document();
class Document {
const Document();
Element createElement(String el) => InputElement();
}
class File {
final int? lastModified = null;
final String? type = null;
}
class Element {
const Element();
}
class InputElement extends Element {
const InputElement();
final List<File>? files = null;
Stream<Object> get onChange => Stream.empty();
set type(String type) {}
set multiple(bool multiple) {}
set accept(String s) {}
void readAsDataUrl(File file) {}
void click() {}
}
class FileReader {
Stream<void Function(Object error, [StackTrace? stackTrace])> get onError =>
Stream.empty();
void readAsDataUrl(File file) {}
Stream<Object> get onLoad => Stream.empty();
final Object? result = null;
}
And WebFileModel:
import 'dart:typed_data';
import 'html_nonweb.dart' if (dart.library.js) 'dart:html' as html;
class WebFileModel {
final String path;
final String type;
final DateTime createdAt;
final html.File? htmlFile;
final Uint8List? uIntFile;
WebFileModel({
required this.path,
required this.type,
required this.createdAt,
this.htmlFile,
this.uIntFile,
});
WebFileModel copyWith({
String? path,
String? type,
DateTime? createdAt,
html.File? htmlFile,
Uint8List? uIntFile,
}) {
return WebFileModel(
path: path ?? this.path,
type: type ?? this.type,
createdAt: createdAt ?? this.createdAt,
htmlFile: htmlFile ?? this.htmlFile,
uIntFile: uIntFile ?? this.uIntFile,
);
}
}
To get a Uint8List, you need to do the following:
final imageBase64 =media.path.replaceFirst(RegExp(r'data:image/[^;]+;base64,'), '');
final uIntFile = base64Decode(imageBase64);
Related
I'm getting empty string when I trying to get the titles. Please help me.
Here's my source code
Here's my task.dart file
// task.dart
class Task {
int? id;
String? title;
String? note;
int? isCompleted;
String? date;
String? startTime;
String? endTime;
int? color;
int? remind;
String? repeat;
Task({
this.id,
this.title,
this.note,
this.isCompleted,
this.date,
this.startTime,
this.endTime,
this.color,
this.remind,
this.repeat,
});
Task.fromJson(Map<String, dynamic> json) {
id = json['id'];
title = json['title'];
note = json['note'];
isCompleted = json['isCompleted'];
date = json['date'];
startTime = json['startTime'];
endTime = json['endTime'];
color = json['color'];
remind = json['remind'];
repeat = json['repeat'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic> ();
data['id'] = this.id;
data['title'] = this.title;
data['note'] = this.note;
data['isCompleted'] = this.isCompleted;
data['date'] = this.date;
data['startTime'] = this.startTime;
data['endTime'] = this.endTime;
data['color'] = this.color;
data['remind'] = this.remind;
data['repeat'] = this.repeat;
return data;
}
}
Here's my task_controller.dart file
import 'package:calendar_app/db/db_helper.dart';
import 'package:calendar_app/models/task.dart';
import 'package:get/get.dart';
// task_controller.dart
class TaskController extends GetxController {
#override
void onReady() {
getTasks();
super.onReady();
}
var taskList = <Task>[].obs;
Future<int> addTask({Task? task}) async{
return await DBHelper.insert(task);
}
void getTasks() async {
List<Map<String, dynamic>> tasks = await DBHelper.query();
taskList.assignAll(tasks.map((data) => new Task.fromJson(data)).toList());
}
}
db_helper.dart
import 'package:calendar_app/models/task.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart';
import 'dart:developer' as devtools show log;
// db_helper.dart
class DBHelper {
static Database? _db;
static final int _version = 1;
static final String _tableName = 'Tasks';
static Future<void> initDb() async {
if (_db != null) {
return;
}
try {
String _path = await getDatabasesPath() + 'tasks.db';
_db = await openDatabase(
_path,
version: _version,
onCreate: (db, version) {
devtools.log('Creating a new one');
return db.execute(
"CREATE TABLE $_tableName("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"title STRING, note TEXT, date STRING, "
"startTime STRING, endTime STRING, "
"remind INTEGER, repeat STRING, "
"color INTEGER, "
"isCompleted INTEGER)",
);
},
);
} catch (e) {
devtools.log(e.toString());
}
}
static Future<int> insert(Task? task) async {
devtools.log('Insert func called');
return await _db?.insert(_tableName, task!.toJson()) ?? 1;
}
static Future<List<Map<String, dynamic>>> query() async {
devtools.log('Query func called');
return await _db!.query(_tableName);
}
}
When I trying to get a print statement of title it return empty. I have no idea what's happening here. Please help me.
print(_taskController.taskList[index].note.toString());
I'm following dbstech tutorial. If anyone have the source code please let me know.
First in getTask function try to reset taskList by calling this:
void getTasks() async {
taskList = []; // <--- add this
List<Map<String, dynamic>> tasks = await DBHelper.query();
taskList.assignAll(tasks.map((data) => new Task.fromJson(data)).toList());
}
Then I think you issue is your table is empty. After run your code try add something to your table then print it and see the result.
im new in Dart/Flutter and im struggling with consuming API, here is my file thats inside my model folder:
List<Heroes> heroesFromJson(String str) =>
List<Heroes>.from(json.decode(str).map((x) => Heroes.fromJson(x)));
String heroesToJson(List<Heroes> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Heroes {
Heroes({
required this.id,
required this.name,
required this.localizedName,
required this.primaryAttr,
required this.attackType,
required this.roles,
});
int id;
String name;
String localizedName;
String primaryAttr;
String attackType;
List<String> roles;
factory Heroes.fromJson(Map<String, dynamic> json) => Heroes(
id: json["id"],
name: json["name"],
localizedName: json["localized_name"],
primaryAttr: json["primary_attr"],
attackType: json["attack_type"],
roles: List<String>.from(json["roles"].map((x) => x)),
);
Map<String, dynamic> toJson() => {
"id": id,
"name": name,
"localized_name": localizedName,
"primary_attr": primaryAttr,
"attack_type": attackType,
"roles": List<dynamic>.from(roles.map((x) => x)),
};
}
And here is where im getting the error, inside services folder:
class DotaServices {
Future<List<String>?> getHeroes() async {
var client = http.Client();
var url = Uri.parse('https://api.opendota.com/api/heroes');
var response = await client.get(url);
if (response.statusCode == 200) {
var json = response.body;
return heroesFromJson(json);
}
}
}
The error is occuring in that line:
return heroesFromJson(json);
And the message that appears is:
A value of type 'List<Heroes>' can't be returned from the method 'getHeroes' because it has a return type of 'Future<List<String>?>'.
how to solve it? Im struggling real hard on this :/
Your method returns a list of heroes... so... you need to return a list of heroes:
Future<List<String>?> getHeroes() async {
needs to be
Future<List<Heroes>?> getHeroes() async {
heroesFromJson returns a list of heroes so getHeroes has to return a list of heroes:
Future<List<Heroes>?> getHeroes()
Also, your method heroesFromJson returns a List<Heroes> not nullable, but your method getHeroes() return a List<Heroe>? which is nullable.
You either can make your return from heroesFromJson a nullable list List<T>? or your return from getHeroes() a non-nullable list List
Be careful making your List nullable or non-nullable List<Hero>?, not your Hero List<Hero?>
It seems to me that such code should work more reliably.
return Hero.fromJsonList(json as List);
This small example (including function main) was generated with a very small script.
import 'dart:convert';
import 'package:http/http.dart' as http;
void main(List<String> args) async {
final svc = DotaServices();
final heroes = await svc.getHeroes();
print('Heroes: ${heroes.length}');
}
class DotaServices {
Future<List<Hero>> getHeroes() async {
final client = http.Client();
final url = Uri.parse('https://api.opendota.com/api/heroes');
final response = await client.get(url);
if (response.statusCode == 200) {
final source = response.body;
final json = jsonDecode(source);
return Hero.fromJsonList(json as List);
}
throw StateError('Http error: ${response.statusCode}');
}
}
class Hero {
Hero(
{required this.id,
required this.name,
required this.localizedName,
required this.primaryAttr,
required this.attackType,
required this.roles});
factory Hero.fromJson(Map json) {
return Hero(
id: json['id'] as int,
name: json['name'] as String,
localizedName: json['localized_name'] as String,
primaryAttr: json['primary_attr'] as String,
attackType: json['attack_type'] as String,
roles: json['roles'] == null
? []
: (json['roles'] as List).map((e) => e as String).toList(),
);
}
final int id;
final String name;
final String localizedName;
final String primaryAttr;
final String attackType;
final List<String> roles;
static List<Hero> fromJsonList(List json) {
return json.map((e) => Hero.fromJson(e as Map)).toList();
}
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'localized_name': localizedName,
'primary_attr': primaryAttr,
'attack_type': attackType,
'roles': roles,
};
}
static List<Map<String, dynamic>> toJsonList(List<Hero> list) {
return list.map((e) => e.toJson()).toList();
}
}
Using this codegen script you can generate the models and serializers.
It also generates a working example.
import 'dart:io';
import 'package:object_serializer/json_serializer_generator.dart';
import 'package:yaml/yaml.dart';
void main() {
final classes = loadYaml(_classes) as Map;
final g = JsonSerializerGenerator();
final classesCode = g.generateClasses(classes);
final values = {
'classes': classesCode,
};
var source = g.render(_template, values);
source = g.format(source);
File('bin/stackoverflow.dart').writeAsStringSync(source);
}
const _classes = '''
Hero:
fields:
id: int
name: String
localizedName: {type: String, alias: localized_name}
primaryAttr: {type: String, alias: primary_attr}
attackType: {type: String, alias: attack_type}
roles: List<String>
''';
const _template = r'''
import 'dart:convert';
import 'package:http/http.dart' as http;
void main(List<String> args) async {
final svc = DotaServices();
final heroes = await svc.getHeroes();
print('Heroes: ${heroes.length}');
}
class DotaServices {
Future<List<Hero>> getHeroes() async {
final client = http.Client();
final url = Uri.parse('https://api.opendota.com/api/heroes');
final response = await client.get(url);
if (response.statusCode == 200) {
final source = response.body;
final json = jsonDecode(source);
return Hero.fromJsonList(json as List);
}
throw StateError('Http error: ${response.statusCode}');
}
}
{{classes}}
''';
I am getting data from Firebase Database and Adding it to a List of my Model class. I tested the incoming data by printing to Console and it works fine, but once i add the data to my model class, it disappears.
Here's my Provider class where i'm loading the data.
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:local_stuffs_notification/apis/fcm.dart';
import 'package:local_stuffs_notification/models/request_model.dart';
import 'package:shared_preferences/shared_preferences.dart';
class IncomingRequest with ChangeNotifier {
List<RequestModel> _incomingRequests = [];
IncomingRequest(this._incomingRequests);
List<RequestModel> get incomingRequest {
return [..._incomingRequests];
}
Future<void> setIncomingRequest(RequestModel requestModel) async {
try {
DatabaseReference reference =
FirebaseDatabase.instance.ref("incomingRequests");
reference.child(requestModel.id).child(Fcm.getUid()).set(
{
"name": requestModel.name.toString(),
"phone": requestModel.phone.toString(),
"email": requestModel.email.toString(),
"fcmToken": requestModel.fcmToken.toString(),
},
);
notifyListeners();
} catch (error) {
rethrow;
}
}
Future<void> loadIncomingRequests() async {
try {
SharedPreferences preferences = await SharedPreferences.getInstance();
DatabaseReference reference = FirebaseDatabase.instance
.ref('incomingRequests/${preferences.getString('userId')!}');
Stream<DatabaseEvent> stream = reference.onValue;
stream.listen((DatabaseEvent event) {
print(event.snapshot.value);
final data = event.snapshot.value as Map;
print('data: $data');
final List<RequestModel> loadedRequest = [];
data.forEach(
(key, value) {
print('requestData: ${value['name']}');
loadedRequest.add(
RequestModel(
id: key.toString(),
name: value['name'].toString(),
fcmToken: value['fcmToken'].toString(),
phone: value['phone'].toString(),
email: value['email'].toString(),
),
);
print(loadedRequest);
},
);
_incomingRequests = loadedRequest;
print('LoadedRequests: $loadedRequest');
notifyListeners();
});
// reference.onValue.listen(
// (event) {
// if (event.snapshot.value == null) {
// return;
// }
// final data = event.snapshot.value as Map;
// final List<RequestModel> loadedRequests = [];
// data.forEach(
// (key, requestData) {
// loadedRequests.add(
// RequestModel(
// id: key,
// name: requestData['name'],
// fcmToken: requestData['fcmToken'],
// phone: requestData['phone'],
// email: requestData['email'],
// ),
// );
// },
// );
// _incomingRequests = loadedRequests;
// notifyListeners();
// },
//);
} catch (error) {
rethrow;
}
}
}
Here's my Model Class
class RequestModel {
final String id;
final String name;
final String fcmToken;
final String phone;
final String email;
RequestModel({
required this.id,
required this.name,
required this.fcmToken,
required this.phone,
required this.email,
});
}
I'm getting the data until i added it to loadedRequest List
Please help, i've spent hours on this and i don't know what i'm doing wrong. When i print the loadedRequest list, i get an empty list. Thanks.
Those logs aren't showing an empty list - It says [Instance of 'RequestModel']. That means there is a value there, but Dart simply doesn't know how to convert RequestModel to a String so that it can be printed out on the console.
An empty list would be printed simply as [], and if you had two values, for example, you would see [Instance of 'RequestModel', Instance of 'RequestModel'].
To print out your values with more detail, you can override the toString() method on your class.
For example:
class RequestModel {
final String id;
final String name;
final String fcmToken;
final String phone;
final String email;
RequestModel({
required this.id,
required this.name,
required this.fcmToken,
required this.phone,
required this.email,
});
#override
String toString() =>
"RequestModel(id: $id, name: $name, fcmToken: $fcmToken, phone: $phone, email: $email)";
}
take a look at the raw data once again, it contains all the users data so you need to get the access the uid before the name
final uid = FirebaseAuth.instance.currentUser!.uid;
and then for the RequestModel:
name: data[uid]['name']
I'm following a tutorial that builds a messaging app using flutter and rethinkdb. It hasn't been updated for null safety, but I'm trying to move forward and learn it with the most updated packages & latest version of flutter as I go, as a beginner.
I'm trying to test typing notifications, and I'm getting an error that I can't decipher.
type '_CompactLinkedHashSet<String>' is not a subtype of type 'Map<dynamic, dynamic>?'
package:rethink_db_ns/src/ast.dart:1223
TypingNotification.send
package:chat/…/typing/typing_notification.dart:22
main.<fn>
test/typing_notification_test.dart:44
main.<fn>
test/typing_notification_test.dart:37
All of the Maps that I have are Map<String, dynamic>, so I'm not sure where the Map<dynamic, dynamic> is coming from. I am also using package:rethink_db_ns instead of what the tutorial author uses, which is the rethink package before ns was added. So I'm not sure if I have to change things further for the new package. I also made sure I didn't see any red squiggles before I tried running the test.
Here are my files:
Here is the testing file where I try to run the test and get the aforementioned error:
import 'package:chat/src/models/typing_event.dart';
import 'package:chat/src/models/user.dart';
import 'package:chat/src/services/typing/typing_notification.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:rethink_db_ns/rethink_db_ns.dart';
import 'helpers.dart';
void main() {
RethinkDb r = RethinkDb();
late Connection connection;
late TypingNotification sut;
setUp(() async {
connection = await r.connect();
await createDb(r, connection);
sut = TypingNotification(r, connection);
});
tearDown(() async {
sut.dispose();
await cleanDb(r, connection);
});
final user = User.fromJson({
'id': '1234',
'active': true,
'lastSeen': DateTime.now(),
});
final user2 = User.fromJson({
'id': '1111',
'active': true,
'lastSeen': DateTime.now(),
});
test('sent typing notification successfully', () async {
TypingEvent typingEvent = TypingEvent(
from: user2.id!,
to: user.id!,
event: Typing.start,
);
final res = await sut.send(event: typingEvent, to: user);
expect(res, true);
});
}
Here is typing_notification.dart:
import 'dart:async';
import 'package:chat/src/models/user.dart';
import 'package:chat/src/models/typing_event.dart';
import 'package:chat/src/services/typing/typing_notification_service_contract.dart';
import 'package:rethink_db_ns/rethink_db_ns.dart';
class TypingNotification implements ITypingNotification {
final Connection _connection;
final RethinkDb _r;
final _controller = StreamController<TypingEvent>.broadcast();
StreamSubscription? _changefeed; // added ? to this
TypingNotification(this._r, this._connection); //added this._change
#override
Future<bool> send({required TypingEvent event, required User to}) async {
if (!to.active) return false;
Map record = await _r
.table('typing_events')
.insert(event.toJson(), {'conflict:' 'update'}).run(_connection);
return record['inserted'] == 1;
}
#override
Stream<TypingEvent> subscribe(User user, List<String> userIds) {
_startReceivingTypingEvents(user, userIds);
return _controller.stream;
}
#override
void dispose() {
_changefeed?.cancel();
_controller.close();
}
_startReceivingTypingEvents(User user, List<String> userIds) {
_changefeed = _r
.table('typing_events')
.filter((event) {
return event('to')
.eq(user.id)
.and(_r.expr(userIds).contains(event('from')));
})
.changes({'include_initial': true})
.run(_connection)
.asStream()
.cast<Feed>()
.listen((event) {
event
.forEach((feedData) {
if (feedData['new_val'] == null) return;
final typing = _eventFromFeed(feedData);
_controller.sink.add(typing);
_removeEvent(typing);
})
.catchError((err) => print(err))
.onError((error, stackTrace) => print(error));
});
}
TypingEvent _eventFromFeed(feedData) {
return TypingEvent.fromJson(feedData['new_val']);
}
_removeEvent(TypingEvent event) {
_r
.table('typing_events')
.get(event.id)
.delete({'return_changes': false}).run(_connection);
}
}
Here is typing_event.dart:
enum Typing { start, stop }
extension TypingParser on Typing {
String value() {
return toString().split('.').last;
}
static Typing fromString(String event) {
return Typing.values.firstWhere((element) => element.value() == event);
}
}
class TypingEvent {
final String from;
final String to;
final Typing event;
String? _id;
String? get id => _id;
TypingEvent({
required this.from,
required this.to,
required this.event,
});
Map<String, dynamic> toJson() => {
'from': this.from,
'to': this.to,
'event': this.event.value(),
};
factory TypingEvent.fromJson(Map<String, dynamic> json) {
var event = TypingEvent(
from: json['from'],
to: json['to'],
event: TypingParser.fromString(json['event']),
);
event._id = json['id'];
return event;
}
}
And here is typing_notification_service_contract.dart:
import 'package:chat/src/models/typing_event.dart';
import 'package:chat/src/models/user.dart';
abstract class ITypingNotification {
Future<bool> send(
{required TypingEvent event, required User to}); // the tutorial did not include ", required User to" but I added it to prevent an error, since required User to is passed in another file
Stream<TypingEvent> subscribe(User user, List<String> userIds);
void dispose();
}
And finally, typing_event.dart
enum Typing { start, stop }
extension TypingParser on Typing {
String value() {
return toString().split('.').last;
}
static Typing fromString(String event) {
return Typing.values.firstWhere((element) => element.value() == event);
}
}
class TypingEvent {
final String from;
final String to;
final Typing event;
String? _id;
String? get id => _id;
TypingEvent({
required this.from,
required this.to,
required this.event,
});
Map<String, dynamic> toJson() => {
'from': this.from,
'to': this.to,
'event': this.event.value(),
};
factory TypingEvent.fromJson(Map<String, dynamic> json) {
var event = TypingEvent(
from: json['from'],
to: json['to'],
event: TypingParser.fromString(json['event']),
);
event._id = json['id'];
return event;
}
}
I'm a total beginner & trying to push myself to learn null safety, so there is a good chance my error is very simple and I'm just not seeing it. Can anyone point me in the right direction or offer any guidance here? I can also add more details upon request if you think it might be helpful.
Try replacing
.insert(event.toJson(), {'conflict:' 'update'}).run(_connection);
with
.insert(event.toJson(), {'conflict': 'update'}).run(_connection);
^
add colon
in typing_notification,.dart line 22, it's expecting Map, probably you meant to place ":" outside the string
I have a model class for Inbox which contains a normal constructor and a factory constructor that accepts a list. Everything seems to work except after calling the factory constructor the code does not continue to the next line. _inbox = Inbox.fromList(_fetchInboxResBody['inbox']);
Here is the code where I called the Inbox's factory constructor
import 'package:flutter/foundation.dart';
import 'dart:async';
import 'package:http/http.dart' as http;
import 'dart:convert';
import '../models/inbox.dart';
class InboxProvider with ChangeNotifier {
final _rootUrl = 'http://localhost:8080';
// final _rootUrl = 'http://firstcarestartup.appspot.com';
//inbox as an array
Map<String, dynamic> _fetchInboxResBody;
Map<String, dynamic> _fetchInboxResError;
Inbox _inbox;
//getters
Inbox get inbox => _inbox;
Map<String, dynamic> get fetchInboxResError => _fetchInboxResError;
fetchInbox(String accessToken) async {
print('fetching inbox');
try {
var response = await http.get('$_rootUrl/customer/inbox', headers: {
'Content-Type': 'application/json',
'Authorization': 'bearer $accessToken'
});
_fetchInboxResBody = json.decode(response.body);
if (_fetchInboxResBody.containsKey('error')) {
_fetchInboxResError = _fetchInboxResBody;
notifyListeners();
} else {
_fetchInboxResError = null;
print('yeah1');
_inbox = Inbox.fromList(_fetchInboxResBody['inbox']);
print('yeah');
notifyListeners();
}
} catch (e) {
_fetchInboxResError = {'error': e.toString()};
notifyListeners();
}
}
}
And here is inbox.dart
import 'package:flutter/material.dart';
class Inbox {
List<Message> messages;
int unread;
Inbox({#required this.messages, #required this.unread});
factory Inbox.fromList(List response) {
int counter = 0;
List msgs = new List();
response.forEach((f) {
if(f['read'] == false){
counter++;
}
Message msg = Message.fromMap(f);
msgs.add(msg);
});
print(msgs[2].content);
return new Inbox(messages: msgs, unread: counter);
}
}
class Message {
String content;
String date;
String header;
String id;
String imageUrl;
bool read;
String type;
Message(
{#required this.content,
#required this.date,
#required this.header,
#required this.id,
this.imageUrl,
#required this.read,
this.type});
factory Message.fromMap(Map messageMap) {
var content = messageMap['content'];
var date = messageMap['date'];
var header = messageMap['header'];
var id = messageMap['id'];
var imageUrl = messageMap['image'] ?? null;
var read = messageMap['read'];
var type = messageMap['type'];
return new Message(content: content, date: date, header: header, id: id, read: read, imageUrl: imageUrl, type: type);
}
}