I am trying to load images from google blogger API, I'm succeed in loading "title" but when I try to load "image", I get an error in debug console -
'package:flutter/src/painting/_network_image_io.dart': Failed assertion: line 22 pos 14: 'url != null': is not true.
Code from blogger API:
"author": {
"id": "123",
"displayName": "Rajdeep Thakare",
"url": "https://www.blogger.com/profile/123",
"image": {
"url": "//lh3.googleusercontent.com/abcdefg"
}
},
A piece of code from my project:
body: Center(
child: _isLoading
? CircularProgressIndicator()
: ListView.builder(
itemCount: this.items != null ? this.items.length : 0,
itemBuilder: (context, i) {
final item = this.items[i];
return Column(
children: <Widget>[
Image.network(item["image"]),
Text(item["title"]),
Divider(),
],
);
//return Text("Row: $i");
}
),
),
Is there a problem in API or blogger won't allow me or my image insertion technique is wrong?
=================================================================================
Is it possible to retrive the post images from blogger API -
"selfLink": "https://www.googleapis.com/blogger/v3/blogs/633372055355686443/posts/8104714868335749943",
"title": "second post",
"content": "\u003cdiv dir=\"ltr\" style=\"text-align: left;\" trbidi=\"on\"\u003e\n\u003cdiv class=\"separator\" style=\"clear: both; text-align: center;\"\u003e\n\u003ca href=\"https://1.bp.blogspot.com/-CbTldBdA_u8/Xn3R8evahLI/AAAAAAAAHJ0/b9gOD6EfI5QL9tXL-w05Qn2Z4eH_qVduQCLcBGAsYHQ/s1600/micheile-henderson-Xgn6rIpBEWo-unsplash.jpg\" imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"\u003e\u003cimg border=\"0\" data-original-height=\"1600\" data-original-width=\"1067\" height=\"320\" src=\"https://1.bp.blogspot.com/-CbTldBdA_u8/Xn3R8evahLI/AAAAAAAAHJ0/b9gOD6EfI5QL9tXL-w05Qn2Z4eH_qVduQCLcBGAsYHQ/s320/micheile-henderson-Xgn6rIpBEWo-unsplash.jpg\" width=\"213\" /\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cbr /\u003e\nthis is my second post\u003c/div\u003e\n",
"author": {
You cannot access the image URL directly via item["image"] because in your JSON String the URL is nested in Author->Image->URL. If we look closely at the error it tells us 'url != null': is not true which simply means your URL (aka the item["image"] variable) == null
To access the URL you have to go through the keys in your JSON string. Here is an example code:
import 'dart:convert';
var jsonString =
"""
{
"author": {
"id": "123",
"displayName": "Rajdeep Thakare",
"url": "https://www.blogger.com/profile/123",
"image": {
"url": "//lh3.googleusercontent.com/abcdefg"
}
}
}
""";
void main() {
var obj = json.decode(jsonString);
print(obj['author']['image']['url']); // prints out '//lh3.googleusercontent.com/abcdefg'
}
Just check out the below example as url is wrong so it will not work but if you put the right url it will work for sure.
Just check the below example :
Below is the sample json that you provided i have just changed the image objects url as your url was not accessible :
{
"author": {
"id": "123",
"displayName": "Rajdeep Thakare",
"url": "https://www.blogger.com/profile/123",
"image": {
"url": "https://i.picsum.photos/id/178/536/354.jpg"
}
}
}
Later i have created a model class for it which is mentioned below:
// To parse this JSON data, do
//
// final author = authorFromJson(jsonString);
import 'dart:convert';
Author authorFromJson(String str) => Author.fromJson(json.decode(str));
String authorToJson(Author data) => json.encode(data.toJson());
class Author {
AuthorClass author;
Author({
this.author,
});
factory Author.fromJson(Map<String, dynamic> json) => Author(
author: AuthorClass.fromJson(json["author"]),
);
Map<String, dynamic> toJson() => {
"author": author.toJson(),
};
}
class AuthorClass {
String id;
String displayName;
String url;
Image image;
AuthorClass({
this.id,
this.displayName,
this.url,
this.image,
});
factory AuthorClass.fromJson(Map<String, dynamic> json) => AuthorClass(
id: json["id"],
displayName: json["displayName"],
url: json["url"],
image: Image.fromJson(json["image"]),
);
Map<String, dynamic> toJson() => {
"id": id,
"displayName": displayName,
"url": url,
"image": image.toJson(),
};
}
class Image {
String url;
Image({
this.url,
});
factory Image.fromJson(Map<String, dynamic> json) => Image(
url: json["url"],
);
Map<String, dynamic> toJson() => {
"url": url,
};
}
And at last the main file for ui and data fetching :
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart';
import 'dummy.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool _isLoading = false;
List<Author> authorList = List();
#override
void initState() {
// TODO: implement initState
super.initState();
youMethodCall();
}
youMethodCall() async {
setState(() {
_isLoading = true;
});
String jsonString = await loadFromAssets();
// I have taken the local json file you can
// make a api call here and when you get the
// response just pass to the below method as show below
// final author = authorFromJson(response.body);
// else every thing is the same.
final author = authorFromJson(jsonString);
authorList.add(author);
setState(() {
_isLoading = false;
});
}
Future<String> loadFromAssets() async {
return await rootBundle.loadString('json/parse.json');
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: _isLoading
? CircularProgressIndicator()
: ListView.builder(
itemCount: authorList.length,
itemBuilder: (context, i) {
final item = authorList[i].author;
return Column(
children: <Widget>[
CachedNetworkImage(
imageUrl: item.image.url,
placeholder: (context, url) =>
CircularProgressIndicator(),
errorWidget: (context, url, error) =>
Icon(Icons.error),
),
Text(item
.displayName), // As you title field was not displayed
Divider(),
],
);
//return Text("Row: $i");
}),
),
),
);
}
}
I'll try my best to help you to solve the issue.
Question 1: Retrieve image through URL
Try to print Image.network(items->author->url), and add items->author->image->url to see if there's the url.
print(obj['author']['image']['url']);
Question 2: Retrieve images in a post
You can find all <img> tags and get src="" attribute to get the image urls from items->content
I hope this helps.
Related
I want to get data from API and I tried the link in postman and its working here it is: [ { "Id": "14", "title": "Facebook vs instagram?", }, { "Id": "15", "title": "Facebook vs instagram?", }, { "Id": "16", "title": "Facebook vs instagram?", }, ]
but when I am trying to do a map this error appears : error catch type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'List' in type cast.
Here is my code : This error appears in this file and print(recieved) print the same data as postman but the problem in map httpservice.dart:
`
class HttpService {
final String postsURL =
"";
Future<List<Post>> getPosts() async {
var request = http.MultipartRequest("POST", Uri.parse(postsURL));
request.headers.addAll(headers);
List<Post> Posts = [];
try {
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
var response = await http.Response.fromStream(streamedResponse);
final result = jsonDecode(response.body) as List;
List<Post> posts = result
.map(
(dynamic item) => Post.fromJson(item),
)
.toList();
return Posts;
}
}
`
post_model.dart :
`
class Post {
final String Id;
final String title;
Post({
required this.Id,
required this.title,
});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
Id: json['Id'] as String,
title: json['title'] as String,
);
}
}
` post.dart :
class PostsPage extends StatelessWidget {
final HttpService httpService = HttpService();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Posts"),
),
body: FutureBuilder(
future: httpService.getPosts(),
builder: (BuildContext context, AsyncSnapshot<List<Post>> snapshot) {
if (snapshot.hasData) {
List<Post> posts = snapshot.data!;
return ListView(
children: posts
.map(
(Post post) => ListTile(
title: Text(post.title),
subtitle: Text("${post.Id}"),
),
)
.toList(),
);
} else {
return Center(child: CircularProgressIndicator());
}
},
),
);
}
}
The http package gives the data as json in default. You are again trying to decode the decode the response body but the jsonDecode expects string type as parameter.
Simply, all you need to do is remove the jsonDecode
From
final result = jsonDecode(response.body) as List;
to
final result = response.body as Map<String, dynamic>;
I'm trying to make a simple ListView with GetX but it gives me this error when starting the app "Unhandled Exception: NoSuchMethodError: The method 'map' was called on null.", I'm new to flutter and dart, that's why I'm starting with the "easiest" and for work reasons they ask me to add GetX
Home
class HomePage extends GetView<HomeController> {
const HomePage({super.key});
#override
Widget build(BuildContext context) {
// final homeController = Get.put(HomeController());
var title = "HomePage";
return Scaffold(
body: Obx(() {
HomeController controller = Get.find<HomeController>();
return controller.regionList.isEmpty
? const Center(
child: Text('No hay regiones'),
)
: ListView.builder(
itemCount: controller.regionList.length,
itemBuilder: (context, index) => ListTile(
title: Text(
controller.regionList[index].name,
)));
}),
);
}
}
Controller
class HomeController extends GetxController {
//late Regiones model;
var regionList = <Regiones>[].obs;
Future<List<Regiones>> getRegiones() async {
var response = await rootBundle.loadString('assets/response.json');
var results = (jsonDecode(response)['regions'] ?? []) as List;
return results.map((x) => Regiones.fromJson(x)).toList();
//return Regiones.fromJson(jsonDecode(response));
}
//Json['regions'] == null ? Null :
#override
Future<void> onInit() async {
// TODO: implement onInit
super.onInit();
regionList.assignAll(await getRegiones());
}
}
Json
{
"name": "Chile",
"regions": [
{
"name": "Arica y Parinacota",
"romanNumber": "XV",
"number": "15",
"abbreviation": "AP",
"communes": [
{ "name": "Arica", "identifier": "XV-1" },
{ "name": "Camarones", "identifier": "XV-2" },
{ "name": "General Lagos", "identifier": "XV-3" },
{ "name": "Putre", "identifier": "XV-4" }
]
},
{
...
Model
Regiones regionesFromJson(String str) => Regiones.fromJson(json.decode(str));
String regionesToJson(Regiones data) => json.encode(data.toJson());
class Regiones {
Regiones({
required this.name,
required this.regions,
});
String name;
List<Region> regions;
factory Regiones.fromJson(Map<String, dynamic> json) => Regiones(
name: json["name"],
regions:
List<Region>.from(json["regions"].map((x) => Region.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"name": name,
"regions": List<dynamic>.from(regions.map((x) => x.toJson())),
};
}
class Region {
Region({
required this.name,
required this.romanNumber,
required this.number,
required this.abbreviation,
required this.communes,
});
String? name;
String? romanNumber;
String? number;
String? abbreviation;
List<Commune> communes;
factory Region.fromJson(Map<String, dynamic> json) => Region(
name: json["name"],
romanNumber: json["romanNumber"],
number: json["number"],
abbreviation: json["abbreviation"],
communes: List<Commune>.from(
json["communes"].map((x) => Commune.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"name": name,
"romanNumber": romanNumber,
"number": number,
"abbreviation": abbreviation,
"communes": List<dynamic>.from(communes.map((x) => x.toJson())),
};
}
class Commune {
Commune({
required this.name,
required this.identifier,
});
String name;
String identifier;
factory Commune.fromJson(Map<String, dynamic> json) => Commune(
name: json["name"],
identifier: json["identifier"] ?? '',
);
Map<String, dynamic> toJson() => {
"name": name,
"identifier": identifier,
};
}
You call ['regions'] in two place:
1:
var results = (jsonDecode(response)['regions'] ?? []) as List;
2: inside Regiones.fromJson
so in your HomeController instead of this:
return results.map((x) => Regiones.fromJson(x)).toList();
try this:
return results.map((x) => Region.fromJson(x)).toList();
and then make your getRegiones return Future<List> like this:
Future<List<Regione>> getRegiones() async {
...
}
*After many documentations readed, I saw that Flutter is not compatible with strapi v4, to use it with Flutter, you have to use a
strapi project under v4.
I'm trying to connect my Flutter app to Strapi.
I followed the official Strapi tuto for flutter and some videos on Youtube about it but I'm stuck to read datas.
I have this error while my view begins:
_TypeError (type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Iterable')
This is my full code for this view:
import 'dart:convert';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:strapitests/user.dart';
class MyList extends StatefulWidget {
const MyList({Key? key}) : super(key: key);
#override
State<MyList> createState() => _MyListState();
}
class _MyListState extends State<MyList> {
List<User> users = [];
Future getAll() async {
var data = await http.get(Uri.parse("http://10.0.2.2:1337/api/apis"));
var jsonData = json.decode(data.body);
for (var u in jsonData) {
users.add(
u['name'],
);
}
return users;
}
#override
Widget build(BuildContext context) {
return Container(
child: FutureBuilder(
future: getAll(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Container(
child: const Center(
child: Text("Loading..."),
),
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(snapshot.data[index].name),
subtitle: Text(snapshot.data[index].email),
);
},
);
}
},
),
);
}
}
And this is my 'User' class:
class User {
String name;
String email;
String password;
User(this.name, this.email, this.password);
}
While i make a 'GET' on my browser, the result is:
"data": [
{
"id": 1,
"attributes": {
"name": "john",
"password": "dfdf",
"email": "test#gmail.com",
"createdAt": "2022-05-23T20:38:27.725Z",
"updatedAt": "2022-05-23T20:38:28.466Z",
"publishedAt": "2022-05-23T20:38:28.464Z"
}
},
{
"id": 2,
"attributes": {
"name": "text",
"password": "mp",
"email": "mail",
"createdAt": "2022-05-23T20:47:56.717Z",
"updatedAt": "2022-05-23T20:47:56.717Z",
"publishedAt": "2022-05-23T20:47:56.712Z"
}
},
{
"id": 3,
"attributes": {
"name": "name",
"password": "mp",
"email": "mail",
"createdAt": "2022-05-23T20:52:07.911Z",
"updatedAt": "2022-05-23T20:52:07.911Z",
"publishedAt": "2022-05-23T20:52:07.910Z"
}
}
],
Thanks for helping!
First, you will need to decode your users from JSON. Since this is a simple class, you can just write a quick fromJson constructor for your User class:
class User {
String name;
String email;
String password;
User(this.name, this.email, this.password);
factory User.fromJson(Map<String, dynamic> json) {
final attributes = json['attributes'];
return User(
attributes['name'],
attributes['email'],
attributes['password'],
);
}
}
Next, the data you're receiving is a map, which cannot be iterated through with a for-loop.
Instead, iterate over the list keyed by "data", and decode each element with the User.fromJson constructor we just defined:
Future<List<User>> getAll() async {
var data = await http.get(Uri.parse("http://10.0.2.2:1337/api/apis"));
var jsonData = json.decode(data.body);
final users = jsonData['data'];
return users.map((userJson) => User.fromJson(userJson)).toList();
}
Finally, since you're using a FutureBuilder, you actually don't need this to be a stateful widget, and you don't need to store users as a property on your class. You can simply use the list returned in the snapshot - Though you'll need change your code so that the Future is a final member so that the widget doesn't construct a new future on each build:
class MyList extends StatelessWidget {
late final Future<List<User>> users = getAll();
#override
Widget build(BuildContext context) {
return Container(
child: FutureBuilder(
future: users,
// ...
),
);
}
}
Also — and this is beside the point in terms of your question — but it's a good idea to look into ways of avoiding storing passwords on your server. If you do store passwords, definitely avoid returning them in any API responses for a production app :).
Here are a couple of good articles on the topic:
https://auth0.com/blog/hashing-passwords-one-way-road-to-security/
https://auth0.com/blog/adding-salt-to-hashing-a-better-way-to-store-passwords/
I am a newbie in the world of flutter and GetX package and I am trying to create a simple app using my API and I have only JSON API and a model how to create my controller and response data??
Here is my JSON response data from the API
{
"isSuccess": true,
"datacount": 77,
"data": [
{
"provinceID": 1,
"provinceNameEN": "Bangkok",
"geoID": 2
},
{
"provinceID": 2,
"provinceNameEN": "Samut Prakan",
"geoID": 2
}
],
"error": {
"code": null,
"messageToDeveloper": null,
"messageToUser": null
}
}
And this is my model
// To parse this JSON data, do
//
// final provicesModel = provicesModelFromJson(jsonString);
import 'dart:convert';
ProvicesModel provicesModelFromJson(String str) => ProvicesModel.fromJson(json.decode(str));
String provicesModelToJson(ProvicesModel data) => json.encode(data.toJson());
class ProvicesModel {
ProvicesModel({
this.isSuccess,
this.datacount,
this.data,
this.error,
});
bool isSuccess;
int datacount;
List<Datum> data;
Error error;
factory ProvicesModel.fromJson(Map<String, dynamic> json) => ProvicesModel(
isSuccess: json["isSuccess"],
datacount: json["datacount"],
data: List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))),
error: Error.fromJson(json["error"]),
);
Map<String, dynamic> toJson() => {
"isSuccess": isSuccess,
"datacount": datacount,
"data": List<dynamic>.from(data.map((x) => x.toJson())),
"error": error.toJson(),
};
}
class Datum {
Datum({
this.provinceId,
this.provinceNameEn,
this.geoId,
});
int provinceId;
String provinceNameEn;
int geoId;
factory Datum.fromJson(Map<String, dynamic> json) => Datum(
provinceId: json["provinceID"],
provinceNameEn: json["provinceNameEN"],
geoId: json["geoID"],
);
Map<String, dynamic> toJson() => {
"provinceID": provinceId,
"provinceNameEN": provinceNameEn,
"geoID": geoId,
};
}
class Error {
Error({
this.code,
this.messageToDeveloper,
this.messageToUser,
});
dynamic code;
dynamic messageToDeveloper;
dynamic messageToUser;
factory Error.fromJson(Map<String, dynamic> json) => Error(
code: json["code"],
messageToDeveloper: json["messageToDeveloper"],
messageToUser: json["messageToUser"],
);
Map<String, dynamic> toJson() => {
"code": code,
"messageToDeveloper": messageToDeveloper,
"messageToUser": messageToUser,
};
}
This is my services
import 'package:dio/dio.dart';
class ProvinceService {
var dio = Dio();
Future<dynamic> provinceService() async {
return await dio.get(
'URL');
}
}
This is my Controller
class RegisterController extends GetxController {
var provicesList = <ProvicesModel>[].obs;
void fetchprovices() async {
ProvinceService request = ProvinceService();
request.provinceService().then((value) {
if (value.statusCode == 200) {
for (var item in value.data) {
<<< Have Error _TypeError (type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Iterable')>>>
provicesList.add(ProvicesModel.fromJson(item));
}
} else {
print('Backend error');
}
}).catchError((onError) {
printError();
});
}
}
This is my page response
class Register extends StatefulWidget {
const Register({Key? key}) : super(key: key);
#override
State<Register> createState() => _RegisterState();
}
class _RegisterState extends State<Register> {
#override
void initState() {
registerController.fetchprovices();
super.initState();
}
final registerController = Get.put(RegisterController());
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(child: GetX<RegisterController>(
builder: (controller) {
return ListView.builder(
itemCount: controller.provicesList.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('${controller.provicesList[index].datacount}'),
subtitle: Text(
'${controller.provicesList[index].data[index].provinceNameEn}'),
);
},
);
},
))
],
),
);
}
}
Refer this for more info :-> getx_dio_example
/////
var provicesList = ProvicesModel().obs;
void fetchprovices() async {
ProvinceService request = ProvinceService();
request.provinceService().then((value) {
if (value.statusCode == 200) {
final response = ProvicesModel.fromJson(value.data);
provicesList.value = response;
} else {
print('Backend error');
}
}).catchError((onError) {
printError();
});
}
///
return ListView.builder(
itemCount: controller.provicesList.data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('${controller.provicesList.datacount}'),
subtitle: Text(
'${controller.provicesList.data[index].provinceNameEn}'),
);
},
);
I am trying to parse a nested JSON document in my app. The JSON structure looks like this:
[
{
"id": 1,
"content": [
{
"type": "text",
"value": "This is a Text1"
},
{
"type": "latex",
"value": "\frac00"
},
{
"type": "text",
"value": "This is a Text2"
},
{
"type": "latex",
"value": "\frac00"
},
{
"type": "text",
"value": "This is a Text3"
}
]
},
{
"id": 2,
"content": [
{
"type": "text",
"value": "This is a Text"
}
]
}
]
And here are my model classes:
class Tutorial {
String id;
List<Content> content;
Tutorial({this.id, this.content});
Tutorial.fromJson(Map<String, dynamic> json) {
id = json['id'];
if (json['content'] != null) {
content = new List<Content>();
json['content'].forEach((v) {
content.add(new Content.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
if (this.content != null) {
data['content'] = this.content.map((v) => v.toJson()).toList();
}
return data;
}
}
class Content {
String type;
String value;
Content({this.type, this.value});
Content.fromJson(Map<String, dynamic> json) {
type = json['type'];
value = json['value'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['type'] = this.type;
data['value'] = this.value;
return data;
}
}
This is how I retrieve that Json and make the response object:
import 'package:Mathzi/pages/courses/models/tutorialModel.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'dart:async' show Future;
import 'dart:convert' as convert;
class TutorialService {
Future<List> fetchTutorial() async {
var response = await rootBundle.loadString('assets/tutorial.json');
final jsonResponse = convert.jsonDecode(response) as List;
return jsonResponse.map((tutorial) => Tutorial.fromJson(tutorial)).toList();
}
}
And here are my Screen Widget tree:
final TutorialService tutorialService = TutorialService();
#override
Widget build(BuildContext context) {
return FutureProvider(
create: (context) => tutorialService.fetchTutorial(),
catchError: (context, error) => print(error.toString()),
child: SizeTransition(
axis: Axis.vertical,
sizeFactor: animation,
child: GestureDetector(
//behavior: HitTestBehavior.opaque,
onTap: onTap,
child: SizedBox(
height: 50.0,
width: MediaQuery.of(context).size.width,
child: TutParagraph()
),
),
),
);
}
And my TutParagraph.dart:
import 'package:Mathzi/pages/courses/models/tutorialModel.dart';
import 'package:catex/catex.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'models/tutorialModel.dart';
class TutParagraph extends StatelessWidget {
const TutParagraph({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
List<Content> parag = Provider.of<List<Content>>(context);
return (parag == null)
? Center(child: CircularProgressIndicator())
: ListView.builder(
itemCount: parag.length,
itemBuilder: (context, index) {
if (parag[index].type.toString() == "text")
return Text(parag[index].value.toString());
else if (parag[index].type.toString() == "latex")
return CaTeX(parag[index].value.toString());
else
return null;
},
);
}
}
if the type is equal to text I use a Text() widget to display it and if it is latex I use CaTex()
When I run my code it gives me this error message:
Error:
Could not find the correct Provider<List> above this
TutParagraph Widget
To fix, please:
Ensure the Provider<List> is an ancestor to this
TutParagraph Widget * Provide types to Provider<List> *
Provide types to Consumer<List> * Provide types to
Provider.of<List>() * Ensure the correct context is being
used.
The best solution is to try to cast and explicitly tell the type of object the List uses to avoid this sort of problems instead of let it infere it
class TutorialService {
Future<List<Tutorial>> fetchTutorial() async { //Tell the trturn type of the List
var response = await rootBundle.loadString('assets/tutorial.json');
final jsonResponse = convert.jsonDecode(response) as List;
return jsonResponse.map<Tutorial>((tutorial) => Tutorial.fromJson(tutorial)).toList();
//Cast the type in the map method <Tutorial>
}
}
Again in the FutureProvider
FutureProvider<List<Tutorial>>( //perhaps it can infere it correctly now that the return type explicitly says is a List<Tutorial>, but lets add it anyway just in case
create: (context) => tutorialService.fetchTutorial(),
catchError: (context, error) => print(error.toString()),
child: ...
)
And in TutParagraph
class TutParagraph extends StatelessWidget {
const TutParagraph({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
List<Tutorial> tutorial = Provider.of<List<Tutorial>>(context); //it should find the FutureProvider
List<Content> parag = (tutorial?.isEmpty ?? true) ? null : tutorial[0].content; //but this will only give you the list of the first element of the tutorial List
return (parag == null)
? Center(child: CircularProgressIndicator())
: ListView.builder(
itemCount: parag.length,
itemBuilder: (context, index) {
if (parag[index].type.toString() == "text")
return Text(parag[index].value.toString());
else if (parag[index].type.toString() == "latex")
return CaTeX(parag[index].value.toString());
else
return null;
},
);
}
}
Now if you want to retrieve only a List<Content> you should try to change the logic of tutorialService.fetchTutorial() to return only that type of list, because the Provider doesn't know what types are inside of Tutorial and obviously if you have a List<Tutorial> it doesn't know the List<Content> of what index of the list of Tutorial you really want