Can I use POST request in future builder? - flutter

I was trying to fetch data from my backend which is developed using Laravel framework. I need to use POST request for each request to pass the API middleware.
When I use GET request without headers, future builder works just fine and got updated immediately after the data on the backend has changed but I can't get Laravel to grab the current authenticated user because no token provided.
But when I use POST request or GET request with headers, it stops updating and I need to switch to another page and go back in order to get the changes.
Please take a look at my script below:
Future<List<Document>> fetchDocuments(http.Client http, String token) async {
final headers = {'Authorization': 'Bearer $token'};
final response = await http.post(
'http://192.168.1.2:8000/api/documents/all',
headers: headers,
);
// Use the compute function to run parseDocuments in a separate isolate.
return compute(parseDocuments, response.body);
}
// A function that converts a response body into a List<DOcument>
List<Document> parseDocuments(String responseBody) {
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Document>((json) => Document.fromJSON(json)).toList();
}
class Document {
final int id;
final String name;
Document({this.id, this.name});
factory Document.fromJSON(Map<String, dynamic> json) {
return Document(
id: json['id'] as int,
name: json['name'] as String,
);
}
}
class StudentDocumentScreen extends StatefulWidget {
StudentDocumentScreen({Key key}) : super(key: key);
_StudentDocumentScreenState createState() => _StudentDocumentScreenState();
}
class _StudentDocumentScreenState extends State<StudentDocumentScreen> {
final storage = FlutterSecureStorage();
final _uploadURL = 'http://192.168.1.2:8000/api/documents';
final _scaffoldKey = GlobalKey<ScaffoldState>();
final SnackBar uploadingSnackbar = SnackBar(
content: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Uploading file, this may take a while...',
style: TextStyle(color: Colors.white),
),
),
);
final SnackBar successSnackbar = SnackBar(
content: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'File uploaded!',
style: TextStyle(color: Colors.white),
),
),
backgroundColor: Colors.green,
);
final SnackBar errorSnackbar = SnackBar(
content: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Oops... Something went wrong!',
style: TextStyle(color: Colors.white),
),
),
backgroundColor: Colors.red,
);
String _token;
String _path;
#override
void initState() {
super.initState();
_getToken();
}
void _getToken() async {
String token = await storage.read(key: 'accessToken');
setState(() => _token = token);
}
void _openFileExplorer() async {
try {
_path = null;
_path = await FilePicker.getFilePath();
} on PlatformException catch (e) {
print('Unsupported operation');
print(e);
}
if (!mounted) return;
if (_path != null || _path.isNotEmpty) {
_uploadDocument(_path);
}
}
void _uploadDocument(String path) async {
_scaffoldKey.currentState.showSnackBar(uploadingSnackbar);
try {
var multipartFile = await http.MultipartFile.fromPath('document', path);
var request = http.MultipartRequest('POST', Uri.parse(_uploadURL));
request.headers.addAll({'Authorization': 'Bearer $_token'});
request.files.add(multipartFile);
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
_scaffoldKey.currentState.showSnackBar(successSnackbar);
} else {
_scaffoldKey.currentState.showSnackBar(errorSnackbar);
}
} catch (e) {
print('Error when uploading files');
print(e);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text('Koleksi Dokumen'),
),
body: _buildBody(),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: _openFileExplorer,
),
);
}
Widget _buildBody() {
return FutureBuilder<List<Document>>(
future: fetchDocuments(http.Client(), _token),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? DocumentList(documents: snapshot.data)
: Center(child: CircularProgressIndicator());
},
);
}
}
class DocumentList extends StatelessWidget {
final List<Document> documents;
const DocumentList({Key key, this.documents}) : super(key: key);
#override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: documents.length,
itemBuilder: (context, index) {
return Card(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(bottom: 20.0),
child: Icon(
Icons.insert_drive_file,
color: Colors.black12,
size: 50.0,
),
),
Text(
documents[index].name,
textAlign: TextAlign.center,
),
],
),
),
);
},
);
}
}
And below is the laravel script:
# in the `api.php`
Route::post('documents/all', 'DocumentController#index');
# in the `DocumentController`
public function index()
{
$userId = auth()->user()->id;
return Document::whereUserId($userId)
->get()
->load('user');
}
Any idea? Thanks in advance.

