I used exactly same code from Flutter Documentation to fetch data from internet.
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<Album> fetchAlbum() async {
final response = await http
.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return Album.fromJson(jsonDecode(response.body));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
class Album {
final int userId;
final int id;
final String title;
const Album({
required this.userId,
required this.id,
required this.title,
});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['title'],
);
}
}
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({super.key});
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late Future<Album> futureAlbum;
#override
void initState() {
super.initState();
futureAlbum = fetchAlbum();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: const Text('Fetch Data Example'),
),
body: Center(
child: FutureBuilder<Album>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!.title);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
// By default, show a loading spinner.
return const CircularProgressIndicator();
},
),
),
),
);
}
}
It should display data with text widget after getting data, but http.get is not returning and it just keeps showing CircularProgressIndicator like:
I'm using Windows 10, Flutter 3.7.0 stable channel, Dart 2.19.0, http 0.13.5 version. How to fix this?
This could be because you haven't added the interne permissions in you android manifest
<manifest...
...
<uses-permission android:name="android.permission.INTERNET"/>
....
/manifest>
Related
I am trying to create an app that shows the current data in my web server using the Flutter plugin. I want to access the details of my web server using HTTP GET.
I followed this guide (https://docs.flutter.dev/cookbook/networking/fetch-data) and used the code as well but I can't make it work on my HTTP GET link since it has nested parameters.
Here is the code from the guide:
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<Album> fetchAlbum() async {
final response = await http
.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return Album.fromJson(jsonDecode(response.body));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
class Album {
final int userId;
final int id;
final String title;
const Album({
required this.userId,
required this.id,
required this.title,
});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['title'],
);
}
}
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({super.key});
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late Future<Album> futureAlbum;
#override
void initState() {
super.initState();
futureAlbum = fetchAlbum();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: const Text('Fetch Data Example'),
),
body: Center(
child: FutureBuilder<Album>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!.title);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
// By default, show a loading spinner.
return const CircularProgressIndicator();
},
),
),
),
);
}
}
Here is my link by the way: https://api.thingspeak.com/channels/1864145/fields/1.json?results=1
and this is the array it contains
{
"channel":{
"id":1864145,
"name":"channel name",
"description":"description",
"latitude":"0.0",
"longitude":"0.0",
"field1":"first field",
"field2":"second field",
"field3":"third field",
"field4":"fourth field",
"created_at":"2022-09-18T11:53:33Z",
"updated_at":"2022-09-18T12:26:54Z",
"last_entry_id":10742
},
"feeds":[
{"created_at":"2022-12-27T05:34:12Z",
"entry_id":10741,
"field1":"111",
"field2":"4",
"field3":"28",
"field4":"61"
}
]
}
///edit
For those confused, I don't have a code yet since I have no idea how to do it but I tried to replace the variables based on our own parameters and it did not work so I thought there is a different way/code for the nested array parameters.
I'm trying to build a simple list in flutter, I based my code of a flutter cookbook https://docs.flutter.dev/cookbook/networking/background-parsing but I tried applying DDD so its separated in different classes.
import 'package:corsiapp/Domain/Course/course.dart';
import 'package:corsiapp/Infraestructure/remote_data_source.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//3232
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.red,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: FutureBuilder<List<Course>>(
future: RemoteDataSourceImpl(client: http.Client()).getCoursefromAPI(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text('An error has occurred!'),
);
} else if (snapshot.hasData) {
return CourseList(course: snapshot.data!);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
}
class CourseList extends StatelessWidget {
const CourseList({super.key, required this.course});
final List<Course> course;
#override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: course.length,
itemBuilder: (context, index) {
return Text(course[index].title);
},
);
}
}
import 'package:equatable/equatable.dart';
import 'package:corsiapp/Domain/Course/lesson.dart';
class Course extends Equatable {
const Course(
{required this.id,
required this.title,
required this.urlImage,
required this.description});
final int id;
final String title;
final String urlImage;
final String description;
factory Course.fromJson(Map<String, dynamic> json) {
return Course(
id: json['id'] as int,
title: json['title'] as String,
urlImage: json['urlimage'] as String,
description: json['description'] as String);
}
#override
List<Object?> get props => [id, title, urlImage, description];
}
import 'dart:convert';
import 'package:corsiapp/Domain/Course/course.dart';
import 'package:http/http.dart' as http;
abstract class RemoteDataSource {
Future<List<Course>> getCoursefromAPI();
}
class RemoteDataSourceImpl implements RemoteDataSource {
final http.Client client;
RemoteDataSourceImpl({required this.client});
#override
Future<List<Course>> getCoursefromAPI() async {
final response = await client
.get(Uri.parse('https://638c1e60eafd555746a0b852.mockapi.io/Course'));
if (response.statusCode == 200) {
return parseCourse(response.body);
} else {
print('Serch Local Repository');
throw Exception();
}
}
// A function that converts a response body into a List<Photo>.
List<Course> parseCourse(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Course>((json) => Course.fromJson(json)).toList();
}
}
Using a basic print I captured the error of the snapshot that error being Expected a value of type 'int', but got one of type 'String', in the tutorial they show a list of pictures I wanted to show a list of titles.
the JSON returned from https://638c1e60eafd555746a0b852.mockapi.io/Course indicates that the id is of type String not int
try this:
factory Course.fromJson(Map<String, dynamic> json) {
return Course(
id: int.parse(json['id']),
title: json['title'] as String,
urlImage: json['urlimage'] as String,
description: json['description'] as String,
);
}
I basically have everything functioning following this tutorial on how to implement a API in flutter: https://docs.flutter.dev/cookbook/networking/fetch-data. I followed the tutorial but i get an error message "Failed to load Album". My understanding of this is that the server didn't respond with a 200 ok repsonse, but im not sure. I don't know how what the problem is and how i can get it to display the information. The API im using is a car registry, where you can type in a numberplate and get all the details for a vehicle.
Future<Album> fetchAlbum() async {
final response = await http
.get(Uri.parse('http://api.nrpla.de/Cz33849?api_token=JjCYzJA0B5FQPPjlWeyUqN3KSuaErwMyrryuQCmX7R6epHagtINaMjxfwaTGGyrl'),
headers: {
});
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return Album.fromJson(jsonDecode(response.body));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
class Album {
final String registration;
final String type;
final String brand;
final String model;
final String version;
final String fuel_type;
final String registration_status;
final int engine_power;
final String doors;
final String leasing_period_start;
final String leasing_period_end;
final String color;
final String body_type;
Album({
required this.registration,
required this.type,
required this.brand,
required this.model,
required this.version,
required this.fuel_type,
required this.registration_status,
required this.engine_power,
required this.doors,
required this.leasing_period_start,
required this.leasing_period_end,
required this.color,
required this.body_type,
});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
registration: json['registration'],
type: json['type'],
brand: json['brand'],
model: json['model'],
version: json['version'],
fuel_type: json['fuel_type'],
registration_status: json['registration_status'],
engine_power: json['engine_power'],
doors: json['doors'],
leasing_period_start: json['leasing_period_start'],
leasing_period_end: json['leasing_period_end'],
color: json['color'],
body_type: json['body_type'],
);
}
}
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late Future<Album> futureAlbum;
#override
void initState() {
super.initState();
futureAlbum = fetchAlbum();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: const Text('Fetch Data Example'),
),
body: Center(
child: FutureBuilder<Album>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!.registration);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
// By default, show a loading spinner.
return const CircularProgressIndicator();
},
),
),
),
);
}
}
You have consumed your monthly limit that's why you are not getting data from the API status code is 429
I want to pass some parameters in flutter url
here is example:
parameters are source , destination , type, fare.
url is http://localhost:9000/api/bmrc/fare/source/destination/fare/type
url need to send http://localhost:9000/api/bmrc/fare/1/17/2/SJT
(workinng in postman/thunderclient)
I tried after passing these parameters in body but its not worked for me
They are Path Parameters, not body JSON.
You can call your API like this:
callAPI(int source,int destination,int fare,String type){
String _url= "http://localhost:9000/api/bmrc/fare/${source}/${destination}/${fare}/${type}";
... //call your API with _url
}
You can change the param types as you like
its a POST Call:
To get data from from your url, try this.
Here is the sample Example:
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Future<Post> post;
#override
void initState() {
super.initState();
post = fetchPost();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter REST API Example',
theme: ThemeData(
primarySwatch: Colors.green,
),
home: Scaffold(
appBar: AppBar(
title: Text('Flutter REST API Example'),
),
body: Center(
child: FutureBuilder<Post>(
future: post,
builder: (context, abc) {
if (abc.hasData) {
return Text(abc.data.title);
} else if (abc.hasError) {
return Text("${abc.error}");
}
// By default, it show a loading spinner.
return CircularProgressIndicator();
},
),
),
),
);
}
}
Future<Post> fetchPost() async {
final response = await http.get('Give your JSON file web link.');
if (response.statusCode == 200) {
// If the call to the server was successful (returns OK), parse the JSON.
return Post.fromJson(json.decode(response.body));
} else {
// If that call was not successful (response was unexpected), it throw an error.
throw Exception('Failed to load post');
}
}
class Post {
final int userId;
final int id;
final String title;
final String description;
Post({this.userId, this.id, this.title, this. description});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
userId: json['userId'],
id: json['id'],
title: json['title'],
description: json[' description'],
);
}
}
Stream builder in Flutter is getting recalled. I am not sure why. I believe the problem might be that i have a bloc provider in stream builder. My stream dataBloc.dataStream is not changing, to cause the streambuilder to build again. Not sure, what i am doing wrong. Does stream builder build my widgets again and again without any change in stream. Obviously that's not true! Right?
Widget build(context) {
final DataBloc dataBloc = DataBlocProvider.of(context);
print("dropdown build called again");
// this doesn't print recursively so this is perfect.
// So my build is not getting called again.
return StreamBuilder(
stream: dataBloc.dataStream,
builder: (context, snapshot) {
//ToDo remove prints
print("dropdown ${snapshot.data}");
// there is no change in snapshot.data, however print is getting called recursively. This is bad and wrong
// so my stream builder is getting called again, and this is wrong
String key = dataElement.conditionalField;
String _valueArray = dataElement.conditionalValues.toString();
String conditionalValue =
_valueArray.substring(1, _valueArray.length - 1);
Map<String, String> dataMap = snapshot.hasData ? snapshot.data : {};
bool isVisible = true;
if (key != "" &&
dataMap.containsKey(key) &&
dataMap[key] == conditionalValue.toString()) {
isVisible = true;
} else if (key != "") {
isVisible = false;
}
return Visibility(
child: BlocDropDownProvider(
fieldName: dataElement.key,
dataBloc: dataBloc,
child: Card(
color: Colors.grey[100],
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
label,
new Container(
height: 8.0,
),
dropDown,
],
),
),
),
visible: isVisible? true:false,
);
output on console is :
I/flutter (14422): dropdown {idnumber: 10}
I/flutter (14422): dropdown {idnumber: 10}
I can't really replicate this issue with 1:1 accuracy base from the given details. What I encountered similarly is that build within StreamBuilder is being called again on ConnectionState changes.
Here's a minimal repro using StreamBuilder sending a HTTP request. The HTTP request sample here is based from this Flutter Networking guide.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
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> {
final streamController = StreamController();
#override
void initState() {
super.initState();
fetchAlbum().then((response) => streamController.add(response));
}
#override
void dispose() {
super.dispose();
streamController.close();
}
#override
Widget build(BuildContext context) {
debugPrint('build');
return StreamBuilder(
stream: streamController.stream,
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
debugPrint('Stream $snapshot');
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: snapshot.hasData
? Text('Album ${snapshot.data.title}')
: Text('Waiting...'),
),
);
},
);
}
Future<Album> fetchAlbum() async {
final response =
await http.get('https://jsonplaceholder.typicode.com/albums/1');
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return Album.fromJson(jsonDecode(response.body));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
}
class Album {
final int userId;
final int id;
final String title;
Album({this.userId, this.id, this.title});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['title'],
);
}
}
Then again, I suggest not being too concerned with build costs as long as the Widgets inside the build is manageable. More details about Flutter best practices are discussed in this doc.