Failed assertion: line 1916 pos 14: 'children != null': is not true.? - flutter

I have been trying to run a flutter app and it keeps giving me Failed assertion error. below is my code. I just want to show the list the second code
class Post extends StatefulWidget {
final String postId;
final String ownerId;
final String username;
final String location;
final String description;
final String mediaUrl;
final dynamic likes;
Post({
this.postId,
this.ownerId,
this.username,
this.location,
this.description,
this.mediaUrl,
this.likes,
});
factory Post.fromDocument(DocumentSnapshot doc) {
return Post(
postId: doc['postId'],
ownerId: doc['ownerId'],
username: doc['username'],
location: doc['location'],
description: doc['description'],
mediaUrl: doc['mediaUrl'],
likes: doc['likes'],
);
}
int getLikeCount(likes) {
// if no likes, return 0
if (likes == null) {
return 0;
}
int count = 0;
// if the key is explicitly set to true, add a like
likes.values.forEach((val) {
if (val == true) {
count += 1;
}
});
return count;
}
#override
_PostState createState() => _PostState(
postId: this.postId,
ownerId: this.ownerId,
username: this.username,
location: this.location,
description: this.description,
mediaUrl: this.mediaUrl,
likes: this.likes,
likeCount: getLikeCount(this.likes),
);
}
class _PostState extends State<Post> {
final String postId;
final String ownerId;
final String username;
final String location;
final String description;
final String mediaUrl;
Int likeCount;
Map likes;
_PostState({
this.postId,
this.ownerId,
this.username,
this.location,
this.description,
this.mediaUrl,
this.likes,
this.likeCount,
});
Widget postingHeading() {
return FutureBuilder(
future:
FirebaseFirestore.instance.collection('User').doc(ownerId).get(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
}
return ListTile(
onTap: () => print('go check the url'),
leading: CircleAvatar(
backgroundImage:
NetworkImage("${snapshot.data.data()['photoUrl']}"),
),
title: Text('${snapshot.data.data()['username']}'),
subtitle: Text(location),
trailing: IconButton(
onPressed: () => print('deleting post'),
icon: Icon(Icons.more_vert),
),
);
});
}
Widget postImagePicture() {
return Stack(
alignment: Alignment.center,
children: [
Image.network(mediaUrl),
],
);
}
Widget postDetailsComment() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
IconButton(icon: Icon(Icons.favorite_border), onPressed: () {}),
IconButton(icon: Icon(Icons.favorite_border), onPressed: () {})
],
),
Row(
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 20.0),
child: Text(
"$likeCount likes",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
],
),
Row(
children: [
Text(username),
SizedBox(
width: 1,
),
Flexible(child: Text(description))
],
)
],
);
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
postingHeading(),
postImagePicture(),
postDetailsComment(),
],
);
}
}
here is where I convert it to List... I don't even no what is wrong.... please help.... thanks in advance
class Profile extends StatefulWidget {
final String currentUser;
Profile({this.currentUser});
#override
_ProfileState createState() => _ProfileState();
}
class _ProfileState extends State<Profile> {
final _firestore = FirebaseFirestore.instance;
int postLenght;
List<Post> post;
bool pleaseWait;
#override
void initState() {
super.initState();
getUsersPicsDetails();
}
getUsersPicsDetails() async {
setState(() {
pleaseWait = false;
});
QuerySnapshot _getPost = await _firestore
.collection('post')
.doc(widget.currentUser)
.collection('userPost')
.orderBy('timeStamp', descending: true)
.get();
setState(() {
pleaseWait = true;
postLenght = _getPost.docs.length;
post = _getPost.docs.map((e) => Post.fromDocument(e)).toList();
print(postLenght);
});
}
fellowEditButtton({String test, Function press}) {
return TextButton(
onPressed: press,
child: Container(
alignment: Alignment.center,
height: 25,
width: 250,
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
border: Border.all(),
borderRadius: BorderRadius.circular(8.0)),
child: Text(
test,
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
)),
);
}
gettingUserprofile() {
return StreamBuilder(
stream: _firestore.collection('User').doc(widget.currentUser).snapshots(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.connectionState != ConnectionState.active) {
return Text('this stuff en');
}
return ListView.builder(
itemCount: 1,
itemBuilder: (context, index) => Container(
padding: EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
FullScreenWidget(
child: Hero(
tag: 'smallImage',
child: CircleAvatar(
backgroundImage: NetworkImage(
'${snapshot.data.data()['photoUrl']}'),
radius: 50.0,
),
),
),
Expanded(
flex: 1,
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [
topColum(buttom: 'Post', number: 1),
topColum(buttom: 'Fellowers', number: 2),
topColum(buttom: 'Following', number: 0),
],
),
Column(
crossAxisAlignment:
CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20.0,
),
child: widget.currentUser ==
_auth.currentUser.uid
? fellowEditButtton(
test: 'Edit Profile',
press: profileEditingButton)
: fellowEditButtton(
test: 'Fellow', press: () {})),
],
),
],
),
),
],
),
Divider(),
//! this is i call the list
//getUserPicture(),
getUserPicture(),
],
),
));
},
);
}
profileEditingButton() {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => EditProfile(
userProfile: widget.currentUser,
)));
}
Widget topColum({int number, String buttom}) {
return Column(
children: [
Text(
'$number',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
Text(
buttom,
style: TextStyle(
fontSize: 20,
),
),
],
);
}
//! this is the where i return the list
Widget getUserPicture() {
if (pleaseWait = false) {
return CircularProgressIndicator();
}
return Column(
children: post,
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: header(context, titleString: "Profile"),
body:
//Text('${widget.currentUser}')
gettingUserprofile());
// userPictursPost(),
}
}
I want to the get the post at the bottom... thanks in advance
below is the error image
enter image description here

