how to get data object in flutter - flutter

hello i have json data like this
{
"iduser": 3,
"fname": "joni"
}
I want to display it on the home page
Previously I have created a model class below
usermodel.dart
class UserModel {
int id;
String fname;
UserModel(
this.id,
this.fname,
);
UserModel.fromJson(Map<String, dynamic> response) {
id = response['iduser'];
fname = response['fname'];
}
Map<String, dynamic> toJson() {
return {
'id': id,
'fname': fname,
};
}
}
and I created a service page to interact with api
class AuthService {
String baseUrl = 'https://myurl.com';
Future<UserModel> getUser() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var id = prefs.getInt('id');
var token = prefs.getString('token');
var url = '$baseUrl/users/$id';
var headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer $token'
};
var response = await http.get(
Uri.parse(url),
headers: headers,
);
print(response.body);
if (response.statusCode == 200) {
var data = jsonDecode(response.body);
UserModel user = UserModel.fromJson(data);
return user;
} else {
print(response.body);
throw Exception('Failed');
}
}
}
home.dart
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Center(
child: Text( ), //get json fname
),
);
}
}
before I run but I get error type
'_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'FutureOr<List<GetUserModel>>'
how to display the fname I get from the service on the home page?
thank you !

Make home.dart a stateful widget and get the data in initstate and store in a variable. Use that variable to display the data here is how
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
void initState() {
super.initState();
getAsync();
}
UserModel user;
getAsync() async {
try {
user = await AuthService().getUser();
} catch (e) {
print(e);
}
if (mounted) setState(() {});
}
#override
Widget build(BuildContext context) {
if (user == null) return Center(child: CircularProgressIndicator());
else
return Container(
color: Colors.white,
child: Center(
child: Text(user.fname), //get json fname
),
);
}
}

You have two options;
Use FutureBuilder
Convert to StatefullWidget
I give you FutureBuilder example;
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FutureBuilder<UserModel>(
future: AuthService().getUser(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting: return Text('Loading....');
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
final data = snapshot.data;
return Container(
color: Colors.white,
child: Center(
child: Text(data.fname), //get json fname
),
);
}
}
},
);
}
}
Also, as far as I can see, there is a problem with the id conversion of the fromJson and toJson methods. Related fields should be 'iduser' according to json data.
class UserModel {
int id;
String fname;
UserModel(
this.id,
this.fname,
);
UserModel.fromJson(Map<String, dynamic> response) {
id = response['iduser'];
fname = response['fname'];
}
Map<String, dynamic> toJson() {
return {
'iduser': id,
'fname': fname,
};
}
}

First, you may want to be consistent in your map key to get the desired result.
You have to replace the key of flutter map version from:
id = response['id']; => id = response['iduser'];
or vice versa.
Now in your homepage, you need to instantiate the AuthService class in order to access the function that will get the specified user.
You need to use FutureBuilder in order to automatically update the Text if the data was fetched.
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
AuthService _authService = AuthService();
return Container(
color: Colors.white,
child: FutureBuilder<User>(
future: _authService.getUser(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.firstName);
}
/// Show some loading artifact while fetching the
/// user data from the server.
else {
return CircularProgressIndicator();
}
},
),
);
}
}

Related

Flutter type 'Null' is not a subtype of type 'int', trying to get complicated JSON into flutter

