Flutter when comes first time on screen sharedpreferences value shows null - flutter

hello friends i really need your help ! i am working on project and i am stuck to get sharedPreferences value when page is load....! this is my photogallery project ! i have photlisting in that u can swipe left and right to see images..! now what is the problem is when i call api to get details... i can get data in my initState() i can see response but in that i can see isLike count is printed ** and saved in my isLike veriable which i Did you mean: declarer...! but when i go to set condition part for the color set ! the first time when page is open isLike is showing null...! but when i scroll i can get value....! what i need is when page is open i want value in isLike ! plz help me i will help u in code to understand
Can u show me any suggestions idea on my code
int index;
int isLike; /// here is my veribe
Future<PhotoDetail> pagedetail() async {
sharedPreferences = await SharedPreferences.getInstance();
Map data = {
"AccessToken": sharedPreferences.getString("AccessToken"),
"CustomerId": sharedPreferences.getInt("CustomerId"),
"ImageId": indexx == null ? widget.images[widget.currentindex].photoId : indexx
};
print(data);
final http.Response response = await http.post(
Constants.CUSTOMER_WEBSERVICE_URL + "/photo/detail",
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(data),
);
var jsonResponse = json.decode(response.body);
if (response.statusCode == 200) {
print("Response status : ${response.statusCode}");
print("Response status : ${response.body}");
isLike = jsonResponse["IsLike"]; //here i saving value when api is run
print("iSLiked Value:" +isLike.toString()); // value is printing here
return PhotoDetail.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to load data');
}
}
PageController _pageController;
Future<FolderSummery> _futureSummary;
List<Widget> imagefilter;
#override
void initState() {
setState(() {
pagedetail(); //here is my api run when page is load
});
super.initState();
imageid = widget.ImageId[index].photoId;
_pageController = PageController(initialPage: widget.currentindex);
print("like first time page load :" + isLike.toString()); //showing null isLike
}
void pageChange(int index){ //this method is when i scroll page
setState(() {
pagedetail(); // api is call again and again
indexx = widget.images[index].photoId;
print("When page change Like :" + isLike.toString()); //isLiked is printing here
print("Image ID :" + indexx.toString());
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white54,
leading: Padding(
padding: const EdgeInsets.only(left: 1,top: 5,bottom: 5),
child: Container(
height: 8,
width: 35,
child: InkWell(
onTap: (){
Navigator.pop(context);
},
child: IconButton(
icon: Icon(Icons.keyboard_arrow_left,color: Color(0xfff58634,),size: 40,),
),
)
),
),
title: Center(
child: Text("Dhara Weds Bharat")
),
actions: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 20,top: 5,bottom: 5),
child: Container(
height: 8,
width: 35,
child: IconButton(
icon: Icon(Icons.filter_list,color: Color(0xfff58634),size: 40,),
)
),
),
],
),
extendBodyBehindAppBar: true,
body: Stack(
children: <Widget>[
Container(
child:
Center(
child: PhotoViewGallery.builder(
itemCount: widget.images.length,
builder: (BuildContext context , int index) {
return PhotoViewGalleryPageOptions(
imageProvider:
NetworkImage(widget.images[index].photoPreviewImagePath),
maxScale: PhotoViewComputedScale.covered * 1.8,
minScale: PhotoViewComputedScale.contained * 1.0,
);
},
pageController: _pageController,
enableRotation: false,
scrollDirection: Axis.horizontal,
onPageChanged: pageChange,
loadingBuilder: (BuildContext context , ImageChunkEvent event){
return Center(child: CircularProgressIndicator(),);
},
)
)
),
Positioned(
bottom: 10,
left: 30,
right: 30,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
InkWell(
child: InkWell(
child: Icon(Icons.thumb_up,size: 20,color: isLike == 1 ? Color(0xfff58634) :
Colors.white), //HERE IS THE ISSUE WHEN I COME FIRST TIME VALUE IS NULL ! BUT WHEN SCROOLLING VALU IS PRINTING
),
),
InkWell(
child: Icon(Icons.comment,size: 20,color: Colors.white),
),
InkWell(
onTap: (){},
child: Icon(Icons.share,size: 20,color: Colors.white,),
),
InkWell(
onTap: (){},
child: Icon(Icons.photo_album,size: 20,color: Colors.white,),
),
InkWell(
onTap: (){},
child: Icon(Icons.print,size: 20,color: Colors.white,),
),
InkWell(
onTap: (){},
child: Icon(Icons.card_giftcard,size: 20,color: Colors.white,),
),
]
),
),
],
),
);
}
DO U HAVE ANY TIPS PLZ EDIT MY CODE AND GIVE ME PROPER SOLUTION PLZ..
API RESPONSE
import 'dart:convert';
PhotoDetail photoDetailFromJson(String str) => PhotoDetail.fromJson(json.decode(str));
String photoDetailToJson(PhotoDetail data) => json.encode(data.toJson());
class PhotoDetail {
PhotoDetail({
this.successCode,
this.successMessage,
this.likeCount,
this.commentCount,
this.selectedCount,
this.isLike,
});
String successCode;
String successMessage;
int likeCount;
int commentCount;
int selectedCount;
int isLike;
factory PhotoDetail.fromJson(Map<String, dynamic> json) => PhotoDetail(
successCode: json["SuccessCode"],
successMessage: json["SuccessMessage"],
likeCount: json["LikeCount"],
commentCount: json["CommentCount"],
selectedCount: json["SelectedCount"],
isLike: json["IsLike"],
);
Map<String, dynamic> toJson() => {
"SuccessCode": successCode,
"SuccessMessage": successMessage,
"LikeCount": likeCount,
"CommentCount": commentCount,
"SelectedCount": selectedCount,
"IsLike": isLike,
};
}