I just found a temporary solution, putting setState(() {}); after uploading progress do the job.
Please feel free to answer if you guys has a better one.
Solution:
void _uploadDocument(String path) async {
_scaffoldKey.currentState.showSnackBar(uploadingSnackbar);
try {
var multipartFile = await http.MultipartFile.fromPath('document', path);
var request = http.MultipartRequest('POST', Uri.parse(_uploadURL));
request.headers.addAll({'Authorization': 'Bearer $_token'});
request.files.add(multipartFile);
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
_scaffoldKey.currentState.showSnackBar(successSnackbar);
setState(() {}); // reload state
} else {
_scaffoldKey.currentState.showSnackBar(errorSnackbar);
}
} catch (e) {
print('Error when uploading files');
print(e);
}
}

Related

Duplicate GlobalKey detected in tree widget scaffoldState

I was trying to update data (add, edit, delete) from my list using API, when I clicked something to update the list e.g deleting an item it showed a successfully updated. but in the Debug Console, there's an error that says
The following assertion was thrown while finalizing the widget tree:
Duplicate GlobalKey detected in the widget tree. The following GlobalKey
was specified multiple times in the widget tree. This will lead to
parts of the widget tree being truncated unexpectedly, because the
second time a key is seen, the previous instance is moved to the new
location. The key was:
[LabeledGlobalKey #47c37] This was determined by noticing that after the widget with the above global key was moved out
of its previous parent, that previous parent never updated during this
frame, meaning that it either did not update at all or updated before
the widget was moved, in either case implying that it still thinks
that it should have a child with that global key.
And when I tried going back to the Previous screen it just showed Blank Screen
Here's the code
Model
import 'dart:convert';
class Nasabah {
int id;
String nama_debitur;
String alamat;
String no_telp;
String no_ktp;
String no_selular;
Nasabah({
this.id = 0,
this.nama_debitur,
this.alamat,
this.no_telp,
this.no_ktp,
this.no_selular,
});
factory Nasabah.fromJson(Map<String, dynamic> json) => Nasabah(
id: json["id"],
nama_debitur: json["nama_debitur"],
alamat: json["alamat"],
no_telp: json["no_telp"],
no_ktp: json["no_ktp"],
no_selular: json["no_selular"],
);
factory Nasabah.fromMap(Map<String, dynamic> map) => Nasabah(
id: map["id"],
nama_debitur: map["nama_debitur"],
alamat: map["alamat"],
no_telp: map["no_telp"],
no_ktp: map["no_ktp"],
no_selular: map["no_selular"],
);
Map<String, dynamic> toJson() => {
"id": id,
"nama_debitur": nama_debitur,
"alamat": alamat,
"no_telp": no_telp,
"no_ktp": no_ktp,
"no_selular": no_selular,
};
#override
String toString() {
return 'Nasabah{id: $id, nama_debitur: $nama_debitur, alamat: $alamat, no_telp: $no_telp, no_ktp: $no_ktp, no_selular: $no_selular}';
}
}
class NasabahResult {
String status;
List<Nasabah> data = new List<Nasabah>();
NasabahResult({
this.status,
this.data,
});
factory NasabahResult.fromJson(Map<String, dynamic> data) => NasabahResult(
status: data["status"],
data: List<Nasabah>.from(
data["data"].map((item) => Nasabah.fromJson(item))),
);
}
NasabahResult nasabahResultFromJson(String jsonData) {
final data = json.decode(jsonData);
return NasabahResult.fromJson(data);
}
String nasabahToJson(Nasabah nasabah) {
final jsonData = nasabah.toJson();
return json.encode(jsonData);
}
Service
import 'package:flutter_auth/Models/nasabah.dart';
import 'dart:convert';
import 'package:http/http.dart' show Client;
class ApiService {
final String baseUrl = '192.168.100.242:8080';
Client client = Client();
Future<List<Nasabah>> getNasabah() async {
final response = await client.get('http://$baseUrl/api/mstdebitur');
print(response.body);
if (response.statusCode == 200) {
final nasabah = (json.decode(response.body) as List)
.map((e) => Nasabah.fromMap(e))
.toList();
return nasabah;
} else {
throw Exception('Failed to load post');
}
}
Future<bool> createNasabah(Nasabah data) async {
String url = new Uri.http("$baseUrl", "/api/mstdebitur/").toString();
final response = await client.post(url,
headers: {"Content-Type": "application/json"},
body: nasabahToJson(data));
if (response.statusCode == 201) {
return true;
} else {
return false;
}
}
Future<bool> updateNasabah(Nasabah data) async {
String url =
new Uri.http("$baseUrl", "/api/mstdebitur/${data.id}").toString();
final response = await client.put(url,
headers: {"Content-Type": "application/json"},
body: nasabahToJson(data));
if (response.statusCode == 200) {
return true;
} else {
return false;
}
}
Future<bool> deleteNasabah(int id) async {
String url = new Uri.http("$baseUrl", "/api/mstdebitur/$id").toString();
final response = await client.delete(url);
if (response.statusCode == 200) {
return true;
} else {
return false;
}
}
}
Home
import 'package:flutter/material.dart';
import 'package:flutter_auth/Models/nasabah.dart';
import 'package:flutter_auth/network/nasabah_service.dart';
import 'FormAddNasabah.dart';
import 'ListNasabah.dart';
GlobalKey<ScaffoldState> _scaffoldState = GlobalKey<ScaffoldState>();
// ignore: must_be_immutable
class DataNasabah extends StatefulWidget {
DataNasabah({Key key}) : super(key: key);
String title;
#override
_DataNasabahState createState() => _DataNasabahState();
}
class _DataNasabahState extends State<DataNasabah> {
ApiService apiService;
ListNasabah _listNasabah;
List<Nasabah> data;
#override
void initState() {
super.initState();
apiService = ApiService();
_listNasabah = new ListNasabah(apiService: apiService);
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldState,
appBar: AppBar(
title: Text(
'Data Nasabah',
style: TextStyle(color: Colors.white),
),
actions: <Widget>[
GestureDetector(
onTap: () async {
var result = await Navigator.push(
_scaffoldState.currentContext,
MaterialPageRoute(builder: (BuildContext context) {
return FormAddNasabah(nasabah: null);
}),
);
if (result != null) {
setState(() {});
}
},
child: Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Icon(
Icons.add,
color: Colors.white,
),
),
)
],
),
body: _listNasabah.createViewList(),
);
}
}
List
import 'package:flutter/material.dart';
import 'package:flutter_auth/Models/nasabah.dart';
import 'package:flutter_auth/network/nasabah_service.dart';
import 'package:flutter_auth/screens/Menu/DataNasabah/FormAddNasabah.dart';
import 'package:flutter_auth/screens/Menu/DataNasabah/NasabahHome.dart';
import 'package:rflutter_alert/rflutter_alert.dart';
class ListNasabah {
ApiService apiService;
ListNasabah({this.apiService});
Widget createViewList() {
return SafeArea(
child: FutureBuilder(
future: apiService.getNasabah(),
builder: (BuildContext context, AsyncSnapshot<List<Nasabah>> snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(
'Something wrong with message: ${snapshot.error.toString()}',
textAlign: TextAlign.center,
),
);
} else if (snapshot.connectionState == ConnectionState.done) {
List<Nasabah> nasabah = snapshot.data;
return nasabahListView(nasabah);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
Widget nasabahListView(List<Nasabah> listnasabah) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
child: ListView.builder(
shrinkWrap: true,
itemBuilder: (context, index) {
Nasabah nasabah = listnasabah[index];
return Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
nasabah.nama_debitur,
style: Theme.of(context).textTheme.bodyText1,
),
Text(nasabah.alamat),
Text(nasabah.no_ktp),
Text(nasabah.no_telp),
Text(nasabah.no_selular),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FlatButton(
onPressed: () {
apiService.deleteNasabah(nasabah.id).then((value) =>
Navigator.of(context).pushNamed('start'));
},
child: Text(
"Hapus",
style: TextStyle(color: Colors.red),
),
),
FlatButton(
onPressed: () async {
var result = await Navigator.push(context,
MaterialPageRoute(builder: (context) {
return FormAddNasabah(
nasabah: nasabah,
);
}));
},
child: Text(
'Edit',
style: TextStyle(color: Colors.blue),
),
),
],
)
],
),
),
),
);
},
itemCount: listnasabah.length,
),
);
}
}
Form
import 'package:flutter/material.dart';
import 'package:flutter_auth/Models/nasabah.dart';
import 'package:flutter_auth/network/nasabah_service.dart';
import 'package:flutter_auth/screens/Menu/DataNasabah/NasabahHome.dart';
import 'package:rflutter_alert/rflutter_alert.dart';
class FormAddNasabah extends StatefulWidget {
Nasabah nasabah;
FormAddNasabah({this.nasabah});
#override
_FormAddNasabahState createState() => _FormAddNasabahState();
}
class _FormAddNasabahState extends State<FormAddNasabah> {
ApiService apiService;
TextEditingController _contNama = TextEditingController();
TextEditingController _contAlamat = TextEditingController();
TextEditingController _contNoTelp = TextEditingController();
TextEditingController _contNoKtp = TextEditingController();
TextEditingController _contNoSelular = TextEditingController();
#override
void initState() {
super.initState();
apiService = ApiService();
if (widget.nasabah != null) {
_contNama.text = widget.nasabah.nama_debitur;
_contAlamat.text = widget.nasabah.alamat;
_contNoTelp.text = widget.nasabah.no_telp;
_contNoKtp.text = widget.nasabah.no_ktp;
_contNoSelular.text = widget.nasabah.no_selular;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
iconTheme: IconThemeData(color: Colors.white),
title: Text(
widget.nasabah == null ? "Tambah Nasabah" : "Edit Nasabah",
style: TextStyle(color: Colors.white),
),
),
body: Stack(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
_buildTextField(_contNama, "Nama Nasabah"),
_buildTextField(_contAlamat, "Alamat"),
_buildTextField(_contNoTelp, "No Telp"),
_buildTextField(_contNoKtp, "No KTP"),
_buildTextField(_contNoSelular, "No Selular"),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: RaisedButton(
child: Text(widget.nasabah == null ? "Tambah" : "Edit"),
color: Colors.blue,
onPressed: () {
int id = 0;
if (widget.nasabah != null) {
id = widget.nasabah.id;
}
Nasabah nasabah = Nasabah(
id: id,
nama_debitur: _contNama.text,
alamat: _contAlamat.text,
no_telp: _contNoTelp.text,
no_ktp: _contNoKtp.text,
no_selular: _contNoSelular.text);
if (widget.nasabah == null) {
apiService.createNasabah(nasabah);
} else {
apiService.updateNasabah(nasabah);
}
Navigator.of(context)
.pushNamed('start')
.whenComplete(() => Navigator.pop(context));
},
),
)
],
),
)
],
),
);
}
Widget _buildTextField(TextEditingController _cont, String label) {
return TextField(
controller: _cont,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
labelText: label,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
);
}
}
Thank you for your attention!