This is my json here: https://my-json-server.typicode.com/fluttirci/testJson/db
This code only works if there is an only one json object however, with this employees JSON, it doesn't work. Flutter documentation isn't very clear about this subject. They only work on one line jsons. What I wanna do is, I wanna get all that data into my phone screen. If I get it, I will show them on a table or a grid. But yet it doesn't won't work. It says type 'Null' is not a subtype of type 'int' . Here is my code:
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://my-json-server.typicode.com/fluttirci/testJson/db'));
print(response);
Map<String, dynamic> userMap = jsonDecode(response.body);
if (response.statusCode == 200) {
return Album.fromJson(userMap); //testing
} else {
throw Exception('Failed to load album');
}
}
class Album {
final int userId;
final int id;
final String title;
Album(this.userId, this.id, this.title);
Album.fromJson(Map<String, dynamic> json)
: userId = json['userId'],
id = json['id'],
title = json['title'];
Map<String, dynamic> toJson() => {
'userId': userId,
'id': id,
'title': 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;
late Future<Album> user;
#override
void initState() {
super.initState();
user = fetchAlbum();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
brightness: Brightness.dark,
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: const Text('Fetch Data Example'),
),
body: Center(
child: FutureBuilder<Album>(
future: user,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!.title);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
),
),
),
);
}
}
Try this:
Future<List<Album>> fetchAlbum() async {
final response = await http.get(
Uri.parse('https://my-json-server.typicode.com/fluttirci/testJson/db'));
print(response);
Map<String, dynamic> userMap = jsonDecode(response.body);
if (response.statusCode == 200) {
return (userMap['employees'] as List).map((e) => Album.fromJson(e)).toList()
} else {
throw Exception('Failed to load album');
}
}
then change your FutureBuilder to this:
FutureBuilder<List<Album>>(
future: user,
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Album> data = snapshot.data ?? [];
return ListView.builder(
itemBuilder: (context, index) {
return Column(children: [
Text(data[index].title ?? ""),
]);
},
itemCount: data.length,
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
)
Your response.body return a list on employees key. And test this model with the response
Future<List<Album>?> fetchAlbum() async {
final response = await http.get(
Uri.parse('https://my-json-server.typicode.com/fluttirci/testJson/db'));
if (response.statusCode == 200) {
final data = jsonDecode(response.body)["employees"] as List?;
return data?.map((e) => Album.fromMap(e)).toList();
} else {
throw Exception('Failed to load album');
}
}
class Album {
final int userId;
final int id;
final String title;
Album(this.userId, this.id, this.title);
Map<String, dynamic> toMap() {
final result = <String, dynamic>{};
result.addAll({'userId': userId});
result.addAll({'id': id});
result.addAll({'title': title});
return result;
}
factory Album.fromMap(Map<String, dynamic> map) {
return Album(
map['userId']?.toInt() ?? 0,
map['id']?.toInt() ?? 0,
map['title'] ?? '',
);
}
String toJson() => json.encode(toMap());
factory Album.fromJson(String source) => Album.fromMap(json.decode(source));
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late final user = fetchAlbum();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FutureBuilder<List<Album>?>(
future: user,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data?.length,
itemBuilder: (context, index) =>
Text("${snapshot.data?[index].title}"),
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
),
),
);
}
}
so the resultMap should look right like this :
{
"employees": [
{
"userId": 1,
"id": 2,
"title": "Doe"
},
{
"userId": 2,
"id": 3,
"title": "Smith"
},
{
"userId": 3,
"id": 4,
"title": "Jones"
}
]
}
This is a map that only has one property, which it values as a List of other maps
so accessing json['userId'] will try to get the userId from that map, which doesn't exist in the map
you need to access the employees property :
json["employees"]
then you get the value of it, which is the nested List of maps, and now you can access an element in the List with its index, then get the userId :
json["employees"][0]["userId"] // 1
json["employees"][1]["userId"] // 2
json["employees"][2]["userId"] // 3
hope this gives you a better approach to what you are trying to do, and what you need to do.
so this :
return Album.fromJson(userMap);
should be replaced with this, as an example:
return Album.fromJson(userMap["employees"][0]);
here the userMap["employees"][0] is :
{
"userId": 1,
"id": 2,
"title": "Doe"
},
and now it should make an Album object from it.
if you want to get a List instead of the List<Map<string, dynamic>>, you need to iterate over the whole list using the map method or with a for loop
hope it helps

convert future builder to listview builder

i want to fetch data withour using future, can someone help me to convert it ? direct using listview.builder without using future builder. and how can i post it ? i already try it for a couple days and stuck here. please explain it too
thank you
import 'package:flutter/material.dart';
import 'package:flutter/src/foundation/key.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:latihan_dio/src/features/home/domain/user.dart';
import '../../../../dio_client.dart';
class myHomepage extends StatefulWidget {
const myHomepage({Key? key}) : super(key: key);
#override
State<myHomepage> createState() => _myHomepageState();
}
class _myHomepageState extends State<myHomepage> {
// List<User> users = [];
var selectedIndex = 0;
#override
void initState() {
super.initState();
// fetchData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FutureBuilder<List<User>>(
future: fetchData(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text('Loading....');
default:
// if (snapshot.hasError) {
// return Text('Error: ${snapshot.error}');
// } else {
List<User>? data = snapshot.data;
return ListView.builder(
itemBuilder: (context, index) {
return Column(children: [
Text(data![index].firstName!),
]);
},
itemCount: data?.length,
);
}
}
// },
),
));
}
Future<List<User>> fetchData() async {
var Response = await DioClient().apiCall(
url: 'https://reqres.in/api/users?page=2',
requestType: RequestType.GET,
// queryParameters: {},
);
if (Response.statusCode == 200) {
List<dynamic> listUser = Response.data['data'];
List<User> users = listUser.map((e) => User.fromJson(e)).toList();
return users;
} else {
return [];
}
}
}
// Future<void> fetchData() async {
// var Response = await DioClient().apiCall(
// url: 'https://reqres.in/api/users?page=2',
// requestType: RequestType.GET,
// // queryParameters: {},
// );
// // List<dynamic> listUser = Response.data;
// // OR
// List<dynamic> listUser =
// Response.data['data']; // if you want to access data inside it
// List<User> users = listUser.map((e) => User.fromJson(e)).toList();
// }
as u can see here is my homepage. i make a freeze class and using dio client here.
Try this
class _myHomepageState extends State<myHomepage> {
List<User> user = [];
bool isLoading = false;
#override
void initState() {
initFunction();
super.initState();
}
void initFunction() async {
setState((){
isLoading= true;
})
user = await fetchData();
setState((){
isLoading = false;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: isLoading
? CircularProgressIndicator()
: ListView.builder(
itemBuilder: (context, index) {
return Column(children: [
Text(user[index].firstName!),
]);
},
itemCount: user.length,
);
),
));
}
Future<List<User>> fetchData() async {
var Response = await DioClient().apiCall(
url: 'https://reqres.in/api/users?page=2',
requestType: RequestType.GET,
// queryParameters: {},
);
if (Response.statusCode == 200) {
List<dynamic> listUser = Response.data['data'];
List<User> users = listUser.map((e) => User.fromJson(e)).toList();
return users;
} else {
return [];
}
}
}