Related

Change the id in the URL according to the selected card

Up!
I am a beginner in Flutter and I am making an application in which I have a first "boats" route. I access to the list of my boats contained in "Card" thanks to this route. Then, I have a second route, accessible from a button in each of these "Cards" allowing me to access the details of each boat.
To retrieve my data, I use a fake API to perform my tests (mockapi.io). For the list of boats, the route is "/boats". Then for the details of each boat, it becomes "/boats/:id/details". Here is my problem, how do I handle the fact that the id changes depending on the boat selected?
Here is the method to access the API:
Future<List<ListDetails>?> getListDetails({required id}) async
{
var client = http.Client();
var uri = Uri.parse('https://63e21b26109336b6cbff9ce9.mockapi.io/api/v1/boats_control/$id/details');
var response = await client.get(uri);
if(response.statusCode == 200)
{
var json = response.body;
return listDetailsFromJson(json);
}
}
And here is my boat_details view, where I try to display the API data:
class ControlListDetailViewState extends State<ControlListDetailView> {
#override
Widget build(BuildContext context) {
late final id = ModalRoute.of(context)?.settings.arguments;
List<ListDetails>? listDetails;
var isLoaded = false;
print(listDetails);
getData() async {
listDetails = await RemoteService().getListDetails(id: id);
if (listDetails != null) {
setState(() {
isLoaded = true;
});
}
}
#override
void initState() {
super.initState();
getData();
}
And here is my model :
import 'dart:convert';
List<BoatsControl> boatsControlFromJson(String str) => List<BoatsControl>.from(json.decode(str).map((x) => BoatsControl.fromJson(x)));
String boatsControlToJson(List<BoatsControl> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class BoatsControl {
BoatsControl({
required this.model,
required this.location,
required this.id,
});
String model;
String location;
String id;
factory BoatsControl.fromJson(Map<String, dynamic> json) => BoatsControl(
model: json["model"],
location: json["location"],
id: json["id"],
);
Map<String, dynamic> toJson() => {
"model": model,
"location": location,
"id": id,
};
}
Finally, here is my widget where I use this model:
Widget build(BuildContext context) {
print('ControlViewState - build');
return Scaffold(
drawer: NavDrawableWidget(), // Hamburger menu
bottomNavigationBar: FooterWidget(), // Footer Copyright
appBar: AppBar(
title: Text("${AppSettings.APP_NAME} | ${AppSettings.APP_VERSION}",
style: TextStyle(fontSize: 16)),
),
body: Column(
children: [
Text('\n${AppSettings.strings.controlTitle}',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)
),
Visibility(
visible: isLoaded,
replacement: const Center(
child: CircularProgressIndicator(),
),
child: const Text(''),
),
if(isLoaded)
Expanded(
child: ListView.builder(
itemCount: boatsControl?.length ?? 0,
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.all(8.0),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
side: BorderSide(color: Colors.black12, width: 2),
),
child: Padding(
padding: EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
ListTile(
title: Text('${boatsControl![index].id} | ${boatsControl![index].model}'),
leading: Icon(Icons.anchor),
),
Row(
children: <Widget>[
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(boatsControl![index].location,
style: TextStyle(fontSize: 14)),
],
),
),
Align(
alignment: Alignment.centerRight,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
CustomFloatingActionControlButton(
onPressed: () =>
{
Navigator.pushNamed(
context, '/controlListDefect',
arguments: boatsControl![index].id
)
},
tag: 'listDefectButton',
icon: Icons.equalizer,
),
],
),
),
],
),
],
),
),
),
);
},
),
)
]
),
);
}
Thank you in advance if you take the time to answer me and help me.

Error : Failed to detect image file format using the file header. File header was [0x3c 0x68 0x74 0x6d 0x6c 0x3e 0x0a 0x20 0x20 0x20]

