Flutter FireStore: Unable to reverse a firebase snapshot list - flutter

I am developing a chat app using firebase and flutter. When i type a msg the order of the message appearing is random. I want the messages to appear by timestamp. I am really new to coding. Can someone help me out?
For ex:
I just typed-this
And the messaged just appeared at some random place-Should have appeared after last message
Here's the code:-
class MessagesStream extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
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['text'];
final messageSender = message['sender'];
final currentUser = loggedInUser.email;
final messageBubble = MessageBubble(
sender: messageSender!,
text: messageText!,
isMe: currentUser == messageSender,
);
messageBubbles.add(messageBubble);
}
return Expanded(
child: ListView(
reverse: true,
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
children: messageBubbles,
),
);
},
stream: _firestore.collection('messages').snapshots(),
);
}
}
class MessageBubble extends StatelessWidget {
const MessageBubble({
super.key,
required this.sender,
required this.text,
required this.isMe,
});
final String sender;
final String text;
final bool isMe;
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment:
isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: [
Text(
sender,
style: TextStyle(
fontSize: 12,
color: Colors.black54,
),
),
Material(
elevation: 5,
borderRadius: isMe
? BorderRadius.only(
topLeft: Radius.circular(30),
bottomLeft: Radius.circular(30),
bottomRight: Radius.circular(30),
)
: BorderRadius.only(
topRight: Radius.circular(30),
bottomLeft: Radius.circular(30),
bottomRight: Radius.circular(30),
),
color: isMe ? Colors.lightBlueAccent : Colors.white,
child: Padding(
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 20),
child: Text(
text,
style: TextStyle(
color: isMe ? Colors.white : Colors.black,
fontSize: 15,
),
),
),
),
],
),
);
}
}
I tried .reverese but didn't work!
final messages = snapshot.data!.docs.reversed;
But it didn't work. I am really new to this, Please help me on this stackoverflow gods.

You need to add a timestamp field to your messages and then use the .orderBy() method in your query to get the latest messages.
https://firebase.google.com/docs/firestore/query-data/order-limit-data
The timestamp field can either be a DateTime or an int.
DateTime
This way is a little more involved but it let's you directly use the DateTime type in your code.
Create a DateTimeConverter
class DateTimestampConverter implements JsonConverter<DateTime?, Timestamp?> {
const DateTimestampConverter();
#override
DateTime? fromJson(Timestamp? timestamp) => timestamp?.toDate();
#override
Timestamp? toJson(DateTime? dateTime) => dateTime != null ? Timestamp.fromDate(dateTime) : null;
}
Install the json_serializable package.
Annotate your message class with the #JsonSerializable annotation
part 'message.g.dart';
#JsonSerializable(
explicitToJson: true,
converters: [DateTimestampConverter()],
)
class Message {
String id;
...
factory Message.fromJson(Map<String, dynamic> json) => _$MessageFromJson(json);
Map<String, dynamic> toJson() => _$MessageToJson(this);
}
Run the build runner
flutter packages pub run build_runner build --delete-conflicting-outputs
Int
Alternatively, save the timeCreated field as an int like this:
Message newMessage = Message(
timeCreated: DateTime.now().millisecondsSinceEpoch,
)

Related

Filter search filter data showing from Api using provider