When you get an error, please post it along with your question. When you are getting an error, it means that something is wrong with your code,and most likely not the flutter engine. Both are important for debugging, the error+your code.
Try changing this
QuerySnapshot _getPost = await _firestore
.collection('post')
.doc(widget.currentUser)
.collection('userPost')
.orderBy('timeStamp', descending: true)
.get();
setState(() {
pleaseWait = true;
postLenght = _getPost.docs.length;
post = _getPost.docs.map((e) => Post.fromDocument(e)).toList();
print(postLenght);
});
into this:
QuerySnapshot _getPost = await _firestore
.collection('post')
.doc(widget.currentUser)
.collection('userPost')
.orderBy('timeStamp', descending: true)
.get();
if(_getPost.docs.isNotEmpty){
List<Post> tempPost = _getPost.docs.map((e) => Post.fromDocument(e)).toList();
setState(() {
pleaseWait = true;
post =tempPost
print(postLenght);
});
}else{
print('The List is empty);}
You are not checking if the Query result has data or not. If it's empty, you will pass an empty List post down your tree, and you will get the error you are having.

For people facing similar issues, let me tell what I found in my code:
The error says that the children is null, not empty !
So if you are getting the children for the parent widget like Row or Column from a separate method, just check if you are returning the constructed child widget from the method.
Row(
children: getMyRowChildren()
)
.
.
.
getMyRowChildren(){
Widget childWidget = ... //constructed
return childWidget; //don't forget to return!
}
Else it would return null, which results in the children being null and we get the mentioned above error!

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);
});

Why does text form field re-render when user clicks on it in Flutter?