I'm trying to display an image from Firestore in to my UI. But it returns the error above, and the file format also changes when I run it on Edge (not on physical device or Emulator). I can't figure out how. Here's my code.
class ViewPola extends StatefulWidget {
const ViewPola({Key? key}) : super(key: key);
static String id = 'view_pola';
#override
State<ViewPola> createState() => _ViewPolaState();
}
class _ViewPolaState extends State<ViewPola> {
Function getSnaps = () async {
final polaMap = {
'Kode Pola': '',
'Bagian Pola': '',
'image_url': '',
};
FirebaseFirestore.instance
.collection('PolaWillyJKT')
.limit(1)
.get()
.then((snapshot) {
if (snapshot.size == 0) {
FirebaseFirestore.instance.collection('PolaWillyJKT').add(polaMap);
FirebaseFirestore.instance.collection('PolaWillyJKT');
print('add');
} else {
print('disini nge get');
var a = FirebaseFirestore.instance.collection('PolaWillyJKT').get();
Map<String, dynamic> data = a as Map<String, dynamic>;
print(data);
}
});
};
var _selectedItem;
var _showList = false;
#override
void initState() {
getSnaps();
super.initState();
}
final Stream<QuerySnapshot> _polaWilly =
FirebaseFirestore.instance.collection('PolaWillyJKT').snapshots();
#override
Widget build(BuildContext context) {
final screenHeight = ScreenInfo.screenHeight(context);
final screenWidth = ScreenInfo.screenWidth(context);
return StreamBuilder<QuerySnapshot>(
stream: _polaWilly,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return SafeArea(
child: Scaffold(
body: Padding(
padding: EdgeInsets.all(25),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text(
'Pilih Bagian Pola',
style: TextStyle(fontSize: 25),
),
const SizedBox(
height: 20,
),
DropdownButton(
isExpanded: true,
value: _selectedItem,
items: snapshot.data?.docs
.map(
(value) => DropdownMenuItem(
value: value.get("Bagian Pola"),
child: Text('${value.get("Bagian Pola")}'),
),
)
.toList(),
onChanged: (newValue) {
setState(() {
_selectedItem = newValue.toString();
_showList = true;
});
}),
Padding(
padding: const EdgeInsets.all(25),
child: Visibility(
visible: _showList,
child: Container(
height: screenHeight * 0.4,
child: ListView(
children: snapshot.data!.docs
.where(
(e) => e.get("Bagian Pola") == _selectedItem)
.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return Center(
child: Column(
children: [
Text(
'Bagian Pola: ${data["Bagian Pola"]}',
style: TextStyle(fontSize: 15),
),
const SizedBox(
height: 15,
),
Text(
'Kode Pola : ${data["Kode Pola"]}',
style: TextStyle(fontSize: 15),
),
const SizedBox(
height: 15,
),
Image(
image:
NetworkImage(document.get("Foto Pola")),
height: 200,
width: 200,
), // this is the Image widget where I tried to put the image from firestore
Text(document.get('Foto Pola').toString()),
],
),
);
}).toList(),
),
),
),
),
],
),
),
),
);
},
);
}
}
and here's the class that uploads the image to Firestore
class Input_Pola extends StatefulWidget {
const Input_Pola({Key? key}) : super(key: key);
static String id = 'input_pola';
#override
State<Input_Pola> createState() => _Input_PolaState();
}
class _Input_PolaState extends State<Input_Pola> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
void _checkDuplicate() {
if (options.contains(_textfieldValue.text)) {
// Display bottom sheet that says item already exists
_scaffoldKey.currentState?.showBottomSheet((context) => Container(
height: 300,
child: Column(
children: [
const Text('Item already exists'),
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Dismiss'))
],
),
));
} else {
// Add item to options list
setState(() {
options.add(_textfieldValue.text);
});
}
}
XFile? image;
final ImagePicker picker = ImagePicker();
String _image_url = '';
void _addToFirebase(String dropdownValue, String _kodePolaController) {
var imageFile = File(image!.path);
String fileName = pth.basename(imageFile.path);
FirebaseStorage storage = FirebaseStorage.instance;
Reference ref = storage.ref().child("WillyJKT/polaWillyJKT");
UploadTask uploadTask = ref.putFile(imageFile);
uploadTask.whenComplete(() async {
var url = await ref.getDownloadURL();
_image_url = url.toString();
});
FirebaseFirestore.instance
.collection('PolaWillyJKT')
.doc(_selectedOption)
.set({
'Bagian Pola': _selectedOption,
'Kode Pola': _kodePolaController,
'Foto Pola': _image_url
});
}
String _dropdownValue = 'kg';
String _property1 = '';
String _property2 = '';
String _property3 = '';
bool _isOptionSelected = false;
final TextEditingController _kodePolaController = TextEditingController();
var _selectedOption;
final TextEditingController _textfieldValue = TextEditingController();
final List<String> options = [];
#override
void initState() {
super.initState();
_selectedOption = options.isNotEmpty ? options[0] : null;
}
//we can upload image from camera or from gallery based on parameter
Future getImage(ImageSource media) async {
var img = await picker.pickImage(source: media);
setState(() {
image = img;
});
}
void myAlert() {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
title: Text('Please choose media to select'),
content: Container(
height: MediaQuery.of(context).size.height / 6,
child: Column(
children: [
ElevatedButton(
//if user click this button, user can upload image from gallery
onPressed: () {
Navigator.pop(context);
getImage(ImageSource.gallery);
},
child: Row(
children: [
Icon(Icons.image),
Text('From Gallery'),
],
),
),
ElevatedButton(
//if user click this button. user can upload image from camera
onPressed: () {
Navigator.pop(context);
getImage(ImageSource.camera);
},
child: Row(
children: [
Icon(Icons.camera),
Text('From Camera'),
],
),
),
],
),
),
);
});
}
#override
Widget build(BuildContext context) {
final screenHeight = ScreenInfo.screenHeight(context);
final screenWidth = ScreenInfo.screenWidth(context);
return SafeArea(
child: Scaffold(
key: _scaffoldKey,
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
_addToFirebase(_dropdownValue, _kodePolaController.text);
},
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(25),
child: Container(
height: screenHeight,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
TextFormField(
decoration: const InputDecoration(
hintText: 'Input Pola',
border: UnderlineInputBorder(),
),
onChanged: (value) {
setState(() {
_textfieldValue.text = value;
});
},
),
DropdownButton<String>(
value: _selectedOption,
onChanged: (value) {
setState(() {
_selectedOption = value!;
_isOptionSelected = true;
_kodePolaController.clear();
});
},
hint: const Text('Input from Text Field Above'),
items: options.map((option) {
return DropdownMenuItem<String>(
value: option,
child: Text(option),
);
}).toList(),
),
TextButton(
onPressed: _checkDuplicate,
child: const Text("Add Option"),
),
Visibility(
visible: _isOptionSelected,
child: Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
TextField(
controller: _kodePolaController,
decoration: const InputDecoration(
labelText: "Kode Pola"),
onChanged: (value) {
setState(() {
_property1 = value;
});
},
),
const SizedBox(height: 20,),
ElevatedButton(
onPressed: () {
myAlert();
},
child: Text('Upload Photo'),
),
SizedBox(
height: 10,
),
//if image not null show the image
//if image null show text
image != null
? Padding(
padding:
const EdgeInsets.symmetric(horizontal: 20),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.file(
//to show image, you type like this.
File(image!.path),
fit: BoxFit.cover,
width: MediaQuery.of(context).size.width,
height: 300,
),
),
)
: const Text(
"No Image",
style: TextStyle(fontSize: 20),
),
],
),
),
)
],
),
),
),
),
),
);
}
}
It also displays the error on Edge, but when I build the apk and run it on my physical device, it just shows nothing on the screen except for the data that's got above it. Any idea on how I can fix this?
var a = FirebaseFirestore.instance.collection('PolaWillyJKT').get();
This returns a promise but not the actual data. In fact it could still be fetching the data when you attempt to convert it to a map.
You can await
var a = await FirebaseFirestore.instance.collection('PolaWillyJKT').get();
myDoc = a.docs.first();
Or use a callback that will run once the collection fetch completes:
FirebaseFirestore.instance.collection('PolaWillyJKT').get().then((response) => {
print(response);
});