The element type 'Future<List<Organization>>?' can't be assigned to the list type 'Widget'

class Organization_Api{
static Future<List<dynamic>> getData(
{required String target, String? limit}) async {
try {
var uri = Uri.https(
BASE_URL,
"api/$target",
target == "organizations"
? {
"offset": "0",
"limit": limit,
}
: {});
var response = await http.get(uri);
var data = jsonDecode(response.body);
List tempList = [];
if (response.statusCode != 200) {
throw data["message"];
}
for (var v in data) {
tempList.add(v);
}
return tempList;
} catch (error) {
log("An error occured $error");
throw error.toString();
}
}
static Future<List<Organization>> getAllOrganizations(
{required String limit}) async {
List temp = await getData(
target: "organizations",
limit: limit,
);
return Organization.organizationsToList(temp);
}
static Future<Organization> getOrganizationById({required String id}) async {
try {
var uri = Uri.https(
BASE_URL,
"api/organizations/$id",
);
var response = await http.get(uri);
var data = jsonDecode(response.body);
if (response.statusCode != 200) {
throw data["message"];
}
return Organization.fromJson(data);
} catch (error) {
log("an error occured while getting organization info $error");
throw error.toString();
}
}
}
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
static String routeName = "/home";
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
Future<List<Organization>>? result ;
void initState(){
result = Organization_Api.getAllOrganizations(limit: '4');
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Organizations", style: TextStyle(color: Colors.black),
),
backgroundColor: Colors.white,
centerTitle: true,
),
body: Padding(
padding: EdgeInsets.all(10.0),
child: Column(
children:
<Widget>[
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children:<Widget>[
ListView(
shrinkWrap: true,
children:<Widget> [result],
)
],
),
)
],
),
),
);
}
}
class Organization{
final int OrganizationId;
final String OrganizationName;
Organization({required this.OrganizationId,required this.OrganizationName});
factory Organization.fromJson(Map<String,dynamic> json){
return Organization(OrganizationId: json['OrganizationId'], OrganizationName: json['OrganizationName']);
}
Map toJson(){
return{
"OrganizationId": this.OrganizationId,
"OrganizationName": this.OrganizationName,
};
}
static List<Organization> organizationsToList(List organizationToList) {
return organizationToList.map((data) {
return Organization.fromJson(data);
}).toList();
}
}
Error = The element type >'Future<List>?' can't be assigned to the list type 'Widget'.
I just want to check the data coming from the service, but I couldn't find how to do it.
What did I do wrong or what did I miss to list the incoming data?
I shared the screen page and the codes on how I got the information from the service.
Your Organization_Api.getAllOrganizations provide a future. You can use FutureBuilder.
class _HomeScreenState extends State<HomeScreen> {
Future<List<Organization>>? myFuture;
#override
void initState() {
myFuture = Organization_Api.getAllOrganizations(limit: '4');
super.initState();
}
And on future builder
FutureBuilder<List<Organization>?>(
future: myFuture,
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.data!.isNotEmpty) {
return ListView(
shrinkWrap: true,
//children: snapshot.data!, // when `Organization` is a widget
children:// when `Organization` is a data model class
snapshot.data!.map((e) => Text(e.toString())).toList(),
);
}
return CircularProgressIndicator();
},
)
Also check Randal L. Schwartz video on using Future

FutureBuilder class argument future is an async function with arguments

