How to show list of recent chats on chat screen? - flutter

I am currently working on Flutter application and creating a built in chat app for the application. I am not using shared preferences. I want to show all the recents chats on chat screens.
Like what's app, messenger when someone send you the message it appears in a ListTile.
Does anyone have related code or can tell me how to do it?
import 'package:bpe_application/chat/chatroom.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
class Conversation extends StatefulWidget {
#override
_ConversationState createState() => _ConversationState();
}
class _ConversationState extends State<Conversation> with WidgetsBindingObserver {
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
#override
void initState() {
// TODO: implement initState
super.initState();
WidgetsBinding.instance.addObserver(this);
setStatus("Online");
}
void setStatus(String status) async{
await _firestore.collection('registration').doc(_auth.currentUser!.uid).update({
"status": status,
});
}
#override
void didChangeAppLifecycleState(AppLifecycleState state){
if (state == AppLifecycleState.resumed){
//online
setStatus("Online");
}else{
//offline
setStatus("Offline");
}
}
final FirebaseAuth _auth = FirebaseAuth.instance;
Map<String, dynamic>? userMap;
bool isLoading= false;
final TextEditingController _search = TextEditingController();
ScrollController scrollController = ScrollController();
String chatroomId(String user1, String user2){
if(user1[0].toLowerCase().codeUnits[0] > user2.toLowerCase().codeUnits[0]){
return "$user1$user2";
}else{
return "$user2$user1";
}
}
void onSearch() async {
FirebaseFirestore _firestore = FirebaseFirestore.instance;
setState(() {
isLoading = true;
});
await _firestore.collection("registration").
where("email", isEqualTo: _search.text).
get().then((value) {
setState(() {
userMap= value.docs[0].data();
isLoading=false;
});
print(userMap);
}
);
}
void listView() async {
FirebaseFirestore _firestore = FirebaseFirestore.instance;
setState(() {
isLoading = true;
});
await _firestore.collection("registration").
where("email", isEqualTo: _search.text).
get().then((value) {
setState(() {
userMap= value.docs[0].data();
isLoading=false;
});
print(userMap);
}
);
}
#override
Widget build(BuildContext context) {
final size= MediaQuery.of(context).size;
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: Colors.transparent, //top bar color
systemNavigationBarColor: Colors.black, //bottom bar color
systemNavigationBarIconBrightness: Brightness.dark,
));
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
elevation: 0.0,
backgroundColor: Colors.transparent,
title: Padding(
padding: const EdgeInsets.fromLTRB(70, 0, 0, 0),
child: Text(
"Chat",
style: GoogleFonts.limelight(color: Colors.white),
),
),
),
body: isLoading? Center(
child: Container(
height: size.height/20,
width: size.width/20,
child: CircularProgressIndicator(),
),
)
: ClipRRect(
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(40.0),
topRight: const Radius.circular(40.0),
),
child: Container(
height: 800.0,
width: double.infinity,
color: Colors.grey.shade200,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(mainAxisSize: MainAxisSize.min, children: [
SizedBox(
height: 10,
),
Container(
height: size.height/14,
width: size.width,
alignment: Alignment.center,
child: Container(
height: size.height/14,
width: size.width/1.2,
child: TextField(
controller: _search,
decoration: InputDecoration(
hintText: "Search",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
)
),
),
),
),
SizedBox(
height: size.height/70,
),
ElevatedButton(
onPressed: onSearch,
child: Text(
"Search",style: GoogleFonts.roboto(
color: Colors.white,
),
),
),
SizedBox(
height: size.height/60,
),
userMap != null
? ListTile(
onTap: () {
String roomId = chatroomId(
_auth.currentUser!.displayName!,
userMap!['name']);
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => chatroom(
chatRoomId: roomId,
userMap: userMap!,
),
),
);
},
leading: Icon(
Icons.account_box,
color: Colors.black,
),
title: Text(
userMap!['name'],
style: GoogleFonts.roboto(
color: Colors.black,
fontSize: 17,
fontWeight: FontWeight.w500,
),
),
subtitle: Text(userMap!['email']),
trailing: Icon(
Icons.chat,
color: Colors.black,
),
)
: Container(),
],
),
),
),
),
);
}
}
Here is my home/ chat screen code
Here is my chatroom code
import 'dart:io';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/services.dart';
import 'package:flutter_chat_ui/flutter_chat_ui.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path/path.dart';
import 'package:uuid/uuid.dart';
class chatroom extends StatelessWidget {
final Map<String, dynamic> userMap;
final String chatRoomId;
chatroom({required this.chatRoomId, required this.userMap});
final TextEditingController _message = TextEditingController();
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
final FirebaseAuth _auth = FirebaseAuth.instance;
File? imageFile;
Future getImage() async {
ImagePicker _picker = ImagePicker();
await _picker.pickImage(source: ImageSource.gallery).then((xFile) {
if (xFile != null) {
imageFile = File(xFile.path);
uploadImage();
}
});
}
Future uploadImage() async {
String fileName = Uuid().v1();
int status = 1;
await _firestore
.collection("chatroom")
.doc(chatRoomId)
.collection('chats')
.doc(fileName)
.set({
"sendBy": _auth.currentUser!.displayName,
"message":"",
"type":"img",
"time": FieldValue.serverTimestamp(),
});
var ref =
FirebaseStorage.instance.ref().child('images').child("$fileName.jpg");
var uploadTask = await ref.putFile(imageFile!).catchError((error) async{
await _firestore
.collection("chatroom")
.doc(chatRoomId)
.collection('chats')
.doc(fileName).delete();
status = 0 ;
});
if(status==1){
String imageUrl = await uploadTask.ref.getDownloadURL();
await _firestore
.collection("chatroom")
.doc(chatRoomId)
.collection('chats')
.doc(fileName).update({
"message": imageUrl,
});
print(imageUrl);
}
}
void onSendMessage() async {
if (_message.text.isNotEmpty) {
Map<String, dynamic> messages = {
"sendBy": _auth.currentUser!.displayName,
"message": _message.text,
"type": "text",
"time": FieldValue.serverTimestamp(),
};
await _firestore
.collection('chatroom')
.doc(chatRoomId)
.collection('chats')
.add(messages);
_message.clear();
} else {
print("Enter Some Text");
}
}
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return Scaffold(
appBar: AppBar(
elevation: 0.0,
backgroundColor: Colors.black,
title: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
child: StreamBuilder<DocumentSnapshot>(
stream: _firestore
.collection("registration")
.doc(userMap['uid'])
.snapshots(),
builder: (context, snapshot) {
if (snapshot.data != null) {
return Container(
child: Column(
children: [
Text(
userMap['name'],
style: GoogleFonts.roboto(
color: Colors.white,
fontSize: 18,
),
),
Padding(
padding: const EdgeInsets.only(right: 100),
child: Text(
snapshot.data!['status'],
style: GoogleFonts.roboto(
color: Colors.white,
fontSize: 14,
),
),
),
],
),
);
} else {
return Container();
}
},
),
),
),
body: SingleChildScrollView(
child: Column(
children: [
Container(
height: size.height / 1.25,
width: size.width,
child: StreamBuilder<QuerySnapshot>(
stream: _firestore
.collection('chatroom')
.doc(chatRoomId)
.collection('chats')
.orderBy("time", descending: false)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.data != null) {
return ListView.builder(
itemCount: snapshot.data?.docs.length,
itemBuilder: (context, index) {
Map<String, dynamic> map = snapshot.data!.docs[index]
.data() as Map<String, dynamic>;
return messages(size, map,context);
},
);
} else {
return Container();
}
},
),
),
],
),
),
bottomNavigationBar: Container(
color: Colors.white,
height: size.height / 10,
width: size.width,
alignment: Alignment.center,
child: Container(
height: size.height / 12,
width: size.width / 1.1,
child: Row(
children: [
Container(
height: size.height / 17,
width: size.width / 1.3,
child: TextField(
controller: _message,
decoration: InputDecoration(
suffixIcon: IconButton(
onPressed: () => getImage(),
icon: Icon(Icons.photo),
),
hintText: "Send Message",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
),
IconButton(
onPressed: onSendMessage,
icon: Icon(Icons.send),
)
],
),
),
),
);
}
Widget messages(Size size, Map<String, dynamic> map, BuildContext context) {
return map['type'] == "text"
? Container(
width: size.width,
alignment: map['sendBy'] == _auth.currentUser!.displayName
? Alignment.centerRight
: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 14),
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: Colors.blue,
),
child: Text(
map['message'],
style: GoogleFonts.roboto(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
),
)
: Container(
height: size.height / 2.5,
width: size.width,
padding: EdgeInsets.symmetric(vertical: 5,horizontal: 5),
alignment: map['sendBy'] == _auth.currentUser!.displayName
? Alignment.centerRight
: Alignment.centerLeft,
child: InkWell(
onTap: () => Navigator.of(context).push(
MaterialPageRoute(builder: (_)=> ShowImage(imageURl: map['message'],
),
),
),
child: Container(
height: size.height / 2.5,
width: size.width / 2,
decoration: BoxDecoration(
border: Border.all(),
),
alignment: map['message']!="" ? null :Alignment.center,
child: map['message'] != ""
? Image.network(map['message'],fit: BoxFit.cover,)
: CircularProgressIndicator(),
),
),
);
}
}
class ShowImage extends StatelessWidget {
final imageURl;
const ShowImage({required this.imageURl, Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size;
return Scaffold(
body: Container(
height: size.height,
width: size.width,
color: Colors.black,
child: Image.network(imageURl),
),
);
}
}
I repeat I am not using shared preferences.
I need the recent chats like that in the image.

Without looking at any code, I think the idea needs planning, and then routes towards that plan's completion.
An instant chat simulation turns out to be a stream of incoming data, and a list of recents chats is also a stream of incoming data.
The answer must be to code an instant message simulation, and to then code a stream that counts conversations.
That amounts to creating a stream of streams, where a list of conversations should be developed.
To have the above in mind before the coding begins is a good idea so that you know that after producing a single chat screen, that there's still more to go.
The next question will be, how to delete conversations from the list, like, is there a widget that helps slide them away like in Whatsapp?

Related

WebViewController. Exception has occurred. ArgumentError (Invalid argument(s): Missing scheme in uri: null)

I am trying to pass data from previous page(news_card.dart) to another page(news_details.dart) to view in WebViewController. but for loadRequest(Uri.parse(widget.link)) I have received an error "Exception has occurred.
ArgumentError (Invalid argument(s): Missing scheme in uri: null)"
Here is my code for news_card.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:lumi_news/page/news/news_details.dart';
class NewsCard extends StatelessWidget {
final CollectionReference collectionReference;
NewsCard({super.key, required this.collectionReference});
PageController? _controller;
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<QuerySnapshot>(
stream: collectionReference.snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasError) {
return Text("error");
}
if (snapshot.hasData) {
QuerySnapshot querySnapshot = snapshot.data;
List<QueryDocumentSnapshot> documents = querySnapshot.docs;
List<Map> items = documents
.map((e) => {
'id': e.id,
'newsTitle': e['newsTitle'],
'publisherName': e['publisherName'],
'imageUrl': e['imageUrl'],
'publisherImageUrl': e['publisherImageUrl'],
'publisherRectangleUrl': e['publisherRectangleUrl'],
'updated': e['updated'],
'link"': e['link'],
})
.toList();
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
Map thisItem = items[index];
return Container(
//padding: const EdgeInsets.fromLTRB(10, 10, 10, 0),
height: 150,
width: double.maxFinite,
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NewsDetails(
link: '${thisItem['link']}',
image: '${thisItem['publisherImageUrl']}',
title: '${thisItem['publisherName']}',
)));
},
child: Card(
elevation: 3,
child: Row(
children: <Widget>[
Container(
width: 110,
margin: EdgeInsets.fromLTRB(10, 10, 10, 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: Colors.white,
image: DecorationImage(
fit: BoxFit.fill,
image: NetworkImage('${thisItem['imageUrl']}'),
),
),
),
Container(
child: Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 10,
),
Text(
'${thisItem['publisherName']}',
style: TextStyle(
color: Colors.grey,
fontSize: 12,
),
),
Text(
'${thisItem['newsTitle']}',
maxLines: 3,
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
SizedBox(
height: 30,
),
Text(
'${thisItem['updated']}',
style: TextStyle(
color: Colors.grey,
fontSize: 12,
),
),
],
),
),
),
],
),
),
),
);
},
);
}
return Center(child: CircularProgressIndicator());
},
),
);
}
}
and here news_detail.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:webview_flutter/webview_flutter.dart';
class NewsDetails extends StatefulWidget {
final String link;
final String title;
final String image;
NewsDetails({required this.link, required this.title, required this.image});
#override
State<NewsDetails> createState() => _NewsDetailsState(link, title, image);
}
class _NewsDetailsState extends State<NewsDetails> {
WebViewController? _controller;
final String link;
final String title;
final String image;
_NewsDetailsState(this.link, this.image, this.title);
#override
void initState() {
// TODO: implement initState
super.initState();
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(const Color(0x00000000))
..setNavigationDelegate(
NavigationDelegate(
onProgress: (int progress) {
// Update loading bar.
},
onPageStarted: (String url) {},
onPageFinished: (String url) {},
onWebResourceError: (WebResourceError error) {},
onNavigationRequest: (NavigationRequest request) {
if (request.url.startsWith('https://www.youtube.com/')) {
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
),
)
..loadRequest(Uri.parse(widget.link));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 1,
backgroundColor: Colors.white,
leading: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Icon(
Icons.arrow_back,
color: Colors.black,
),
),
title: Container(
width: double.maxFinite,
child: Row(
children: <Widget>[
Container(
child: Image.network(
widget.image,
fit: BoxFit.contain,
height: 32,
),
),
Container(
padding: EdgeInsets.only(left: 5),
child: Text(
widget.title,
style: TextStyle(color: Colors.black),
),
)
],
),
),
),
body: WebViewWidget(controller: _controller!),
);
}
}
Does anyone know how to solve this? Thanks for any help !

