I am trying to update an array in flutter. I found out that it is not possible directly.
I have understood that I must first create a list[], transfer my document fields value into the created list, then I must update the information in my list and only then can I can update my Firebase Firestore array with my updated list.
My code is below. Problem is, when I use my function the simulator crashes and I am getting the error lost connection.
I am looking for any advice. Many thanks.
List listTest =[];
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
class DetailScreen_CheckList_V3 extends StatefulWidget {
//final Map listName;
final docID;
const DetailScreen_CheckList_V3( this.docID,{
Key key}) : super(key: key);
#override
_DetailScreen_CheckList_V3State createState() => _DetailScreen_CheckList_V3State(docID);
}
class _DetailScreen_CheckList_V3State extends State<DetailScreen_CheckList_V3> {
// Map listName;
var docID;
_DetailScreen_CheckList_V3State( //this.listName,
this.docID);
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text('Your list items'),
leading:
InkWell(
child:
Icon(Icons.fast_rewind_outlined),
onTap: () {
Navigator.pop(context);
},),
),
body: MyBody(context, docID),
floatingActionButton: FloatingActionButton(
onPressed: () {
showAddNewItemToAList();
setState(() {});
},
child: const Icon(Icons.add),
backgroundColor: Colors.blue,
),
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
);
}
Widget MyBody(BuildContext context, var docID) {
return SingleChildScrollView(
child: Column(
children: [
Container(
height: MediaQuery
.of(context)
.size
.height / 1.4,
width: MediaQuery
.of(context)
.size
.width,
child: StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection('Users')
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('lists')
.doc(docID)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
} else {
DocumentSnapshot data = snapshot.requireData;
return ListView.builder(
itemCount: data['allItems'].length,
itemBuilder: (context, index) {
return Card(
child:
InkWell(
child: ListTile(
leading: data['allItems'][index]['itemChecked'] ==
'Yes' ? Icon(
Icons.check_box,
color: Colors.blue,) : Icon(
Icons.check_box_outline_blank),
title:
Text(
(data['allItems'][index]['itemName'])),
onTap: () {
String checked = data['allItems'][index]['itemChecked'];
String myItemName = data['allItems'][index]['itemName'];
String myListName = data['listName'];
listTest.addAll(data['allItems']);
print('before');
print (listTest);
setState(() {
if (checked == 'Yes') {
checked = 'No';
listTest[index]['itemChecked'] = checked;
print('after');
print(listTest);
myTest(myListName,index);
}
else {
checked = 'Yes';
listTest[index]['itemChecked'] = checked;
print('after');
print(listTest);
myTest(myListName,index);
}
});
}
),
onTap: () {
},)
);
});
}
}))
]),
);
}
void showAddNewItemToAList() {
TextEditingController _noteField = new TextEditingController();
showDialog(
context: context,
builder: (BuildContext context) {
return CustomAlertDialog(
content: Container(
width: MediaQuery
.of(context)
.size
.width / 1.3,
height: MediaQuery
.of(context)
.size
.height / 4,
child: Column(
children: [
TextField(
controller: _noteField,
maxLines: 4,
decoration: InputDecoration(
border: const OutlineInputBorder(
borderSide:
const BorderSide(color: Colors.black, width: 1.0),
),
),
),
SizedBox(height: 10),
Material(
elevation: 5.0,
borderRadius: BorderRadius.circular(25.0),
color: Colors.white,
child: MaterialButton(
minWidth: MediaQuery
.of(context)
.size
.width / 1.5,
onPressed: () {
if (_noteField.text != '') {
setState(() {
AddObjectItemToArray(_noteField.text);
});
Navigator.of(context).pop();
}
else {
return;
}
},
padding: EdgeInsets.fromLTRB(10.0, 15.0, 10.0, 15.0),
child: Text(
'Add Item',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.0,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
)
],
),
),
);
});
}
Future AddObjectItemToArray(newItemName,) async {
AllItems _allItems = AllItems(newItemName, 'No');
FirebaseFirestore.instance
.collection('Users')
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('lists')
.doc(docID).update({
"allItems": FieldValue.arrayUnion([_allItems.toMap()])
},);
}
Future ModifyCheckedStatus(newItemName, newCheckedStatus,
currentListName) async {
AllItems _allItems = AllItems(newItemName, newCheckedStatus);
FirebaseFirestore.instance
.collection('Users')
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('lists')
.doc(docID).update(
{'listName': currentListName,
"allItems": ([_allItems.toMap()]),
}, //SetOptions(merge: true),
);
}
Future myTest(currentListName,index) async {
FirebaseFirestore.instance
.collection('Users')
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('lists')
.doc(docID).set(
{'listName': currentListName,
"allItems": [listTest],
},//SetOptions(merge: true)
);
}
}
So I guess your AddObjectToArray method is working well, you are correctly using arrayUnion. But then the ModifyCheckStatus is not working as expected because you are still using arrayUnion.
ArrayUnion adds an object to the array. What you have to do in ModifyCheckStatus is to extract all items, toggle the checked status of a particular item, then update the entire items list in Firebase (instead of using arrayUnion). This should only be in ModifyCheckStatus.
Something like the following
Future ModifyCheckedStatus(newItemName, newCheckedStatus,
currentListName) async {
// TODO: obtain all items
// changed the checked status of a particular item
allItems.where((i) => i.name == item.name).forEach((i) {
i.checked = !i.checked;
});
// update as you did
FirebaseFirestore.instance
.collection('Users')
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('lists')
.doc(docID)
.update({"allItems": allItems});
}
Related
#override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldKey,
backgroundColor: FlutterFlowTheme.of(context).primaryBackground,
body: SafeArea(
child: GestureDetector(
onTap: () => FocusScope.of(context).requestFocus(_unfocusNode),
child: StreamBuilder<List<UsersRecord>>(
stream: queryUsersRecord(
queryBuilder: (usersRecord) =>
usersRecord.where('display_name', isEqualTo: usertag==""?"Nischal Nayak":usertag),
singleRecord: true,
),
builder: (context, snapshot) {
// Customize what your widget looks like when it's loading.
if (!snapshot.hasData) {
return Center(
child: SizedBox(
width: 38,
height: 38,
child: SpinKitThreeBounce(
color: FlutterFlowTheme.of(context).primaryColor,
size: 38,
),
),
);
}
List<UsersRecord> textUsersRecordList = snapshot.data!;
// Return an empty Container when the item does not exist.
if (snapshot.data!.isEmpty) {
return Container();
}
final textUsersRecord = textUsersRecordList.isNotEmpty
? textUsersRecordList.first
: null;
return InkWell(
onTap: () async {
logFirebaseEvent('TEST_PAGE_Text_i1by10in_ON_TAP');
logFirebaseEvent('Text_navigate_to');
context.pushNamed(
'Profile',
queryParams: {
'userref': serializeParam(
textUsersRecord!.reference,
ParamType.DocumentReference,
),
}.withoutNulls,
);
},
child: LinkifyText(
widget.text,
textStyle: TextStyle(
color: Colors.blue),
linkStyle: TextStyle(
color: widget.linkColor, fontSize: widget.fontSize),
onTap: (Link) {
if(Link.type == LinkType.url) {
_launchInBrowser(Uri(scheme: "https", host: Link.value),);
}else
if(Link.type == LinkType.userTag) {
setState(() {
usertag = Link.value!.substring(1);
});
}
} ,
),
);
},
),
),
),
);
}
}
how to pass Link.value to isEqualto in usersRecord.where('display_name', isEqualTo: usertag==""?"Nischal Nayak":usertag) please help i want to open the profile by pssing useref from linkytext . how to pass usertag string to streambuilder and everytime i tap it serch the doc and pass the userref . when i tap on the text (linkyfy text )now the linkvalue is still empty and "Nischal Nayak " is passed in the streambuilder
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),
);
},
);
what I want to achieve is to change the image in CircleAvatar when I'm selecting an image, here is the code:
ProfileController:
class ProfileController extends GetxController {
final TextEditingController emailController = TextEditingController();
final ImagePicker _picker = ImagePicker();
Rx<String?> avatarPath = null.obs;
avatarFromCamera() async {
var localAvatar = await _picker.pickImage(
source: ImageSource.camera, imageQuality: 50
);
if (localAvatar != null) {
avatarPath = localAvatar.path.obs;
update();
}
}
avatarFromGallery() async {
var localAvatar = await _picker.pickImage(
source: ImageSource.gallery, imageQuality: 50
);
if (localAvatar != null) {
avatarPath = localAvatar.path.obs;
update();
}
}
String? emailValidator(String? value) {
if (value == null || value.isEmpty) {
return null;
}
if (!EmailValidator.validate(value, false)) {
return 'Invalid email address';
}
}
#override
void onClose() {
emailController.dispose();
super.onClose();
}
String? emailValidator(String? value) {
if (value == null || value.isEmpty) {
return null;
}
if (!EmailValidator.validate(value, false)) {
return 'Invalid email address';
}
}
void save(GlobalKey<FormState> profileFormKey) {
if (profileFormKey.currentState!.validate()) {
print('valid');
}
}
}
and here is the ProfileScreen widget:
lass ProfileScreen extends StatelessWidget {
final ProfileController _profileController = Get.put<ProfileController>(ProfileController());
GlobalKey<FormState> profileFormKey = GlobalKey<FormState>();
ProfileScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Update user details'),
),
body: SingleChildScrollView(
child: Form(
key: profileFormKey,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(30.0),
child: TextFormField(
keyboardType: TextInputType.text,
controller: _profileController.emailController,
decoration: const InputDecoration(
labelText: 'Enter email',
),
validator: _profileController.emailValidator,
),
),
Center(
child: GestureDetector(
onTap: () {
showModalBottomSheet(
context: context,
builder: (BuildContext bc) {
return SafeArea(
child: Wrap(
children: <Widget>[
ListTile(
leading: const Icon(Icons.photo_library),
title: const Text('Photo Library'),
onTap: () {
_profileController.avatarFromGallery();
Navigator.of(context).pop();
}),
ListTile(
leading: const Icon(Icons.photo_camera),
title: const Text('Camera'),
onTap: () {
_profileController.avatarFromCamera();
Navigator.of(context).pop();
},
),
],
),
);
}
);
},
child: CircleAvatar(
radius: 55,
backgroundColor: Colors.pink,
child: Obx(() =>(_profileController.avatarPath.value != null)
? ClipRRect(
borderRadius: BorderRadius.circular(50),
child: Image.file(
File(_profileController.avatarPath.value!),
width: 100,
height: 100,
fit: BoxFit.fitHeight
),
)
: Container(
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(50)),
width: 100,
height: 100,
child: Icon(
Icons.camera_alt,
color: Colors.grey[800],
),
),
),
),
)),
Container(
margin: const EdgeInsets.all(10),
width: double.infinity,
child: MaterialButton(
color: Colors.blue,
onPressed: () => _profileController.save(profileFormKey),
child: const Text(
'Submit',
style: TextStyle(color: Colors.white),
),
),
),
],
),
),
),
);
}
}
as you can see, I have Obx and my avatarPath reactive, and I'm running update everywhere I changing it, but it's not udpated. I also tried to use empty string as initial value of imagePath like this Rx<String> avatarPath = ''.obs; and it's not working. What I'm doing wrong??? Thank you in advice!!!
There are two things to revise for it. Firstly, change avatarPath = localAvatar.path.obs; with avatarPath.value = localAvatar.path;. Because localAvatar.path.obs create the new observable and changes will not be reflected to previous observers.
Secondly, create a new stateless widget having the widget tree of bottom sheet's builder like
showModalBottomSheet(
context: context,
builder: (BuildContext bc) {
return CustomBottomView();
}
);
Then inside the CustomBottomView copy your bottom sheet widget tree.
class CustomBottomView extends GetView<ProfileController> {
return YourBottomSheetWidgetTreeHere();
}
Dont worry about ProfileController here. You have already put it in DI in previous route. First follow the first step if you still face the problem second step will definitely resolve it.
I'm building a generalised flutter widget based on the flutter alertDialog, I want to have this as a separate widget which can be called with onPressed method in other widgets.
Currently the alertDialog opens with the onPressed method which is part of the current widget within ElevatedButton widget. I want to get rid of this ElevatedButton as the button to open alertDialog is part of other widget.
Class AppAlertDialog extends StatelessWidget {
const AppAlertDialog({
required this.buttonName,
required this.title,
required this.content,
required this.secondaryButtonName,
required this.primaryButtonName,
Key? key,
}) : super(key: key);
final String buttonName;
final String title;
final String content;
final String secondaryButtonName;
final String primaryButtonName;
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => _showAlertDialog(context),
child: Text(buttonName),
);
//Get rid of this ElevatedButton and call the _showAlertDialog method to open the
//alertDialog from other onPressed methods in other files
}
_showAlertDialog(BuildContext context) {
final titleTextStyle = Theme.of(context).textTheme.headline5!;
const buttonPadding = EdgeInsets.all(20);
showDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: Text(
title,
style: titleTextStyle,
),
content: Text(content),
contentPadding: const EdgeInsets.fromLTRB(24, 24, 24, 24),
actions: <Widget>[
ElevatedButton(
onPressed: () => Navigator.pop(context),
style: ElevatedButton.styleFrom(
padding: buttonPadding,
primary: SharedColorsButton.secondaryButtonBgColor,
onPrimary: SharedColorsButton.secondaryButtonFgColor,
side: const BorderSide(
color: SharedColorsInputDecoration.borderColor,
),
),
child: Text(secondaryButtonName),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
padding: buttonPadding,
),
onPressed: () => Navigator.pop(context),
child: Text(primaryButtonName),
),
],
actionsPadding: const EdgeInsets.fromLTRB(24, 16, 24, 16),
),
);
}
}
Please see the example.
showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
insetPadding: EdgeInsets.symmetric(horizontal: 8, vertical: 24),
title: Text("New request"),
content: Container(
height: double.maxFinite,
width: double.maxFinite,
child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('requests')
.where('accepted', isEqualTo: 0)
.snapshots(),
builder: (context, snapshot) {
print("Second ride data ==> ${snapshot.hasData}");
if (snapshot.hasData) {
final requests = snapshot.data.docs;
if (User.homeModel == null) {
return Container(width: 0, height: 0);
}
if (User.homeModel.vehicleDetails == null) {
return Container(width: 0, height: 0);
}
List<RequestCard> allTrains = [];
for (var doc in requests) {
print(
"Second ride data ==> ID ${doc['request_id']}");
if (Home.removeRequests.contains(doc['request_id']))
continue;
//seats compare
int seatCapacity =
User.homeModel.vehicleDetails.passengerCapacity;
print('seatCapacity => $seatCapacity');
var seatRequest = doc['seats'];
print('seatRequest => $seatRequest');
//baby_seats compare
var babySeatsCapacity =
User.homeModel.vehicleDetails.children;
print('babySeatsCapacity => $babySeatsCapacity');
var babySeatsRequest = doc['baby_seats'];
print('babySeatsRequest => $babySeatsRequest');
//WheelChair compare
var hasWheelChair =
User.homeModel.vehicleDetails.wheelchair == '1'
? true
: false;
print('hasWheelChair => $hasWheelChair');
var needWheelChair = doc['wheelchair'];
print('needWheelChair => $needWheelChair');
//compare vehicles with requests
if (seatRequest <= seatCapacity &&
babySeatsRequest <= babySeatsCapacity &&
(needWheelChair == hasWheelChair ||
hasWheelChair == true)) {
print('Vehicle Condition Satisfy');
final _rideReq = RideRequest(
userName: doc['user_name'],
currentLocation: doc['pick_up_location'],
destination: doc['drop_off_location'],
fare: doc['bid_amount'],
desLatitude: doc['drop_off_lat'].toString(),
desLongitude: doc['drop_off_long'].toString(),
distance: doc['distance'],
image: doc['user_image'],
latitude: doc['pick_up_lat'].toString(),
longitude: doc['pick_up_long'].toString(),
phone: doc['phone'],
pickUpPoint: doc['pick_up_location'],
userId: doc['user_id'],
requestId: doc['request_id']);
final requestCard = RequestCard(
onAcceptFunction: onAcceptRequest,
onRejectFunction: onRejectRequest,
rideRequest: _rideReq,
);
allTrains.add(requestCard);
}
}
if (allTrains.length > 0) {
return ListView(
children: allTrains,
);
} else {
Future.delayed(Duration.zero)
.then((value) => Navigator.pop(context));
}
}
return Container(width: 0, height: 0);
}),
),
actions: <Widget>[
FlatButton(
onPressed: () {
HomeBottomNavigationBar._isNewRequestOpenDialog = false;
Navigator.pop(context, true);
},
child: Text("Close"),
),
FlatButton(
onPressed: () {
changeTabs(0);
HomeBottomNavigationBar._isNewRequestOpenDialog = false;
Navigator.pop(context, true);
},
child: Text("Go to Home"),
),
],
);
},
);
},
)
I try to create an onTap function on ChatRoomListTile which navigates me to the chatscreen where the id is = chatRoomId. Unfortunately I don't know how to deliver the ID.
Unfortunately I am very unexperienced in flutter and I made this app via tutorial... It works nice but I am still learning, so a big big big thanks!
my Database.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:signal/helperfunctions/sharedpref_helper.dart';
class DatabaseMethods{
Future addUserInfoToDB(String userId, Map<String, dynamic>userInfoMap)
async {
return FirebaseFirestore.instance
.collection("users")
.doc(userId)
.set(userInfoMap);
}
Future<Stream<QuerySnapshot>> getUserByUserName(String username) async{
return FirebaseFirestore.instance
.collection("users")
.where("username", isEqualTo: username)
.snapshots();
}
Future addMessage(String chatRoomId, String messageId, Map messageInfoMap) async {
return FirebaseFirestore.instance
.collection ("chatrooms")
.doc(chatRoomId)
.collection("chats")
.doc(messageId)
.set(messageInfoMap);
}
updateLastMessageSend(String chatRoomId, Map lastMessageInfoMap){
return FirebaseFirestore.instance
.collection("chatrooms")
.doc(chatRoomId)
.update(lastMessageInfoMap);
}
createChatRoom(String chatRoomId, Map chatRoomInfoMap) async{
final snapShot = await FirebaseFirestore.instance
.collection("chatrooms")
.doc(chatRoomId)
.get();
if(snapShot.exists){
//chatroom already exists
return true;
}else{
//chatroom does not exists
return FirebaseFirestore.instance
.collection("chatrooms")
.doc(chatRoomId)
.set(chatRoomInfoMap);
}
}
Future<Stream<QuerySnapshot>> getChatRoomMessages(chatRoomId) async {
return FirebaseFirestore.instance
.collection("chatrooms")
.doc(chatRoomId)
.collection("chats")
.orderBy("ts", descending: true)
.snapshots();
}
// Future<Stream<QuerySnapshot>> openChatRoom(String chatRoomId) async{
// return FirebaseFirestore.instance
// .collection("chatrooms")
// .doc(chatRoomId)
// .collection("chats")
// .snapshots();
// }
Future<Stream<QuerySnapshot>> getChatRooms() async {
String myUsername = await SharedPreferenceHelper().getUserName();
return FirebaseFirestore.instance
.collection("chatrooms")
.orderBy("lastMessageSendTs", descending: true)
.where("users",arrayContains: myUsername)
.snapshots();
}
Future<QuerySnapshot> getUserInfo(String username) async {
return await FirebaseFirestore.instance
.collection("users")
.where("username", isEqualTo: username)
.get();
}
}
and my home.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:signal/helperfunctions/sharedpref_helper.dart';
import 'package:signal/services/auth.dart';
import 'package:signal/services/database.dart';
import 'package:signal/views/signin.dart';
import 'services/colorpicker.dart';
import 'chatscreen.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
bool isSearching = false;
String myName, myProfilePic, myUserName, myEmail, messageSentTs;
Stream usersStream, chatRoomsStream;
TextEditingController searchUsernameEditingController =
TextEditingController();
getMyInfoFromSharedPreference() async{
myName = await SharedPreferenceHelper().getDisplayName();
myProfilePic = await SharedPreferenceHelper().getUserProfileUrl();
myUserName = await SharedPreferenceHelper().getUserName();
myEmail = await SharedPreferenceHelper().getUserEmail();
}
//dieser string sollte eigentlich aus sicherheitsgründen random generiert werden
getChatRoomIdByUsernames(String a, String b){
if(a.substring(0,1).codeUnitAt(0) > b.substring(0,1).codeUnitAt(0)){
return "$b\_$a";
}else{
return "$a\_$b";
}
}
onSearchBtnClick() async {
isSearching = true;
setState(() {});
usersStream = await DatabaseMethods()
.getUserByUserName(searchUsernameEditingController.text);
setState(() {});
}
Widget chatRoomsList(){
return StreamBuilder(
stream: chatRoomsStream,
builder: (context, snapshot){
return snapshot.hasData
? ListView.builder(
itemCount: snapshot.data.docs.length,
shrinkWrap: true,
itemBuilder: (context, index){
DocumentSnapshot ds = snapshot.data.docs[index];
return ChatRoomListTile(ds["lastMessage"], ds.id,myUserName);
})
: Center(child: CircularProgressIndicator());
},
);
}
// print("this is the values we have $myUserName $username"));
// Chatroom ID Kontrolle ( achtung beim randomisen der CRId)
Widget searchUserListTile({String profileUrl, name, username, email}){
return GestureDetector(
onTap: (){
var chatRoomId = getChatRoomIdByUsernames(myUserName, username);
Map<String, dynamic> chatRoomInfoMap = {
"users" : [myUserName, username]
};
DatabaseMethods().createChatRoom(chatRoomId, chatRoomInfoMap);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChatScreen(username, name)
)
);
},
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(30),
child: Image.network(
profileUrl,
height: 20,
width: 20,
),
),
SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children:[
Text(name),
Text(email),
],
)
],
),
);
}
Widget searchUsersList(){
return StreamBuilder(
stream: usersStream,
builder: (context, snapshot){
return snapshot.hasData
? ListView.builder(
itemCount: snapshot.data.docs.length,
shrinkWrap: true,
itemBuilder: (context, index){
DocumentSnapshot ds = snapshot.data.docs[index];
return searchUserListTile(
profileUrl: ds["imgUrl"],
name: ds["name"],
username: ds["username"],
email: ds["email"]
);
},
) : Center(child: CircularProgressIndicator(),);
}
);
}
getChatRooms() async {
chatRoomsStream = await DatabaseMethods().getChatRooms();
setState(() {});
}
onScreenLoaded() async {
await getMyInfoFromSharedPreference();
getChatRooms();
}
#override
void initState() {
onScreenLoaded();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromRGBO(233, 240, 244, 1),
appBar: AppBar(
title: Image.asset('assets/images/logo.png', width: 40,),
elevation: 0.0,
backgroundColor: Colors.white,
actions: [
InkWell(
onTap: (){
AuthMethods().signOut().then((s) {
Navigator.pushReplacement(context, MaterialPageRoute
(builder: (context) => SignIn()));
});
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Icon(Icons.exit_to_app)),
)
],
),
body: Container(
margin: EdgeInsets.symmetric(horizontal: 20),
child: Column(
children: [
Row(
children: [
isSearching
? GestureDetector(
onTap: (){
isSearching = false;
searchUsernameEditingController.text = "";
setState(() {});
},
child: Padding(
padding: EdgeInsets.only(right: 12),
child: Icon(Icons.arrow_back)),
) : Container(),
Expanded(
child: Container(
// Disable Searchbar
width: 0,
height: 0,
margin: EdgeInsets.symmetric(vertical: 16),
padding: EdgeInsets.symmetric(horizontal:16),
decoration: BoxDecoration
(border: Border.all(
color: Colors.black87,
width: 1,
style: BorderStyle.solid), borderRadius: BorderRadius.circular(24)),
child: Row(
children: [
Expanded(
child: TextField(
controller: searchUsernameEditingController,
decoration: InputDecoration(
border: InputBorder.none, hintText: "Finde einen Freund"),
)),
GestureDetector(
onTap:(){
if(searchUsernameEditingController.text != ""){
onSearchBtnClick();
}
},
// Disable Search Icon
// child: Icon(Icons.search),
)
],
),
),
)
]
),
isSearching ? searchUsersList() : chatRoomsList()
],
),
),
);
}
}
class ChatRoomListTile extends StatefulWidget {
final String lastMessage, chatRoomId, myUsername;
ChatRoomListTile(this.lastMessage, this.chatRoomId, this.myUsername);
#override
_ChatRoomListTileState createState() => _ChatRoomListTileState();
}
class _ChatRoomListTileState extends State<ChatRoomListTile> {
String profilePicUrl ="", name="", username="";
getThisUserInfo() async {
username = widget.chatRoomId.replaceAll(widget.myUsername, "").replaceAll("_", "");
QuerySnapshot querySnapshot = await DatabaseMethods().getUserInfo(username);
name = "${querySnapshot.docs[0]["name"]}";
profilePicUrl = "${querySnapshot.docs[0]["imgUrl"]}";
setState(() {});
}
#override
void initState() {
getThisUserInfo();
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(vertical: 2, horizontal: 0,),
padding: EdgeInsets.symmetric(horizontal:12,vertical: 20,),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.network(
profilePicUrl,
height: 35,
width: 35,
),
),
SizedBox(width: 15),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
name,
style: TextStyle(
fontSize: 14,
fontFamily: 'Roboto',
color: Color.fromRGBO(57, 59, 85, 0.8),
fontWeight: FontWeight.w500,
),
),
SizedBox(height: 5),
Container(
width: (MediaQuery.of(context).size.width) - 150,
child: Text(
widget.lastMessage,
style: new TextStyle(
fontSize: 13.0,
fontFamily: 'Roboto',
color: Color.fromRGBO(171, 183, 201, 1),
fontWeight: FontWeight.w400,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
],
)
],
),
);
}
}
you can pass the id as a constructor from the listTile,
Navigator.of(context).push.... ChatScreen(id: chatRoomId)
as so, and in ChatScreen you can receive it like so
class ChatScreenextends StatefulWidget {
final String id;
ChatScreen({requried this.id});
#override
_ChatScreenState createState() => _ChatScreenState ();
}
class _ChatScreenState extends State<ChatScreen> {
if it is a stateless Widget class you can directly access the id as id, if it is a stateful Widget then widget.id ,
Another way of doing this is passing as Navigator argument you can check the docs for more info Flutter Docs
Hello and thank you very much for your time ... i tried to implement it but unfortunate i get an error. here the GestureDetector i added to my home.dart ::: I also tried to change id: chatRoomId to this.Id or just chatRoomId .. but it keeps underlined and erroring :
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => ChatScreen(id: chatRoomId)));
},
child: Container(
margin: EdgeInsets.symmetric(vertical: 2, horizontal: 0,),
padding: EdgeInsets.symmetric(horizontal:12,vertical: 20,),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
and here the update of the chatscreen.dart
class ChatScreen extends StatefulWidget {
final String chatWidthUsername, name, id;
ChatScreen(this.chatWidthUsername, this.name, {this.id});
#override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
String chatRoomId, messageId = "";
Stream messageStream;
String myName, myProfilePic, myUserName, myEmail;
TextEditingController messageTextEditingController = TextEditingController();
thanks again so much !
greetings
alex