how to implement search filter with API using provider? i have post Api when user hit Api it send back data to the user
My api
Future<List<Searchmodel>>Search(String keyword) async {
final response=await http.post(Uri.parse('https://example.com/api/library/search'),body: {
'Keyword': keyword,
});
if(response.statusCode==200){
final json=response.body..toString().replaceAll("\n","");
var data=jsonDecode(json);
final Iterable fetch = data["success"];
return fetch.map((category) => Searchmodel.fromJson(category)).toList();
}else{
throw Exception("Unable to perform request!");
}
}
//model class
class Searchmodel {
Searchmodel({
required this.id,
required this.name,
required this.file,
required this.categoryId,
required this.subcategoryId,
required this.userId,
required this.status,
required this.createdAt,
required this.updatedAt,});
Searchmodel.fromJson(dynamic json) {
id = json['id'];
name = json['name'];
file = json['file'];
categoryId = json['category_id'];
subcategoryId = json['subcategory_id'];
userId = json['user_id'];
status = json['status'];
createdAt = json['created_at'];
updatedAt = json['updated_at'];
}
int? id;
String? name;
String? file;
int? categoryId;
int? subcategoryId;
String? userId;
String? status;
String? createdAt;
String? updatedAt;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['id'] = id;
map['name'] = name;
map['file'] = file;
map['category_id'] = categoryId;
map['subcategory_id'] = subcategoryId;
map['user_id'] = userId;
map['status'] = status;
map['created_at'] = createdAt;
map['updated_at'] = updatedAt;
return map;
}
}
//UI
import 'package:flutter/material.dart';
import 'package:livinghopegeetzaboor/Statemanagment/Library/LibraryProvider.dart';
import 'package:livinghopegeetzaboor/constant/AppColor.dart';
import 'package:livinghopegeetzaboor/sacreen/Geet/Geetsubcategory.dart';
import 'package:livinghopegeetzaboor/services/Services.dart';
import 'package:provider/provider.dart';
class Homepage extends StatefulWidget {
#override
State<Homepage> createState() => _HomepageState();
}
class _HomepageState extends State<Homepage> {
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Center(
child: Padding(
padding: EdgeInsets.fromLTRB(0, 0, 0, 0),
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
height: 100,
width: 100,
child: Image.asset('images/logo.png')
),
// Note: Same code is applied for the TextFormField as well
// Note: Same code is applied for the TextFormField as well
// Note: Same code is applied for the TextFormField as well
Padding(
padding: EdgeInsets.all(40),
child:Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
color: AppColor.color,
),
height: 80,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(
child: TextField(
decoration: InputDecoration(
suffixIcon: Icon(Icons.search,
size: 40,
),
enabledBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.white, width:2),
),
filled: true, //<-- SEE HERE
fillColor: Colors.white,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(20),
),
hintText: 'Search for any songs',
),
),
),
SizedBox(width: 3,),
// Container(
// height: 59,
// width: 59,
// color:Colors.white,
// constraints: const BoxConstraints(
//
// ),
// // child: Icon(
// // Icons.search_sharp,
// // size: 40,
// // color: AppColor.color,
// // ),
// )
],
),
),
),
),
Consumer<LibraryProvider>(
builder:(BuildContext context, value, Widget? child) {
if(value.isloading){
return CircularProgressIndicator(
color: AppColor.color,
);
}else{
return GridView.builder(
itemCount:value.library.length,
scrollDirection: Axis.vertical,
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 4.0,
mainAxisSpacing: 4.0
),
itemBuilder: (BuildContext context, int index){
var subtitle=value.library[index].name2;
return Padding(
padding: EdgeInsets.all(10),
child: Material(
color: Colors.white.withOpacity(0.0),
child: InkWell(
splashColor: Colors.orange,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
color: Colors.black,
),
//color: Colors.black,
height:200,
width: 200,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.all(5),
child: Center(
child: Text(value.library[index]!.name.toString(),style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18
),),
),
),
SizedBox(height: 4,),
Padding(
padding: EdgeInsets.fromLTRB(10, 0, 0, 0),
child: Center(
child: subtitle!=null ? Text(value.library[index].name2.toString(),style: TextStyle(
color: Colors.white,
fontSize: 12
),):Text('',style: TextStyle(
color: Colors.white,
fontSize: 12
),)
),
),
],
),
),
onTap: (){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Geetsubcategory(id: value.library[index].id,)),
);
},
),
),
);
},
);
}
},
)
],
),
// your main content
),
),
)
);
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_){
Provider.of<LibraryProvider>(context,listen: false).fetchlibary();
// Add Your Code here.
});
}
}
You need to add the api call from inside your provider function.
Like this
class SearchProvider with ChangeNotifier, DiagnosticableTreeMixin {
List<dynamic>? _items;
List<dynamic>? get items => _items;
void searchItems(String val) async {
_items = await ApiCall.search(val);
notifyListeners();
}
}
You can create a common object of provider on the page to use throughout your page like this.
SearchProvider _provider = SearchProvider();
and use it like this in your provider and consumer on the same page
ChangeNotifierProvider<SearchProvider>(
create: (_) => _provider,
child: Column(
children: [
... // other code
Consumer<SearchProvider>(
builder: (context, provider, w) {
if (provider.items != null && provider.items!.isNotEmpty) {
return SearchList(list: provider.items);
}
return const Center(child: Text("Nothing found"));
},
),
]),
),
Then setup one TextField to type & fire search query. You can setup onSubmitted callback of the Textfield to call your provider method.
TextField(
onSubmitted: (val) {
if(val.length > 0){
_provider.searchItems(val);
}
else {
_provider.getAll();
}
},
decoration: const InputDecoration(
labelText: 'Search',
suffixIcon: Icon(Icons.search),
),
Using this code will call the provider method when you press enter after typing some query in TextField. That provider method will then call the api & store the response in a List then call notifyListeners() from that method to notify all the consumers. Those consumer then try to read the list & create a listview if they find data in the list.
Here is an example of full working code:
https://dartpad.dev/?id=a5950f2da95d58e32dc9176433d9bcb3
I have also added the onload list which can be filtered from api itself & it user delete search query it will again call the full list api for data.
Hope this will help you understand things better using provider.

The following _CastError was thrown while handling a gesture: Null check operator used on a null value

So i made an Create methode for my Sqlapi. I made a test project where the create method worked. So now i try to implement it in to the real application but now i get some strange error (Shown in the image). Any help would be amazing.
Error:
All the containers are the same.
My flutter code:
import 'package:flutter/material.dart';
import 'package:schepp/main.dart';
import '../Data_provider/api_service.dart';
import '../Model/KlantModel.dart';
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const appTitle = 'Inloggen';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: appTitle,
home: Registerenpage(
title: appTitle
),
);
}
}
class Registerenpage extends StatefulWidget {
const Registerenpage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_RegisterenpageState createState() => _RegisterenpageState();
}
class _RegisterenpageState extends State<Registerenpage> {
_RegisterenpageState();
final ApiService api = ApiService();
final _addFormKey = GlobalKey<FormState>();
final _mailaddres = TextEditingController();
final _wachtwoord = TextEditingController();
final _klantvoornaam = TextEditingController();
final _tussenvoegsel = TextEditingController();
final _klantachternaam = TextEditingController();
final _bedrijfsnaam = TextEditingController();
final _telefoonnummer = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: SingleChildScrollView(
child: Column(
key: _addFormKey,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children:<Widget>[
Container(
margin: const EdgeInsets.only(left: 10.0, right: 10.0, top:80.0),
child: const Text("Registeren",style: TextStyle(color:Colors.white,fontSize:20),),
),
Container(),
Container(),
Container(),
Container(),
Container(
margin: const EdgeInsets.only(left: 50.0, right: 50.0),
child: Column(
children: <Widget>[
const Text('Mailaddres'),
TextFormField(
controller: _mailaddres,
decoration: const InputDecoration(
border: UnderlineInputBorder(),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white),
),
labelText: 'Mailaddress *', labelStyle: TextStyle(color:Colors.white),
),
style: const TextStyle(color: Colors.white, fontSize: 16.0,),
validator: (value) {
if (value!.isEmpty) {
return 'Please enter mailaddres';
}
return null;
},
onChanged: (value) {},
),
],
),
),
Container(),
Container(),
Container(
padding: const EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
ElevatedButton(
child: const Text('Registeren', style: TextStyle(color:Colors.white,fontSize:16)),
onPressed: () {
if (_addFormKey.currentState!.validate()) {
_addFormKey.currentState!.save();
api.createCase(Cases(mailaddres: _mailaddres.text, wachtwoord: _wachtwoord.text,
klantvoornaam: _klantvoornaam.text, tussenvoegsel: _tussenvoegsel.text,
klantachternaam: _klantachternaam.text, bedrijfsnaam: _bedrijfsnaam.text,
telefoonnummer: _telefoonnummer.text));
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Loginpagina(title: widget.title),
),
);
}
},
style: ElevatedButton.styleFrom(
primary: Colors.deepOrange,
padding: const EdgeInsets.all(20),
),
)
],
),
),
]
),
),
);
}
}
This is my KlantModel:
import 'dart:convert';
List<Cases> welcomeFromJson(String str) => List<Cases>.from(json.decode(str).map((x) => Cases.fromJson(x)));
String welcomeToJson(List<Cases> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Cases {
Cases({
this.klantId,
this.mailaddres,
this.wachtwoord,
this.klantvoornaam,
this.tussenvoegsel,
this.klantachternaam,
this.bedrijfsnaam,
this.telefoonnummer,
});
int? klantId;
String? mailaddres;
String? wachtwoord;
String? klantvoornaam;
String? tussenvoegsel;
String? klantachternaam;
String? bedrijfsnaam;
String? telefoonnummer;
factory Cases.fromJson(Map<String, dynamic> json) => Cases(
klantId: json["KlantId"],
mailaddres: json["Mailaddres"],
wachtwoord: json["Wachtwoord"],
klantvoornaam: json["Klantvoornaam"],
tussenvoegsel: json["Tussenvoegsel"],
klantachternaam: json["Klantachternaam"],
bedrijfsnaam: json["Bedrijfsnaam"],
telefoonnummer: json["Telefoonnummer"],
);
Map<String, dynamic> toJson() => {
"KlantId": klantId,
"Mailaddres": mailaddres,
"Wachtwoord": wachtwoord,
"Klantvoornaam": klantvoornaam,
"Tussenvoegsel": tussenvoegsel,
"Klantachternaam": klantachternaam,
"Bedrijfsnaam": bedrijfsnaam,
"Telefoonnummer": telefoonnummer,
};
}
--------! UPDATE --------
I get this error when i press on the ElevatedButton in the last container. i'am trying to sent the information to another dart file which updates it to a rest api. if i'm correct it gets stuck at the if (_addFormKey.currentState!.validate())
You are not using Form widget. Wrap your Column with Form and use the _addFormKey on Form widget instead of Column.
child: Form(
key: _addFormKey,
child: Column(
And validator
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter mailaddres';
}
return null;
},
And
onPressed: () {
final isValidate = _addFormKey.currentState?.validate();
if (isValidate == null) {
print("got Null isValidate");
return;
}
if (isValidate) {
_addFormKey.currentState!.save();
More about Form

How do I solve this error: LateInitializationError: Field 'weatherInfo' has not been initialized?

I'm developing a weather app using an API from OpenWeatherMap. Every time I try to run my code, the error late initialization pops up. I've tried adding the null safety check(!) in place of late but it's still the same. Any idea where I could have gone wrong? Any help would be deeply appreciated. A sample code for solving the error would help a lot.
Thanks!!
The error I'm getting in the console:
Performing hot restart... 1,492ms
Restarted application in 1,501ms.
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following LateError was thrown building:
LateInitializationError: Field 'weatherInfo' has not been initialized.
When the exception was thrown, this was the stack:
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/internal/js_dev_runtime/private/ddc_runtime/errors.dart 251:49 throw
packages/finalproject/LastProject/LastProject_Screen.dart 13:44 get weatherInfo
packages/finalproject/LastProject/LastProject_Screen.dart 10:56 mainBodyWidget
packages/finalproject/LastProject/LastProject_Screen.dart 10:56
packages/flutter/src/widgets/sliver.dart 456:22 build
packages/flutter/src/widgets/sliver.dart 1214:28 [_build]
packages/flutter/src/widgets/sliver.dart 1227:55
packages/flutter/src/widgets/framework.dart 2573:19 buildScope
packages/flutter/src/widgets/sliver.dart 1220:5 createChild
packages/flutter/src/rendering/sliver_multi_box_adaptor.dart 349:23
packages/flutter/src/scheduler/binding.dart 1080:9 handleDrawFrame
packages/flutter/src/scheduler/binding.dart 863:7
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/private/isolate_helper.dart 48:19 internalCallback
Note: This is just a part of the errors in the console because there are many.
The Code for UI:
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'LastProject_model.dart';
class LastProjectScreen extends StatefulWidget {
const LastProjectScreen({Key? key}) : super(key: key);
#override
_LastProjectScreenState createState() => _LastProjectScreenState();
}
class _LastProjectScreenState extends State<LastProjectScreen> {
//initializing model class object
late WeatherInfo weatherInfo;
TextEditingController _textEditingController = TextEditingController();
//overriding initstate to parse the data and display it on the screen
#override
void initState() {
super.initState();
}
//data parsing function to call the data from the json file
getData() async {
http.Response jsondata = await http.get(Uri.parse(
'https://api.openweathermap.org/data/2.5/forecast/daily?q=${_textEditingController.text}&units=metric&cnt=7&appid=3c044b7295d2df14f8bee74c19d4b96f'));
print(jsondata.body);
weatherInfo = WeatherInfo.fromJson(json.decode(jsondata.body));
return weatherInfo;
}
//first row of the screen
Widget uirow1(WeatherInfo weatherInfo) {
return Container(
padding: EdgeInsets.all(5),
margin: EdgeInsets.only(left: 20, right: 20, top: 20),
decoration: BoxDecoration(
// color: Colors.white,
gradient: LinearGradient(
colors: [Colors.blue.shade200, Colors.blue.shade50]),
borderRadius: BorderRadius.all(Radius.circular(20)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 5,
blurRadius: 7,
offset: Offset(0, 3),
)
]),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
'name',
// weatherInfo.city.name,
style: TextStyle(fontSize: 17, fontWeight: FontWeight.w700),
),
SizedBox(height: 5),
Text('temp °F',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.black)),
SizedBox(height: 5),
Text('description',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Colors.black))
],
),
Icon(
Icons.cloud,
color: Colors.pink[400],
size: 100,
),
],
),
);
}
//second row of the screen
Widget uirow2() {
return Container(
padding: EdgeInsets.all(5),
margin: EdgeInsets.all(20),
decoration: BoxDecoration(
// color: Colors.white,
gradient: LinearGradient(
colors: [Colors.blue.shade200, Colors.blue.shade50]),
borderRadius: BorderRadius.all(Radius.circular(20)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 5,
blurRadius: 7,
offset: Offset(0, 3),
)
]),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('windSpeed',
style: TextStyle(
fontSize: 15,
)),
// SizedBox(width: 3),
Text('humidity',
style: TextStyle(
fontSize: 15,
)),
// SizedBox(width: 5),
Text('°F',
style: TextStyle(
fontSize: 15,
)),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Icon(Icons.air_outlined, color: Colors.grey[600], size: 25),
// const SizedBox(width: 5),
Icon(Icons.whatshot_rounded, color: Colors.grey[600], size: 25),
// const SizedBox(width: 5),
Icon(Icons.air_outlined, color: Colors.grey[600], size: 25)
],
),
],
),
);
}
//main body widget of the screen
Widget mainBodyWidget(BuildContext context) {
return
// ListView(scrollDirection: Axis.vertical, children: [
Container(
padding: EdgeInsets.all(15),
decoration: const BoxDecoration(
image: DecorationImage(
image: NetworkImage(
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSZCv1LyQUyAJQE21-uNUMx3S8ZgK2U9S--1wQB59QaTFHBp2VqxGTAen5FRA7m5h-E4OU&usqp=CAU'),
fit: BoxFit.fill)),
child: Column(
children: [
searchwidget(),
const SizedBox(height: 15),
uirow1(weatherInfo),
const SizedBox(height: 15),
uirow2(),
const SizedBox(height: 15),
uirow3()
],
),
);
}
//third row of the screen
Widget uirow3() {
return Container(
padding: EdgeInsets.all(15),
margin: EdgeInsets.only(left: 20, right: 20),
height: 180,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue.shade200, Colors.blue.shade50]),
borderRadius: BorderRadius.all(Radius.circular(20)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 5,
blurRadius: 7,
offset: Offset(0, 3),
)
]),
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Container(
child: uirow3data(),
);
},
separatorBuilder: (BuildContext context, int index) {
return SizedBox(width: 20);
},
itemCount: 7
// weatherInfo.weatherListInfo.length,
));
}
//data for the third row of the screen
Widget uirow3data() {
return Column(
children: [
Center(
child: Text('Saturday',
style: TextStyle(
fontWeight: FontWeight.bold,
))),
SizedBox(height: 5),
Icon(
Icons.cloud,
color: Colors.pink[400],
size: 50,
),
SizedBox(height: 4),
Text('High: 71°F'),
SizedBox(height: 3),
Text('Low: 71°F'),
SizedBox(height: 3),
Text('Hum: 40%'),
SizedBox(height: 3),
Text('Win: 4 mi/h')
],
);
}
//search textfiled widget
Widget searchwidget() {
return Container(
margin: EdgeInsets.only(left: 20, right: 20, top: 10),
child: Center(
child: TextField(
controller: _textEditingController,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
gapPadding: 3.0,
borderSide: const BorderSide(color: Colors.grey, width: 2.0),
borderRadius: BorderRadius.circular(11)),
hintText: 'Search',
hintStyle: const TextStyle(color: Colors.grey),
prefixIcon: const Icon(
Icons.search_sharp,
color: Colors.grey,
),
suffixIcon: InkWell(
child: const Icon(Icons.navigate_next, color: Colors.grey),
onTap: () => getData(),
)),
),
),
);
}
//main container widget
Widget mainContainerWidget(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: getData(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(child: Text("Couldn't load the data"));
}
if (snapshot.hasData) {
return Text("Data is being processed");
}
if (snapshot.data == null) {
return ListView.builder(
scrollDirection: Axis.vertical,
itemCount: 1,
shrinkWrap: true,
itemBuilder: (context, index) {
return Column(
children: [mainBodyWidget(context)],
);
});
}
return CircularProgressIndicator();
}));
}
//Main build function
#override
Widget build(BuildContext context) {
return mainContainerWidget(context);
}
}
The Code for Model class:
class WeatherInfo {
late CityInfo city;
late String cod;
late double message;
late int cnt;
late List<WeatherListInfo> weatherListInfo;
WeatherInfo(
this.city, this.cod, this.message, this.cnt, this.weatherListInfo);
factory WeatherInfo.fromJson(Map<String, dynamic> json) {
return WeatherInfo(
CityInfo.fromJson(json["city"]),
json["cod"],
json["message"],
json["cnt"],
(json["list"] as List).map((e) => WeatherListInfo.fromJson(e)).toList());
}
}
class CityInfo {
late String id;
late String name;
late String country;
late String population;
late String timezone;
CityInfo(this.id, this.name, this.country, this.population, this.timezone);
factory CityInfo.fromJson(Map<String, dynamic> json) {
return CityInfo(json["id"].toString(), json["name"], json["country"],
json["population"].toString(), json["timezone"].toString());
}
}
class WeatherListInfo {
late String dt;
late String sunrise;
late String sunset;
late TemperatureListInfo temperatureListInfo;
late String pressure;
late String humidity;
late List<WeatherDetailInfo> weatherDetailInfo;
late FeelsLikeListInfo feelsLikeInfo;
late String speed;
late String deg;
late String gust;
late String clouds;
late String pop;
late String rain;
WeatherListInfo(
this.dt,
this.sunrise,
this.sunset,
this.temperatureListInfo,
this.pressure,
this.humidity,
this.weatherDetailInfo,
this.feelsLikeInfo,
this.speed,
this.deg,
this.gust,
this.clouds,
this.pop,
this.rain,
);
factory WeatherListInfo.fromJson(Map<String, dynamic> json) {
return WeatherListInfo(
json["dt"].toString(),
json["sunrise"].toString(),
json["sunset"].toString(),
TemperatureListInfo.fromJson(json["temp"]),
json["pressure"].toString(),
json["humidity"].toString(),
(json["weather"] as List).map((e) => WeatherDetailInfo.fromJson(e)).toList(),
FeelsLikeListInfo.fromJson(json["feels_like"]),
json["speed"].toString(),
json["deg"].toString(),
json["gust"].toString(),
json["clouds"].toString(),
json["pop"].toString(),
json["rain"].toString(),
);
}
}
class TemperatureListInfo {
late String day;
late String min;
late String max;
late String night;
late String eve;
late String morn;
TemperatureListInfo(
this.day, this.night, this.eve, this.morn, this.min, this.max);
factory TemperatureListInfo.fromJson(Map<String, dynamic> json) {
return TemperatureListInfo(
json["day"].toString(),
json["night"].toString(),
json["eve"].toString(),
json["morn"].toString(),
json["min"].toString(),
json["max"].toString(),
);
}
}
class FeelsLikeListInfo {
late String day;
late String night;
late String eve;
late String morn;
FeelsLikeListInfo(this.day, this.night, this.eve, this.morn);
factory FeelsLikeListInfo.fromJson(Map<String, dynamic> json) {
return FeelsLikeListInfo(
json["day"].toString(),
json["night"].toString(),
json["eve"].toString(),
json["morn"].toString(),
);
}
}
class WeatherDetailInfo {
late String id;
late String main;
late String description;
late String icon;
WeatherDetailInfo(this.id, this.main, this.description, this.icon);
factory WeatherDetailInfo.fromJson(Map<String, dynamic> json) {
return WeatherDetailInfo(
json["id"].toString(),
json["main"],
json["description"],
json["icon"],
);
}
}
The error message says it, you didn't initialize weatherInfo. When you mark a variable as late you must initialize it later, but in your case, you only initialize it when you call getData(), so ui complains because it uses a variable that is not yet initialized. You have to options here,
first: you call getData in initState, so the variable will be initialized.
or, second: you remove the late keyword and make the object nullable, but now you'll have to add null checks all over the code.

all messages time stamps keep updating to current time when a message is sent

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}',

Flutter: DateTime format

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