Change radius of google search api with a dropdown button in flutter

I'm trying to get a list of veterinary clinics within a certain radius using google-maps-api. I am able to get the clinics in one radius but not getting any results when trying to add more radius' with a dropdownbutton. the radius' should be 2.5km,5km and 10km radius. this is my code:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:vet_bookr/constant.dart';
import 'package:vet_bookr/models/vet_clinic.dart';
import 'package:vet_bookr/oScreens/vetMaps.dart';
import 'package:http/http.dart' as http;
import '../utils/constants.dart';
class PetClinicsPage extends StatefulWidget {
// PetClinicsPage(this.vetClinic);
const PetClinicsPage({super.key});
#override
State<PetClinicsPage> createState() => _PetClinicsPageState();
}
class _PetClinicsPageState extends State<PetClinicsPage> {
bool isLoading = true;
String dropdownvalue = 'in 2.5Kms';
var apiChanger = 0;
var apis = [
'in 2.5Kms',
'in 5Kms',
'in 10Kms',
];
#override
void initState() {
// TODO: implement initState
super.initState();
getTotalData();
}
late List<VetClinic>? vetClinic;
late GoogleMapController googleMapController;
static const String _kLocationServicesDisabledMessage =
'Location services are disabled.';
static const String _kPermissionDeniedMessage = 'Permission denied.';
static const String _kPermissionDeniedForeverMessage =
'Permission denied forever.';
static const String _kPermissionGrantedMessage = 'Permission granted.';
void _onMapCreated(GoogleMapController controller) {
googleMapController = controller;
}
Set<Marker> _markers = Set<Marker>();
Future<Position> determinePosition() async {
///Check if location is enabled
bool isLocationEnabled = await Geolocator.isLocationServiceEnabled();
if (!isLocationEnabled) {
return Future.error(_kLocationServicesDisabledMessage);
}
/**
* Request Location Permission
*/
await Geolocator.requestPermission();
///Check if the kind of permission we got
LocationPermission locationPermission = await Geolocator.checkPermission();
if (locationPermission == LocationPermission.denied) {
return Future.error(_kPermissionDeniedMessage);
}
if (locationPermission == LocationPermission.deniedForever) {
return Future.error(_kPermissionDeniedForeverMessage);
}
return Geolocator.getCurrentPosition();
}
Future<List<double>> getLatLng() async {
Position position = await determinePosition();
List<double> latLong = [position.latitude, position.longitude];
return latLong;
}
Future<void> getTotalData() async {
List<double> latLng = await getLatLng();
String vetsUrl = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?keyword=vets&location=${latLng[0]},${latLng[1]}&radius=$apiChanger&type=veterinary_care&key=${Constants.apiKey}";
///Getting the data
final response = await http.get(Uri.parse(vetsUrl));
final Map<String, dynamic> data = jsonDecode(response.body);
print(data);
vetClinic = (data["results"] as List).map((vetJson) {
print(vetJson);
return VetClinic.fromJson(vetJson);
}).toList();
print(vetClinic);
/**
* Adding the markerss
*/
if (!mounted) return;
setState(() {
isLoading = false;
});
}
clinicTile(data) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => VetsMaps(
vetClinic: data,
)));
},
child: Container(
margin: EdgeInsets.only(top: 0.03.sh),
width: 0.9.sw,
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.sp),
),
child: Padding(
padding: EdgeInsets.all(10.0.sp),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
data.name,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w400,
color: Color(0xffFF8B6A)),
),
SizedBox(
height: 0.008.sh,
),
Text(
data.address,
style:
TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w300),
),
SizedBox(
height: 0.005.sh,
),
Text(
data.timing ? "Currently Open" : "Currently Closed",
style: TextStyle(
fontSize: 15.sp,
color:
data.timing ? Colors.greenAccent : Colors.redAccent),
),
SizedBox(
height: 0.005.sh,
),
Container(
child: RatingBar.builder(
initialRating: data.rating,
direction: Axis.horizontal,
allowHalfRating: true,
itemCount: 5,
itemPadding: EdgeInsets.symmetric(horizontal: 1.0),
itemSize: 0.03.sh,
itemBuilder: (context, _) => Icon(
Icons.star,
color: Colors.amber,
),
onRatingUpdate: (rating) {
print(rating);
},
ignoreGestures: true,
),
)
],
),
),
),
),
);
}
void apisChanger() async {
if(dropdownvalue == apis[0]){
apiChanger = 2500;
}
if(dropdownvalue == apis[1]){
apiChanger = 5000;
}
if(dropdownvalue == apis[2]){
apiChanger = 10000;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
leading: IconButton(
icon: Icon(
Icons.arrow_back,
color: Colors.black,
),
onPressed: () {
Navigator.pop(context);
},
),
),
backgroundColor: kBackgroundColor,
body: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.only(top: 10.sp, left: 10.sp, right: 10.sp),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// sBox(h: 10),
Row(
children: [
Padding(
padding: EdgeInsets.only(left: 10.0.sp, top: 15.sp),
child: Text(
'Veterinary Clinics Near Me',
style: TextStyle(color: Colors.deepOrange[300], fontSize: 22),
),
),
Container(
padding: EdgeInsets.only(top: 0.02.sh, left: 0.01.sw),
height: 0.04.sh,
child: DropdownButton(
value: dropdownvalue,
underline: SizedBox(),
icon: const Icon(Icons.keyboard_arrow_down),
items: apis.map((String items) {
return DropdownMenuItem(
value: items,
child: Text(items),
);
}).toList(),
onChanged: (String? newValue) {
apisChanger();
setState(() {
dropdownvalue = newValue!;
});
},
),
), ],
),
Divider(
thickness: 2,
color: Colors.deepOrange[300],
indent: 15,
endIndent: 10,
),
// sBox(h: 1),
isLoading
? Container(
width: 1.sw,
height: 0.7.sh,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
height: 15.sp,
width: 15.sp,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Color(0xffFF8B6A),
),
),
],
),
)
: ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: vetClinic?.length,
itemBuilder: ((context, index) {
return clinicTile(vetClinic![index]);
}),
)
],
),
),
),
);
}
}
ive tried creating a variable which changes depending on the index of the array for the dropdown button but when i try this it doesnt work