Why does text form field re-render when user clicks on it in Flutter?
My Flutter form contains a TextFormField for Name. When my user clicks on it, the entire form immediately re-renders (reloads), making it impossible for my user to enter anything.
Code
The TextFormField is commented so that you can easily find it.
You'll notice that this page also contains a second field that works perfectly. It's a Switch inside a StatefulBuilder that handles setting a TextEditingController for _importantController.
<!-- language: flutter -->
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/material.dart';
class CrudPage2 extends StatefulWidget {
final String docId;
const CrudPage2({Key? key, required this.docId}) : super(key: key);
#override
CrudPage2State createState() => CrudPage2State();
}
class CrudPage2State extends State<CrudPage2> {
late String name = "";
late bool isImportant = false;
final TextEditingController _nameController = TextEditingController();
final TextEditingController _importantController = TextEditingController();
Stream<DocumentSnapshot<Object?>> groceryItem(docID) =>
FirebaseFirestore.instance
.collection("groceries")
.doc(docID)
.snapshots();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => Navigator.of(context).pop(),
),
title: Text("Grocery Item"),
),
body: SizedBox(
width: double.infinity,
child: Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom + 20),
child: StreamBuilder<DocumentSnapshot>(
stream: groceryItem(widget.docId),
builder: (BuildContext context, AsyncSnapshot<DocumentSnapshot> streamSnapshot) {
if (streamSnapshot.connectionState == ConnectionState.waiting) {
print("****** Loading ******"); // debugging
return const Text("Loading");
} else if (streamSnapshot.hasData) {
if (widget.docId != "NEW") {
// Retrieve existing item
var jsonData = streamSnapshot.data?.data();
Map<String, dynamic> myData = jsonData as Map<String, dynamic>;
name = myData['name'];
isImportant = myData['important'];
}
_nameController.text = name;
if (isImportant) {
_importantController.text = "true";
} else {
_importantController.text = "false";
}
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//--------------------------------------------------------
// PROBLEM: Clicking on this field re-renders entire form.
Flexible(
child: TextFormField(
controller: _nameController,
decoration: const InputDecoration(labelText: 'Name'),
),
),
//--------------------------------------------------------
// No problem with this switch
StatefulBuilder(
builder: (BuildContext context, StateSetter importantStateSetter) {
return Row(
children: [
const Text("Important: "),
Switch(
value: isImportant,
onChanged: (value) {
importantStateSetter(() => isImportant = value);
},
),
],
);
},
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
child: ElevatedButton(
child: const Text('Cancel'),
onPressed: () async {
Navigator.of(context).pop();
}),
),
const SizedBox(
width: 10,
),
SizedBox(
child: ElevatedButton(
child: const Text("Submit"),
onPressed: () async {
final String name = _nameController.text;
if (widget.docId == 'NEW') {
addGroceryItem(name, 1.0, "test",
isImportant);
} else {
updateGroceryItem(widget.docId, name,
1.0, "test", isImportant);
}
// Clear the text fields
_nameController.text = '';
_importantController.text = "";
// Hide the bottom sheet
Navigator.of(context).pop();
},
),
)
],
),
],
);
} else {
return const Text("No Data");
}
})
),
),
);
} // Widget Build
//-------------------------------------------------------------
// Add New Grocery Item
//-------------------------------------------------------------
Future<void> addGroceryItem(
String name, double quantity, String category, bool isImportant) async {
await FirebaseFirestore.instance.collection('groceries').add({
"active": true,
"name": name,
"quantity": quantity,
"category": category,
"important": isImportant
});
}
//-------------------------------------------------------------
// Update Existing Grocery Item
//-------------------------------------------------------------
Future<void> updateGroceryItem(String docID, String name, double quantity,
String category, bool isImportant) async {
await FirebaseFirestore.instance.collection('groceries').doc(docID).update({
"active": true,
"name": name,
"quantity": quantity,
"category": category,
"important": isImportant
});
}
}
I added print("****** Loading ******"); line to help debug. When user clicks on text form field, the Console displays:
I/flutter (28767): ****** Loading ******
I/flutter (28767): ****** Loading ******
Why is the stream refreshing every time this widget is clicked?
Thank you for your time!
After a lot of Googling, I decided that my problem was coming from doing this entirely wrong. Here are some of the changes I made:
Pass-in values as JSON object parameter
Eliminate call to Firebase
Eliminate Stream Builder
Code below solves my problem using these changes:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class CrudPage2 extends StatefulWidget {
final String docId;
final Object? docSnap;
const CrudPage2({Key? key,
required this.docId,
required this.docSnap})
: super(key: key);
#override
CrudPage2State createState() => CrudPage2State();
}
class CrudPage2State extends State<CrudPage2> {
//--- Form State Variables...
late String name = "";
late bool isImportant = false;
//--- Controllers for Form Fields...
final TextEditingController _nameController = TextEditingController();
final TextEditingController _importantController = TextEditingController();
#override
initState() {
super.initState();
if (widget.docId != "NEW") {
Map<String, dynamic> myData = widget.docSnap as Map<String, dynamic>;
name = myData['name'];
isImportant = myData['important'];
}
_nameController.text = name;
if (isImportant) {
_importantController.text = "true";
} else {
_importantController.text = "false";
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => Navigator.of(context).pop(),
),
title: Text("Grocery Item"),
),
body: SizedBox(
width: double.infinity,
child: Padding(
padding: EdgeInsets.only(
top: 20,
left: 20,
right: 20,
bottom: MediaQuery.of(context).viewInsets.bottom + 20),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
controller: _nameController,
decoration: const InputDecoration(labelText: 'Name'),
),
StatefulBuilder(
builder:
(BuildContext context, StateSetter importantStateSetter) {
return Row(
children: [
const Text("Important: "),
Switch(
value: isImportant,
onChanged: (value) {
importantStateSetter(() => isImportant = value);
},
),
],
);
},
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 110,
child: ElevatedButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(
Colors.grey),
padding: MaterialStateProperty.all(
const EdgeInsets.all(5)),
textStyle: MaterialStateProperty.all(
const TextStyle(fontSize: 24))),
child: const Text('Cancel'),
onPressed: () async {
Navigator.of(context).pop();
}),
),
SizedBox(
width: 200,
child: ElevatedButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.green),
padding: MaterialStateProperty.all(
const EdgeInsets.all(5)),
textStyle: MaterialStateProperty.all(
const TextStyle(fontSize: 24))),
child: const Text("Submit"),
onPressed: () async {
final String name = _nameController.text;
if (widget.docId == 'NEW') {
addGroceryItem(name, 1.0, "Test",
isImportant);
} else {
updateGroceryItem(widget.docId, name,
1.0, "Test", isImportant);
}
// Clear the text fields
_nameController.text = '';
_importantController.text = "";
// Hide the bottom sheet
Navigator.of(context).pop();
},
),
)
],
),
]
)
)
),
);
} // Widget Build
Future<void> addGroceryItem(
String name, double quantity, String category, bool isImportant) async {
await FirebaseFirestore.instance.collection('groceries').add({
"active": true,
"name": name,
"quantity": quantity,
"category": category,
"important": isImportant
});
}
Future<void> updateGroceryItem(String docID, String name, double quantity,
String category, bool isImportant) async {
await FirebaseFirestore.instance.collection('groceries').doc(docID).update({
"active": true,
"name": name,
"quantity": quantity,
"category": category,
"important": isImportant
});
}
}
Any comments and/or suggestions are still appreciated.
I hope this helps someone else in the future.

