Android Studio Getting value from Textfield and inserting them into API url - flutter

The API im using is a car registry where you type in a numberplate and it will show details about the vehicle. Basically i have tried implementing a Textfield where i can retrieve it's value so i can insert in the API url (VALUE FROM TEXTFIELD). I have have tried implementing it using this tutorial: https://docs.flutter.dev/cookbook/forms/retrieve-input but with no luck.
Future<Album> fetchAlbum() async {
final response = await http
.get(Uri.parse('https://v1.motorapi.dk/vehicles/(VALUE FROM TEXTFIELD)'),
headers: {"X-AUTH-TOKEN": "rfrzsucnc7eo3m5hcmq6ljdzda1lz793",
"Content-Type": "application/json",
"Accept": "application/json",
});
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');
}
}
I have tried to inserting Textfield in the widget but that didn't work
class _MyAppState extends State<MyApp> {
late Future<Album> futureAlbum;
#override
void initState() {
super.initState();
futureAlbum = fetchAlbum();
TextEditingController nummerpladeController = new TextEditingController();
}
#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 Column( mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [ Text("Registreringsnummer: ""${snapshot.data!.registration_number}"),
Text("Status: ""${snapshot.data!.status}"),
Text("Type: ""${snapshot.data!.type}"),
Text("Brug: ""${snapshot.data!.use}"),
Text("Første registrerings dato: ""${snapshot.data!.first_registration}"),
Text("Vin nummer: ""${snapshot.data!.vin}"),
Text("Mærke: ""${snapshot.data!.make}"),
Text("Model: ""${snapshot.data!.model}"),
Text("Variant: ""${snapshot.data!.variant}"),
Text("Model type: ""${snapshot.data!.model_type}"),
Text("Farve: ""${snapshot.data!.color}"),
Text("Bil type: ""${snapshot.data!.chasis_type}"),
Text("Brændstof: ""${snapshot.data!.fuel_type}"),
Text("Sidste syn: ""${snapshot.data!.date}"),
], );
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
// By default, show a loading spinner.
return const CircularProgressIndicator();
},
),
),
),
);
}
}

You have to assign the TextEditingController to a TextField widget like so:
TextField(
controller: nummerpladeController,
);
To retrieve its value you have to call nummerpladeController.text
Furthermore, you currently are not able to access your nummerpladeController in your build method because it is defined in the initState method. To fix this, do the following:
late final TextEditingController nummerpladeController;
#override
void initState() {
super.initState();
futureAlbum = fetchAlbum();
nummerpladeController = new TextEditingController();
}
You can read more about what late is here: https://dart.dev/null-safety/understanding-null-safety#lazy-initialization

Related

Waiting for Async Data in InitState

I need to get data from a Future to a Stateful widget before it displays on startup. I have tried async/await, FutureBuilder, and the Sync package implementing a WaitGroup within the initState method; however, nothing I do waits for the data to return from the Future before it renders the screen.
In the below examples, I have a simple String strName that I initialize to "Default Name" that I am using for testing and displaying in the Scaffold. It only displays the initialized "Default Name," and not the name returned from the Future. The closest I got was using a FutureBuilder, at least it updated the screen after the initialized "Default Name" was shown. However, I need to get the data prior to the screen rendering. Does anyone have any ideas?
Here's an example of what I tried with Sync WaitGroup:
class _MyHomePageState extends State<MyHomePage> {
String strName = "Default Name";
Future<String> _getName() async {
var name = await Future<String>.delayed(const Duration(seconds: 5), () => "New Name");
return name;
}
#override
void initState() {
WaitGroup wg = WaitGroup();
wg.add(1);
Future<String> futureName = _getName();
futureName.then(
(value) {
strName = value;
wg.done();
},
);
wg.wait();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(strName),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
strName,
style: Theme.of(context).textTheme.headline4,
),
],
),
),
);
}
}
This is what my async/await method looked like:
class _MyHomePageState extends State<MyHomePage> {
String strName = "Default Name";
Future<String> _getName() async {
var name = await Future<String>.delayed(const Duration(seconds: 5), () => "Jimbo");
return name;
}
#override
void initState() {
Future<String> futureName = _getName();
futureName.then(
(value) {
strName = value;
},
);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(strName),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
strName,
style: Theme.of(context).textTheme.headline4,
),
],
),
),
);
}
}
I've never worked with a language where there asynchronous is the default structure of so many parts. How do you deal with making async synchronous in Dart? I haven't even got into the SQLite and HTTP part of it, and it is killing me. I've been at it for four days and got so frustrated I almost broke a keyboard yesterday.
The best is to use a loading screen while fetching your data
and use snapshot.data
full implementation using FutureBuilder:
class _MyHomePageState extends State<MyHomePage> {
String strName = "Default Name";
Future<String> _getName() async {
var name = await Future<String>.delayed(
const Duration(seconds: 5), () => "New Name");
return name;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(strName),
),
body: FutureBuilder<String>(
future: _getName(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
snapshot.data!,
style: Theme.of(context).textTheme.headline4,
),
],
);
}
return Center(
child: CircularProgressIndicator(),
);
}),
);
}
}
This is really a bad practice
but if you really need to resolve some future data before the app renders you can use the void main() method.
void main()async {
Future<String> futureName = _getName();
futureName.then(
(value) {
strName = value;
runApp(MyApp());
},
);
}