Flutter firebase chat problem in redirecting user chatroom

Hi guys i'm a begginer in flutter development , i am stuck in a problem,
problem is i want to redirect from selected (homepage.dart file) user in the list to chatroom where i can chat this user but i am not able to do, here is problem i'm facing: Anyone give me solution .
code:-
homepage.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:jobong/model/chatroom_model.dart';
import 'package:jobong/model/signup.dart';
import 'package:jobong/view/chatroom.dart';
class HomePage extends StatefulWidget {
final List<User>? users;
const HomePage({Key? key, this.users}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final auth = FirebaseAuth.instance;
final systemColor =
const SystemUiOverlayStyle(statusBarColor: Colors.transparent);
final User? currentUser = FirebaseAuth.instance.currentUser;
final String uid = FirebaseAuth.instance.currentUser!.uid;
final searchController = TextEditingController();
String search = '';
#override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(systemColor);
return Scaffold(
backgroundColor: Colors.grey[300],
drawer: const Drawer(
child: DrawerPage(),
),
appBar: AppBar(
titleSpacing: 0,
backgroundColor: Colors.blue[900],
title: Text(
"ChatApp",
style: GoogleFonts.raleway(),
),
actions: [
IconButton(
onPressed: () async {
await FirebaseAuth.instance.signOut();
setState(() {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const SignUp()),
);
});
},
icon: const Icon(Icons.logout_outlined),
),
],
),
body: Column(
children: [
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.all(20.0),
child: TextFormField(
controller: searchController,
onChanged: (String value) {
search = value;
},
decoration: InputDecoration(
hintText: 'Search user',
prefixIcon: const Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(20),
),
hintStyle: GoogleFonts.raleway(
fontSize: 16, fontWeight: FontWeight.normal),
),
),
),
ElevatedButton(
onPressed: () {
setState(() {});
},
child: Text(
"Search",
style: GoogleFonts.raleway(),
),
),
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream:
FirebaseFirestore.instance.collection('Users')
.where('uid',isNotEqualTo: currentUser!.uid).snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot snap = snapshot.data!.docs[index];
final chatRoomModel = ChatRoomModel(
friendName: currentUser!.displayName.toString(),
friendUid: currentUser!.uid,
friendEmail: currentUser!.email.toString(),
);
final targetUserName = chatRoomModel.friendName;
final targetUserEmail = chatRoomModel.friendEmail ;
final targetUserUid = chatRoomModel.friendUid;
if (search.isEmpty) {
return ListTile(
title: Text(snap['name']),
subtitle: Text(snap['email']),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ChatRoom(
friendName: snap['name'] ,
friendEmail: snap['email'],
friendUid: snap['uid'],
),
),
);
},
leading: Container(
height: 45,
width: 45,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: Colors.white),
child: const Center(
child: Icon(Icons.person),
),
),
);
}
if (snap['name'].toString().toLowerCase().startsWith(search.toLowerCase()) ||
snap['name'].toString().toUpperCase().startsWith(search.toUpperCase())) {
return ListTile(
title: Text(snap['name']),
subtitle: Text(snap['email']),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ChatRoom(
friendName: snap['name'] ,
friendEmail: snap['email'],
friendUid: snap['uid'],
),
),
);
},
leading: Container(
height: 45,
width: 45,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: Colors.white),
child: const Center(
child: Icon(Icons.person),
),
),
);
}
return Container();
},
);
} else if (snapshot.hasError) {
return const Center(
child: Text("Something went wrong!"),
);
} else {
return Container();
}
},
),
),
],
),
);
}
}
//..............................User Details Fetch here ??..........................................
class DrawerPage extends StatefulWidget {
const DrawerPage({Key? key}) : super(key: key);
#override
State<DrawerPage> createState() => _DrawerPageState();
}
class _DrawerPageState extends State<DrawerPage> {
#override
void initState() {
super.initState();
getUserData();
}
String name = '';
String email = '';
String password = '';
final currentUser = FirebaseAuth.instance.currentUser;
Future<void> getUserData() async {
final user = await FirebaseFirestore.instance
.collection('Users')
.doc(currentUser!.uid)
.get();
setState(() {
name = user.data()!['name'];
email = user.data()!['email'];
password = user.data()!['password'];
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Name: $name',
style: GoogleFonts.raleway(
fontWeight: FontWeight.normal, fontSize: 16),
),
const SizedBox(height: 10),
Text(
'Email: $email',
style: GoogleFonts.raleway(
fontWeight: FontWeight.normal, fontSize: 16),
),
const SizedBox(height: 10),
Text(
'Password: $password',
style: GoogleFonts.raleway(
fontWeight: FontWeight.normal, fontSize: 16),
),
],
),
),
);
}
}
second chatroom.dart file =================================
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:jobong/model/chatroom_model.dart';
class ChatRoom extends StatefulWidget {
final ChatRoomModel friendName;
final ChatRoomModel friendUid;
final ChatRoomModel friendEmail;
const ChatRoom({
Key? key,
required this.friendName, required this.friendUid ,
required this.friendEmail
}) : super(key: key);
#override
State<ChatRoom> createState() => _ChatRoomState();
}
class _ChatRoomState extends State<ChatRoom> {
void showToast(String message) {
Fluttertoast.showToast(
msg: message,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0
);
}
final email = FirebaseAuth.instance.currentUser!.email;
void sendMessage() async {
try{
await FirebaseFirestore.instance.collection('chats')
.doc(uid).collection('messages').doc()
.set({
'sender':email,
'receiver':widget.friendUid,
'message':_message.text.trim(),
'time':FieldValue.serverTimestamp(),
});
}on FirebaseAuthException catch (e) {
showToast('${e.message}');
}
}
final bool isMe = false;
final _message = TextEditingController();
final uid = FirebaseAuth.instance.currentUser!.uid;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Column(
children: [
Text(
widget.friendName.toString(),
style: GoogleFonts.lato(),
),
Text(
widget.friendEmail.toString(),
style: GoogleFonts.lato(),
),
],
),
),
body: Column(
children: [
Expanded(
child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('chats')
.doc(uid)
.collection('messages')
.doc()
.snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(color: Colors.blue),
);
} else {
return ListView.builder(
shrinkWrap: true,
reverse: true,
itemCount: snapshot.data!.doc.length,
itemBuilder: (BuildContext context, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: isMe
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
borderRadius: isMe
? BorderRadius.circular(15.0)
: BorderRadius.circular(15.0),
color: isMe
? Colors.green[500]
: Colors.red[500]),
child: Center(
child: Text(_message.toString(),
style: isMe
? GoogleFonts.lato(color: Colors.black)
: GoogleFonts.lato(
color: Colors.white)),
),
),
],
),
);
},
);
}
},
),
),
Padding(
padding: const EdgeInsets.only(left: 8.0, right: 8.0, bottom: 8.0),
child: Container(
height: 60,
width: double.infinity,
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(25),
bottomRight: Radius.circular(25),
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
),
color: Colors.grey[900],
),
child: TextFormField(
controller: _message,
style: GoogleFonts.lato(color: Colors.white),
decoration: InputDecoration(
hintText: 'send a message ...',
hintStyle: GoogleFonts.lato(color: Colors.white),
border: InputBorder.none,
prefixIcon: const Icon(Icons.file_present_rounded,
color: Colors.white),
suffixIcon: GestureDetector(
onTap:sendMessage,
child: const Icon(Icons.near_me, color: Colors.white),
),
),
),
),
),
],
),
);
}
}