Update State without reloading a widget in Flutter

I have a widget on a screen that receives its data from API calls. The API call is made inside the init method of the Navigation Bar so that continuous API calls can be prevented when going back and forth between screens. Although this works fine, I'm facing a real challenge in trying to get the state of the widget updated when new data is added to that particular API that the widget relies on for displaying data. I would therefore need to know how to display the updated data that I added to the Database by making a post request on a different screen. The only way this happens now is by way of reloading the entire app or by killing it. Any help will be appreciated.
This is the NavBar where the API is getting called. I usually make all the API calls at once here and something I have done here too.
NavBar
class CustomBottomNavigationState extends State<CustomBottomNavigation> {
bool isLoading = true;
int index = 2;
final screens = [
MenuScreen(),
LeaveScreen(),
// TaskList(),
HomeScreen(),
// PaySlipScreen(),
TaskList(),
Claimz_category(),
// ClaimzScreen()
];
#override
void initState() {
// TODO: implement initState
Provider.of<LeaveRequestViewModel>(context, listen: false)
.getLeaveRequest()
.then((value) {
Provider.of<AnnouncementViewModel>(context, listen: false)
.getAllAnouncements()
.then((value) {
Provider.of<TodaysTaskList>(context, listen: false)
.getTodaysTasks() //This is the API call in question
.then((value) {
setState(() {
isLoading = false;
});
});
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
final items = ['The icons are stored here'];
// TODO: implement build
return SafeArea(
child: Scaffold(
body: isLoading
? const Center(
child: CircularProgressIndicator(),
)
: screens[index],
extendBody: true,
bottomNavigationBar: Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(200),
topRight: Radius.circular(200)),
boxShadow: [
BoxShadow(
color: Colors.transparent,
blurRadius: 10,
offset: Offset(1, 2))
]),
child: CurvedNavigationBar(
items: items,
index: index,
height: 60,
color: const Color.fromARGB(255, 70, 70, 70),
backgroundColor: Colors.transparent,
onTap: (index) => setState(() {
this.index = index;
})),
),
),
);
}
}
ToDoList widget(This the widget where the updates never reflect without reloading)
class ToDoListState extends State<ToDoList> {
#override
Widget build(BuildContext context) {
final toDoList = Provider.of<TodaysTaskList>(context).getToDoList; //This is the getter method that stores the data after it has been fetched from API
// TODO: implement build
return ContainerStyle(
height: SizeVariables.getHeight(context) * 0.35,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.only(
top: SizeVariables.getHeight(context) * 0.015,
left: SizeVariables.getWidth(context) * 0.04),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
// color: Colors.red,
child: FittedBox(
fit: BoxFit.contain,
child: Text(
'To do list',
style: Theme.of(context).textTheme.caption,
),
),
),
],
),
),
SizedBox(height: SizeVariables.getHeight(context) * 0.01),
Padding(
padding: EdgeInsets.only(
left: SizeVariables.getWidth(context) * 0.04,
top: SizeVariables.getHeight(context) * 0.005,
right: SizeVariables.getWidth(context) * 0.04),
child: SizedBox(
height: SizeVariables.getHeight(context) * 0.25,
child: Container(
// color: Colors.red,
child: toDoList['today'].isEmpty
? Center(
child: Lottie.asset('assets/json/ToDo.json'),
)
: ListView.separated(
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) => Row(
children: [
Icon(Icons.circle,
color: Colors.white,
size:
SizeVariables.getWidth(context) * 0.03),
SizedBox(
width:
SizeVariables.getWidth(context) * 0.02),
FittedBox(
fit: BoxFit.contain,
child: Text(
toDoList['today'][index]['task_name'], //This is where it is used
overflow: TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.bodyText1),
)
],
),
separatorBuilder: (context, index) => Divider(
height: SizeVariables.getHeight(context) * 0.045,
color: Colors.white,
thickness: 0.5,
),
itemCount: toDoList['today'].length > 4
? 4
: toDoList['today'].length),
),
),
)
],
),
);
}
}
The other widget where the date gets added
class _TaskListState extends State<TaskList> {
#override
Widget build(BuildContext context) {
var floatingActionButton;
return Scaffold(
backgroundColor: Colors.black,
floatingActionButton: Container(
....
....,
child: FloatingActionButton(
backgroundColor: Color.fromARGB(255, 70, 69, 69),
onPressed: openDialog, //This is the method for posting data
child: Icon(Icons.add),
),
),
),
body: Container(
....
....
....
),
);
}
Future<dynamic> openDialog() => showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: Color.fromARGB(255, 87, 83, 83),
content: Form(
key: _key,
child: TextFormField(
controller: taskController,
maxLines: 5,
style: Theme.of(context).textTheme.bodyText1,
decoration: InputDecoration(
border: InputBorder.none,
),
validator: (value) {
if (value!.isEmpty || value == '') {
return 'Please Enter Task';
} else {
input = value;
}
},
),
),
actions: [
InkWell(
onTap: () async {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2010),
lastDate:
DateTime.now().add(const Duration(days: 365)))
.then((date) {
setState(() {
_dateTime = date;
});
print('Date Time: ${dateFormat.format(_dateTime!)}');
});
},
child: const Icon(Icons.calendar_month, color: Colors.white)),
TextButton(
child: Text(
"Add",
style: Theme.of(context).textTheme.bodyText1,
),
onPressed: () async {
Map<String, dynamic> _data = {
'task': taskController.text,
'task_date': dateFormat.format(_dateTime!).toString()
};
print(_data);
if (_key.currentState!.validate()) {
await Provider.of<ToDoViewModel>(context, listen: false)
.addToDo(_data, context) //This is the post method
.then((_) {
Navigator.of(context).pop();
Provider.of<TodaysTaskList>(context, listen: false)
.getTodaysTasks(); //I did this here again to re-initialize the data. I was under the impression that the new data would get initialized for the widget to reflect it on the other screen.
});
}
},
),
],
),
);
void add() {
Navigator.of(context).pop();
}
}
The Get API Call
class TodaysTaskList with ChangeNotifier {
Map<String, dynamic> _getToDoList = {};
Map<String, dynamic> get getToDoList {
return {..._getToDoList};
}
Future<void> getTodaysTasks() async {
SharedPreferences localStorage = await SharedPreferences.getInstance();
var response = await http.get(Uri.parse(AppUrl.toDoList), headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ${localStorage.getString('token')}'
});
if (response.statusCode == 200) {
_getToDoList = json.decode(response.body);
} else {
_getToDoList = {};
}
print('TO DO LIST: $_getToDoList');
notifyListeners();
}
}
Please let me know for additional input.
i think it's because you didn't call the provider to update your state correctly
as i see that you declare new variable to store your provider like this
final toDoList = Provider.of<TodaysTaskList>(context).getToDoList;
then you use it like this
Text(
toDoList['today'][index]['task_name'], //This is where it is used
overflow: TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.bodyText1),
)
it's not updating the state, you should wrap the widget that need to be updated with Consumer
Consumer<TodaysTaskList>(
builder: (context, data, child) {
return _Text(
data.[your_list]['today'][index]['task_name'],
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodyText1),
);
},
);