How can I map a JSON map to single key multiple values

This might be a bit of broad question.
I have an JSON Map Array which i can parse normally. But i would like to MAP multiple Values to a Single key. I am stuck and unsure how to proceed.
My JSON output. I cannot change this.
{
"pos":"1",
"element":"37542",
"title":"Easy On Me",
"inter":"Adele",
"isnew":"0"
},
{
"pos":"66",
"element":"37183",
"title":"Send My Love",
"inter":"Adele",
"isnew":"0"
}
this is just a snippet of the actual JSON objects
what I would like to do is something like Map<String,List<String,String>>
Adele
Easy On Me, 37542
Send My Love, 37183
Searching online I find a lot of examples how to parse JSON normally, but never found a version where you can iterate through a JSON version like above and pull parts of the map out and add it to another.
any help would be appreciated. Thank you in advance
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'dart:async';
class SongList {
final List<Song> songs;
SongList({
required this.songs,
});
factory SongList.fromJson(List<dynamic> parsedJson) {
List<Song> songs = <Song>[];
songs = parsedJson.map((i) => Song.fromJson(i)).toList();
return SongList(songs: songs);
}
}
class Song {
final String? pos;
final String? element;
final String? title;
final String? inter;
final String? isnew;
const Song(
{required this.pos,
required this.element,
required this.title,
required this.inter,
required this.isnew});
factory Song.fromJson(Map<String, dynamic> data) {
return Song(
pos: data['pos'] as String?,
element: data['element'] as String?,
title: data['title'] as String?,
inter: data['inter'] as String?,
isnew: data['isnew'] as String?,
);
}
}
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';
class Songs extends StatefulWidget {
final String pos;
final String element;
final String title;
final String inter;
final String isnew;
const Songs(
{super.key,
required this.pos,
required this.element,
required this.title,
required this.inter,
required this.isnew});
#override
State<Songs> createState() => _SongsState();
}
class _SongsState extends State<Songs> {
late AudioPlayer player;
#override
void initState() {
super.initState();
player = AudioPlayer();
}
#override
void dispose() {
player.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
var titles = Row(
mainAxisSize: MainAxisSize.min,
children: [
Column(children: [
SizedBox(
height: 25,
width: 200,
child: Text(
widget.title,
style: const TextStyle(fontSize: 14),
textAlign: TextAlign.center,
),
),
SizedBox(
height: 25,
width: 200,
child: Text(
widget.inter,
style: const TextStyle(fontSize: 10),
textAlign: TextAlign.center,
),
),
]),
],
);
return FittedBox(
fit: BoxFit.contain,
child: Card(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
Center(
child: SizedBox(
height: 50,
width: 50,
child: Align(
alignment: Alignment.center,
child: widget.isnew != "1"
? Text(
widget.pos.toString(),
textAlign: TextAlign.center,
)
: const Text(
"new",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.red),
)))),
],
),
Column(
children: [
SizedBox(
height: 50,
width: 50,
child: Image.network(
'${'http://www.example.com/${widget.element}'}.jpg'),
),
],
),
Column(
children: [
titles,
],
),
Column(
children: [
SizedBox(
height: 50,
width: 50,
child: TextButton(
onPressed: () async {
final url =
'${'http://www.example.com/${widget.element}'}.mp3';
await player.setUrl(url);
player.play();
},
child: Image.asset('assets/images/btn_play.png'),
),
),
],
),
Column(
children: [
SizedBox(
height: 50,
width: 50,
child: TextButton(
// ignore: avoid_print
onPressed: () =>
print('pressed button download!! ${widget.element}'),
child: Image.asset('assets/images/btn_download.png'),
),
),
],
),
],
)));
}
}
Future<SongList> fetchSong() async {
final response = await http
.get(Uri.parse('http://example.com'));
if (response.statusCode == 200) {
return SongList.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to Load Songs');
}
}
//snippet from class that instatiates
late Future<SongList> futureSong;
#override
void initState() {
super.initState();
futureSong = fetchSong();
}
//snippet from class that builds the list
FutureBuilder<SongList>(
future: futureSong,
builder: (context, AsyncSnapshot<SongList> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.songs.length,
itemBuilder: (context, index) {
if (snapshot.data!.songs[index].element.toString() !=
"null") {
return Songs(
element:
snapshot.data!.songs[index].element.toString(),
inter: snapshot.data!.songs[index].inter.toString(),
isnew: snapshot.data!.songs[index].isnew.toString(),
pos: snapshot.data!.songs[index].pos.toString(),
title: snapshot.data!.songs[index].title.toString());
} else {
return const SizedBox(height: 0);
}
});
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
// By default, show a loading spinner.
return const CircularProgressIndicator();
}
This might work:
void main() {
var data=[
{
"pos":"1",
"element":"37542",
"title":"Easy On Me",
"inter":"Adele",
"isnew":"0"
},
{
"pos":"66",
"element":"37183",
"title":"Send My Love",
"inter":"Adele",
"isnew":"0"
},
{
"pos":"80",
"element":"5000",
"title":"Enjoy the silence",
"inter":"Depeche Mode",
"isnew":"0"
},
{
"pos":"100",
"element":"6000",
"title":"In your room",
"inter":"Depeche Mode",
"isnew":"0"
}
];
var result = <String,List<Map<String,String>>>{};
for (var d in data) {
print(d);
var e={"element":d["element"]!, "title": d["title"]!};
var key=d["inter"]!;
if (result.containsKey(key)) {
result[key]!.add(e);
} else {
result[key]=[e];
}
}
print(result);
}
You can achieve this by creating a subclass.
Interpret interpretFromJson(String st) => Interpret.fromJson(json.decode(st));
class Interpret{
Interpret({
required this.name,
required this.titles,
});
final String name;
final List<Title> titles;
factory Interpret.fromJson(Map<String, dynamic> json) => Title(
name: json["inter"],
titles: List<Title>.from(json.map((x) => Title.fromJson(x))),
);
}
class Title {
Title({
required this.name,
required this.element,
});
final String name;
final double element;
factory Title.fromJson(Map<String, dynamic> json) => Title(
name: json["name"],
element: json["element"],
);
Then you can just call it like.
InterpretFromJson(json);

flutter riverpod leaving screen then come back it doesn't maintain the state

So I have two screens:
-Book_screen to display all the books(click on any book to go to article_screen)
-article_screen to display articles
In article_screen, I can click on article to save it as favorites.
but when I go back to book_screen then come back to article_screen, those favorited articles doesn't show the favorited status(icon red heart).
this is my article screen code:
class ArticleENPage extends ConsumerStatefulWidget{
final String bookName;
const ArticleENPage({Key? key,#PathParam() required this.bookName,}) : super(key: key);
#override
ArticleENScreen createState()=> ArticleENScreen();
}
class ArticleENScreen extends ConsumerState<ArticleENPage> {
late Future<List<Code>> codes;
#override
void initState() {
super.initState();
codes = fetchCodes();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.bookName,style: const TextStyle(fontSize: 24,fontWeight: FontWeight.bold),),backgroundColor: Colors.white,foregroundColor: Colors.black,elevation: 0,),
body: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//SizedBox(height: 10),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15.0),
child: Container(
margin: const EdgeInsets.only(top:10),
height: 43,
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 2),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50),
border: Border.all(
color: Colors.black.withOpacity(0.32),
),
),
child: Consumer(
builder: (context,ref,_) {
return TextField(
onChanged: (value) {
searchStringController controller = ref.read(searchStringProvider.notifier);
controller.setText(value.toLowerCase());
},
decoration: const InputDecoration(
border: InputBorder.none,
icon: Icon(Icons.search,size:18),
hintText: "Search Here",
hintStyle: TextStyle(color: Color.fromRGBO(128,128, 128, 1)),
),
);
}
),
),
),
const SizedBox(height: 10),
Expanded(
child: FutureBuilder(
builder: (context, AsyncSnapshot<List<Code>> snapshot) {
if (snapshot.hasData) {
return Center(
child: Consumer(
builder: (context,ref,child) {
final searchString = ref.watch(searchStringProvider);
return ListView.separated(
padding: const EdgeInsets.all(8),
itemCount: snapshot.data!.length,
itemBuilder: (BuildContext context, int index) {
return snapshot.data![index].name
.toLowerCase()
.contains(searchString) ||
snapshot.data![index].description
.toLowerCase()
.contains(searchString)
? Consumer(
builder: (context,ref,child) {
final favlist = ref.watch(FavoriteListController.favoriteListProvider);
print(favlist);
final alreadySaved = favlist.contains(snapshot.data![index]);
return Card(
child:Padding(
padding: const EdgeInsets.all(10),
child:ExpandableNotifier(
child: ScrollOnExpand(
child: ExpandablePanel(
theme: const ExpandableThemeData(hasIcon: true),
header: RichText(text: TextSpan(children: highlight(snapshot.data![index].name, searchString,'title')),),
collapsed: RichText(text: TextSpan(children: highlight(snapshot.data![index].description, searchString,'content')), softWrap: true, maxLines: 3, overflow: TextOverflow.ellipsis,),
expanded: Column(
children: [
RichText(text: TextSpan(children: highlight(snapshot.data![index].description, searchString,'content')), softWrap: true ),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButton(
icon: Icon(
alreadySaved ? Icons.favorite : Icons.favorite_border,
color: alreadySaved ? Colors.red : null,
semanticLabel: alreadySaved ? 'Remove from saved' : 'Save',
),
onPressed: () {
FavoriteListController controller = ref.read(FavoriteListController.favoriteListProvider.notifier);
if (alreadySaved) {
controller.toggle(snapshot.data![index]);
} else {
controller.toggle(snapshot.data![index]);
}
},
),
IconButton(
icon: const Icon(Icons.content_copy),
onPressed: () {
setState(() {
Clipboard.setData(ClipboardData(text: snapshot.data![index].name+"\n"+snapshot.data![index].description))
.then((value) {
ScaffoldMessenger.of(context).showSnackBar(new SnackBar(content: Text('Copied')));
},);
});
},
),],),],)),),)));})
: Container();
},
separatorBuilder: (BuildContext context, int index) {
return snapshot.data![index].name
.toLowerCase()
.contains(searchString) ||
snapshot.data![index].description
.toLowerCase()
.contains(searchString)
? Divider()
: Container();
},
);
}
),
);
} else if (snapshot.hasError) {
return const Center(child: Text('Something went wrong :('));
}
return const Align(alignment:Alignment.topCenter,child:CircularProgressIndicator());
},
future: codes,
),
),
],
),
);
}
//read from files
Future<List<Code>> fetchCodes() async {
final response =
await rootBundle.loadString('assets/articles.json');
var CodeJson = json.decode(response)[widget.bookName] as List<dynamic>;
return CodeJson.map((code) => Code.fromJson(code)).toList();
}
}
I tried using riverpod for provider and save to sharedpreference the list of code that I favorited.
final sharedPrefs =
FutureProvider<SharedPreferences>((_) async => await SharedPreferences.getInstance());
class FavoriteListController extends StateNotifier<List<Code>>{
FavoriteListController(this.pref) : super(Code.decode(pref?.getString("favcode")??""));
static final favoriteListProvider = StateNotifierProvider<FavoriteListController, List<Code>>((ref) {
final pref = ref.watch(sharedPrefs).maybeWhen(
data: (value) => value,
orElse: () => null,
);
print(pref?.getString("favcode"));
return FavoriteListController(pref);
});
final SharedPreferences? pref;
void toggle(Code code) {
if (state.contains(code)) {
state = state.where((id) => id != code).toList();
} else {
state = [...state, code];
}
final String encodedData = Code.encode(state);
pref!.setString("favcode", encodedData);
}
}
I am not sure what is the cause of this but I think it might be because of futurebuilder? I am confused to how to solve this issue...
I am stuck in a dead end so any help or advice would be really appreciated
edit 1-
this is my source code in case I have not include all the necessary codes
https://github.com/sopheareachte/LawCode
edit-2
do I need to change "late Future<List> codes;" that fetch all the codes for futurebuilder to riverpod futureprovider too for it to work?
Maybe the problem is, that you define a static provider inside of your controller class. Try this code:
final sharedPrefs = FutureProvider<SharedPreferences>((_) async => await SharedPreferences.getInstance());
final favoriteListProvider = StateNotifierProvider<FavoriteListController, List<Code>>((ref) {
final pref = ref.watch(sharedPrefs).maybeWhen(
data: (value) => value,
orElse: () => null,
);
print(pref?.getString("favcode"));
return FavoriteListController(pref);
});
class FavoriteListController extends StateNotifier<List<Code>>{
FavoriteListController(this.pref) : super(Code.decode(pref?.getString("favcode")??""));
final SharedPreferences? pref;
void toggle(Code code) {
if (state.contains(code)) {
state = state.where((id) => id != code).toList();
} else {
state = [...state, code];
}
final String encodedData = Code.encode(state);
pref!.setString("favcode", encodedData);
}
}