Field '_image' has not been initialized in flutter

I wastrying image upload in flutter using imagepicker. While I was choose image the image cant display in one container. I was no error in error console. But the error was"Field '_image' has not been initialized. I am confused in flutter. Please he me guys
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
File? _image;
final picker = ImagePicker();
TextEditingController namecontroller = TextEditingController();
Future chooseimage() async {
var pickedImage = await picker.pickImage(source: ImageSource.gallery);
setState(() {
//_image = File(pickedImage!.path);
_image = File(pickedImage!.path);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Upload Image"),
),
body: Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(
onPressed: chooseimage,
child: Text("Select Image"),
style: ElevatedButton.styleFrom(primary: Colors.green),
),
TextField(
controller: namecontroller,
decoration: InputDecoration(label: Text("Name")),
),
SizedBox(
height: 30.0,
),
ElevatedButton(
onPressed: () {},
child: Text("Upload"),
style: ElevatedButton.styleFrom(primary: Colors.blue),
),
Container(
child: _image == null
? Text('No Image Select')
: Image.file(_image!),
),
],
),
),
),
);
}
}
///You can take a reference through this code hope this will work for you. Thanks
import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:provider/provider.dart';
import 'package:traveling/Provider/EmployeeProvider/EmployeeProfileProvider.dart';
import 'package:traveling/helpers/AppColors.dart';
///To get the crop value creating a class
enum AppState {
free,
picked,
cropped,
}
class EmployeeProfile extends StatefulWidget {
final bool leadingIcon;
final number;
final cardName;
EmployeeProfile(
{Key? key, required this.leadingIcon, this.number, this.cardName})
: super(key: key);
#override
_EmployeeProfileState createState() => _EmployeeProfileState();
}
class _EmployeeProfileState extends State<EmployeeProfile>
with TickerProviderStateMixin {
EmployeeProfileProvider? _provider;
///controller widget used in Spinkit plugin to show loading process
animationControllerField(){
AnimationController animationController =AnimationController(
vsync: this, duration: const Duration(milliseconds: 900));
}
bool? visible;
double? _width;
File? _image;
var url;
late AppState state;
final picker = ImagePicker();
///pop-up to choose camera gallery while uploading image
Future<void> _showChoiceDialog(BuildContext context) {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
"Choose option",
style: TextStyle(color: AppColors.hotelListDarkBlue),
),
content: SingleChildScrollView(
child: ListBody(
children: [
Divider(
height: 1,
color: AppColors.baseLightBlueColor,
),
ListTile(
onTap: () {
Navigator.pop(context, _pickImage(ImageSource.gallery,context));
},
title: Text(
"Gallery",
style: TextStyle(color: AppColors.hotelListDarkBlue),
),
leading: Icon(
Icons.account_box,
color: AppColors.baseLightBlueColor,
),
),
Divider(
height: 1,
color: AppColors.baseLightBlueColor,
),
ListTile(
onTap: () {
Navigator.pop(context, _pickImage(ImageSource.camera,context));
},
title: Text(
"Camera",
style: TextStyle(color: AppColors.hotelListDarkBlue,),
),
leading: Icon(
Icons.camera,
color: AppColors.baseLightBlueColor,
),
),
],
),
),
);
});
}
///crop image selecting by the user
Future<Null> _cropImage() async {
File? croppedFile = await ImageCropper.cropImage(
sourcePath: _image!.path,
aspectRatioPresets: Platform.isAndroid
? [
CropAspectRatioPreset.square,
CropAspectRatioPreset.ratio3x2,
CropAspectRatioPreset.original,
CropAspectRatioPreset.ratio4x3,
CropAspectRatioPreset.ratio16x9
]
: [
CropAspectRatioPreset.original,
CropAspectRatioPreset.square,
CropAspectRatioPreset.ratio3x2,
CropAspectRatioPreset.ratio4x3,
CropAspectRatioPreset.ratio5x3,
CropAspectRatioPreset.ratio5x4,
CropAspectRatioPreset.ratio7x5,
CropAspectRatioPreset.ratio16x9
],
androidUiSettings: AndroidUiSettings(
toolbarTitle: 'Cropper',
toolbarColor: AppColors.baseLightBlueColor,
toolbarWidgetColor: Colors.white,
initAspectRatio: CropAspectRatioPreset.original,
lockAspectRatio: false),
iosUiSettings: IOSUiSettings(
title: 'Cropper',
));
if (croppedFile != null) {
_image = croppedFile;
setState(() {
state = AppState.cropped;
});
///to get upload image
url = await _provider!.setUserUpdatedImageInfo(_image!, );
setState(() {
///to set the image
_provider!.currentLoggedInUser!.image = url;
});
}
}
///icon change while user edit image
Widget _buildButtonIcon() {
if (state == AppState.free)
return Icon(
MdiIcons.squareEditOutline,
color: AppColors.popUpBlueColor,
size: 20,
);
else if (state == AppState.picked)
return Icon(Icons.crop,color: AppColors.popUpBlueColor,
size: 20,);
else if (state == AppState.cropped)
return Icon(
MdiIcons.squareEditOutline,
color: AppColors.popUpBlueColor,
size: 20,
);
else
return Container();
}
#override
void initState() {
// TODO: implement initState
super.initState();
print("initState called");
state = AppState.free;
_provider = EmployeeProfileProvider();
_provider!.getUserInfo();
}
///this widget is the main widget and it is used for building a UI in the app.
#override
Widget build(BuildContext context) {
_width = MediaQuery.of(context).size.width;
return ChangeNotifierProvider<EmployeeProfileProvider?>(
create: (context) => _provider!,
child: Consumer<EmployeeProfileProvider?>(
builder: (context, provider, child) {
return provider!.currentLoggedInUser==null?
Material(
color: AppColors.white,
child: Center(
child:
SpinKitCubeGrid(
color: AppColors.baseLightBlueColor,
size: 50.0,
controller:animationControllerField()
),
),
):Container(
width: _width!,
color: AppColors.white,
child: Stack(
children: [
Column(
children: [
Material(
color: AppColors.baseLightBlueColor,
elevation: 15,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.only(bottomRight: Radius.circular(30)),
),
child: Container(
height: 180,
decoration: BoxDecoration(
color: AppColors.baseLightBlueColor,
// AppColors.blue,
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(30)),
),
),
),
],
),
Positioned(
top: 80,
child:
Stack(
children: [
Container(
height: 150,
width: _width,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CachedNetworkImage(
filterQuality: FilterQuality.high,
maxHeightDiskCache: 100,
fit: BoxFit.cover,
imageUrl: provider.currentLoggedInUser!.image.toString(),
imageBuilder: (context, imageProvider) => Container(
height: 120,
width: 120,
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
image: imageProvider, fit: BoxFit.cover),
border: Border.all(
color: AppColors.white, width: 2.0),
),
),
placeholder: (context, url) =>
const CircularProgressIndicator(),
errorWidget: (context, url, error) => Container(
height: 120,
width: 120,
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
image:DecorationImage(
fit: BoxFit.cover,
image: AssetImage('assets/managerPicture.jpeg')),
border: Border.all(
color: AppColors.white, width: 2.0),
),
),
fadeOutDuration: const Duration(seconds: 1),
fadeInDuration: const Duration(seconds: 3),
),
],
),
),
Positioned(
top: 60,
right: 0,
left: 100,
bottom: 0,
child: GestureDetector(
onTap: () {
if (state == AppState.free)
_showChoiceDialog(context);
else if (state == AppState.picked)
_cropImage();
else if (state == AppState.cropped) _showChoiceDialog(context);
},
child: Container(
height: 10,
width: _width,
child: Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Container(
height: 35,
width: 35,
decoration: BoxDecoration(
color:
AppColors.profiePicBackground,
borderRadius:
BorderRadius.circular(60),
),
child: ClipRRect(
borderRadius:
BorderRadius.circular(60.0),
child: _buildButtonIcon(),
/*Icon(
MdiIcons.squareEditOutline,
color: AppColors.popUpBlueColor,
size: 20,
),*/
),
),
],
),
),
),
)
],
)
),
Positioned(
top: 40,
child: Container(
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.only(left: 8, right: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
widget.leadingIcon == true
? Align(
alignment: Alignment.topLeft,
child: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Icon(
MdiIcons.arrowLeft,
color: Colors.white,
)),
)
: Container(),
],
),
)),
],
),
);
}));
}
///ImageSource: Camera and Gallery.
Future<Null> _pickImage(ImageSource source,context) async {
final pickedImage = await ImagePicker().pickImage(source: source);
_image = pickedImage != null ? File(pickedImage.path) : null;
if (_image != null) {
setState(() {
state = AppState.picked;
});
}
}
}
class EmployeeProfileProvider extends ChangeNotifier {
late bool _isInfoEditable;
FirebaseFirestore? fireStoreInstance;
EmployeeProfileModel? _userData;
// EmployeeMyExpenseReport ? _employeeMyExpenseReport;
///to update the employee profile image
Future<String?> setUserUpdatedImageInfo(File _image, ) async {
var data= await EmployeeServices.getInstance().uploadImageToFirebase(_image, );
notifyListeners();
return data;
}
///to update Personal details of employee
EmployeeProfileModel? get getUploadedImage => _userData;
#override
void dispose() {
//print("Provider disposed called");
super.dispose();
}
//upload employee image on firebase
String ?url;
Future <String?>uploadImageToFirebase(File _image) async {
User? currentUser = FirebaseAuth.instance.currentUser;
String fileName = basename(_image.path);
firebase_storage.Reference ref = firebase_storage.FirebaseStorage.instance
.ref().child('uploads')
.child('/$fileName');
final metadata = firebase_storage.SettableMetadata(
contentType: 'image/jpeg',
customMetadata: {'picked-file-path': fileName});
firebase_storage.UploadTask uploadTask;
uploadTask = ref.putFile(io.File(_image.path), metadata);
//String ?url;
await uploadTask.whenComplete(() async {
url = await uploadTask.snapshot.ref.getDownloadURL();
});
Future.value(uploadTask)
.then((value) => {
print("Upload file path ${value.ref.fullPath}"),
FirebaseFirestore.instance.collection(FirestoreCollections.employees).doc(currentUser!.uid).update({'image': '$url'}),
})
.onError((error, stackTrace) =>
{
print("Upload file path error ${error.toString()} ")}
);
return url;
}
}

