Sorry if the code isn't well formatted.
I had a look to the other related subjects but i can't get it right (apparently the best way is .toDate() ). I am a newb so, I am not sure where to do it right. The code works fine but the date in the chat is not user friendly so I wanted to format DateTime.now() to be 'yMd' way (this piece of code 'time': DateFormat('yMd').format(DateTime.now()),) . If I use the DateTime.now() works fine. And "$sender ${time.toDate()}",
enter image description here
So this is the format that I get following the tuto, but I wanted to make it more simple...
final _firestore = FirebaseFirestore.instance;
User loggedInUser;
class ChatScreen extends StatefulWidget {
static const String id = 'chat_screen';
#override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final messageTextController = TextEditingController();
final _auth = FirebaseAuth.instance;
String messageText;
#override
void initState() {
super.initState();
getCurrentUser();
}
void getCurrentUser() async {
try {
final user = _auth.currentUser;
if (user != null) {
loggedInUser = user;
print(loggedInUser.email);
}
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: null,
actions: <Widget>[
IconButton(
icon: Icon(Icons.close),
onPressed: () {
// getMessages();
// messagesStream();
_auth.signOut();
Navigator.pop(context);
}),
],
title: Text('⚡️Chat'),
backgroundColor: Colors.lightBlueAccent,
),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
MessagesStream(),
Container(
decoration: kMessageContainerDecoration,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextField(
controller: messageTextController,
onChanged: (value) {
messageText = value;
},
decoration: kMessageTextFieldDecoration,
),
),
TextButton(
onPressed: () {
messageTextController.clear();
_firestore.collection('messages').add({
'text': messageText,
'sender': loggedInUser.email,
'time': DateFormat('yMd').format(DateTime.now()),
});
},
child: Text(
'Send',
style: kSendButtonTextStyle,
),
),
],
),
),
],
),
),
);
}
}
class MessagesStream extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
//de donde vienen los mensajes (data)
stream: _firestore.collection('messages').snapshots(),
//
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.lightBlueAccent,
),
);
}
final messages = snapshot.data.docs.reversed;
List<MessageBubble> messageBubbles = [];
for (var message in messages) {
final messageText = message.data()['text'];
final messageSender = message.data()['sender'];
final messageTime = message.data()['time'];
final currentUser = loggedInUser.email;
final messageWidget = MessageBubble(
text: messageText,
sender: messageSender,
isMe: currentUser == messageSender,
time: messageTime,
);
messageBubbles.add(messageWidget);
messageBubbles.sort((a, b) => b.time.compareTo(a.time));
}
return Expanded(
child: ListView(
reverse: true,
padding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 20,
),
children: messageBubbles,
),
);
},
);
}
}
class MessageBubble extends StatelessWidget {
MessageBubble({this.sender, this.text, this.isMe, this.time});
final String sender;
final String text;
final bool isMe;
final Timestamp time; //added
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment:
isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: <Widget>[
Text(
"$sender ${time.toDate()}",
style: TextStyle(fontSize: 11.0, color: Colors.black54),
),
Material(
borderRadius: isMe
? BorderRadius.only(
bottomLeft: Radius.circular(30.0),
bottomRight: Radius.circular(30.0),
topLeft: Radius.circular(30.0),
)
: BorderRadius.only(
bottomLeft: Radius.circular(30.0),
bottomRight: Radius.circular(30.0),
topRight: Radius.circular(30.0),
),
elevation: 9.0,
color: isMe
? Colors.lightBlueAccent
: Colors.redAccent, //Colors.lightBlueAccent,
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: 20.0,
vertical: 10.0,
),
child: Text(
'$text',
style: TextStyle(
color: isMe ? Colors.white : Colors.black54,
fontWeight: FontWeight.bold,
fontSize: 15.0,
),
),
),
),
],
),
);
}
}
Timestamp is an integer.
"$sender ${DateTime.fromMillisecondsSinceEpoch(timestamp)}"
Firestore gives you timestamps with its Timestamp class, which represents an epoch timestamp (i.e. milliseconds/microseconds since the epoch, a.k.a. January 1st, 1970 at 12:00 AM UTC). You will have to convert this to a date before you can format it.
As far as the actual formatting, the most straightforward way to do this is with the intl package. There is a DateFormat class with presets for all the common formatting options, including "yMd":
import 'package:intl/intl.dart';
...
final now = Timestamp.now();
final formatted = DateFormat.yMd().format(now.toDate());
Related
I'm new to Flutter and I'm having problems with expansion panel lists. Firstly, let me explain what I'm trying to do. I have a map application that gets the user's current location when they press the "start" button and displays the distance, date and time when the journey is finished and the "finish" button is pressed. Then, this information is stored in an expansion panel list on a separate "history" page.
However, I also have a third button called "add location". This button appears when the "start" button is pressed and allows the user to add their current location while the journey is ongoing. These locations are added to a list called "tripLocations" within my code. Then, when the "finish" button is pressed, all of this information is added to the expansion panel list.
My problem is that every time a journey is finished, the "tripLocations" list is displayed in the expansion panel list and shows all of the previous locations. However, I want each row in the expansion panel list to represent a single journey and only show the locations that were added during that journey. Essentially, there should be a "tripLocations" list for each row in the expansion panel list and it should be reset every time a new journey is started.
The following codes belong to my button.dart page.
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:material_dialogs/shared/types.dart';
import 'package:material_dialogs/widgets/buttons/icon_outline_button.dart';
import 'package:yolumukaydet/constant/constant.dart';
import 'package:latlong/latlong.dart';
import 'package:material_dialogs/material_dialogs.dart';
import 'package:lottie/lottie.dart';
import 'package:material_dialogs/widgets/buttons/icon_button.dart';
import 'package:geocoding/geocoding.dart';
import 'package:geocoding_platform_interface/geocoding_platform_interface.dart';
class GradientText extends StatelessWidget {
const GradientText(
this.text, {
required this.gradient,
this.style,
});
final String text;
final TextStyle? style;
final Gradient gradient;
#override
Widget build(BuildContext context) {
return ShaderMask(
blendMode: BlendMode.srcIn,
shaderCallback: (bounds) => gradient.createShader(
Rect.fromLTWH(0, 0, bounds.width, bounds.height),
),
child: Text(text, style: style),
);
}
}// texte gradient atamak için yapıldı
class StartHistory{
String city1;
String district1;
bool isExpanded;
StartHistory({
required this.city1,
required this.district1,
this.isExpanded = false,});
}//varış yeri konumu gösterme
class StartHistoryData {
static List<StartHistory> starthistory = [];
static void addStartHistory(String city1, String district1) {
starthistory.add(StartHistory(city1: city1, district1: district1));
}
}//varış yeri konumu gösterme
class TripLocation {
String address;
TripLocation({
required this.address,
});
}
class History {
String distance;
String date;
String time;
String city;
String district;
bool isExpanded;
History({
required this.distance,
required this.date,
required this.time,
required this.city,
required this.district,
this.isExpanded = false,
});
}
class HistoryData {
static List<History> history = [];
static List<TripLocation> tripLocations = [];
static void addHistory(String distance, String date, String time, String city, String district,) {
history.add(History(distance: distance, date: date, time: time, city: city, district: district,));
}
static void addTripLocation(TripLocation tripLocation) {
tripLocations.add(tripLocation);
}
}//mesafe ,tarih ve saat bilgileri
class Location {
final double latitude;
final double longitude;
final String note;
Location({
required this.latitude,
required this.longitude,
required this.note,
});
}//Konum Ekle butonu için konum alıyor
TextEditingController _noteController = TextEditingController();
List<Map<String, String>> _locations = [];
class ImageButton extends StatefulWidget {
#override
_ImageButtonState createState() => _ImageButtonState();
}
class _ImageButtonState extends State<ImageButton>with TickerProviderStateMixin {
bool _isStarted = false;
bool _isStopped = true;
void _getCurrentLocation() async {
final position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.best);
setState(() {
if (position != null) {
_currentPosition = position;
}
});
}
late Position _startPosition;
late Position _currentPosition;
double _distance = 0.0;
String _time = "";
Geolocator geolocator = Geolocator();
int _selectedIndex = 0;
final List<String> imageList = [
'assets/images/pwb3.png',
'assets/images/pwb4.png',
];
void _onPressed1() {
setState(() {
_selectedIndex = (_selectedIndex + 1) % imageList.length;
});
// buraya butona tıklama olayında yapmanız gereken diğer işlemleri ekleyebilirsiniz
}
late AnimationController _controller;
bool isOpened = false;
#override
void initState() {
_getCurrentLocation();
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 400),
);
super.initState();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
child: Align(
child: Stack(
children: <Widget>[
Container(
margin: EdgeInsets.only(left:160,top: 540.0,right:150),
width: 350.0,
height: 350.0,
child: FloatingActionButton(
child: Image.asset(
imageList[_selectedIndex],
height: 350.0,
width: 350.0,
),
onPressed: () {
setState(() {
_onPressed1();
isOpened = !isOpened;
if (isOpened) {
_controller.forward();
} else {
_controller.reverse();
}
});
},
),
),
Visibility(
visible: _isStarted,
child: Align(
alignment: Alignment.topRight,
child: Container(
width: 75.0,
height: 85.0,
margin: EdgeInsets.only(top: 540.0,right:50),
child: ScaleTransition(
scale: _controller,
child: FloatingActionButton(
backgroundColor: Constant.green,
child: Icon(Icons.add,size: 40.0,color: Constant.darkgrey,),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
String noteText = '';
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
child: Container(
padding: EdgeInsets.all(20.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Konumunuz Eklenecek',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0,
),
),
SizedBox(height: 10.0),
Divider(
color: Colors.black,
height: 1.0,
),
SizedBox(height: 10.0),
Text(
'Eklediğiniz konum ve notlar geçmiş sayfasına kaydedilecektir.',
style: TextStyle(
fontSize: 8.0,
),
),
SizedBox(height: 10.0),
TextField(
controller: _noteController,
decoration: InputDecoration(hintText: 'Notunuzu Ekleyin'),
),
SizedBox(height: 10.0),
ElevatedButton(
onPressed: () async {
_getCurrentLocation();
//BURASI İL VE İLÇE KONUM ADLARINI ALIYOR VE CİTY İLE DİSTİRCT DEĞİŞKENİN ATIYOR
List<Placemark> placemarks = await placemarkFromCoordinates(_currentPosition.latitude, _currentPosition.longitude);
String _address = '${placemarks[0].administrativeArea}, ${placemarks[1].subAdministrativeArea}';
var tripLocation = TripLocation(address: _address);
HistoryData.addTripLocation(tripLocation);
Navigator.pop(context);
setState(() {});
print("tripLocation");
},
child: Text('Tamam'),
),
],
),
),
);
},
);
},
),
),
),
)),// KONUM EKLE BUTTONU
Visibility(
visible: _isStarted,
child: Align(
alignment: Alignment.topLeft,
child: Container(
width: 75.0,
height: 75.0,
margin: EdgeInsets.only(left:50,top: 540.0,),
child: ScaleTransition(
scale: _controller,
child: FloatingActionButton(
backgroundColor: Constant.green,
child: Icon(Icons.stop,size:40.0,color: Constant.darkgrey,),
onPressed: () async {
setState(() {
_isStopped = true;
_isStarted = false;
});
//BURASI İL VE İLÇE KONUM ADLARINI ALIYOR VE CİTY İLE DİSTİRCT DEĞİŞKENİN ATIYOR
List<Placemark> placemarks = await placemarkFromCoordinates(_currentPosition.latitude, _currentPosition.longitude);
String? city = placemarks.isNotEmpty ? placemarks[0].administrativeArea: '';
String? district = placemarks.isNotEmpty ? placemarks[1].subAdministrativeArea : '';
//
//
_currentPosition = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.best);
final Distance distance = new Distance();
var km = distance.as(
LengthUnit.Kilometer,
new LatLng(_startPosition.latitude, _startPosition.longitude),
new LatLng(_currentPosition.latitude,_currentPosition.longitude));
setState(() {
_time = DateTime.now().toString().substring(0, 19);
});
setState(() {
_distance = km as double;
});
showDialog(
context: context,
builder: (context) {
return AlertDialog(
backgroundColor: Constant.darkgrey,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50.0),
),
title: Container(
alignment: Alignment.center,
child: Text('Mesafe Bilgisi',
style: TextStyle(
color: Colors.white
),)),
content: Container(
height: 400,
width: 400,
decoration: BoxDecoration(
color: Constant.darkgrey,
borderRadius: BorderRadius.circular(50.0),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
GradientText(
'$_distance km',
style: const TextStyle(fontSize: 80,
fontFamily: 'digital',
),
gradient: LinearGradient(colors: [
Colors.white54,
Colors.white70,
]),
),
Text("Tarih ${DateTime.now().toString().split(' ').first} / SAAT ${DateTime.now().toString().split(' ').last.substring(0, 8)}",
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.white
),
),
Text(
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.white
),"$city / $district"),
Container(
width:350,
height: 45,
child: FloatingActionButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(5),
topRight: Radius.circular(5),
bottomLeft: Radius.circular(5),
bottomRight: Radius.circular(5),
),
),
backgroundColor: Constant.green,
child: Text("TAMAM",
style: TextStyle(
color: Constant.darkgrey,
),),
onPressed: () {
HistoryData.addHistory("$_distance km", "${DateTime.now().toString().split(' ').first}", "${DateTime.now().toString().split(' ').last.substring(0, 8)}","$city","$district",);
Navigator.of(context).pop();
},
),
),
],
),
),
);
},
);
},
),
),
),
),
),// STOP BUTTONU
Visibility(
visible: _isStopped,
child: Align(
alignment: Alignment.topCenter,
child: Container(
width: 75.0,
height: 75.0,
margin: EdgeInsets.only(top: 440.0),
child: ScaleTransition(
scale: _controller,
child: FloatingActionButton(
backgroundColor: Constant.green,
child: Icon(Icons.start,size:40.0,color: Constant.darkgrey,),
onPressed: () async {
List<Placemark> placemarks = await placemarkFromCoordinates(_currentPosition.latitude, _currentPosition.longitude);
String? city1 = placemarks.isNotEmpty ? placemarks[0].administrativeArea: '';
String? district1 = placemarks.isNotEmpty ? placemarks[1].subAdministrativeArea : '';
StartHistoryData.addStartHistory("$city1","$district1");
setState(() {
_isStarted = true;
_isStopped = false;
});
_startPosition = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.best);
},
),
),
),
),) // START BUTTONU
],
),
),
);
}
}
These codes belong to my historypage.dart file
import 'dart:ffi';
import 'package:flutter/material.dart';
import 'package:yolumukaydet/appbar.dart';
import 'package:yolumukaydet/constant/constant.dart';
import 'package:yolumukaydet/menu.dart';
import 'package:yolumukaydet/navigator_bar.dart';
import 'package:flutter/material.dart';
import 'package:yolumukaydet/button.dart';
class GecmisPage extends StatefulWidget {
#override
_GecmisPageState createState() => _GecmisPageState();
}
class _GecmisPageState extends State<GecmisPage> {
List<Map<String, dynamic>> _locations = [];
void _removeItem(String date) {
int indexToRemove = HistoryData.history.indexWhere((element) =>
element.date == date);
setState(() {
HistoryData.history.removeAt(indexToRemove);
});
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
duration: const Duration(milliseconds: 600),
content: Text('#$date tarihli bilginiz silindi.')));
} // delete tuşu
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Constant.darkgrey,
appBar: AppBarPage(),
body: Column(
children: [
ExpansionPanelList(
expansionCallback: (int index, bool isExpanded) {
setState(() {
HistoryData.history[index].isExpanded = !isExpanded;
});
},
children: List.generate(
HistoryData.history.length,
(index) {
return ExpansionPanel(
backgroundColor: Constant.opacgrey,
headerBuilder: (BuildContext context, bool isExpanded) {
return ListTile(
title: Text("Tarih: " + HistoryData.history[index].date + " - " + HistoryData.history[index].city + "/" + HistoryData.history[index].district),
leading: IconButton(
onPressed: () => _removeItem(HistoryData.history[index].date),
icon: const Icon(
Icons.delete,
color: Constant.green,
),
),
);
},
body: Container(
height: 500,
width: double.infinity,
color: Constant.opacgrey,
child: Container(
height: 250,
width: double.infinity,
decoration: BoxDecoration(),//boş bırakıldı
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Mesafe :", style: TextStyle(fontSize: 18.0)),
Text("${HistoryData.history[index].distance}",
style: TextStyle(fontSize: 18.0)),
Text(" / ", style: TextStyle(fontSize: 18.0)),
Text("Saat :", style: TextStyle(fontSize: 18.0)),
Text("${HistoryData.history[index].time}",
style: TextStyle(fontSize: 18.0)),
],
),
),
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Varış Yeri :", style: TextStyle(fontSize: 18.0)),
Text("${StartHistoryData.starthistory[index].city1}",
style: TextStyle(fontSize: 18.0)),
Text(" / ", style: TextStyle(fontSize: 18.0)),
Text("${StartHistoryData.starthistory[index].district1}",
style: TextStyle(fontSize: 18.0)),
],
),
),
Container(
height: 400,
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(color: Colors.black,width: 2)
),
child:
ListView.builder(
itemCount: HistoryData.tripLocations.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(HistoryData.tripLocations[index].address),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
setState(() {
HistoryData.tripLocations.removeAt(index);
});
},
),
);
},
)
),
],
),
),
),
),
isExpanded: HistoryData.history[index].isExpanded,
);
},
),
),
],
),
);
}
}
Where should I make the corrections here? I'm waiting for your help, thank you.
I'm building a chat app with Firebase in flutter and I want to be able to search from a list of users,. I also want that when no text is typed, no user should be shown
I tried a lot of things but I never got it right. This is what I did :
search_page.dart:
class SearchPage extends StatefulWidget {
const SearchPage({Key? key}) : super(key: key);
#override
State<SearchPage> createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
TextEditingController searchController = TextEditingController();
#override
void initState() {
super.initState();
searchController.addListener(_onSearchChanged);
}
_onSearchChanged() {
print(searchController.text);
}
#override
void dispose() {
searchController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return StreamProvider<List<AppUserData>>.value(
initialData: [],
value: DatabaseService().users,
child: Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: dDarkGrey,
body:
Column(crossAxisAlignment: CrossAxisAlignment.center, children: [
const SizedBox(
height: 3,
),
Stack(
alignment: Alignment.center,
children: [
Container(
height: 90,
decoration: BoxDecoration(color: dDarkGrey, boxShadow: [
BoxShadow(
color: dBlack.withOpacity(0.16),
spreadRadius: 3,
blurRadius: 3,
offset: const Offset(0, 4))
]),
),
Column(
children: [
const SizedBox(
height: 20,
),
Row(
children: [
IconButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => const HomePage()));
},
icon: const Icon(SocketIconv2.ic_back),
color: dLightGrey,
),
SizedBox(
width: MediaQuery.of(context).size.width - 60,
child: TextField(
enabled: true,
controller: searchController,
style: const TextStyle(color: dLightGrey),
decoration: const InputDecoration(
contentPadding:
EdgeInsets.fromLTRB(0, 0, 0, 0),
filled: true,
fillColor: dDarkGrey,
prefixIcon: Icon(
SocketIconv2.ic_search,
color: dLightGrey,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(15))),
hintStyle: TextStyle(
color: dLightGrey,
fontFamily: 'SegoeUI',
fontSize: 18,
fontWeight: FontWeight.w300),
hintText: 'Search',
))),
],
),
],
),
],
),
// search bar
SizedBox(
height: MediaQuery.of(context).size.height - 95,
width: MediaQuery.of(context).size.width,
child: SearchList(
controller: searchController,
),
)
])),
);
}
}
search_list.dart:
class SearchList extends StatelessWidget {
SearchList({Key? key, required this.controller}) : super(key: key);
final TextEditingController controller;
#override
Widget build(BuildContext context) {
final users = Provider.of<List<AppUserData>>(context);
return Scaffold(
backgroundColor: Colors.transparent,
body: ListView.separated(
itemBuilder: (context, index) {
if (controller.text.isEmpty) {
return Container();
}
if (controller.text.isNotEmpty) {
return searchAccount(context, name, username, users[index]);
}
if (users[index].name.startsWith(controller.text.toLowerCase())) {
return searchAccount(context, name, username, users[index]);
} else {
return Container();
}
},
itemCount: users.length,
separatorBuilder: (context, index) => const SizedBox(height: 20),
));
}
}
search_account.dart:
Widget searchAccount(
BuildContext context, String name, String username, AppUserData users) {
return Row(
children: [
const SizedBox(
width: 20,
),
Row(
children: [
ClipOval(
child: Image.asset(
imagePp,
scale: 9,
),
),
const SizedBox(
width: 30,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(users.name,
style: SegoeUiPreset(dLightGrey).customTileName()),
Text(users.username,
style: SegoeUiPreset(dLightGrey).customTileSubtitle())
],
)
],
),
],
);
}
user.dart:
class AppUser {
final String? uid;
AppUser({this.uid});
}
class AppUserData {
final String? uid;
final String name;
final String username;
AppUserData({this.uid, required this.name, required this.username});
}
database.dart:
class DatabaseService {
final String? uid;
DatabaseService({this.uid});
final CollectionReference chatRoomCollection =
FirebaseFirestore.instance.collection("chatrooms");
final CollectionReference userCollection =
FirebaseFirestore.instance.collection("users");
Future<void> saveUser(String name, String username) async {
return await userCollection
.doc(uid)
.set({'name': name, 'username': username});
}
AppUserData _userFromSnapshot(DocumentSnapshot snapshot) {
return AppUserData(
name: snapshot['name'],
uid: snapshot.id,
username: snapshot['username']);
}
Stream<AppUserData> get user {
return userCollection.doc(uid).snapshots().map(_userFromSnapshot);
}
List<AppUserData> _userListFromSnapshot(QuerySnapshot snapshot) {
return snapshot.docs.map((doc) {
return _userFromSnapshot(doc);
}).toList();
}
Stream<List<AppUserData>> get users {
return userCollection.snapshots().map(_userListFromSnapshot);
}
}
Got any hints ? I'm a begginner :/
Thank you in advance :)
I am new to flutter, I have built a quizz app that takes 5 questions randomly from a pool of questions and presents them to the user one after the other, then displays the total score at the end (on a different) screen with the option of retaking the quiz (with another set of randomly picked questions).
My issue I am facing is that when I choose to retake the quiz, if in the pool of questions presented there is a question from the past quiz, it still has its options highlighted (marked either wrong or correct as per the previous selection).
Can someone help me on how to totally dismiss previous choices after taking a quiz ?
This is an example of question answered in the previous quiz, and it came back with the option already highlighted (my previous answer).
[enter image description here][1]
[1]: https://i.stack.imgur.com/U1YFf.png[enter image description here][1]
Here is my code:
import 'package:flutter/material.dart';
import 'package:percent_indicator/percent_indicator.dart';
import 'package:schoolest_app/widgets/quizz/quizz_image_container.dart';
import '../../models/quizz.dart';
import '../../widgets/quizz/options_widget.dart';
import '../../widgets/quizz/quizz_border_container.dart';
import '../../widgets/quizz/result_page.dart';
class QuizzDisplayScreen extends StatefulWidget {
const QuizzDisplayScreen({
Key? key,
}) : super(key: key);
static const routeName = '/quizz-display';
#override
State<QuizzDisplayScreen> createState() => _QuizzDisplayScreenState();
}
class _QuizzDisplayScreenState extends State<QuizzDisplayScreen> {
enter code here
late String quizzCategoryTitle;
late List<Question> categoryQuestions;
late List<Question> quizCategoryQuestions;
var _loadedInitData = false;
#override
void didChangeDependencies() {
if (!_loadedInitData) {
final routeArgs =
ModalRoute.of(context)!.settings.arguments as Map<String, String>;
quizzCategoryTitle = (routeArgs['title']).toString();
// final categoryId = routeArgs['id'];
categoryQuestions = questions.where((question) {
return question.categories.contains(quizzCategoryTitle);
}).toList();
quizCategoryQuestions =
(categoryQuestions.toSet().toList()..shuffle()).take(5).toList();
_loadedInitData = true;
}
super.didChangeDependencies();
}
late PageController _controller;
int _questionNumber = 1;
int _score = 0;
int _totalQuestions = 0;
bool _isLocked = false;
void _resetQuiz() {
for (var element in quizCategoryQuestions) {
setState(()=> element.isLocked == false);
}
}
#override
void initState() {
super.initState();
_controller = PageController(initialPage: 0);
}
#override
void dispose() {
_controller.dispose();
_resetQuiz();
super.dispose();
}
#override
Widget build(BuildContext context) {
final myPrimaryColor = Theme.of(context).colorScheme.primary;
final mySecondaryColor = Theme.of(context).colorScheme.secondary;
double answeredPercentage =
(_questionNumber / quizCategoryQuestions.length);
return quizCategoryQuestions.isEmpty
? Scaffold(
appBar: AppBar(
title: Text(
'Quizz - $quizzCategoryTitle',
style: TextStyle(color: myPrimaryColor),
),
iconTheme: IconThemeData(
color: myPrimaryColor,
),
centerTitle: true,
backgroundColor: Colors.transparent,
elevation: 0,
flexibleSpace: Container(
decoration: BoxDecoration(`enter code here`
borderRadius: const BorderRadius.only(`enter code here`
bottomLeft: Radius.circular(15),
bottomRight: Radius.circular(15),
),
color: mySecondaryColor,
border: Border.all(color: myPrimaryColor, width: 1.0),
),
),
),
body: const Center(
child: Text('Cette catégorie est vide pour l\'instant'),
))
: Scaffold(
appBar: AppBar(
title: Text(
'Quizz - $quizzCategoryTitle',
style: TextStyle(color: myPrimaryColor),
),
iconTheme: IconThemeData(
color: myPrimaryColor,
),
centerTitle: true,
backgroundColor: Colors.transparent,
elevation: 0,
flexibleSpace: Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(15),
bottomRight: Radius.circular(15),
),
color: mySecondaryColor,
border: Border.all(color: myPrimaryColor, width: 1.0),
),
),
),
body: Container(
// height: 600,
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
children: [
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(
'Question $_questionNumber/${quizCategoryQuestions.length}',
style: const TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
),
CircularPercentIndicator(
radius: 40,
// animation: true,
// animationDuration: 2000,
percent: answeredPercentage,
progressColor: myPrimaryColor,
backgroundColor: Colors.cyan.shade100,
circularStrokeCap: CircularStrokeCap.round,
center: Text(
// ignore: unnecessary_brace_in_string_interps
'${(answeredPercentage * 100).round()} %',
style: const TextStyle(
fontSize: 10, fontWeight: FontWeight.bold),
),
// lineWidth: 10,
)
],
),
const SizedBox(height: 10),
Divider(
thickness: 1,
color: myPrimaryColor,
),
Expanded(
child: PageView.builder(
itemCount: quizCategoryQuestions.length,
controller: _controller,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
final _question = quizCategoryQuestions[index];
return buildQuestion(_question);
},
),
),
_isLocked
? buildElevatedButton(context)
: const SizedBox.shrink(),
const SizedBox(height: 10),
],
),
),
);
}
Column buildQuestion(Question question) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 10),
question.text!.isNotEmpty
? QuizzBorderContainer(
childWidget: Text(
question.text!,
style: const TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
),
)
: const SizedBox.shrink(),
question.imagePath!.isNotEmpty
? QuizzImageContainer(imagePath: question.imagePath!)
: const SizedBox.shrink(),
Expanded(
child: OptionsWidget(
question: question,
onClickedOption: (option) {
if (question.isLocked) {
return;
} else {
setState(() {
question.isLocked = true;
question.selectedOption = option;
});
_isLocked = question.isLocked;
if (question.selectedOption!.isCorrect) {
_score++;
}
}
},
),
),
],
);
}
ElevatedButton buildElevatedButton(BuildContext context) {
final mySecondaryColor = Theme.of(context).colorScheme.secondary;
return ElevatedButton(
onPressed: () {
if (_questionNumber < quizCategoryQuestions.length) {
_controller.nextPage(
duration: const Duration(milliseconds: 1000),
curve: Curves.easeInExpo,
);
setState(() {
_questionNumber++;
_isLocked = false;
});
} else {
setState(() {
// _isLocked = false;
_totalQuestions = quizCategoryQuestions.length;
});
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) =>
ResultPage(score: _score, totalQuestions: _totalQuestions),
),
);
}
},
child: Text(
_questionNumber < quizCategoryQuestions.length
? 'Suivant'
: 'Voir le résultat',
style: TextStyle(
color: mySecondaryColor,
fontWeight: FontWeight.bold,
),
),
);
}
}
I don't seem to the solution to this.
And this is the code on the result page:
import 'package:flutter/material.dart';
import '../../screens/quizz/quizz_screen.dart';
class ResultPage extends StatefulWidget {
final int score;
final int totalQuestions;
const ResultPage({
Key? key,
required this.score,
required this.totalQuestions,
}) : super(key: key);
#override
State<ResultPage> createState() => _ResultPageState();
}
class _ResultPageState extends State<ResultPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SizedBox(
height: 150,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
'You got ${widget.score}/${widget.totalQuestions}',
style: const TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const QuizzScreen(),
),
);
},
child: const Text('OK'),
),
],
),
),
),
);
}
}
I don't know what is missing to get the reset right.
When you want to take retest try to dispose all the answers which are saved in the memory. Or you can use these navigators to which might help you in solving the issue. Try using pushReplacement or pushAndRemoveUntil when navigating to retest, this will clear the memory of last pages and you will achive the goal which you want.
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class ChatScreen extends StatefulWidget {
static const String screenRoute = 'chat_screen';
const ChatScreen({Key? key}) : super(key: key);
#override
State<ChatScreen> createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final _firestore = FirebaseFirestore.instance;
final _auth = FirebaseAuth.instance;
late User signedInUser;
String? messageText;
#override
void initState() {
super.initState();
getCurrentUser();
}
void getCurrentUser() {
try {
final user = _auth.currentUser;
if (user != null) {
signedInUser = user;
print(signedInUser.email);
}
} catch (e) {
print(e);
}
}
//void getMessages() async {
// final messages = await _firestore.collection('messages').get();
//for (var message in messages.docs) {
// print(message.data());
// }
//}
void messagesStream() async {
await for (var snapshot in _firestore.collection('messages').snapshots()) {
for (var message in snapshot.docs) {
print(message.data());
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.yellow[900],
title: Row(
children: [
Image.asset('assets/images/logo.png', height: 25),
const SizedBox(width: 10),
const Text('ChatMe'),
],
),
actions: [
IconButton(
onPressed: () {
messagesStream();
//_auth.signOut();
//Navigator.pop(context);
},
icon: const Icon(Icons.download),
),
],
),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
StreamBuilder<QuerySnapshot>(
stream: _firestore.collection('messages').snapshots(),
builder: (context, snapshot) {
List<Text> messageWidgets = [];
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(
backgroundColor: Colors.blue,
),
);
}
final messages = snapshot.data!.docs;
for (var message in messages) {
final messageText = message.get('text');
final messageSender = message.get('sender');
final messageWidget = Text('$messageText - $messageSender');
messageWidgets.add(messageWidget);
}
return ListView(
children: messageWidgets,
);
},
),
Container(
decoration: const BoxDecoration(
border: Border(
top: BorderSide(
color: Colors.orange,
width: 2,
),
),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: TextField(
onChanged: (value) {
messageText = value;
},
decoration: const InputDecoration(
contentPadding: EdgeInsets.symmetric(
vertical: 10,
horizontal: 20,
),
hintText: 'Write Your Message Here..',
border: InputBorder.none,
),
),
),
TextButton(
onPressed: () {
_firestore.collection('messages').add({
'text': messageText,
'sender': signedInUser.email,
});
},
child: Text(
'send',
style: TextStyle(
color: Colors.blue[800],
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
),
],
),
),
],
),
),
);
}
}
════════ Exception caught by widgets library ═══════════════════════════════════
Bad state: field does not exist within the DocumentSnapshotPlatform
The relevant error-causing widget was
StreamBuilder<QuerySnapshot<Object?>>
lib\…\screens\chat_screen.dart:82
════════════════════════════════════════════════════════════════════════════════
below is my code in flutter, when I send a message all the timestamps for every message update to the current time, how do I ensure the times don't change on any old messages?
I have pulled the timestamp out correctly just missing what I am doing wrong to save the individual time stamp. I am not using a firebase timestamp just using what dart gives me for DateTime
import 'dart:ffi';
import 'package:bardsf/components/card_data.dart';
import 'package:bardsf/screens/admin/admin_chat.dart';
import 'package:bardsf/screens/main_screens/qr_screen.dart';
import 'package:bardsf/screens/workouts/workout_selector.dart';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:bardsf/components/reusable_cards.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import '../chat/chat_selector_screen.dart';
import 'package:bardsf/screens/chat/chat_screen.dart';
import 'package:intl/intl.dart';
late String messageText;
class HomePageScreen extends StatefulWidget {
static const String id = 'home_page_screen';
#override
_HomePageScreenState createState() => _HomePageScreenState();
}
class _HomePageScreenState extends State<HomePageScreen> {
final messageTextController = TextEditingController();
final _firestore = FirebaseFirestore.instance;
final _auth = FirebaseAuth.instance;
late User loggedInUser;
static const TextStyle optionStyle = TextStyle(
fontSize: 30, fontWeight: FontWeight.bold);
#override
void initState() {
super.initState();
getCurrentUser();
}
void getCurrentUser() async {
try {
final user = await _auth.currentUser;
if (user != null) {
loggedInUser = user;
print(loggedInUser.email);
}
} catch (e) {
print (e);
}
}
void messagesStream() async {
await for (var snapshot in _firestore.collection('messages').orderBy(
'timestamp').snapshots()) {
for (var message in snapshot.docs) {
print(message.data().cast());
}
}
}
#override
Widget build(BuildContext context) {
return Container(
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("images/bar1.jpg"), fit: BoxFit.fill,
)
),
child: Scaffold(
backgroundColor: Colors.white.withOpacity(0.5),
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ReusableCard(
colour: Colors.white,
cardChild: CardData(
label: 'The Bar',
labeltwo: 'Member Access 24/7', icon: IconData(10),
), onPress: () {},
),
StreamBuilder<QuerySnapshot>(
stream: _firestore.collection('messages').snapshots(),
builder: (context, snapshot) {
List<MessageBubble> messageBubbles = [];
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.lightBlueAccent,
),
);
}
final messages = snapshot.data!.docs;
for (var message in messages) {
final messageText = message['text'];
final messageSender = message['sender'];
final currentUser = loggedInUser.email;
final messageBubble = MessageBubble(
sender: 'The Bar Gym',
text: messageText,
isMe: currentUser == messageSender,
daterTimer: DateTime.now().millisecondsSinceEpoch,
);
messageBubbles.add(messageBubble);
}
return Expanded(
child: Padding(
padding: const EdgeInsets.all(40.0),
child: Container(
color: Colors.white.withOpacity(0.6),
child: ListView(
reverse: false,
padding: EdgeInsets.symmetric(
horizontal: 10, vertical: 10),
children: messageBubbles,
),
),
),
);
},
),
]
),
),
),
),
);
}
}
class MessageBubble extends StatelessWidget {
MessageBubble({required this.sender,required this.text,required this.isMe, required this.daterTimer });
final String sender;
final String text;
final bool isMe;
final int daterTimer;
String readTimestamp(int timestamp) {
var now = new DateTime.now();
var format = new DateFormat('M/d' ' ' 'K:m'' ''a');
var date = DateTime.fromMillisecondsSinceEpoch(timestamp);
var diff = date.difference(now);
var time = '';
if (diff.inSeconds <= 0 || diff.inSeconds > 0 && diff.inMinutes == 0 || diff.inMinutes > 0 && diff.inHours == 0 || diff.inHours > 0 && diff.inDays == 0) {
time = format.format(date);
} else {
if (diff.inDays == 1) {
time = (diff.inDays/360).toString() + 'DAY AGO';
} else {
time = (diff.inDays/360).toString() + 'DAYS AGO';
}
}
return time;
}
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: isMe ? CrossAxisAlignment.stretch : CrossAxisAlignment.start,
children: <Widget>[
Row(
children: [
Expanded(
child: Text(sender,
style: TextStyle(
fontSize: 12.0,
color: Colors.red,
),
),
),
Expanded(child: Text('${readTimestamp(daterTimer)}',
style: TextStyle(color: Colors.black)
),
),
],
),
Material(
// borderRadius: isMe ? BorderRadius.only(topLeft: Radius.circular(30.0),
// bottomLeft: Radius.circular(30.0),
// bottomRight: Radius.circular(30.0),
// topRight: Radius.circular(30.0)
// ) : BorderRadius.only(topRight: Radius.circular(30.0),
// bottomLeft: Radius.circular(30.0),
// bottomRight: Radius.circular(30.0),
// topLeft: Radius.circular(30.0),
// ),
elevation: 5.0,
color: isMe ? Colors.transparent : Colors.white,
child: Padding(
padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 10.0),
child: Text('$text',
style: TextStyle( fontSize: 15.0,
fontWeight: FontWeight.bold,
color: isMe ? Colors.white : Colors.black,),
),
),
),
],
),
);
}
}
Your error is in this line:
daterTimer: DateTime.now().millisecondsSinceEpoch,
It seems you are overriding the value you are reading from Firestore. It should be something like this:
daterTimer: message['timestamp'];,
adjust it to your specific case
Let me know if this does not help.
Edit 1:
Basically, you are not reading the timestamp from Firestore, you are just reading the time now.
Try this:
daterTimer: (json['timestamp'] == null) ? null : (json['timestamp'] as Timestamp).toDate(),
If it does not work, you need to show me what type is 'timestamp' field in Firestore as well as the code you are using to write to Firestore.
Edit 2:
I had a typo. Try this:
daterTimer: (message['timestamp'] == null) ? null : (message['timestamp'] as Timestamp).toDate(),
I had to change these lines and it works
daterTimer: (message['timestamp'] == null) ? null : (message['timestamp'] as Timestamp).toDate(),
final DateTime? daterTimer;
Expanded(child: Text('${daterTimer}',