I'm developing a Flutter mobile application which uses Google APIs. In one of the screens of my application I want to let the user type in a place (city, address, ...) and call the Google Places API to generate a list of suggestions based on user input. Whenever the text input changes a new GET request is issued.
To handle user input I am using a TextEditingController and in order to have a better user experience I want to use FutureBuilder class in order to show a loading spinner when the data is not ready. This is the code:
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
class Info extends StatefulWidget {
static const routeName = '/info';
#override
_InfoState createState() => _InfoState();
}
class _InfoState extends State<Info> {
final controller = TextEditingController();
#override
void initState() {
// Start listening to changes.
controller.addListener(buildPredictionList);
super.initState();
}
#override
void dispose() {
// Clean up the controller when the widget is disposed.
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Info'),
),
body: Column(
children: <Widget>[
TextField(
controller: controller,
),
Container(
height: 200,
child: buildPredictionList(),
),
],
),
);
}
Widget buildPredictionList() {
return FutureBuilder(
future: fetchPredictions, // <-- Error! fetchPredictions expects a parameter!
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
Prediction pred = snapshot.data[index];
return Card(
child: ListTile(
leading: Icon(Icons.pin_drop),
title: Text('${pred.description}'),
),
);
},
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return CircularProgressIndicator();
},
);
}
}
class Prediction {
final String placeId;
final String description;
Prediction({this.placeId, this.description});
factory Prediction.fromJson(Map<String, dynamic> json) {
return Prediction(
placeId: json['place_id'],
description: json['description'],
);
}
}
Future<List<Prediction>> fetchPredictions(String query) async {
const GOOGLE_API_KEY = '...';
final lat = 40.758058;
final lng = -73.985626;
final radius = 2000;
final lang = 'en';
var url =
'https://maps.googleapis.com/maps/api/place/autocomplete/json?input=$query&key=$GOOGLE_API_KEY&location=$lat,$lng&radius=$radius&language=$lang&strictbounds=true';
final response = await http.get(url);
if (response.statusCode == 200) {
var predictionsJson = json.decode(response.body)['predictions'] as List;
List<Prediction> predictions = predictionsJson
.map((predictionJson) => Prediction.fromJson(predictionJson))
.toList();
return predictions;
} else {
throw Exception('Failed to fetch Predictions');
}
}
My async function fetchPredictions expects an argument, which is the query string used for the GET request (so the input address, city, ...). But I cannot wrap this in an anonymous function because the future argument is expecting a return type of Future.
Thanks in advance!

Error when using StreamProvider and StreamBuilder

I am trying to use StreamProvider and StreamBuilder to pull data from firestore into my app with the code below. I am getting the error "streamusers and "userslist" are not defined as well as "testuser" is not a type. Here is a picture of my firestore databasefirestore setup]1
does anyone know how I can fix this so that it pulls the data from firestore and updates dynamically when new users are added?
Main.dart:
class _MyHomePageState extends State<MyHomePage> {
final auth = FirebaseAuth.instance;
final db = DatabaseService();
#override
Widget build(BuildContext context) {
var user = Provider.of<FirebaseUser>(context);
bool loggedIn = user != null;
final _width = MediaQuery.of(context).size.width;
final _height = MediaQuery.of(context).size.height;
StreamProvider<List<User>>.value(
value: db.streamUsers(user),
child: UsersList(),
),
StreamBuilder<TestUser>(
stream: db.streamTestUser(user.uid),
builder: (context, snapshot) {
var user = snapshot.data;
if (user != null) {
return Stack(...
I also have my db.dart file as so:
class DatabaseService {
final Firestore _db = Firestore.instance;
Future<User> getUser(String id) async {
var snap = await _db.collection('users').document(id).get();
return User.fromMap(snap.data);
}
Stream<User> streamTestUser(String id) {
return _db
.collection('users')
.document(id)
.snapshots()
.map((snap) => User.fromMap(snap.data));
}
}
And finally my user_model.dart file:
class User {
final String name;
final String photourl;
final int totalquestions;
User({this.name, this.photourl, this.totalquestions});
factory User.fromMap(Map data) {
return User(
name: data['name'] ?? '',
photourl: data['photourl'] ?? '',
totalquestions: data['totalquestions'] ?? '',
);
}
}
Try using Builder inside StreamProvider instead of StreamBuilder.
Mine is working using this approach.
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
var user = Provider.of<FirebaseUser>(context);
return StreamProvider<User>.value(
value: db.getUser(user?.uid),
catchError: (_, __) => null,
child: Builder(
builder: (context) {
///Passing UserData Down the Builder
var _userSnapshot = Provider.of<UserData>(context);
///Check UserData Availability
if (_userSnapshot == null) {
return Center(
child: Text('User Empty'),
);
} else {
return Scaffold(
body: Column(
children: <Widget>[
Text(_userSnapshot?.name),
Text(_userSnapshot?.photourl),
Text(_userSnapshot?.totalquestions),
],
),
);
}
},
),
);
}