Flutter setState vs snapshot

i would like to know whats the diffrence between using setState and snapshot when fetching apis for example
the way i fetch the apis is like the following
Widget text = Container;
Future<AnyClass> fetch() async{
final response = await http
.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));
var result = AnyClass.fromJson(jsonDecode(response.body));
setState(()=> text = result.title)
}
#override
Widget build(BuildContext context) {
return Contianer(child:text)
}
there is another way which uses the snapshot to featch the data instead of using state like the following
Future<Album> fetchAlbum() async { final response = await http
.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));
return Album.fromJson(jsonDecode(response.body));
} #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();
},
),
),
),
); }
so i would like to know what are the diffrence between these two methods. Thanks
None, you can check the FutureBuilder source code to see that is does exactly the same: call setState when it has a response.
The advantage of FutureBuilder are:
Easy handling of the different state (loading, loaded, failure)
You don't have to create a StatefulWidget, which means less line of code

Getting json object and reloading in flutter.. Oh so lost

I am very new to Flutter and have been trying for a few days now to get this right. Either I get stuck with the set state that does not reload(With a spinner) or the JSON cant is decoded as my example.
I want to get the JSON into a listview and then when the floatingActionButton button is pushed refresh the widget with the CircularProgressIndicator. This is what I have so far.
All examples that I find seem not to be Null safe and again I am lost.
My example says "List' is not a subtype of type 'Map<String, dynamic>" and I can see this is because my JSON is a list List ??.
A few pointers would be greatly appreciated.
This is the JSON its pulling:
[
{
"userId": 1,
"id": 1,
"title": "How to make friends"
},
{
"userId": 2,
"id": 1,
"title": "Gone with the wind"
}
]
```
Future<Album> fetchAlbum() async {
print("Fetching json....");
final response = await http.get(
Uri.parse(myLocksEp),
);
if (response.statusCode == 200) {
print(response.body);
return Album.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load album');
}
}
class Album {
final int id;
final String title;
Album({required this.id, required this.title});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
id: json['id'],
title: json['name'],
);
}
}
void main() {
runApp(mylocks());
}
class mylocks extends StatefulWidget {
#override
_MyAppState createState() {
return _MyAppState();
}
}
class _MyAppState extends State<mylocks> {
late Future<Album> _futureAlbum;
#override
void initState() {
super.initState();
_futureAlbum = fetchAlbum();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(8.0),
child: FutureBuilder<Album>(
future: _futureAlbum,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[],
);
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
//Error message goers here
}
}
return const CircularProgressIndicator();
},
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.refresh_outlined, color: Colors.black),
backgroundColor: Colors.grey[200],
onPressed: () {
setState(() {
_futureAlbum = fetchAlbum();
});
},
),
),
);
}
}
Sorry for the long post, I hope I make sense
Thank you in advance
Robby
If I'm not wrong, here you are providing a List<Map<String, dynamic>>
return Album.fromJson(jsonDecode(response.body));
while the Album.fromJson is expecting to receive a Map<String, dynamic>
So, thats why you are getting the error "List' is not a subtype of type 'Map<String, dynamic>"
Apart from that, take into account that every time you build the widget, FutureBuilder will always make a call to the future that you pass to it, so in this case, when you press the FloatingActionButton, you will make a call, and then, when the widget start to rebuild, you will make another one.
You should make some adjustments to avoid this. You could change the FloatingActionButton onPressed callback to be empty:
setState(() {});
Again, if I'm not wrong this will make the widget rebuild itself since you are saying that the state has changed, and when rebuilding, the FutureBuilder will make the request, and therefore, update the list.
EDIT BASED ON THE COMMENTS
class _MyAppState extends State<mylocks> {
late Future<Album> _futureAlbum;
//ADD ALBUMS VAR
List<Album> _albums = [];
#override
void initState() {
super.initState();
//INVOKE METHOD AND SET THE STATE OF _albums WHEN IT FINISHES
fetchAlbum().then((List<Album> albums){
setState((){_albums = albums);
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(8.0),
child: Column(
//REPLACE FUTURE BUILDER WITH DESIRED WIDGET, WHILE _albums IS EMPTY IT WILL RENDER A CircularProgressIndicator
children: [
_albums.isEmpty ? Center(
child: CircularProgressIndicator(),
) : ..._albums,
]
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.refresh_outlined, color: Colors.black),
backgroundColor: Colors.grey[200],
onPressed: () {
//AGAIN, WHEN FINISHING IT SHOULD REBUILD THE WIDGET AND THE DATA
fetchAlbum().then((List<Album> albums){
setState((){_albums = albums);
});
},
),
),
);
}
}
Don't worry about creating models for your Json just use this link to autogenerate a model.

How to add CircularProgressIndicator in flutter?

I have call three web services in one page now for that i want show only one CircularProgressIndicator.How to show CircularProgressIndicator first when i open screen after CircularProgressIndicator show all UI.
Use Future.wait to merge three futures (web service calls) then use FutureBuilder to show CircularProgressIndicator while waiting for the merged Future to complete.
Try it like this,
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String _data1;
String _data2;
String _data3;
Future<void> _webCall;
#override
void initState() {
_webCall = _callWebServices();
super.initState();
}
Future<bool> _callWebServices() async {
await Future.wait([_webService1(), _webService2(), _webService3()]);
return true;
}
Future<void> _webService1() async {
await Future.delayed(const Duration(seconds: 1)); //TODO: do webservice call `get` or `post`
_data1 = "This is data1"; //TODO: save the data
}
Future<void> _webService2() async {
await Future.delayed(const Duration(seconds: 5)); //TODO: do webservice call `get` or `post`
_data2 = "This is data2"; //TODO: save the data
}
Future<void> _webService3() async {
await Future.delayed(const Duration(seconds: 3)); //TODO: do webservice call `get` or `post`
_data3 = "This is data3"; //TODO: save the data
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Demo"),
),
body: FutureBuilder(
future: _webCall,
builder: (context, snapshot) {
print(snapshot);
if (snapshot.connectionState == ConnectionState.waiting)
return _buildProgressIndicator();
else if (snapshot.hasError)
return _buildError();
else
return _buildBody();
},
),
);
}
Widget _buildProgressIndicator() {
return Center(
child: CircularProgressIndicator(),
);
}
Widget _buildBody() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_data1),
Text(_data2),
Text(_data3),
],
),
);
}
Widget _buildError() {
return Center(
child: Text("Error while loading Web Services"),
);
}
}

FLutter : The method '[]' was called on null when iterating list

This is the source code I have till now. Can you tell me what is wrong and why I am getting this error?
Any help would be appreciated. Thanks!
The error is:
error in Data[0] it show Flutter : The method '[]' was called on null when iterating list import 'package:flutter/material.dart';
class MyGetHttpDataState extends State<MyGetHttpData> {
final String url = "*******";
List data, a; int index; // Function to get the JSON data
Future<String> getJSONData() async {
var response = await http.get(url);
print(response.body);
setState(() {
json.decode(response.body);
data = dataConvertedToJSON['data']; index = 0;
});
return "Successfull";
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar( title: new Text('List Test'), ),
body: new Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
for (var name in data[0]['INCI']) Text(name)
], ), ), ));
}
#override
void initState() {
super.initState();
this.getJSONData();
}
}
Just change your code to this!
Future<String> getJSONData() async {
var response = await http.get(url);
setState(() {
var dataConvertedToJSON = json.decode(response.body);
data = dataConvertedToJSON['data'];
data = data[0]['INCI'];
});
return "Successfull";
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar( title: new Text('List Test'), ),
body: new Center(
child: data != null ? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
for (var name in data) Text(name)
],
) : new CircularProgressIndicator(),
),
)
);
}