Flutter: Change ListTile based on Firestore query

I'm trying to configure the appearance of a list tile based on a firestore query.
So I have a set of list tiles as such:
What I want to achieve, when this page is loaded, the left side of the tile marks if a user has completed that particular lesson. So an 'x' means he has not but a 'tick' would mean that he has. Currently it is all hard coded to be 'x':
ListTile makeLessonListTile(Lesson lesson) => ListTile(
contentPadding:
EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
leading: Container(
padding: EdgeInsets.only(right: 12.0),
decoration: new BoxDecoration(
border: new Border(
right: new BorderSide(width: 1.0, color: Colors.white24))),
child: IconButton(
icon: Icon(Icons.close, color: Colors.white), // Hardcoded to be 'x'
),
),
title: Text(
lesson.title,
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
subtitle: Row(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
child: LinearProgressIndicator(
backgroundColor: Color.fromRGBO(209, 224, 224, 0.2),
value: lesson.indicatorValue,
valueColor: AlwaysStoppedAnimation(Colors.green)),
)),
Expanded(
flex: 4,
child: Padding(
padding: EdgeInsets.only(left: 10.0),
child: Text(lesson.level,
style: TextStyle(color: Colors.white))),
)
],
),
trailing:
Icon(Icons.keyboard_arrow_right, color: Colors.white, size: 30.0),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => QuizPage(
lesson: lesson,
auth: widget.auth,
onSignedOut: widget.onSignedOut,
userId: widget.userId,
)
)
);
},
);
Card makeLessonCard(Lesson lesson) => Card(
elevation: 8.0,
margin: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: Container(
decoration: BoxDecoration(color: Color.fromRGBO(64, 75, 96, .9)),
child: makeLessonListTile(lesson),
),
);
// the scaffold body
final makeLessonBody = Container(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: lessons.length,
itemBuilder: (BuildContext context, int index) {
return makeLessonCard(lessons[index]);
},
),
);
I know how to do the query, I'm just not sure where to do it such that when the page loads, it is automatically updated to ticks and crosses based on the user results.
The query would be :
FirebaseUser user = await widget.auth.getCurrentUser();
Firestore.instance
.collection('Users')
.document(user.email)
.collection('Quiz Data')
.document('Courses')
.collection(lesson.abbr.toString().substring(5))
.document(lesson.title)
.get()
.then((DocumentSnapshot ds) {
if (ds.exists) {
if (ds['pass']) return true;
}
return false;
});
Base Auth Class I use for Authentication:
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
abstract class BaseAuth {
Future<String> signIn(String email, String password);
Future<String> signUp(String email, String password);
Future<FirebaseUser> getCurrentUser();
Future<void> sendEmailVerification();
Future<void> signOut();
Future<bool> isEmailVerified();
}
class Auth implements BaseAuth {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
Future<String> signIn(String email, String password) async {
FirebaseUser user = await _firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
return user.uid;
}
Future<String> signUp(String email, String password) async {
FirebaseUser user = await _firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password);
return user.uid;
}
Future<FirebaseUser> getCurrentUser() async {
FirebaseUser user = await _firebaseAuth.currentUser();
return user;
}
Future<void> signOut() async {
return _firebaseAuth.signOut();
}
Future<void> sendEmailVerification() async {
FirebaseUser user = await _firebaseAuth.currentUser();
user.sendEmailVerification();
}
Future<bool> isEmailVerified() async {
FirebaseUser user = await _firebaseAuth.currentUser();
return user.isEmailVerified;
}
}
Update - What I tried to do:
Use ternary operator:
class _NavigationPageState extends State<NavigationPage> {
. . . // omitted code
bool passed;
#override
void initState() {
passed = false;
. . . // omitted code
super.initState();
}
checkUserPassedLesson (Lesson lesson) async {
FirebaseUser user = await widget.auth.getCurrentUser();
Firestore.instance
.collection('Users')
.document(user.email)
.collection('Quiz Data')
.document('Courses')
.collection(lesson.abbr.toString().substring(5))
.document(lesson.title)
.get()
.then((DocumentSnapshot ds) {
if (ds.exists) {
if (ds['pass']) {
passed = true;
return;
}
}
passed = false;
});
}
#override
Widget build(BuildContext context) {
// for lesson page
ListTile makeLessonListTile(Lesson lesson) => ListTile(
contentPadding:
EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
leading: Container(
padding: EdgeInsets.only(right: 12.0),
decoration: new BoxDecoration(
border: new Border(
right: new BorderSide(width: 1.0, color: Colors.white24))),
child: IconButton(
icon: passed ? Icon(Icons.done, color: Colors.white) : Icon(Icons.close, color: Colors.white),
),
),
title: Text(
lesson.title,
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
subtitle: Row(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
child: LinearProgressIndicator(
backgroundColor: Color.fromRGBO(209, 224, 224, 0.2),
value: lesson.indicatorValue,
valueColor: AlwaysStoppedAnimation(Colors.green)),
)),
Expanded(
flex: 4,
child: Padding(
padding: EdgeInsets.only(left: 10.0),
child: Text(lesson.level,
style: TextStyle(color: Colors.white))),
)
],
),
trailing:
Icon(Icons.keyboard_arrow_right, color: Colors.white, size: 30.0),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => QuizPage(
lesson: lesson,
auth: widget.auth,
onSignedOut: widget.onSignedOut,
userId: widget.userId,
)
)
);
},
);
Card makeLessonCard(Lesson lesson) => Card(
elevation: 8.0,
margin: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: Container(
decoration: BoxDecoration(color: Color.fromRGBO(64, 75, 96, .9)),
child: makeLessonListTile(lesson),
),
);
// query here and route accordingly
final makeLessonBody = Container(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: lessons.length,
itemBuilder: (BuildContext context, int index) {
checkUserPassedLesson(lessons[index]);
return makeLessonCard(lessons[index]);
},
),
);
. . . // omitted code
return Scaffold(
backgroundColor: Color.fromRGBO(58, 66, 86, 1.0),
appBar: topAppBar,
body: makeLessonBody,
bottomNavigationBar: makeBottom,
);
}
}
Put Query in init:
class _NavigationPageState extends State<NavigationPage> {
... // omitted code
bool passed = false;
Container makeLessonBody;
#override
void initState() {
... // omitted code
// for lesson page
ListTile makeLessonListTile(Lesson lesson) => ListTile(
contentPadding:
EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
leading: Container(
padding: EdgeInsets.only(right: 12.0),
decoration: new BoxDecoration(
border: new Border(
right: new BorderSide(width: 1.0, color: Colors.white24))),
child: IconButton(
icon: passed ? Icon(Icons.done, color: Colors.white) : Icon(Icons.close, color: Colors.white),
),
),
title: Text(
lesson.title,
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
subtitle: Row(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
child: LinearProgressIndicator(
backgroundColor: Color.fromRGBO(209, 224, 224, 0.2),
value: lesson.indicatorValue,
valueColor: AlwaysStoppedAnimation(Colors.green)),
)),
Expanded(
flex: 4,
child: Padding(
padding: EdgeInsets.only(left: 10.0),
child: Text(lesson.level,
style: TextStyle(color: Colors.white))),
)
],
),
trailing:
Icon(Icons.keyboard_arrow_right, color: Colors.white, size: 30.0),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => QuizPage(
lesson: lesson,
auth: widget.auth,
onSignedOut: widget.onSignedOut,
userId: widget.userId,
)
)
);
},
);
Card makeLessonCard(Lesson lesson) => Card(
elevation: 8.0,
margin: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: Container(
decoration: BoxDecoration(color: Color.fromRGBO(64, 75, 96, .9)),
child: makeLessonListTile(lesson),
),
);
makeLessonBody = Container(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: lessons.length,
itemBuilder: (BuildContext context, int index) {
checkUserPassedLesson(lessons[index]);
debugPrint(passed.toString());
return makeLessonCard(lessons[index]);
},
),
);
super.initState();
}
void checkUserPassedLesson (Lesson lesson) async {
FirebaseUser user = await widget.auth.getCurrentUser();
Firestore.instance
.collection('Users')
.document(user.email)
.collection('Quiz Data')
.document('Courses')
.collection(lesson.abbr.toString().substring(5))
.document(lesson.title)
.get()
.then((DocumentSnapshot ds) {
if (ds.exists) {
if (ds['pass']) {
setState(() {
debugPrint(ds['pass'].toString());
passed = true;
});
}
} else {
setState(() {
passed = false;
});}
});
}
... // omitted code
#override
Widget build(BuildContext context) {
... // omitted code
return Scaffold(
backgroundColor: Color.fromRGBO(58, 66, 86, 1.0),
appBar: topAppBar,
body: makeLessonBody,
bottomNavigationBar: makeBottom,
);
}
}
Latest Update: Since the above methods were not working out, I tried to use a FutureBuilder using the ternary operator and it worked.
Full code:
class NavigationPage extends StatefulWidget {
NavigationPage({Key key, this.auth, this.userId, this.onSignedOut, this.userEmail}) : super(key: key);
final BaseAuth auth;
final VoidCallback onSignedOut;
final String userId;
final String userEmail;
#override
_NavigationPageState createState() => _NavigationPageState();
}
class _NavigationPageState extends State<NavigationPage> {
List courses;
List lessons;
String title;
TabStatus tabStatus;
bool showLessons;
bool _isLoading;
#override
void initState() {
title = COURSE_PAGE_TITLE;
_isLoading = false;
tabStatus = TabStatus.COURSE;
showLessons = false;
courses = StaticMethods.getCourses();
// temp value
lessons = StaticMethods.getLessons(Abbr.P01);
super.initState();
}
_signOut() async {
setState(() {
_isLoading = true;
});
try {
await widget.auth.signOut();
widget.onSignedOut();
setState(() {
_isLoading = false;
});
} catch (e) {
setState(() {
_isLoading = false;
});
print(e);
}
}
Widget _showLoading(){
if (_isLoading) {
return Center(
child: ColorLoader5(
dotOneColor: Colors.white24,
dotTwoColor: Colors.white70,
dotThreeColor: Colors.white,
dotType: DotType.circle,
dotIcon: Icon(Icons.adjust),
duration: Duration(seconds: 1),
)
);
}
return Container(height: 0.0, width: 0.0,);
}
Widget _showLoadingTile() {
return Center (
child: Container(
height: MediaQuery.of(context).size.height/10,
width: MediaQuery.of(context).size.width/2,
child: ColorLoader5(
dotOneColor: Colors.white24,
dotTwoColor: Colors.white70,
dotThreeColor: Colors.white,
dotType: DotType.circle,
dotIcon: Icon(Icons.adjust),
duration: Duration(seconds: 1),
),
)
);
}
Future<bool> checkUserPassedLesson (Lesson lesson) async {
bool passed;
await Firestore.instance
.collection('Users')
.document(widget.userEmail)
.collection('Quiz Data')
.document('Courses')
.collection(lesson.abbr.toString().substring(5))
.document(lesson.title)
.get()
.then((DocumentSnapshot ds) {
debugPrint("querying");
if (ds.exists) {
debugPrint('exists');
passed = ds['pass'];
} else passed = false;
});
return passed;
}
#override
Widget build(BuildContext context) {
// for lesson page
ListTile makeLessonListTile(Lesson lesson, bool passed) => ListTile(
contentPadding:
EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
leading: Container(
padding: EdgeInsets.only(right: 12.0),
decoration: new BoxDecoration(
border: new Border(
right: new BorderSide(width: 1.0, color: Colors.white24))),
child: IconButton(
icon: passed ? Icon(Icons.done, color: Colors.white) : Icon(Icons.close, color: Colors.white),
),
),
title: Text(
lesson.title,
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
subtitle: Row(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
child: LinearProgressIndicator(
backgroundColor: Color.fromRGBO(209, 224, 224, 0.2),
value: lesson.indicatorValue,
valueColor: AlwaysStoppedAnimation(Colors.green)),
)),
Expanded(
flex: 4,
child: Padding(
padding: EdgeInsets.only(left: 10.0),
child: Text(lesson.level,
style: TextStyle(color: Colors.white))),
)
],
),
trailing:
Icon(Icons.keyboard_arrow_right, color: Colors.white, size: 30.0),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => QuizPage(
lesson: lesson,
auth: widget.auth,
onSignedOut: widget.onSignedOut,
userId: widget.userId,
)
)
);
},
);
Card makeLessonCard(Lesson lesson, bool passed) => Card(
elevation: 8.0,
margin: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: Container(
decoration: BoxDecoration(color: Color.fromRGBO(64, 75, 96, .9)),
child: makeLessonListTile(lesson, passed),
),
);
final makeLessonBody = Container(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: lessons.length,
itemBuilder: (BuildContext context, int index) {
return FutureBuilder<bool>(
future: checkUserPassedLesson(lessons[index]),
builder: (BuildContext context,
AsyncSnapshot<bool> snapshot) {
if (snapshot.hasError) return new Text('${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: _showLoadingTile());
default:
return makeLessonCard(lessons[index], snapshot.data);
}
},
);
},
),
);
return Scaffold(
backgroundColor: Color.fromRGBO(58, 66, 86, 1.0),
appBar: topAppBar, // omitted code
body: makeLessonBody,
bottomNavigationBar: makeBottom, // omitted code
);
}
}
You don't have to repeat the function just to change the icon. Use a ternary operator instead (C# example but the concept is the same).
bool passed = checkUserPassedLesson(lesson);
...
IconButton(
icon: passed ? Icon(Icons.done, color: Colors.white) : Icon(Icons.close, color: Colors.white),
),
If passed is true it uses the done icon and close icon if its not.