*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/
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 have a json data, I want to display it in drop down such that initial one drop down will shown with name and upon selecting on that it should display child drop down and each child drop down has its own children as well.
class LocationDetail {
final String name;
final String id;
final String categoryId;
final List<LocationDetail>? childrens;
const LocationDetail({
required this.name,
required this.id,
required this.categoryId,
this.childrens,
});
Map<String, dynamic> toMap() {
return {
'name': name,
'id': id,
'categoryId': categoryId,
'childrens': childrens,
};
}
factory LocationDetail.fromMap(Map<String, dynamic> map) {
List<LocationDetail> childrens = [];
if (map['childrens'] != null) {
map['childrens'].forEach((v) {
childrens.add(LocationDetail.fromMap(v));
});
}
return LocationDetail(
name: map['name'] as String,
id: map['id'] as String,
categoryId: map['categoryId'] as String,
childrens: childrens,
);
}
}
Here is the location.json data
[
{
"name": "NewYork",
"id": "NY",
"categoryId": "A",
"childrens": [
{
"name": "Rental Car 1",
"id": "NY-Rental Car1",
"categoryId": "T1"
},
{
"name": "Rental Car 2",
"id": "NY-Rental Car2",
"categoryId": "T2",
"childrens": [
{
"name": "Rental Car 21",
"id": "NY-Rental Car21",
"categoryId": "T21"
},
{
"name": "Rental Car 22",
"id": "NY-Rental Car22",
"categoryId": "T22"
}
]
}
]
}
]
This is how I am reading json and converting to List of locationDetail
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: FutureBuilder<List<LocationDetail>>(
future: _getJsonData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
// all the drop down will come here
} else if (snapshot.hasError) {
return Center(
child: Text(snapshot.error.toString()),
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
Future<List<LocationDetail>> _getJsonData() async {
final contents = await rootBundle.loadString(
'assets/location.json',
);
List<dynamic> dataList = jsonDecode(contents);
List<LocationDetail> locationList =
dataList.map((v) => LocationDetail.fromMap(v)).toList();
return locationList;
}
}
Expected Output:
Initially only one drop down should show which contain only NewYork.
Upon selection on NewYork should show one more drop down which contains Rental Car 1 and Rental Car 2.
If Rental Car 1 is selected nothing should happened since that is the last node.
If Rental Car 2 is selected one more drop down should show to select Rental Car 21 or Rental Car 22.
Same should happened till we reach to end.
P.S Thanks
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
Everytime this runs...
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:rona/Models/Global_model.dart';
class NetworkHelper {
static const String url = 'https://covid-193.p.rapidapi.com/statistics';
static List<GlobalDataModel> parse(String responseBody) {
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed
.map<GlobalDataModel>((json) => GlobalDataModel.fromMap(json))
.toList();
}
static Future<List<GlobalDataModel>> getGlobalData() async {
try {
final response = await http.get(url, headers: {
"x-rapidapi-host": "covid-193.p.rapidapi.com",
"x-rapidapi-key": "1d2e200e8amsh264b6230392cdfcp119d06jsn3f9a7a0e8fd0",
"useQueryString": "true"
});
if (response.statusCode == 200) {
List<GlobalDataModel> list = parse(response.body);
return list;
} else {
throw Exception("Error");
}
} catch (e) {
throw Exception(e.toString());
}
}
}
I get the error below. I have looked at other similar problems from other people but none of them seem to work.
E/flutter (26845): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: Exception: NoSuchMethodError: Class '_InternalLinkedHashMap<String, dynamic>' has no instance method 'cast' with matching arguments.
E/flutter (26845): Receiver: _LinkedHashMap len:5
E/flutter (26845): Tried calling: cast<Map<String, dynamic>>()
E/flutter (26845): Found: cast<RK, RV>() => Map<RK, RV>
My corresponding model file looks like this...
import 'package:rona/Networking/Global_data.dart';
class GlobalDataModel {
String continent;
String country;
int population;
int activeCases;
int criticalCases;
int totalCases;
int recovered;
int totalDeaths;
GlobalDataModel({
this.continent,
this.country,
this.population,
this.activeCases,
this.criticalCases,
this.recovered,
this.totalCases,
this.totalDeaths,
});
Future<dynamic> getGlobalData() async {
await NetworkHelper.getGlobalData().then((data) {
print('Data: ${data.length}');
});
}
factory GlobalDataModel.fromMap(Map<String, dynamic> map) {
return GlobalDataModel(
continent: map['response']['continent'] as String,
country: map['response']['country'] as String,
population: map['response']['population'] as int,
activeCases: map['reponse']['cases']['active'] as int,
criticalCases: map['response']['cases']['critical'] as int,
recovered: map['response']['cases']['recovered'] as int,
totalCases: map['response']['cases']['total'] as int,
totalDeaths: map['response']['deaths']['total'] as int,
);
}
}
And the json looks something like this...
{
"get": "statistics",
"parameters": [],
"errors": [],
"results": 227,
"response": [
{
"continent": "Asia",
"country": "China",
"population": 1439323776,
"cases": {
"new": "+9",
"active": 244,
"critical": 5,
"recovered": 80153,
"1M_pop": "59",
"total": 85031
},
"deaths": {
"new": null,
"1M_pop": "3",
"total": 4634
},
"tests": {
"1M_pop": "62814",
"total": 90410000
},
"day": "2020-08-30",
"time": "2020-08-30T17:00:11+00:00"
},
{
"continent": "Europe",
"country": "Italy",
"population": 60447003,
"cases": {
"new": "+1365",
"active": 24205,
"critical": 86,
"recovered": 208536,
"1M_pop": "4437",
"total": 268218
},
"deaths": {
"new": "+4",
"1M_pop": "587",
"total": 35477
},
"tests": {
"1M_pop": "142130",
"total": 8591341
},
"day": "2020-08-30",
"time": "2020-08-30T17:00:11+00:00"
},
{
"continent": "Europe",
"country": "Spain",
"population": 46757684,
"cases": {
"new": "+3829",
"active": 0,
"critical": 751,
"recovered": 0,
"1M_pop": "9744",
"total": 455621
},
"deaths": {
"new": "+15",
"1M_pop": "620",
"total": 29011
},
"tests": {
"1M_pop": "182161",
"total": 8517446
},
"day": "2020-08-30",
"time": "2020-08-30T17:00:11+00:00"
}]
Please help me out, i have been on this for a while now.
dummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytextdummytext
You are supplying the type arguments to cast incorrectly. You give it the type arguments of the keys and values that you want, not of the Map itself:
final parsed = json.decode(responseBody).cast<String, dynamic>();
For converted JSON strings, though, it's generally safe to just do a normal cast using the as operator:
final parsed = json.decode(responseBody) as Map<String, dynamic>;
Or an implicit cast by giving parsed an explicit type:
final Map<String, dynamic> parsed = json.decode(responseBody);
You can copy paste run full code below
To return List<GlobalDataModel>, you can use List<GlobalDataModel>.from( parsed["response"]
code snippet
static List<GlobalDataModel> parse(String responseBody) {
final Map<String, dynamic> parsed = json.decode(responseBody);
return List<GlobalDataModel>.from(
parsed["response"].map((x) => GlobalDataModel.fromJson(x)));
}
...
factory GlobalDataModel.fromJson(Map<String, dynamic> map) {
return GlobalDataModel(
continent: map['continent'] as String,
country: map['country'] as String,
population: map['population'] as int,
activeCases: map['cases']['active'] as int,
criticalCases: map['cases']['critical'] as int,
recovered: map['cases']['recovered'] as int,
totalCases: map['cases']['total'] as int,
totalDeaths: map['deaths']['total'] as int,
);
working demo
full code
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
class NetworkHelper {
static const String url = 'https://covid-193.p.rapidapi.com/statistics';
static List<GlobalDataModel> parse(String responseBody) {
final Map<String, dynamic> parsed = json.decode(responseBody);
return List<GlobalDataModel>.from(
parsed["response"].map((x) => GlobalDataModel.fromJson(x)));
}
static Future<List<GlobalDataModel>> getGlobalData() async {
try {
final response = await http.get(url, headers: {
"x-rapidapi-host": "covid-193.p.rapidapi.com",
"x-rapidapi-key": "1d2e200e8amsh264b6230392cdfcp119d06jsn3f9a7a0e8fd0",
"useQueryString": "true"
});
if (response.statusCode == 200) {
print(response.body);
List<GlobalDataModel> list = parse(response.body);
return list;
} else {
throw Exception("Error");
}
} catch (e) {
throw Exception(e.toString());
}
}
}
class GlobalDataModel {
String continent;
String country;
int population;
int activeCases;
int criticalCases;
int totalCases;
int recovered;
int totalDeaths;
GlobalDataModel({
this.continent,
this.country,
this.population,
this.activeCases,
this.criticalCases,
this.recovered,
this.totalCases,
this.totalDeaths,
});
Future<dynamic> getGlobalData() async {
await NetworkHelper.getGlobalData().then((data) {
print('Data: ${data.length}');
});
}
factory GlobalDataModel.fromJson(Map<String, dynamic> map) {
return GlobalDataModel(
continent: map['continent'] as String,
country: map['country'] as String,
population: map['population'] as int,
activeCases: map['cases']['active'] as int,
criticalCases: map['cases']['critical'] as int,
recovered: map['cases']['recovered'] as int,
totalCases: map['cases']['total'] as int,
totalDeaths: map['deaths']['total'] as int,
);
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Future<List<GlobalDataModel>> _future;
#override
void initState() {
_future = NetworkHelper.getGlobalData();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: FutureBuilder(
future: _future,
builder: (context, AsyncSnapshot<List<GlobalDataModel>> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('none');
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (snapshot.hasError) {
return Text(
'${snapshot.error}',
style: TextStyle(color: Colors.red),
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Card(
elevation: 6.0,
child: Padding(
padding: const EdgeInsets.only(
top: 6.0,
bottom: 6.0,
left: 8.0,
right: 8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(snapshot.data[index].country
.toString()),
Spacer(),
Text(snapshot.data[index].totalCases
.toString()),
],
),
));
});
}
}
}));
}
}
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
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.