Complete BLoC with Clean Architecture (group chat) Discount !! E-commerce App With Backend Source Code Video and Voice Chatting App Firebase Chatting App Source Code Complete Gym App BLoC State Management Source Code Complete Study App Buy Ticket Booking App Source Code Buy Travel App With Backend Source Code Complete Chat App Udemy Course Special Offer Discount !! Online Learning Course App (BLoC) Online Learning Course App (Riverpod) Online Learning Course App (Getx) Discount !! Shopping App (Provider) Cool Flutter Game Flutter Nodejs Chat App Flutter Nodejs Api And Firebase Chat App Riverpod Task Management App
This tutorial would post here about the update of the app. Since the app is huge in terms of time and content, we decided to make a dedicate tutorial here about.
Here we post any problems that you are facing and their solutions.
4:18:58 time line missing user token
On youtube tutorial at the above timeline, it's missing how to generate the user token. Here's the video for it.
Message schema
During the video uploading, we have missed recording about message.js. Here is the code below
const mongoose = require("mongoose");
const messageSchema = mongoose.Schema(
{
sender: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
content: { type: String, trim: true },
receiver: { type: String, trim: true },
chat: { type: mongoose.Schema.Types.ObjectId, ref: "Chat" },
readBy: [{ type: mongoose.Schema.Types.ObjectId, ref: "User" }],
},
{ timestamps: true }
);
module.exports = mongoose.model("Message", messageSchema);
Image Url
2:27:27 I cannot see the image URL completely and it is not there in the app constants.dart file either
You may use any random url from the internet.
Railway issue connecting with github
Unfortunately, the attached GitHub account does not provide enough data on who you are
In order for one to host a server on railway, railway app should be have access to the repo that you want to host. When you create a new app and configuring where to get the content authorize railway to access that repo alternatively you can give railway to all the repositories that you have and you can be able to search through your repositories and pick the one you want to use.
Code for Drawer Item
This code should be in lib->views->ui
folder.
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart';
import 'package:jobfinder/controllers/zoom_provider.dart';
import 'package:jobfinder/views/common/exports.dart';
import 'package:provider/provider.dart';
class MainScreen extends StatefulWidget {
const MainScreen({Key? key}) : super(key: key);
@override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
@override
Widget build(BuildContext context) {
return Consumer<ZoomNotifier>(
builder: (context, zoomNotifier, child) {
return ZoomDrawer(
menuScreen: DrawerScreen(
indexSetter: (index) {
zoomNotifier.currentIndex = index;
},
),
mainScreen: currentScreen(),
borderRadius: 30,
showShadow: true,
angle: 0.0,
slideWidth: 250,
menuBackgroundColor: Color(kLightBlue.value),
);
},
);
}
Widget currentScreen() {
var zoomNotifier = Provider.of<ZoomNotifier>(context);
switch (zoomNotifier.currentIndex) {
case 0:
return const HomePage();
case 1:
return const ChatsPage();
case 2:
return const BookMarkPage();
case 3:
return const DeviceManagement();
case 4:
return const ProfilePage();
default:
return const HomePage();
}
}
}
class DrawerScreen extends StatefulWidget {
final ValueSetter indexSetter;
const DrawerScreen({Key? key, required this.indexSetter}) : super(key: key);
@override
State<DrawerScreen> createState() => _DrawerScreenState();
}
class _DrawerScreenState extends State<DrawerScreen> {
@override
Widget build(BuildContext context) {
var zoomNotifier = Provider.of<ZoomNotifier>(context);
return GestureDetector(
onDoubleTap: () {
ZoomDrawer.of(context)!.toggle();
},
child: Scaffold(
backgroundColor: Color(kLightBlue.value),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
drawerItems(
AntDesign.home,
"Home",
0,
zoomNotifier.currentIndex == 0
? Color(kLight.value)
: Color(kLightGrey.value),
zoomNotifier.currentIndex == 0
? Color(kLight.value)
: Color(kLightGrey.value)),
const HeightSpacer(size: 20),
drawerItems(
Ionicons.ios_chatbubble_outline,
"Chat",
1,
zoomNotifier.currentIndex == 1
? Color(kLight.value)
: Color(kDarkGrey.value),
zoomNotifier.currentIndex == 1
? Color(kLight.value)
: Color(kDarkGrey.value)),
const HeightSpacer(size: 20),
drawerItems(
Fontisto.bookmark,
"Bookmarks",
2,
zoomNotifier.currentIndex == 2
? Color(kLight.value)
: Color(kDarkGrey.value),
zoomNotifier.currentIndex == 2
? Color(kLight.value)
: Color(kDarkGrey.value)),
const HeightSpacer(size: 20),
drawerItems(
MaterialCommunityIcons.devices,
"Device Mgmt",
3,
zoomNotifier.currentIndex == 3
? Color(kLight.value)
: Color(kDarkGrey.value),
zoomNotifier.currentIndex == 3
? Color(kLight.value)
: Color(kDarkGrey.value)),
const HeightSpacer(size: 20),
drawerItems(
FontAwesome5Regular.user_circle,
"Profile",
4,
zoomNotifier.currentIndex == 4
? Color(kLight.value)
: Color(kDarkGrey.value),
zoomNotifier.currentIndex == 4
? Color(kLight.value)
: Color(kDarkGrey.value)),
],
),
),
);
}
Widget drawerItems(
IconData icon, String text, int index, Color color, Color txtcolor) {
return GestureDetector(
onTap: () {
widget.indexSetter(index);
},
child: Container(
margin: const EdgeInsets.only(left: 20, bottom: 12),
child: Row(
children: [
Icon(
icon,
color: color,
),
const SizedBox(
width: 12,
),
ReusableText(
text: text,
style: appstyle(12, color, FontWeight.bold),
),
],
),
),
);
}
}
JWT_SEC
Here you may set up your .env
file like below
PORT = 5002
MONGO_URL = mongodb+srv://jobhubdb:Thehub2023@jobhubdb.1eeaug2.mongodb.net/jobhubdb
JWT_SEC = jobhub2023
SECRET = jobhub2023
Make sure you set up your port according to your needs. You may also need to set up rest based on your environment and set up.
Update User
The video is missing udpate user code. Make sure you see the code carefully and use the part as you need.
class PersonalDetails extends StatefulWidget {
const PersonalDetails({super.key});
@override
State<PersonalDetails> createState() => _PersonalDetailsState();
}
class _PersonalDetailsState extends State<PersonalDetails> {
TextEditingController phone = TextEditingController();
TextEditingController location = TextEditingController();
TextEditingController skill0 = TextEditingController();
TextEditingController skill1 = TextEditingController();
TextEditingController skill2 = TextEditingController();
TextEditingController skill3 = TextEditingController();
TextEditingController skill4 = TextEditingController();
@override
void dispose() {
phone.dispose();
location.dispose();
skill0.dispose();
skill1.dispose();
skill2.dispose();
skill3.dispose();
skill4.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(body: Consumer<LoginNotifier>(
builder: (context, loginNotifier, child) {
return Form(
key: loginNotifier.profileFormKey,
child: ListView(
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 60.h),
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ReusableText(
text: "Personal Details",
style: appstyle(35, Color(kDark.value), FontWeight.bold)),
Consumer<ImageUpoader>(
builder: (context, imageUploader, child) {
return imageUploader.imageFil.isEmpty
? GestureDetector(
onTap: () {
imageUploader.pickImage();
},
child: CircleAvatar(
backgroundColor: Color(kLightBlue.value),
// backgroundImage: ,
child: const Center(
child: Icon(Icons.photo_filter_rounded),
),
),
)
: GestureDetector(
onTap: () {
imageUploader.imageFil.clear();
setState(() {});
},
child: CircleAvatar(
backgroundColor: Color(kLightBlue.value),
backgroundImage:
FileImage(File(imageUploader.imageFil[0])),
),
);
},
)
],
),
const HeightSpacer(size: 20),
Form(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomTextField(
controller: location,
hintText: "Location",
keyboardType: TextInputType.text,
validator: (location) {
if (location!.isEmpty) {
return "Please enter a valid location";
} else {
return null;
}
},
),
const HeightSpacer(size: 10),
CustomTextField(
controller: phone,
hintText: "Phone Number",
keyboardType: TextInputType.phone,
validator: (phone) {
if (phone!.isEmpty) {
return "Please enter a valid phone";
} else {
return null;
}
},
),
const HeightSpacer(size: 10),
ReusableText(
text: "Professional Skills",
style: appstyle(30, Color(kDark.value), FontWeight.bold)),
const HeightSpacer(size: 10),
CustomTextField(
controller: skill0,
hintText: "Proffessional Skills",
keyboardType: TextInputType.text,
validator: (skill0) {
if (skill0!.isEmpty) {
return "Please enter a valid phone";
} else {
return null;
}
},
),
const HeightSpacer(size: 10),
CustomTextField(
controller: skill1,
hintText: "Proffessional Skills",
keyboardType: TextInputType.text,
validator: (skill1) {
if (skill1!.isEmpty) {
return "Please enter a valid phone";
} else {
return null;
}
},
),
const HeightSpacer(size: 10),
CustomTextField(
controller: skill2,
hintText: "Proffessional Skills",
keyboardType: TextInputType.text,
validator: (skill2) {
if (skill2!.isEmpty) {
return "Please enter a valid phone";
} else {
return null;
}
},
),
const HeightSpacer(size: 10),
CustomTextField(
controller: skill3,
hintText: "Proffessional Skills",
keyboardType: TextInputType.text,
validator: (skill3) {
if (skill3!.isEmpty) {
return "Please enter a valid phone";
} else {
return null;
}
},
),
const HeightSpacer(size: 10),
CustomTextField(
controller: skill4,
hintText: "Proffessional Skills",
keyboardType: TextInputType.text,
validator: (skill4) {
if (skill4!.isEmpty) {
return "Please enter a valid phone";
} else {
return null;
}
},
),
const HeightSpacer(size: 20),
Consumer<ImageUpoader>(
builder: (context, imageUploada, child) {
return CustomButton(
onTap: () {
if (imageUploada.imageFil.isEmpty &&
imageUploada.imageUrl == null) {
Get.snackbar("Image Missing",
"Please upload an image to proceed",
colorText: Color(kLight.value),
backgroundColor: Color(kLightBlue.value),
icon: const Icon(Icons.add_alert));
} else {
ProfileUpdateReq model = ProfileUpdateReq(
location: location.text,
phone: phone.text,
profile: imageUploada.imageUrl.toString(),
skills: [
skill0.text,
skill1.text,
skill2.text,
skill3.text,
skill4.text,
]);
loginNotifier.updateProfile(model);
}
},
text: "Update Profile");
},
)
],
))
],
),
);
},
));
}
}
Auth Helper
Part of the auth helper code is missing in the video. You may check out the code and use some code as you need.
class AuthHelper {
static var client = https.Client();
static Future<bool> login(LoginModel model) async {
Map<String, String> requestHeaders = {'Content-Type': 'application/json'};
var url = Uri.https(Config.apiUrl, Config.loginUrl);
var response = await client.post(
url,
headers: requestHeaders,
body: jsonEncode(model),
);
if (response.statusCode == 200) {
final SharedPreferences prefs = await SharedPreferences.getInstance();
String token = loginResponseModelFromJson(response.body).userToken;
String userId = loginResponseModelFromJson(response.body).id;
String profile = loginResponseModelFromJson(response.body).profile;
await prefs.setString('token', token);
await prefs.setString('userId', userId);
await prefs.setString('profile', profile);
await prefs.setBool('loggedIn', true);
return true;
} else {
return false;
}
}
static Future<bool> signup(SignupModel model) async {
Map<String, String> requestHeaders = {'Content-Type': 'application/json'};
var url = Uri.https(Config.apiUrl, Config.signupUrl);
var response = await client.post(
url,
headers: requestHeaders,
body: jsonEncode(model),
);
if (response.statusCode == 201) {
return true;
} else {
return false;
}
}
static Future<bool> updateProfile(ProfileUpdateReq model) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
String? token = prefs.getString('token');
Map<String, String> requestHeaders = {
'Content-Type': 'application/json',
'token': 'Bearer $token'
};
var url = Uri.https(Config.apiUrl, Config.profileUrl);
var response = await client.put(
url,
headers: requestHeaders,
body: jsonEncode(model),
);
if (response.statusCode == 200) {
return true;
} else {
return false;
}
}
static Future<ProfileRes> getProfile() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
String? token = prefs.getString('token');
Map<String, String> requestHeaders = {
'Content-Type': 'application/json',
'token': 'Bearer $token'
};
var url = Uri.https(Config.apiUrl, Config.profileUrl);
var response = await client.get(
url,
headers: requestHeaders,
);
if (response.statusCode == 200) {
var profile = profileResFromJson(response.body);
return profile;
} else {
throw Exception("Failed to get the profile");
}
}
}
Login Provider
Part of the code is missing in the video. It's about login provider. Take the code as you need.
class LoginNotifier extends ChangeNotifier {
bool _obscureText = true;
bool get obscureText => _obscureText;
set obscureText(bool newState) {
_obscureText = newState;
notifyListeners();
}
bool _firstTime = true;
bool get firstTime => _firstTime;
set firstTime(bool newState) {
_firstTime = newState;
notifyListeners();
}
bool? _entrypoint;
bool get entrypoint => _entrypoint ?? false;
set entrypoint(bool newState) {
_entrypoint = newState;
notifyListeners();
}
bool? _loggedIn;
bool get loggedIn => _loggedIn ?? false;
set loggedIn(bool newState) {
_loggedIn = newState;
notifyListeners();
}
getPrefs() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
entrypoint = prefs.getBool('entrypoint') ?? false;
loggedIn = prefs.getBool('loggedIn') ?? false;
}
final loginFormKey = GlobalKey<FormState>();
final profileFormKey = GlobalKey<FormState>();
bool validateAndSave() {
final form = loginFormKey.currentState;
if (form!.validate()) {
form.save();
return true;
} else {
return false;
}
}
bool profileValidation() {
final form = profileFormKey.currentState;
if (form!.validate()) {
form.save();
return true;
} else {
return false;
}
}
userLogin(LoginModel model) {
AuthHelper.login(model).then((response) {
if (response && firstTime) {
Get.off(() => const PersonalDetails());
} else if (response && !firstTime) {
Get.off(() => const MainScreen());
} else if (!response) {
Get.snackbar("Sign Failed", "Please Check your credentials",
colorText: Color(kLight.value),
backgroundColor: Colors.red,
icon: const Icon(Icons.add_alert));
}
});
}
logout() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setBool('loggedIn', false);
await prefs.remove('token');
_firstTime = false;
}
updateProfile(ProfileUpdateReq model) async {
AuthHelper.updateProfile(model).then((response) {
if (response) {
Get.snackbar("Profile Update", "Enjoy your search for a job",
colorText: Color(kLight.value),
backgroundColor: Color(kLightBlue.value),
icon: const Icon(Icons.add_alert));
Future.delayed(const Duration(seconds: 3)).then((value) {
Get.offAll(() => const MainScreen());
});
} else {
Get.snackbar("Updating Failed", "Please try again",
colorText: Color(kLight.value),
backgroundColor: Color(kOrange.value),
icon: const Icon(Icons.add_alert));
}
});
}
}