How to implement a network data fetch and then display in checkbox in alert inside showDialog with setState

I couldn't find a way to fetch data from network API inside showDialog > StatefulBuilder > AlertDialog. After fetching, this data should display in checkboxes and then finally on click ok, the selected checkboxes data is returned to the parent widget. There are more states other than these checkbox states in the alert. But the Navigator.of(context).pop() can return only single value.
Is there a way to rebuild the StatefulBuilder with setState on parent widget. Or some easy hack to rebuild the StatefulBuilder from an outside function like fetchOrderStatus() in the below code. (might be possible with a key on StatefulBuilder, but don't know how).
Below is my code
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import 'dart:convert';
import 'package:recase/recase.dart';
import 'package:woocommerceadmin/src/orders/widgets/OrderDetailsPage.dart';
import 'package:barcode_scan/barcode_scan.dart';
class OrdersListPage extends StatefulWidget {
final String baseurl;
final String username;
final String password;
OrdersListPage({
Key key,
#required this.baseurl,
#required this.username,
#required this.password,
}) : super(key: key);
#override
_OrdersListPageState createState() => _OrdersListPageState();
}
class _OrdersListPageState extends State<OrdersListPage> {
String baseurl;
String username;
String password;
List ordersListData = List();
int page = 1;
bool hasMoreToLoad = true;
bool isListLoading = false;
bool isSearching = false;
String searchValue = "";
String sortOrderByValue = "date";
String sortOrderValue = "desc";
bool isOrderStatusOptionsReady = false;
bool isOrderStatusOptionsError = false;
String orderStatusOptionsError;
Map<String, bool> orderStatusOptions = {};
final scaffoldKey = new GlobalKey<ScaffoldState>();
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
new GlobalKey<RefreshIndicatorState>();
#override
void initState() {
super.initState();
baseurl = widget.baseurl;
username = widget.username;
password = widget.password;
fetchOrdersList();
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldKey,
appBar: _myAppBar(),
body: RefreshIndicator(
key: _refreshIndicatorKey,
onRefresh: handleRefresh,
child: Column(
children: <Widget>[
Expanded(
child: NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scrollInfo) {
if (hasMoreToLoad &&
!isListLoading &&
scrollInfo.metrics.pixels ==
scrollInfo.metrics.maxScrollExtent) {
handleLoadMore();
}
},
child: ListView.builder(
itemCount: ordersListData.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => OrderDetailsPage(
id: ordersListData[index]["id"],
),
),
);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment:
MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
_orderDate(ordersListData[index]),
_orderIdAndBillingName(
ordersListData[index]),
_orderStatus(ordersListData[index]),
_orderTotal(ordersListData[index])
]),
),
)
]),
),
);
}),
),
),
if (isListLoading)
Container(
height: 60.0,
color: Colors.white,
child: Center(
child: SpinKitFadingCube(
color: Colors.purple,
size: 30.0,
)),
),
],
),
),
);
}
Future<void> fetchOrderStatus() async {
String url =
"$baseurl/wp-json/wc/v3/reports/orders/totals?consumer_key=$username&consumer_secret=$password";
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = false;
});
dynamic response;
try {
response = await http.get(url);
if (response.statusCode == 200) {
if (json.decode(response.body) is List &&
!json.decode(response.body).isEmpty) {
json.decode(response.body).forEach((item) {
if (item is Map) {
orderStatusOptions.putIfAbsent(item["slug"], () => false);
}
});
setState(() {
isOrderStatusOptionsReady = true;
});
} else {
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = true;
orderStatusOptionsError = "Failed to fetch order status options";
});
}
} else {
String errorCode = "";
if (json.decode(response.body) is Map &&
json.decode(response.body).containsKey("code") &&
json.decode(response.body)["code"] is String) {
errorCode = json.decode(response.body)["code"];
}
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = true;
orderStatusOptionsError =
"Failed to fetch order status options. Error: $errorCode";
});
}
} catch (e) {
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = true;
orderStatusOptionsError =
"Failed to fetch order status options. Error: $e";
});
}
}
Widget _myAppBar() {
Widget myAppBar;
myAppBar = AppBar(
title: Text("Orders List"),
actions: <Widget>[
GestureDetector(
child: Padding(
padding: const EdgeInsets.only(left: 10),
child: Icon(Icons.search),
),
onTap: () {
setState(() {
isSearching = !isSearching;
});
},
),
GestureDetector(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Icon(Icons.filter_list),
),
onTap: _orderFilter,
),
],
);
}
return myAppBar;
}
void _orderFilter() async {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context){
// fetchOrderStatus();
return StatefulBuilder(builder: (context, StateSetter setState) {
return AlertDialog(
title: Text("Sort & Filter"),
titlePadding: EdgeInsets.fromLTRB(15, 20, 15, 0),
content: Container(
width: 300,
height: 400,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Sort by",
style: Theme.of(context).textTheme.subhead,
),
Row(
children: <Widget>[
Expanded(
child: Container(
child: DropdownButton<String>(
underline: SizedBox.shrink(),
value: sortOrderByValue,
onChanged: (String newValue) {
FocusScope.of(context)
.requestFocus(FocusNode());
setState(() {
sortOrderByValue = newValue;
});
},
items: <String>[
"date",
"id",
"title",
"slug",
"include"
].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value.titleCase,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.body1,
),
);
}).toList(),
),
),
),
InkWell(
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 10),
child: Icon(
Icons.arrow_downward,
color: (sortOrderValue == "desc")
? Theme.of(context).primaryColor
: Colors.black,
),
),
onTap: () {
setState(() {
sortOrderValue = "desc";
});
},
),
InkWell(
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 10),
child: Icon(
Icons.arrow_upward,
color: (sortOrderValue == "asc")
? Theme.of(context).primaryColor
: Colors.black,
),
),
onTap: () {
setState(() {
sortOrderValue = "asc";
});
},
),
],
),
Text(
"Filter by",
style: Theme.of(context).textTheme.subhead,
),
Text(
"Order Status",
style: Theme.of(context).textTheme.body1.copyWith(
fontWeight: FontWeight.bold, fontSize: 16),
),
isOrderStatusOptionsReady
? ListView(
children:
orderStatusOptions.keys.map((String key) {
return new CheckboxListTile(
title: Text(key),
value: orderStatusOptions[key],
onChanged: (bool value) {
setState(() {
orderStatusOptions[key] = value;
});
},
);
}).toList(),
)
: Container(
child: Center(
child: SpinKitFadingCube(
color: Theme.of(context).primaryColor,
size: 30.0,
),
),
)
],
),
),
),
contentPadding: EdgeInsets.fromLTRB(15, 10, 15, 0),
actions: <Widget>[
FlatButton(
child: Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: Text("Ok"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
});
}
}
You have a couple of options
Fetch the data before showing the dialog. Using either async/await keywords or .then, you wait for the fetching of data to complete, then use the data in the dialog
void _orderFilter() async {
await fetchOrderStatus();
showDialog(...); //Use the response in the dialog
}
Create a new stateful widget for the dialog and have fetchOrderStatus() be a method in that class. This allows you to have more control over what to display as well as state changes in the dialog.
#gaurav-jain I have followed your question from Github where you asked a question about my answer to this problem. How I did it is i have a button that when clicked immediately opens the dialog that then waits for the Future to load data from the API: The async function _showOptions() renders the dialog that renders a list of checkboxes with options fetched from API:
new RaisedButton(
color: Colors.green,
padding: EdgeInsets.all(20.0),
onPressed: () {
if (state._isLoading){
// don't do anything when form is submitting and this button is pressed again
return null;
}
else {
if (state._formKey.currentState.validate()) {
state._showOptions().then((selected){
print(state.selectedOptions);
if (state.selectedOptions.isNotEmpty) {
_submitForm();
}
else {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text('You have not selected any option'),
actions: <Widget>[
FlatButton(
child: Text('OK'),
onPressed: () {
Navigator.of(context, rootNavigator: true).pop();
},
),
],
);
}
);
}
});
}
else {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('Your form has errors. Rectify them and submit again'),
)
);
}
}
},
child: Text(state._isLoading ? 'Submitting...' : 'Submit', style: new TextStyle(color: Colors.white)),
),
The state here is the parent widget's state, but the async dialog has it's own state to make it work. You can refer back to the github comment for the other pieces of the code here https://github.com/flutter/flutter/issues/15194#issuecomment-450490409
As suggested by #wxker, 2nd approach, I have implemented other stateful widget which returns AlertDialog.
Parent Widget Calling showDialog on tap:
IconButton(
icon: Icon(Icons.filter_list),
onPressed: () {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return OrdersListFiltersModal(
baseurl: widget.baseurl,
username: widget.username,
password: widget.password,
sortOrderByValue: sortOrderByValue,
sortOrderValue: sortOrderValue,
orderStatusOptions: orderStatusOptions,
onSubmit:
(sortOrderByValue, sortOrderValue, orderStatusOptions) {
setState(() {
this.sortOrderByValue = sortOrderByValue;
this.sortOrderValue = sortOrderValue;
this.orderStatusOptions = orderStatusOptions;
});
handleRefresh();
},
);
},
);
},
),
Child stateful widget returning alert with default value and function callback in constructor to change parent widget state.
Widget build(BuildContext context) {
return AlertDialog(
title: Text("Sort & Filter"),
titlePadding: EdgeInsets.fromLTRB(15, 20, 15, 0),
content: Container(
height: 400,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Sort by",
style: Theme.of(context).textTheme.subhead,
),
Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: Row(
children: <Widget>[
Expanded(
child: Container(
child: DropdownButton<String>(
underline: SizedBox.shrink(),
value: sortOrderByValue,
onChanged: (String newValue) {
FocusScope.of(context).requestFocus(FocusNode());
setState(() {
sortOrderByValue = newValue;
});
},
items: <String>[
"date",
"id",
"title",
"slug",
"include"
].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value.titleCase,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.body1,
),
);
}).toList(),
),
),
),
InkWell(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Icon(
Icons.arrow_downward,
color: (sortOrderValue == "desc")
? Theme.of(context).primaryColor
: Colors.black,
),
),
onTap: () {
setState(() {
sortOrderValue = "desc";
});
},
),
InkWell(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Icon(
Icons.arrow_upward,
color: (sortOrderValue == "asc")
? Theme.of(context).primaryColor
: Colors.black,
),
),
onTap: () {
setState(() {
sortOrderValue = "asc";
});
},
),
],
),
),
Text(
"Filter by",
style: Theme.of(context).textTheme.subhead,
),
SizedBox(
height: 10,
),
Padding(
padding: const EdgeInsets.only(left: 10),
child: Text(
"Order Status",
style: Theme.of(context)
.textTheme
.body1
.copyWith(fontWeight: FontWeight.bold, fontSize: 16),
),
),
SizedBox(
height: 10,
),
isOrderStatusOptionsError
? Row(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 10, 10, 10),
child: Text(
orderStatusOptionsError,
style: Theme.of(context).textTheme.body1,
),
),
),
],
)
: isOrderStatusOptionsReady
? Column(
children: orderStatusOptions.keys.map((String key) {
return GestureDetector(
onTap: () {
setState(() {
orderStatusOptions[key] =
!orderStatusOptions[key];
});
},
child: Container(
color: Colors.transparent,
height: 30,
child: Row(
children: <Widget>[
Checkbox(
value: orderStatusOptions[key],
onChanged: (bool value) {
setState(() {
orderStatusOptions[key] = value;
});
},
),
Expanded(
child: Text(
key.titleCase,
style:
Theme.of(context).textTheme.body1,
),
),
],
),
),
);
}).toList(),
)
: Container(
padding: EdgeInsets.fromLTRB(0, 20, 0, 0),
child: Center(
child: SpinKitPulse(
color: Theme.of(context).primaryColor,
size: 50,
),
),
)
],
),
),
),
contentPadding: EdgeInsets.fromLTRB(15, 10, 15, 0),
actions: <Widget>[
FlatButton(
child: Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: Text("Ok"),
onPressed: () {
widget.onSubmit(
sortOrderByValue, sortOrderValue, orderStatusOptions);
Navigator.of(context).pop();
},
)
],
);
}
Future<void> fetchOrderStatusOptions() async {
String url =
"${widget.baseurl}?consumer_key=${widget.username}&consumer_secret=${widget.password}";
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = false;
});
http.Response response;
try {
response = await http.get(url);
if (response.statusCode == 200) {
if (json.decode(response.body) is List &&
!json.decode(response.body).isEmpty) {
Map<String, bool> tempMap = orderStatusOptions;
json.decode(response.body).forEach((item) {
if (item is Map &&
item.containsKey("slug") &&
item["slug"] is String &&
item["slug"].isNotEmpty) {
tempMap.putIfAbsent(item["slug"], () => false);
}
});
setState(() {
isOrderStatusOptionsReady = true;
orderStatusOptions = tempMap;
});
} else {
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = true;
orderStatusOptionsError = "Failed to fetch order status options";
});
}
} else {
String errorCode = "";
if (json.decode(response.body) is Map &&
json.decode(response.body).containsKey("code") &&
json.decode(response.body)["code"] is String) {
errorCode = json.decode(response.body)["code"];
}
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = true;
orderStatusOptionsError =
"Failed to fetch order status options. Error: $errorCode";
});
}
} catch (e) {
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = true;
orderStatusOptionsError =
"Failed to fetch order status options. Error: $e";
});
}
}
}

