Related
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.
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
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.
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}',
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());