Setting login cookies in the rest of headers requests Flutter

i'm trying to set the login cookies to the rest of the get requests.
so i use http pachakge and store the login cookies with sharedPreferences and use it in the get request by adding an update function
but i have a problem that when i go to the page i get 400 response just refreshing the page and i get my data and response 200
is there any other solution for setting cookies in the others get requests headers ?
or is there a solution for my bug ?
codes images : [https://ibb.co/kD3dDc9]
[https://ibb.co/25p5fZr]
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:valomnia_reports/Screens/Superviseur%20Screens/SideBar.dart';
import 'user_model.dart';
class SellersPage extends StatefulWidget {
const SellersPage({Key? key}) : super(key: key);
#override
_SellersPage createState() => _SellersPage();
}
class _SellersPage extends State<SellersPage> {
String? finalEmail;
Future? _futureData;
String? rawCookie;
// ignore: must_call_super
void initState() {
getValidationData();
super.initState();
_futureData = getUserApi();
}
Future getValidationData() async {
final SharedPreferences sharedPreferences2 =
await SharedPreferences.getInstance();
var obtainedEmail2 = sharedPreferences2.getString("rawCookie");
setState(() {
rawCookie = obtainedEmail2;
print(rawCookie);
});
}
List<UserModel> userList = [];
Map<String, String> headers = {};
Future<List<UserModel>> getUserApi() async {
http.Response response = await http.get(
Uri.parse('https://valomnia.herokuapp.com/superviseur/getAllVendeurs'),
headers: headers);
response.headers['set-cookie'] = rawCookie!;
updateCookie(response);
var data = jsonDecode(response.body.toString());
String? cookies = response.headers['set-cookie'];
if (response.statusCode == 200) {
for (Map i in data) {
userList.add(UserModel.fromJson(i));
}
print("Cookie : $cookies");
print("200");
return userList;
} else {
print("400");
print(rawCookie);
print(cookies);
return userList;
}
}
void updateCookie(http.Response response) {
String? rawCookie2 = response.headers['set-cookie'];
if (rawCookie2 != null) {
int index = rawCookie2.indexOf(';');
headers['cookie'] =
(index == -1) ? rawCookie2 : rawCookie2.substring(0, index);
}
}
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
new GlobalKey<RefreshIndicatorState>();
Future<Null> _refresh() {
return getUserApi().then((userList) {
setState(() => userList = userList);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: NavigationDrawerWidget(),
appBar: AppBar(
title: Text(
'Sellers list',
),
centerTitle: true,
backgroundColor: Colors.green,
),
body: Column(
children: [
Expanded(
child: FutureBuilder(
future: _futureData,
builder: (context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
return RefreshIndicator(
key: _refreshIndicatorKey,
onRefresh: _refresh,
child: ListView.builder(
itemCount: userList.length,
itemBuilder: (context, index) {
return Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
ReusbaleRow(
title: 'Id',
value:
snapshot.data![index].id.toString()),
ReusbaleRow(
title: 'Name',
value: snapshot.data![index].name
.toString()),
ReusbaleRow(
title: 'Username',
value: snapshot.data![index].username
.toString()),
ReusbaleRow(
title: 'DateCreated',
value: snapshot.data![index].email
.toString()),
],
),
),
);
}),
);
}
},
),
)
],
),
);
}
}
// ignore: must_be_immutable
class ReusbaleRow extends StatelessWidget {
String title, value;
ReusbaleRow({Key? key, required this.title, required this.value})
: super(key: key);
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(title),
Text(value),
],
),
);
}
} ```

Access Data from API in flutter

I want to access data from the API below.
"https://api.categen.com/api.php/recent_activity/1"
and Want to print in text.
Please help me.
Moreover, there is 3 classes
Home . dart file.
DataService . dart file.
Model . dart file
I tried below code.
Home.dart .
import 'dart:convert';
import 'package:categen_api_test/data_service.dart';
import 'package:categen_api_test/model.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final _dataService = DataService();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Categen API"),
),
body: Center(
child: ElevatedButton(
child: Text("Click me"),
onPressed: () {
_getlist();
},
),
),
);
}
void _getlist() async {
final response = await _dataService.getData();
print(response.name);
}
}
DataService
import 'dart:convert';
import 'package:categen_api_test/model.dart';
import 'package:http/http.dart' as http;
class DataService {
Future<ModelData> getData() async {
final String url = "https://api.categen.com/api.php/recent_activity/1";
final uri = Uri.https('api.categen.com', '/api.php/recent_activity/1');
final response = await http.get(uri);
print(response.body);
final json = jsonDecode(response.body);
return ModelData.fromJson(json);
}
}
First create a model like this:
class Model {
final String name;
final String location;
final String action_value;
final String item;
Model(this.name, this.location, this.action_value, this.item);
List<Model> getList(json) {
List<Model> tempList = []
json['records'].forEach((model)=> tempList.add(
Model(
model["name"],
model["location"],
model["action_value"],
model["item"]
)
)
);
return tempList;
}
}
Then create a function to fetch the data:
Future<List<Model>> fetchData() async {
final response = await http.get('https://api.categen.com/api.php/recent_activity/1');
if (response.statusCode == 200) {
return Model.getList(response.body);
} else {
throw Exception('Unable to fetch products from the REST API');
}
}
call the fetch data function in the init state of the HomePage Widget
late Future<List<Model>> futureData;
void initState() {
super.initState();
futureData = fetchData();
}
what is left to do now is to get your data using a FutureBuilder Widget.
and display the list of your data
FutureBuilder<Model>(
future: futureData,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: snaphot.map((e)=>Text(e.name)).toList()
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
)
if you want to reload the data on the click of a button, then call the fetch data whenever the button is clicked and then rebuild state of the Homepage widget like shown below
onPressed: (){
setState(
(){
futureData = fetchData();
}
);
}
Try below code hope its helpful to you. If you get data from API refer my answer here or here or here hope it's helpful to you
Create your home widget:
Center(
child: ElevatedButton(
child: Text('Pressed Me'),
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Jobs(),
),
),
),
),
Create your List Widget.
Your API Call function:
Future<List<dynamic>> getJobsData() async {
String url = 'https://api.categen.com/api.php/recent_activity/1';
var response = await http.get(Uri.parse(url), headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
});
return json.decode(response.body)['records'];
}
Your Widget:
Column(
children: [
Expanded(
child: Center(
child: FutureBuilder<List<dynamic>>(
future: getJobsData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
var name = snapshot.data![index]['name'];
var location = snapshot.data![index]['location'];
var item = snapshot.data![index]['item'];
var action = snapshot.data![index]['action_value'];
var date = snapshot.data![index]['created_timestamp'];
return Card(
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.green.shade300),
borderRadius: BorderRadius.circular(15.0),
),
child: ListTile(
leading: Text(
action.toString(),
),
title: Text(name),
subtitle: Text(
location + '\n' + date,
),
trailing: Text(item),
),
);
},
),
);
}
return CircularProgressIndicator();
},
),
),
),
],
),
Your all class:
class Jobs extends StatelessWidget {
Future<List<dynamic>> getJobsData() async {
String url = 'https://api.categen.com/api.php/recent_activity/1';
var response = await http.get(Uri.parse(url), headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
});
return json.decode(response.body)['records'];
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Jobs'),
),
body: Column(
children: [
Expanded(
child: Center(
child: FutureBuilder<List<dynamic>>(
future: getJobsData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
var name = snapshot.data![index]['name'];
var location = snapshot.data![index]['location'];
var item = snapshot.data![index]['item'];
var action = snapshot.data![index]['action_value'];
var date = snapshot.data![index]['created_timestamp'];
return Card(
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.green.shade300),
borderRadius: BorderRadius.circular(15.0),
),
child: ListTile(
leading: Text(
action.toString(),
),
title: Text(name),
subtitle: Text(
location + '\n' + date,
),
trailing: Text(item),
),
);
},
),
);
}
return CircularProgressIndicator();
},
),
),
),
],
),
);
}
}
Your Home widget output screen->
Your List Widget output screen->

How can we access the list item in flutter widget?

I am trying to access the fullName from the list created in my Future function, but unable to do. I tried it through indexing and this method I have tried snapshot.data.fullName, but still unable to retrieve the data, even after returning from future function I was facing problems.
Below is code for User model
class User {
final String fullname;
final String contactno;
final String address;
final String city;
final String gender;
final String email;
User(this.fullname, this.contactno, this.address, this.city, this.gender, this.email);
factory User.fromMap(Map<String, dynamic> json) {
return User(
json['fullname'],
json['contactno'],
json['address'],
json['city'],
json['gender'],
json['email']
);
}
}
Stateful Class Code
class EditProfile extends StatefulWidget {
// final String user_fullname;
// //const EditProfile(this.user_fullname);
// const EditProfile ({ Key key, this.user_fullname}): super(key: key);
#override
_EditProfileState createState() => _EditProfileState();
}
Future<List<User>> getData() async {
var id = "26";
var url = baseurl + patientData + id;
var data;
var rest;
print('Calling uri: $url');
// 4
http.Response response = await http.get(url);
// 5
if (response.statusCode == 200) {
data = response.body;
print(data);
} else {
print(response.statusCode);
}
//Map<String, dynamic> user = jsonDecode(data);
var jsonData = jsonDecode(data.body);
List<User> users = [];
for (var u in jsonData) {
User user = User(u['fullname'], u['contactno'], u['address'], u['city'], u['gender'], u['email']);
users.add(user);
}
// print(users.length.toString);
}
I want to access the fullName from my above list in future function in Text widget where I have used snapshot.data.fullName below
class _EditProfileState extends State<EditProfile> {
#override
void initState() {
super.initState();
getData();
}
#override
Widget build(BuildContext context) {
getData().then((value) {
print(value);
});
return FutureBuilder(
future: getData(),
// ignore: missing_return
builder: (context, snapshot) {
if (snapshot.hasData) {
return WillPopScope(
onWillPop: () {},
child: Scaffold(
appBar: AppBar(title: Text("Your Profile"), automaticallyImplyLeading: false, actions: <Widget>[
IconButton(
icon: Icon(
Icons.logout,
color: Colors.white,
),
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => PatientDashboard()));
},
)
]),
body: SingleChildScrollView(
child: Container(
// color: Colors.pink,
child: Column(
children: [
Center(
child: Padding(
padding: const EdgeInsets.only(top: 18.0),
child: Container(
// color: kPrimaryLightColor,
child: Stack(
children: [
//Image
CircleAvatar(
radius: 100,
backgroundColor: Colors.red[900],
child: CircleAvatar(
radius: 95,
backgroundImage: _pic == null ? AssetImage("assets/images/doctor2.jpg") : FileImage(_pic),
),
),
Positioned(
bottom: 5,
right: 15,
child: CircleAvatar(
backgroundColor: Colors.cyanAccent,
child: GestureDetector(
onTap: () {
setState(() {
//firstname = widget.user_fullname;
getImage();
state = 19;
});
},
child: Icon(Icons.camera_alt)),
radius: 20,
),
)
],
),
),
),
),
//Name
Container(
width: double.infinity,
//color: Colors.grey[400],
child: Stack(
children: [
Center(
child: Padding(
padding: const EdgeInsets.only(left: 18.0, top: 20),
child: Container(
//color: Colors.cyan[50],
width: MediaQuery.of(context).size.width * 0.78,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Name"),
Padding(
padding: const EdgeInsets.only(top: 2.0),
child: state == 1
? TextField(
decoration: InputDecoration(border: InputBorder.none, hintText: "${snapshot.data}", hintStyle: TextStyle(fontSize: 17, fontWeight: FontWeight.bold)), //style: TextStyle(under),
onChanged: (text) {
firstname = text;
},
)
: Text(
snapshot.data.fullName,
style: TextStyle(fontSize: 17, fontWeight: FontWeight.bold),
),
)],
else {
return Center(child: CircularProgressIndicator());
}
},
);
}
}
but I am getting this error
Error: NoSuchMethodError: 'body'
method not found
```
Arguments: []
at Object.throw_ [as throw] (http://localhost:59886/dart_sdk.js:5333:11)
at Object.defaultNoSuchMethod (http://localhost:59886/dart_sdk.js:5778:15)
at String.noSuchMethod (http://localhost:59886/dart_sdk.js:6878:19)
at Object.noSuchMethod (http://localhost:59886/dart_sdk.js:5774:30)
at Object.dload (http://localhost:59886/dart_sdk.js:5395:17)
at getData (http://localhost:59886/packages/newfypapproach/patient/screens/patientForgotPassword.dart.lib.js:12720:64)
at getData.next (<anonymous>)
at http://localhost:59886/dart_sdk.js:39031:33
at _RootZone.runUnary (http://localhost:59886/dart_sdk.js:38888:58)
at _FutureListener.thenAwait.handleValue (http://localhost:59886/dart_sdk.js:33874:29)
at handleValueCallback (http://localhost:59886/dart_sdk.js:34434:49)
at Function._propagateToListeners (http://localhost:59886/dart_sdk.js:34472:17)
at _Future.new.[_completeWithValue] (http://localhost:59886/dart_sdk.js:34314:23)
at async._AsyncCallbackEntry.new.callback (http://localhost:59886/dart_sdk.js:34337:35)
at Object._microtaskLoop (http://localhost:59886/dart_sdk.js:39175:13)
at _startMicrotaskLoop (http://localhost:59886/dart_sdk.js:39181:13)
at http://localhost:59886/dart_sdk.js:34688:9
```
I had used this different approach for creating a future function returning list of strings and the list was working perfectly having all values.
```
Future<List<String>> getData() async {
var id = "26";
var url = baseurl + patientData + id;
var data;
var rest;
print('Calling uri: $url');
// 4
http.Response response = await http.get(url);
// 5
if (response.statusCode == 200) {
data = response.body;
print(data);
// rest = data['result'] as List;
// print(rest);
//print(data);
} else {
print(response.statusCode);
}
Map<String, dynamic> user = jsonDecode(data);
// var name = user['result']['name'];
String fullName = user['result'][0][0];
String contactNo = user['result'][0][1];
String address = user['result'][0][2];
String city = user['result'][0][3];
String gender = user['result'][0][4];
String email = user['result'][0][5];
return <String>[fullName, contactNo, address, city, gender, email];
}
Change your getData as follows
Future<List<User>> getData() async {
var id = "26";
var url = baseurl + patientData + id;
Map<String, dynamic> data ={};
var rest;
// 4
http.Response response = await http.get(url);
// 5
if (response.statusCode == 200) {
data = response.body;
print(data);
} else {
print(response.statusCode);
}
//Map<String, dynamic> user = jsonDecode(data);
var jsonData = jsonDecode(data);
List<User> users = [];
for (var u in jsonData) {
User user = User(u['fullname'], u['contactno'], u['address'], u['city'], u['gender'], u['email']);
users.add(user);
}
return users; }

Flutter - whenComplete() not working as expected when using Providers

I'm trying to display a loading while doing an API Request and when finished to show the list with the response or a custom widget to show a message(EmptyListWidget). The problem is that the whenComplete() method is being executed before the async function is finished.
I also tried using then() and using FutureBuilder but I also can't make it work using Provider (allways returns null).
If someone could help, I would really appreciate it.. thanks :)
My List Widget:
class _AbsencesListState extends State<AbsencesList> {
bool _isLoading = false;
bool _isInit = true;
#override
void didChangeDependencies() {
super.didChangeDependencies();
if (_isInit) {
setState(() => _isLoading = true);
Provider.of<AbsencesTypes>(context, listen: false)
.getAbsencesTypes(widget.ctx)
.whenComplete(() {
setState(() => _isLoading = false);
});
_isInit = false;
}
}
#override
Widget build(BuildContext context) {
final absences = Provider.of<Absences>(context).items;
return Stack(
children: [
_isLoading
? const Center(child: CircularProgressIndicator())
: absences.length > 0
? Container()
: EmptyListWidget(ListType.InconsistenciesList),
ListView.builder(
itemBuilder: (_, index) {
return GestureDetector(
onTap: () {},
child: Card(
elevation: 2.0,
child: ListTile(
leading: CircleAvatar(
child: const Icon(Icons.sick),
backgroundColor: Theme.of(context).accentColor,
foregroundColor: Colors.white,
),
title: Padding(
padding: const EdgeInsets.only(top: 3),
child: Text(absences[index].absenceType.name),
),
subtitle: Text(
absences[index].firstDate
),
),
),
);
},
itemCount: absences.length,
)
],
);
}
}
The async function:
class AbsencesTypes with ChangeNotifier {
List<AbsenceType> _absencesTypesList = [];
List<AbsenceType> get items {
return [..._absencesTypesList];
}
void emptyAbsencesTypeList() {
_absencesTypesList.clear();
}
Future<void> getAbsencesTypes(BuildContext context) async {
SharedPreferences _prefs = await SharedPreferences.getInstance();
String token = _prefs.getString(TOKEN_KEY);
http.get(
API_URL,
headers: {"Authorization": token},
).then(
(http.Response response) async {
if (response.statusCode == 200) {
final apiResponse = json.decode(utf8.decode(response.bodyBytes));
final extractedData = apiResponse['content'];
final List<AbsenceType> loadedAbsencesTypes = [];
for (var absenceType in extractedData) {
loadedAbsencesTypes.add(
AbsenceType(
id: absenceType["id"],
name: absenceType["name"].toString(),
code: absenceType["code"].toString(),
totalAllowedDays: absenceType["totalAllowedDays"],
),
);
}
_absencesTypesList = loadedAbsencesTypes;
} else if (response.statusCode == 401) {
Utility.showToast(
AppLocalizations.of(context).translate("expired_session_string"));
Utility.sendUserToLogin(_prefs, context);
}
notifyListeners();
},
);
}
}
Your problem here is probably that you're calling http.get without awaiting for it's result.
The getAbsencesTypes returns the Future<void> as soon as the http.get method is executed, without waiting for the answer, and it results in your onComplete method to be triggered.
A simple fix would be to add the await keyword before the http.get, but you could do even better.
In your code, you're not fully using the ChangeNotifierProvider which could solve your problem. You should check the Consumer class which will be pretty useful for you here, but since it's not your initial question I won't go more in depth on this subject.