How to display the JSON data fetched from API in flutter?

I want to fetch the data from API and display it in flutter app. I've been able to fetch the data and display it using ListView builder. But, the problem now I'm facing is that the scroll is not working properly in this case and I don't want to display the data in the form of Lists.
So, is there any other way where we can fetch the data and show it in the app. This is how the UI is going to look.
I'm unable to accomplish this using ListView builder or any other way as I'm very new to flutter.
This is my code which I used in another widget where I was facing the scrolling issue, to create the ListView:
#override
_AllEventsState createState() => _AllEventsState();
}
class _AllEventsState extends State<AllEvents> {
final String url =
"api-url";
List data;
#override
void initState() {
super.initState();
this.getAllEvents();
}
createRoute(id) {
print('args $id');
Navigator.pushNamed(context, '/eventdetail', arguments: id);
}
Future<String> getAllEvents() async {
var response = await http.get(
Uri.encodeFull(url),
headers: {"Accept": "application/json"},
);
setState(() {
var convertDataToJson = jsonDecode(response.body);
data = convertDataToJson["events"];
});
return "Success";
}
#override
Widget build(BuildContext context) {
ScrollController _controller = new ScrollController();
return Container(
child: ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
controller: _controller,
shrinkWrap: true,
itemCount: data == null ? 0 : data.length,
itemBuilder: (BuildContext cont, int index) {
// format date
var dateString = data[index]["eventDate"];
var eventDate = Jiffy(dateString).MMMEd;
return Container(
padding: const EdgeInsets.fromLTRB(30, 7, 30, 7),
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new InkWell(
onTap: () {
print('card tapped');
createRoute(data[index]['_id']);
},
child: Card(
child: Container(
decoration: BoxDecoration(
border: Border.all(
width: 3,
color: hexToColor("#EBEBEB"),
),
borderRadius: BorderRadius.circular(8.0),
),
padding: const EdgeInsets.fromLTRB(20, 20, 20, 20),
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
customText(
eventDate.toString(),
"#000000",
"20.0",
TextAlign.left,
"Roboto Black",
FontWeight.w900,
),
customText(
"10, 9",
"#000000",
"20.0",
TextAlign.right,
"Roboto Black",
FontWeight.w900,
),
],
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: customText(
"${data[index]['city']} | ${data[index]['arenaName']} | ${data[index]['categories'][0]['title']}",
"#000000",
"18.0",
TextAlign.left,
"Roboto Black",
FontWeight.w900,
),
),
],
),
],
),
),
),
),
],
),
),
);
},
),
);
}
}
This is how I'm fetching the data from the API in another widget:
print("$url${widget.eventId}");
var response = await http.get(
Uri.encodeFull("$url${widget.eventId}"),
// headers: {"Accept": "application/json"},
);
print('events response ${response.body}');
setState(() {
var convertDataToJson = jsonDecode(response.body);
data = convertDataToJson["event"];
print(data);
});
return "Success";
}```
Can you please suggest a way as to how I can display the data properly in flutter?
Use FutureBuilder to get data from api like this :-
FutureBuilder(
future: yourMethodName(),
builder: (context, AsyncSnapshot<NameOfModelClass> snapshot) {
if (snapshot.data == null) {
return Center(child: CircularProgressIndicator());
}
// after getting data from api
},
);
Your method can be like this :
Future<NameOfModelClass> YourMethodName() async {
final response = await http.get('Your url here');
return NameOfModelClass.fromJson(json.decode(response.body.toString()));
}
The model class can be generate by using jsonToDart
I hope this help :)