Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
61 views65 pages

BCS 102 - Lab Final Assignment (Guidelines and Rubric)

Uploaded by

Boutchi TMN
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
61 views65 pages

BCS 102 - Lab Final Assignment (Guidelines and Rubric)

Uploaded by

Boutchi TMN
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 65

Faculty of Engineering, Applied Sciences, and Technology

BCS 102 - Introduction to Computing Science I

Lab Final Assignment


Course Code: BCS 102 Course Name: Introduction to Computing Science I
Date: Time: -
Location: -
Number of Students: Number of Pages: -

Lab
Course Learning Outcomes

CLO1. Design and construct simple software in a


conventional procedural programming language
CLO2. Demonstrate the implication of the representation of
integer and floating-point values in a digital
computer for underflow, overflow and round-off
errors
CLO3. Use an interactive development environment
(currently IDLE) to write functions of about 50 lines
X
of code in a conventional procedural programming
language (Python or JAVA)
CLO4. Implement the basic searching and sorting
algorithms: linear and binary search; bubble,
insertion and/or selection

Page 1 of 65
Assignment Description and Requirements
You are asked to implement the below tasks, attach the code, and the screen shots that prove that
your solution cover all test cases.

Rubric
Criterion Needs Improvement Good Excellent
[0-7] [8-11] [12-15]
Q&A The student provided The student answered The student answered
poor answers and some of the questions all questions correctly
insufficient evidence of correctly and showed and showed genuine
contribution to the some contribution to contribution to the
assignment. the assignment. assignment.

Criterion Needs Improvement Good Excellent


[0-1] [2-3] [4-5]
Coding & Report The code does not work The code works The code works fully
properly, many partially and and as expected per
requirements are implements some of the requirements and
missing, and the report the requirements; the the report shows all
does not provide enough report shows some testing scenarios.
proofs (screenshots) of testing scenarios.
testing scenarios.

Submission Instructions
1. This is a group-based assignment (5 members).
2. The assignment consists of two parts: Coding & Reporting part and Q&A session.
3. Coding Part:
a. Your code should work.
b. It should contain embedded comments.
c. You should cover all test cases.
d. You should provide screen shots of the mobile app screen showing the fields and
the app functionality.
e. You must submit two bugs and how you did locate and fix them using the IDE
debug functionality.
f. You should show understanding of your code during the Q&A to deserve the
report marks.
g. You must submit both Part 1 (Report) and Part 2 (Source code .dart file).
4. Q&A Part (Individual grade):

Page 2 of 65
a. Each student will be asked individually.
b. The questions cover theory and practice of the assignment.
5. Use this template for your final report submission. Fill out the names of students on the
front page (double-click the Excel table to edit).
6. Attach your report to the end of this document (copy and paste. Note: the code should be
submitted as text not image. In case of submitting the code as an image, the group will
get zero mark for this assignment).
Upload one submission per group including the report and the source code. In case of having
more than one submission per group, the group will lose the assignment mark.

Page 3 of 65
Introduction:
As universities grow and their programs expand, managing class schedules can become a
challenge. While printed schedules work, they are not always accessible or convenient for
students. This project aims to solve this problem by creating a mobile app that reads from a file
containing university timetable data and displays it in a user-friendly format.

Assignment Application Requirements:


This project aims to implement a Flutter mobile application that can read from a file containing
university’s timetable data and display it in an organized and easy-to-use format for students.
The app should allow students to view their class schedules for the current day.

 The file containing the timetable data should be in a specific format that can be easily parsed
by the app. The data should contain information about the course name, course code,
instructor, classroom, and time of the class.
 The app should have a clean and intuitive user interface that allows students to quickly view
their schedules without any confusion. The schedule should be displayed in a visual format,
such as a calendar or a grid, that clearly shows the time and location of each class.
 The app should also allow students to filter their schedule by course, instructor, or classroom.
 (Bonus Feature: 3 marks) In addition to the basic schedule display functionality, the app
should also include features such as reminders for upcoming classes, and the ability to add
notes or assignments related to specific classes.
 The project should follow good programming practices and principles, including writing
clean, well-documented code and using proper coding standards. The app should also be
tested thoroughly to ensure it is reliable and error-free.

Sample UI App

Page 4 of 65
Page 5 of 65
Code:(for the code we split it to multiple files so it would be organized)
1. main.screen:
import 'package:app_cud/db/db_helper.dart';
import 'package:app_cud/services/constants.dart';
import 'package:app_cud/services/routes.dart';
import 'package:app_cud/UI/splash_screen.dart';
import 'package:app_cud/services/theme_services.dart';
import 'package:flutter/material.dart';
import 'package:get/get_navigation/src/root/get_material_app.dart';
import 'package:get_storage/get_storage.dart';
import 'package:google_fonts/google_fonts.dart';

Future<void> main() async {


WidgetsFlutterBinding.ensureInitialized();
await DBHelper.initDb();
await GetStorage.init();
runApp(const MyApp());
}

class MyApp extends StatelessWidget {


const MyApp({super.key});

// This widget is the root of your application.


@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
//to use light theme for app
theme: ThemeData.light().copyWith(
backgroundColor: kOtherColor,
primaryColor: kPrimaryColor,
accentColor: kPrimaryColor,
brightness: Brightness.light,
appBarTheme: AppBarTheme(
color: kPrimaryColor,
elevation: 0,
),
//use google fonts for our app, we use lato
textTheme:
GoogleFonts.latoTextTheme(Theme.of(context).textTheme)
.apply()
.copyWith(
bodyLarge: const TextStyle(
color: kTextBlackColor,
fontSize: 35.0,
fontWeight: FontWeight.bold),
headlineSmall: const TextStyle(
color: kTextBlackColor,
fontSize: 22.0,
fontWeight: FontWeight.w500),
titleSmall: const TextStyle(
color: kTextBlackColor,
fontSize: 18.0,

Page 6 of 65
fontWeight: FontWeight.w300),
),

//input decoration theme for all our app


inputDecorationTheme: InputDecorationTheme(
//label style
labelStyle:
TextStyle(fontSize: 15.0, color: kTextLightColor, height: 0.5),
//hint style
hintStyle:
TextStyle(fontSize: 16.0, color: kTextBlackColor, height: 0.5),
//we are using underline input border
//not outline
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: kTextLightColor, width: 0.7),
),
border: UnderlineInputBorder(
borderSide: BorderSide(color: kTextLightColor),
),
disabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: kTextLightColor),
),
//on focus change color
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: kPrimaryColor,
),
),
//color change when user enters wrong information,
//we use validation for this process
errorBorder: UnderlineInputBorder(
borderSide: BorderSide(color: kErrorBorderColor, width: 1.2),
),
//same on focus error color
focusedErrorBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: kErrorBorderColor,
width: 1.2,
),
),
),
),
//Dark mode settings
darkTheme: ThemeData.dark().copyWith(
backgroundColor: kDarkGreyColor,
primaryColor: kPrimaryColorDark,
accentColor: kPrimaryColorDark,
brightness: Brightness.dark,
appBarTheme: AppBarTheme(
color: kPrimaryColorDark,
elevation: 0,
),
textTheme:
GoogleFonts.latoTextTheme(Theme.of(context).textTheme)
.apply()

Page 7 of 65
.copyWith(
bodyLarge: const TextStyle(
color: kTextWhiteColor,
fontSize: 35.0,
fontWeight: FontWeight.bold),
headlineSmall: const TextStyle(
color: kTextWhiteColor,
fontSize: 22.0,
fontWeight: FontWeight.w500),
titleSmall: const TextStyle(
color: kTextWhiteColor,
fontSize: 18.0,
fontWeight: FontWeight.w300),
),
//input decoration theme for all our app
inputDecorationTheme: InputDecorationTheme(
//label style
labelStyle:
TextStyle(fontSize: 15.0, color: kTextLightColor, height: 0.5),
//hint style
hintStyle:
TextStyle(fontSize: 16.0, color: kTextBlackColor, height: 0.5),
//we are using underline input border
//not outline
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: kTextLightColor, width: 0.7),
),
border: UnderlineInputBorder(
borderSide: BorderSide(color: kTextLightColor),
),
disabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: kTextLightColor),
),
//on focus change color
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: kPrimaryColor,
),
),
//color change when user enters wrong information,
//we use validation for this process
errorBorder: UnderlineInputBorder(
borderSide: BorderSide(color: kErrorBorderColor, width: 1.2),
),
//same on focus error color
focusedErrorBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: kErrorBorderColor,
width: 1.2,
),
),
),
),
themeMode: ThemeServices().theme,
//initial route is splash screen

Page 8 of 65
// as in the first screen
initialRoute: SplashScreen.routeName,
//define the routes file here to access the routes any where
routes: routes,
);
}
}
2. UI:
a. splash_screen.dart:
import 'package:app_cud/UI/login_screen.dart';
import 'package:app_cud/services/constants.dart';
import 'package:flutter/material.dart';

class SplashScreen extends StatelessWidget {


//route name for the screen
static String routeName = 'SplashScreen';

const SplashScreen({super.key});
@override
Widget build(BuildContext context) {
//we use future to go from one screen to other via duration time
Future.delayed(const Duration(seconds: 5), () {
//no return when user is on login screen and press back, it will not
return the user to the splash screen
Navigator.pushNamedAndRemoveUntil(
context, LoginScreen.routeName, (route) => false);
});

//scaffold color set to primary color in main in our text theme


return Scaffold(
body: ListView(
children: [
//divide the body into two half
Container(
color: kPrimaryColor,
child: SizedBox(
//use media query in order to fit all sizes in same manner
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'assets/images/splash.png',
height: 200.0,
width: 200.0,
),
const SizedBox(
height: kDefaultPadding / 2,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,

Page 9 of 65
children: [
Text(
'Canadian University',
style: Theme.of(context)
.textTheme
.bodyLarge!
.copyWith(color: kTextWhiteColor),
),
],
),
SizedBox(
height: kDefaultPadding / 6,
),
Text(
'of Dubai',
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
color: kTextWhiteColor,
),
),
],
),
),
)
],
),
);
}
}
b. login_screen:
import 'package:app_cud/UI/home_screen.dart';
import 'package:app_cud/controllers/course_controller.dart';
import 'package:app_cud/services/constants.dart';
import 'package:app_cud/services/theme_services.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';

import 'widgets/custom_buttons.dart';

late bool _passwordVisible;

class LoginScreen extends StatefulWidget {


static String routeName = 'LoginScreen';

const LoginScreen({super.key});

@override
State<LoginScreen> createState() => _LoginScreenState();
}

class _LoginScreenState extends State<LoginScreen> {


//validate our form now
final _formKey = GlobalKey<FormState>();
final CourseController _courseController = Get.put(CourseController());

Page 10 of 65
//charges current state
@override
void initState() {
super.initState();
_passwordVisible = true;
}

@override
Widget build(BuildContext context) {
return GestureDetector(
//when user taps anywhere on the screen, keyboard hides
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
child: Scaffold(
appBar: AppBar(
actions: [
GestureDetector(
onTap: () {
ThemeServices().switchTheme();
},
child: Icon(Get.isDarkMode ?
Icons.wb_sunny:Icons.nightlight_round,
size: 20,
),
),
SizedBox(
width: 20,
),
],
),
body: Container(
child: ListView(
children: [
//divide the body into two half
Container(
decoration: BoxDecoration(
color: kPrimaryColor,
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(kDefaultPadding * 1.5),
bottomLeft: Radius.circular(kDefaultPadding * 1.5),
),
),
child: SizedBox(
//use media query in order to fit all sizes in same manner
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height / 2.3,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'assets/images/splash.png',
height: 150.0,
width: 150.0,
),

Page 11 of 65
const SizedBox(
height: kDefaultPadding / 2,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Hi ',
style: Theme.of(context)
.textTheme
.bodyLarge!
.copyWith(
fontWeight: FontWeight.w200,
color: kTextWhiteColor,
),
),
Text(
'Student',
style: Theme.of(context)
.textTheme
.bodyLarge!
.copyWith(
color: kTextWhiteColor,
),
),
],
),
const SizedBox(
height: kDefaultPadding / 6,
),
Text(
'Sign in to continue',
style:

Theme.of(context).textTheme.titleSmall!.copyWith(
color: kTextWhiteColor,
),
),
],
),
),
),
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
decoration: const BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(kDefaultPadding * 1.5),
topRight: Radius.circular(kDefaultPadding * 1.5),
),
),
child: Padding(
padding: const EdgeInsets.all(kDefaultPadding),
child: Column(
children: [

Page 12 of 65
Form(
key: _formKey,
child: Column(
children: [
sizedBox,
buildEmailField(),
sizedBox,
buildPasswordField(),
sizedBox,
DefaultButton(
onPress: () async {
if (_formKey.currentState!.validate()) {
_courseController.getCourses();
await
Navigator.pushNamedAndRemoveUntil(context,
HomeScreen.routeName, (route) =>
false);
}
},
title: 'SIGN IN',
iconData: Icons.arrow_forward_outlined,
),
sizedBox,
Align(
alignment: Alignment.bottomRight,
child: Text(
'Forgot Password',
textAlign: TextAlign.end,
style: TextStyle(
color: kPrimaryColor, fontSize: 15.0),
),
)
],
),
),
],
),
),
)
],
),
)),
);
}

TextFormField buildEmailField() {
return TextFormField(
textAlign: TextAlign.start,
keyboardType: TextInputType.emailAddress,
style: TextStyle(
color: Get.isDarkMode?Colors.white:Colors.black,
fontSize: 17.0,
fontWeight: FontWeight.w300,
),
decoration: const InputDecoration(

Page 13 of 65
labelText: 'Email Address',
floatingLabelBehavior: FloatingLabelBehavior.always,
isDense: true,
),
validator: (value) {
//for validation
RegExp regExp = new RegExp(emailPattern);
if (value == null || value.isEmpty) {
return 'please enter some text';
//if it does not match the pattern, like
//it does not contain @
} else if (!regExp.hasMatch(value)) {
'Please enter a valid email address';
}
},
);
}

TextFormField buildPasswordField() {
return TextFormField(
obscureText: _passwordVisible,
textAlign: TextAlign.start,
keyboardType: TextInputType.visiblePassword,
style: TextStyle(
color: Get.isDarkMode?Colors.white:Colors.black,
fontSize: 17.0,
fontWeight: FontWeight.w300,
),
decoration: InputDecoration(
labelText: 'Password',
floatingLabelBehavior: FloatingLabelBehavior.always,
isDense: true,
suffixIcon: IconButton(
onPressed: () {
setState(() {
_passwordVisible = !_passwordVisible;
});
},
icon: Icon(
_passwordVisible
? Icons.visibility
: Icons.visibility_off_outlined,
),
iconSize: kDefaultPadding,
),
),
validator: (value) {
if (value!.length < 5) {
return 'Must be more than 5 characters';
}
},
);
}
}

Page 14 of 65
c. home_screen.dart:
import 'package:app_cud/UI/assignment_screen.dart';
import 'package:app_cud/UI/calendar_screen.dart';
import 'package:app_cud/UI/my_profile.dart';
import 'package:app_cud/controllers/task_controller.dart';
import 'package:app_cud/services/constants.dart';
import 'package:app_cud/services/theme_services.dart';
import 'package:app_cud/UI/widgets/course_tile.dart';
import 'package:flutter/material.dart';
import
'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:get/get.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:app_cud/controllers/course_controller.dart';
import '../models/course.dart';
import 'widgets/student_data.dart';

class HomeScreen extends StatefulWidget {


const HomeScreen({Key? key}) : super(key: key);
static String routeName = 'HomeScreen';

@override
State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {


final CourseController coursesController = Get.find();
final TaskController _taskController = Get.put(TaskController());

final List<String> _daysOfWeek = [


'MON',
'TUE',
'WED',
'THU',
'FRI',
'SAT',
'SUN'
];

String _selectedDay = 'MON';

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
GestureDetector(
onTap: () {
ThemeServices().switchTheme();
},
child: Icon(
Get.isDarkMode ? Icons.wb_sunny : Icons.nightlight_round,
size: 20,
),
),

Page 15 of 65
SizedBox(
width: 20,
),
],
),
body: Column(
children: [
//we divide the screen into two
//fixed height for first half
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height / 4.5,
decoration: BoxDecoration(
color: Get.isDarkMode? kPrimaryColorDark:kPrimaryColor,
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(kDefaultPadding * 1.6),
bottomLeft: Radius.circular(kDefaultPadding * 1.6),
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
StudentName(
studentName: 'Someone',
),
kHalfSisedBox,
StudentID(studentID: 'ID: 20220002456'),
kHalfSisedBox,
StudentYear(studentYear: 'SP 2022-2023'),
],
),
kHalfSisedBox,
StudentPicture(
picAddress: 'assets/images/splash.png',
onPress: () {
// go to profile detail screen
Navigator.pushNamed(
context, MyProfileScreen.routeName);
})
],
),
Expanded(
child: Align(
alignment: FractionalOffset.bottomCenter,
child: Padding(
padding: EdgeInsets.only(bottom: 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [

Page 16 of 65
//Time Table Button
InkWell(
onTap: () {},
child: Container(
width: MediaQuery.of(context).size.width / 3.33,
height: MediaQuery.of(context).size.height / 15,
decoration: BoxDecoration(
color: Get.isDarkMode?
kDarkGreyColor:kOtherColor,
borderRadius: BorderRadius.circular(200),
),
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
Text(
'Time Table',
style: Theme.of(context)
.textTheme
.titleSmall!
.copyWith(
fontWeight: FontWeight.w500,
color: Get.isDarkMode?
kOtherColor:kPrimaryColor),
),
],
),
),
),
//Calender Button
InkWell(
onTap: () {
Navigator.pushNamed(
context, CalendarScreen.routeName);
},
child: Container(
width: MediaQuery.of(context).size.width / 3.33,
height: MediaQuery.of(context).size.height / 15,
decoration: BoxDecoration(
color: Get.isDarkMode?
kSecondaryColorDark:kSecondaryColor,
borderRadius: BorderRadius.circular(200),
),
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
Text(
'Calender',
style: Theme.of(context)
.textTheme
.titleSmall!
.copyWith(color: kOtherColor),
),
],

Page 17 of 65
),
),
),
//Assignment Button
InkWell(
onTap: () async {
_taskController.getTasks();
await Navigator.pushNamed(
context, AssignmentScreen.routeName);
},
child: Container(
width: MediaQuery.of(context).size.width / 3.33,
height: MediaQuery.of(context).size.height / 15,
decoration: BoxDecoration(
color: Get.isDarkMode?
kSecondaryColorDark:kSecondaryColor,
borderRadius: BorderRadius.circular(200),
),
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
Text(
'Assignments',
style: Theme.of(context)
.textTheme
.titleSmall!
.copyWith(color: kOtherColor),
),
],
),
),
),
],
),
),
),
)
],
),
),

//other will use all the remaining height of screen


Expanded(
child: Column(
children: [
// Create a horizontal ListView to display the days of the week.
Container(
margin: const EdgeInsets.only(top: 20, left: 20, right: 20),
height: 100,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: _daysOfWeek.length,
itemBuilder: (BuildContext context, int index) {
return GestureDetector(

Page 18 of 65
onTap: () {
// Update the state to show the tasks for the selected
day.
setState(() {
_selectedDay = _daysOfWeek[index];
});
},
child: Container(
width: 80,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: _daysOfWeek[index] == _selectedDay
? Get.isDarkMode?
kPrimaryColorDark:kPrimaryColor
: Colors.transparent,
),
child: Text(
_daysOfWeek[index],
style: GoogleFonts.lato(
textStyle: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color: _daysOfWeek[index] == _selectedDay
? Colors.white
: kTextLightColor,
),
),
),
),
);
},
),
),
SizedBox(
height: 20,
),
Expanded(
child: Obx(() {
final List<Course> courses = coursesController.courses;
return ListView.builder(
itemCount: courses.length,
itemBuilder: (context, index) {
final Course course = courses[index];
if(course.day == _selectedDay){
return AnimationConfiguration.staggeredList(
position: index,
child: SlideAnimation(
child: FadeInAnimation(
child: Row(
children: [
GestureDetector(
child: CourseTile(
course),
)

Page 19 of 65
],
),
),
),
);
}else{
return Container();
}
});
}),
)
],
)),
],
),
);
}
}
d. calender_screen:
import 'package:app_cud/UI/assignment_screen.dart';
import 'package:app_cud/UI/home_screen.dart';
import 'package:app_cud/UI/my_profile.dart';
import 'package:app_cud/controllers/course_controller.dart';
import 'package:app_cud/controllers/task_controller.dart';
import 'package:app_cud/services/constants.dart';
import 'package:app_cud/services/theme_services.dart';
import 'package:app_cud/UI/widgets/calendar_widget.dart';
import 'package:app_cud/UI/widgets/student_data.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';
import 'package:table_calendar/table_calendar.dart';

class CalendarScreen extends StatefulWidget {


const CalendarScreen({Key? key}) : super(key: key);
static String routeName = 'CalendarScreen';

@override
State<CalendarScreen> createState() => _CalendarScreenState();
}

class _CalendarScreenState extends State<CalendarScreen> {


final TaskController _taskController = Get.put(TaskController());
final CourseController _courseController = Get.put(CourseController());

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
GestureDetector(
onTap: () {
ThemeServices().switchTheme();

Page 20 of 65
},
child: Icon(
Get.isDarkMode ? Icons.wb_sunny : Icons.nightlight_round,
size: 20,
),
),
SizedBox(
width: 20,
),
],
),
body: Column(
children: [
//we divide the screen into two
//fixed height for first half
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height / 4.5,
decoration: BoxDecoration(
color: Get.isDarkMode? kPrimaryColorDark:kPrimaryColor,
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(kDefaultPadding * 1.6),
bottomLeft: Radius.circular(kDefaultPadding * 1.6),
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
StudentName(
studentName: 'Someone',
),
kHalfSisedBox,
StudentID(studentID: 'ID: 20220002456'),
kHalfSisedBox,
StudentYear(studentYear: 'SP 2022-2023'),
],
),
kHalfSisedBox,
StudentPicture(
picAddress: 'assets/images/splash.png',
onPress: () {
// go to profile detail screen
Navigator.pushNamed(
context, MyProfileScreen.routeName);
})
],
),
Expanded(
child: Align(

Page 21 of 65
alignment: FractionalOffset.bottomCenter,
child: Padding(
padding: EdgeInsets.only(bottom: 10.0),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
//Time Table Button
InkWell(
onTap: () async {
_courseController.getCourses();
await Navigator.pushNamed(
context, HomeScreen.routeName);
},
child: Container(
width: MediaQuery.of(context).size.width /
3.33,
height:
MediaQuery.of(context).size.height /
15,
decoration: BoxDecoration(
color: Get.isDarkMode?
kSecondaryColorDark:kSecondaryColor,
borderRadius:
BorderRadius.circular(200),
),
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
Text(
'Time Table',
style: Theme.of(context)
.textTheme
.titleSmall!
.copyWith(color: kOtherColor),
),
],
),
),
),
//Calender Button
InkWell(
onTap: () {},
child: Container(
width: MediaQuery.of(context).size.width /
3.33,
height:
MediaQuery.of(context).size.height /
15,
decoration: BoxDecoration(
color: Get.isDarkMode?
kDarkGreyColor:kOtherColor,
borderRadius:
BorderRadius.circular(200),

Page 22 of 65
),
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
Text(
'Calender',
style: Theme.of(context)
.textTheme
.titleSmall!
.copyWith(fontWeight:FontWeight.
w500,color: Get.isDarkMode? kOtherColor:kPrimaryColor),
),
],
),
),
),
//Assignment Button
InkWell(
onTap: () async {
_taskController.getTasks();
await Navigator.pushNamed(
context, AssignmentScreen.routeName);

},
child: Container(
width: MediaQuery.of(context).size.width /
3.33,
height:
MediaQuery.of(context).size.height /
15,
decoration: BoxDecoration(
color: Get.isDarkMode?
kSecondaryColorDark:kSecondaryColor,
borderRadius:
BorderRadius.circular(200),
),
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
Text(
'Assignments',
style: Theme.of(context)
.textTheme
.titleSmall!
.copyWith(color: kOtherColor),
),
],
),
),
),
],
))),
)

Page 23 of 65
],
),
),

Expanded(child: CalendarPage()),
],
));
}
}
e. assignment_screen.dart:

import 'package:app_cud/UI/add_task_bar.dart';
import 'package:app_cud/UI/calendar_screen.dart';
import 'package:app_cud/UI/home_screen.dart';
import 'package:app_cud/UI/my_profile.dart';
import 'package:app_cud/models/task.dart';
import 'package:app_cud/services/constants.dart';
import 'package:app_cud/services/theme_services.dart';
import 'package:app_cud/UI/widgets/add_button.dart';
import 'package:app_cud/UI/widgets/student_data.dart';
import 'package:app_cud/UI/widgets/task_tile.dart';
import 'package:date_picker_timeline/date_picker_timeline.dart';
import 'package:flutter/material.dart';
import
'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:get/get.dart';
import 'package:get/get_core/src/get_main.dart';
import 'package:get/get_navigation/get_navigation.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';

import '../controllers/task_controller.dart';

class AssignmentScreen extends StatefulWidget {


const AssignmentScreen({Key? key}) : super(key: key);
static String routeName = 'AssignmentScreen';

@override
State<AssignmentScreen> createState() => _AssignmentScreenState();
}

class _AssignmentScreenState extends State<AssignmentScreen> {


DateTime _selectedDate = DateTime.now();
final TaskController _taskController = Get.put(TaskController());

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
iconTheme: IconThemeData(
color: kOtherColor,
),
actions: [
GestureDetector(

Page 24 of 65
onTap: () {
ThemeServices().switchTheme();
},
child: Icon(
Get.isDarkMode ? Icons.wb_sunny : Icons.nightlight_round,
size: 20,
color: kOtherColor,
),
),
SizedBox(
width: 20,
),
],
),
body: Column(
children: [
//we divide the screen into two
//fixed height for first half
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height / 4.5,
decoration: BoxDecoration(
color: Get.isDarkMode?kPrimaryColorDark:kPrimaryColor,
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(kDefaultPadding * 1.6),
bottomLeft: Radius.circular(kDefaultPadding * 1.6),
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
StudentName(
studentName: 'Someone',
),
kHalfSisedBox,
StudentID(studentID: 'ID: 20220002456'),
kHalfSisedBox,
StudentYear(studentYear: 'SP 2022-2023'),
],
),
kHalfSisedBox,
StudentPicture(
picAddress: 'assets/images/splash.png',
onPress: () {
Navigator.pushNamed(
context, MyProfileScreen.routeName);
})
],
),

Page 25 of 65
Expanded(
child: Align(
alignment: FractionalOffset.bottomCenter,
child: Padding(
padding: EdgeInsets.only(bottom: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
//Time Table Button
InkWell(
onTap: () {
Navigator.pushNamed(
context, HomeScreen.routeName);
},
child: Container(
width:
MediaQuery.of(context).size.width / 3.33,
height:
MediaQuery.of(context).size.height / 15,
decoration: BoxDecoration(
color: Get.isDarkMode?
kSecondaryColorDark:kSecondaryColor,
borderRadius: BorderRadius.circular(200),
),
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
Text(
'Time Table',
style: Theme.of(context)
.textTheme
.titleSmall!
.copyWith(color: kOtherColor),
),
],
),
),
),
//Calender Button
InkWell(
onTap: () {
Navigator.pushNamed(
context, CalendarScreen.routeName);
},
child: Container(
width:
MediaQuery.of(context).size.width / 3.33,
height:
MediaQuery.of(context).size.height / 15,
decoration: BoxDecoration(
color: Get.isDarkMode?
kSecondaryColorDark:kSecondaryColor,
borderRadius: BorderRadius.circular(200),
),

Page 26 of 65
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
Text(
'Calender',
style: Theme.of(context)
.textTheme
.titleSmall!
.copyWith(color: kOtherColor),
),
],
),
),
),
//Assignment Button
InkWell(
onTap: () {},
child: Container(
width:
MediaQuery.of(context).size.width / 3.33,
height:
MediaQuery.of(context).size.height / 15,
decoration: BoxDecoration(
color: Get.isDarkMode?
kDarkGreyColor:kOtherColor,
borderRadius: BorderRadius.circular(200),
),
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
Text(
'Assignments',
style: Theme.of(context)
.textTheme
.titleSmall!
.copyWith(
fontWeight: FontWeight.w500,
color: Get.isDarkMode?
kOtherColor:kPrimaryColor),
),
],
),
),
),
],
),),),
)
],
),
),

Expanded(
child: Column(

Page 27 of 65
children: [
Container(
margin: const EdgeInsets.only(left: 20, right: 20, top: 15),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
DateFormat.yMMMMd().format(DateTime.now()),
style: Theme.of(context)
.textTheme
.bodyLarge!
.copyWith(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Get.isDarkMode
? Colors.grey[400]
: Colors.grey,
),
),
Text(
"Today",
style: Theme.of(context)
.textTheme
.bodyLarge!
.copyWith(
fontSize: 30,
fontWeight: FontWeight.bold,
color: Get.isDarkMode
? Colors.white
: Colors.black),
),
],
),
),
AddButton(
label: '+ Add Task',
onPress: () async {
await Navigator.pushNamed(
context, AddTaskPage.routeName);
_taskController.getTasks();
})
],
),
),
Container(
margin: const EdgeInsets.only(top: 20, left: 20),
child: DatePicker(
DateTime.now(),
height: 100,
width: 80,
initialSelectedDate: DateTime.now(),

Page 28 of 65
selectionColor: Get.isDarkMode?
kPrimaryColorDark:kPrimaryColor,
selectedTextColor: kTextWhiteColor,
dateTextStyle: GoogleFonts.lato(
textStyle: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color: kTextLightColor,
)),
dayTextStyle: GoogleFonts.lato(
textStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: kTextLightColor,
)),
monthTextStyle: GoogleFonts.lato(
textStyle: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: kTextLightColor,
)),
onDateChange: (date) {
setState(() {
_selectedDate=date;
});
},
),
),
SizedBox(
height: 15,
),
Expanded(
child: Obx(() {
return ListView.builder(
itemCount: _taskController.taskList.length,
itemBuilder: (_, index) {
Task task = _taskController.taskList[index];
DateTime startDate =
DateFormat.yMd().parse(task.startDate!);
DateTime endDate =
DateFormat.yMd().parse(task.endDate!);
if
(_selectedDate.isAfter(startDate.subtract(Duration(days: 1))) &&

_selectedDate.isBefore(endDate.add(Duration(days: 1)))) {
return AnimationConfiguration.staggeredList(
position: index,
child: SlideAnimation(
child: FadeInAnimation(
child: Row(
children: [
GestureDetector(
onTap: () {
_showBottomSheet(context, task);
},

Page 29 of 65
child: TaskTile(task),
)
],
),
),
),
);
} else {
return Container();
}
});
}),
)
],
),
),
],
),
);
}

_showBottomSheet(BuildContext context, Task task) {


Get.bottomSheet(
Container(
padding: const EdgeInsets.only(top: 4),
height: task.isCompleted == 1
? MediaQuery.of(context).size.height * 0.24
: MediaQuery.of(context).size.height * 0.32,
color: Get.isDarkMode ? kDarkGreyColor : Colors.white,
child: Column(
children: [
Container(
height: 6,
width: 120,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Get.isDarkMode ? Colors.grey[600] :
Colors.grey[300]),
),
Spacer(),
task.isCompleted == 1
? Container()
: _bottomSheetButton(
label: 'Task Completed',
onTap: () {
_taskController.markTaskCompleted(task.id!);
Get.back();
},
clr: kBlueColor,
context: context,
),

_bottomSheetButton(
label: 'Delete Task',
onTap: () {

Page 30 of 65
_taskController.delete(task);
Get.back();
},
clr:Colors.red[300]!,
context: context),
SizedBox(height: 20,),
_bottomSheetButton(
label: 'Close',
onTap: () {
Get.back();
},
clr:Colors.red[300]!,
isClose: true,
context: context),
SizedBox(height: 10,)
],
),
),
);
}

_bottomSheetButton(
{required String label,
required Function()? onTap,
required Color clr,
bool isClose = false,
required BuildContext context}) {
return GestureDetector(
onTap: onTap,
child: Container(
margin: const EdgeInsets.symmetric(vertical: 4),
height: 55,
width: MediaQuery.of(context).size.width * 0.9,
decoration: BoxDecoration(
border:
Border.all(width: 2, color: isClose == true ? Get.isDarkMode?
Colors.grey[600]!:Colors.grey[300]! : clr),
borderRadius: BorderRadius.circular(20),
color: isClose == true ? Colors.transparent : clr,
),
child: Center(
child: Text(
label,
style: isClose==true?GoogleFonts.lato(
textStyle: TextStyle(fontSize: 16,fontWeight:
FontWeight.w400,color: Get.isDarkMode?
Colors.white:Colors.black)):GoogleFonts.lato(
textStyle: TextStyle(fontSize: 16,fontWeight:
FontWeight.w400,color: Colors.white)),
),
),
),
);
}
}

Page 31 of 65
f. add_task_bar.dart:
import 'package:app_cud/controllers/task_controller.dart';
import 'package:app_cud/models/task.dart';
import 'package:app_cud/services/constants.dart';
import 'package:app_cud/services/theme_services.dart';
import 'package:app_cud/UI/widgets/add_button.dart';
import 'package:app_cud/UI/widgets/input_field.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get/get_core/src/get_main.dart';
import 'package:get/get_navigation/get_navigation.dart';
import 'package:intl/intl.dart';

class AddTaskPage extends StatefulWidget {


const AddTaskPage({Key? key}) : super(key: key);
static String routeName = 'AddTaskPage';

@override
State<AddTaskPage> createState() => _AddTaskPageState();
}

class _AddTaskPageState extends State<AddTaskPage> {


final TaskController _taskController = Get.put(TaskController());
final TextEditingController _titleController = TextEditingController();
final TextEditingController _noteController = TextEditingController();
DateTime _selectedStartDate = DateTime.now();
DateTime _selectedEndDate = DateTime.now();
String _endTime = "9:30 PM";
String _startTime = DateFormat('hh:mm a').format(DateTime.now()).toString();
int _selectedReminder = 5;
List<int> reminderList = [
5,
10,
15,
20,
];
String _selectedRepeat = "None";
List<String> repeatList = ["None", "Daily", "Weekly", "Monthly"];
int _selectedColor = 0;

@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
hideKeyboard(context);
},
child: Scaffold(
appBar: AppBar(
iconTheme: IconThemeData(
color: Get.isDarkMode ? kOtherColor : kDarkGreyColor,
),
automaticallyImplyLeading: true,
backgroundColor: Colors.transparent,
actions: [

Page 32 of 65
GestureDetector(
onTap: () {
ThemeServices().switchTheme();
},
child: Icon(
Get.isDarkMode ? Icons.wb_sunny : Icons.nightlight_round,
size: 20,
color: Get.isDarkMode ? kOtherColor : kDarkGreyColor,
),
),
SizedBox(
width: 20,
),
],
),
body: Container(
padding: const EdgeInsets.only(left: 20, right: 20, top: 0),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 20,
),
Text(
'Add Task',
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
fontSize: 30,
fontWeight: FontWeight.bold,
color: Get.isDarkMode ? Colors.white : Colors.black),
),
MyInputField(
title: 'Title',
hint: 'Enter title here',
controller: _titleController,
),
MyInputField(
title: 'Note',
hint: 'Enter your note',
controller: _noteController,
),
Row(
children: [
Expanded(
child: MyInputField(
title: "Start Date",
hint: DateFormat.yMd().format(_selectedStartDate),
widget: IconButton(
onPressed: () {
_getDateFromUser(isStartDate: true);
},
icon: Icon(
Icons.calendar_today_outlined,
color: kTextLightColor,
),

Page 33 of 65
),
),
),
SizedBox(
width: 12,
),
Expanded(
child: MyInputField(
title: "start Time",
hint: _startTime,
widget: IconButton(
onPressed: () {
_getTimeFromUser(isStartTime: false);
},
icon: Icon(Icons.access_time_rounded),
color: kTextLightColor,
),
),
),
],
),
Row(
children: [
Expanded(
child: MyInputField(
title: "End Date",
hint: DateFormat.yMd().format(_selectedEndDate),
widget: IconButton(
onPressed: () {
_getDateFromUser(isStartDate: false);
},
icon: Icon(
Icons.calendar_today_outlined,
color: kTextLightColor,
),
),
),
),
SizedBox(
width: 12,
),
Expanded(
child: MyInputField(
title: "End Time",
hint: _endTime,
widget: IconButton(
onPressed: () {
_getTimeFromUser(isStartTime: false);
},
icon: Icon(Icons.access_time_rounded),
color: kTextLightColor,
),
),
),
],

Page 34 of 65
),
MyInputField(
title: 'Remind',
hint: "$_selectedReminder minutes early",
widget: DropdownButton(
icon: Icon(
Icons.keyboard_arrow_down,
color: kTextLightColor,
),
iconSize: 32,
elevation: 4,
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Get.isDarkMode
? Colors.grey[100]
: Colors.grey[700],
),
underline: Container(
height: 0,
),
onChanged: (String? newValue) {
setState(() {
_selectedReminder = int.parse(newValue!);
});
},
items:
reminderList.map<DropdownMenuItem<String>>((int value)
{
return DropdownMenuItem<String>(
value: value.toString(),
child: Text(value.toString()),
);
}).toList(),
),
),
SizedBox(
height: 18,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Color",
style:
Theme.of(context).textTheme.bodyLarge!.copyWith(
fontSize: 16,
fontWeight: FontWeight.w400,
color: Get.isDarkMode
? Colors.white
: Colors.black,

Page 35 of 65
),
),
SizedBox(
height: 8.0,
),
Wrap(
children: List<Widget>.generate(6, (int index) {
return GestureDetector(
onTap: () {
setState(() {
_selectedColor = index;
});
},
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: CircleAvatar(
radius: 14,
backgroundColor: index == 0 ? kOrangeColor :
index == 1 ? Colors.amber[700] :index == 2 ? Colors.green[900] : index == 3 ?
kBlueColor : index == 4 ? Colors.purple[600] : kPinkColor,
child: _selectedColor == index
? Icon(
Icons.done,
color: Colors.white,
size: 16,
)
: Container(),
),
),
);
}),
),
],
),
AddButton(
label: 'Create Task',
onPress: () {
_validateData();
_taskController.getTasks();
},
)
],
)
],
),
),
),
),
);
}

_validateData() {
if (_titleController.text.isNotEmpty && _noteController.text.isNotEmpty &&
_selectedStartDate.isBefore(_selectedEndDate)) {
_addTaskToDb();

Page 36 of 65
Get.back();
} else if (_titleController.text.isEmpty || _noteController.text.isEmpty)
{
Get.snackbar(
"Required",
'All fields are required !',
margin: EdgeInsets.only(left: 20, right: 20, bottom: 20),
snackPosition: SnackPosition.BOTTOM,
backgroundColor: Get.isDarkMode?Colors.grey[900]:Colors.grey[200],
colorText: kPrimaryColor,
icon: Icon(
Icons.warning_amber_rounded,
color: kPrimaryColor,
),
);
} else if(_selectedStartDate.isAfter(_selectedEndDate)){
Get.snackbar(
"Required",
'End date to be after Start date',
margin: EdgeInsets.only(left: 20, right: 20, bottom: 20),
snackPosition: SnackPosition.BOTTOM,
backgroundColor: Get.isDarkMode?Colors.grey[900]:Colors.grey[200],
colorText: kPrimaryColor,
icon: Icon(
Icons.warning_amber_rounded,
color: kPrimaryColor,
),
);
}
}

_addTaskToDb() async {
var value = await _taskController.addTask(
task: Task(
note: _noteController.text,
title: _titleController.text,
startDate: DateFormat.yMd().format(_selectedStartDate),
endDate: DateFormat.yMd().format(_selectedEndDate),
startTime: _startTime,
endTime: _endTime,
remind: _selectedReminder,
color: _selectedColor,
isCompleted: 0,
));
print("My id is " + "$value");
}

_getDateFromUser({required bool isStartDate}) async {


DateTime? _pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2020),
lastDate: DateTime(2123));
if (_pickedDate == null) {
print('no date was picked');

Page 37 of 65
} else if (isStartDate == true) {
setState(() {
_selectedStartDate = _pickedDate;
});
} else if (isStartDate == false) {
setState(() {
_selectedEndDate = _pickedDate;
});
}
}

_showTimePicker() {
return showTimePicker(
initialEntryMode: TimePickerEntryMode.input,
context: context,
initialTime: TimeOfDay(
hour: int.parse(_startTime.split(":")[0]),
minute: int.parse(_startTime.split(":")[1].split(' ')[0]),
),
);
}

_getTimeFromUser({required bool isStartTime}) async {


var pickedTime = await _showTimePicker();
String _formatedTime = pickedTime.format(context);
if (pickedTime == null) {
print("time cancelled");
} else if (isStartTime == true) {
setState(() {
_startTime = _formatedTime;
});
} else if (isStartTime == false) {
setState(() {
_endTime = _formatedTime;
});
}
}

void hideKeyboard(BuildContext context) {


FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
}
}

g. Widgets:

i. task_tile.dart:
import 'package:app_cud/models/task.dart';
import 'package:app_cud/services/constants.dart';
import 'package:flutter/cupertino.dart';

Page 38 of 65
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';

class TaskTile extends StatelessWidget {


final Task? task;
TaskTile(this.task);

@override
Widget build(BuildContext context) {
return Container(
padding:
EdgeInsets.symmetric(horizontal: 20),
width: MediaQuery.of(context).size.width,
margin: EdgeInsets.only(bottom: 12),
child: Container(
padding: EdgeInsets.all(16),
// width: SizeConfig.screenWidth * 0.78,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: _getBGClr(task?.color??0),
),
child: Row(children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
task?.title??"",
style: GoogleFonts.lato(
textStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white),
),
),
SizedBox(
height: 12,
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
children: [
Text(
"Start:",
style: GoogleFonts.lato(
textStyle:
TextStyle(fontSize: 13, color: Colors.grey[100]),
),
),
SizedBox(width: 8),
Icon(
Icons.date_range_rounded,
color: Colors.grey[200],
size: 18,

Page 39 of 65
),
SizedBox(width: 4),
Text(
"${task!.startDate}",
style: GoogleFonts.lato(
textStyle:
TextStyle(fontSize: 13, color: Colors.grey[100]),
),
),
],
),
SizedBox(width: 20,),
Row(
children: [
Icon(
Icons.access_time_rounded,
color: Colors.grey[200],
size: 18,
),
SizedBox(width: 4),
Text(
"${task!.startTime}",
style: GoogleFonts.lato(
textStyle:
TextStyle(fontSize: 13, color: Colors.grey[100]),
),
),
],
),
],
),
SizedBox(
height: 12,
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"End: ",
style: GoogleFonts.lato(
textStyle:
TextStyle(fontSize: 13, color: Colors.grey[100]),
),
),
SizedBox(width: 8),
Row(
children: [
Icon(
Icons.date_range_rounded,
color: Colors.grey[200],
size: 18,
),
SizedBox(width: 4),
Text(
"${task!.endDate}",

Page 40 of 65
style: GoogleFonts.lato(
textStyle:
TextStyle(fontSize: 13, color: Colors.grey[100]),
),
),
],
),
SizedBox(width: 20,),
Row(
children: [
Icon(
Icons.access_time_rounded,
color: Colors.grey[200],
size: 18,
),
SizedBox(width: 4),
Text(
"${task!.endTime}",
style: GoogleFonts.lato(
textStyle:
TextStyle(fontSize: 13, color: Colors.grey[100]),
),
),
],
),
],
),
SizedBox(height: 12),
Text(
task?.note??"",
style: GoogleFonts.lato(
textStyle: TextStyle(fontSize: 15, color:
Colors.grey[100]),
),
),
],
),
),
Container(
margin: EdgeInsets.symmetric(horizontal: 10),
height: 60,
width: 0.5,
color: Colors.grey[200]!.withOpacity(0.7),
),
RotatedBox(
quarterTurns: 3,
child: Text(
task!.isCompleted == 1 ? "COMPLETED" : "TODO",
style: GoogleFonts.lato(
textStyle: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.white),
),
),

Page 41 of 65
),
]),
),
);
}

_getBGClr(int no) {
switch (no) {
case 0:
return kOrangeColor;
case 1:
return Colors.amber[700];
case 2:
return Colors.green[900];
case 3:
return kBlueColor;
case 4:
return Colors.purple[600];
case 5:
return kPinkColor;
default:
return kOrangeColor;
}
}
}

ii. courses_tile.dart:
import 'package:app_cud/models/course.dart';
import 'package:app_cud/services/constants.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:google_fonts/google_fonts.dart';

class CourseTile extends StatelessWidget {


final Course? course;
CourseTile(this.course);

@override
Widget build(BuildContext context) {
return Container(
padding:
EdgeInsets.symmetric(horizontal: 20),
width: MediaQuery.of(context).size.width,
margin: EdgeInsets.only(bottom: 12),
child: Container(
padding: EdgeInsets.all(16),
// width: SizeConfig.screenWidth * 0.78,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: Get.isDarkMode? kPrimaryColorDark:kPrimaryColor,
),
child: Row(children: [
Expanded(
child: Column(

Page 42 of 65
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${course!.name}",
style: GoogleFonts.lato(
textStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white),
),
),
SizedBox(
height: 12,
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(
Icons.access_time_rounded,
color: Colors.grey[200],
size: 18,
),
SizedBox(width: 4),
Text(
"${course!.start_time} - ${course!.end_time}",
style: GoogleFonts.lato(
textStyle:
TextStyle(fontSize: 13, color: Colors.grey[100]),
),
),
],
),
SizedBox(height: 12),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width: MediaQuery.of(context).size.width/4,
child: Row(
children: [
Icon(
Icons.meeting_room_rounded,
color: Colors.grey[200],
size: 18,
),
SizedBox(width: 4),
Text(
"${course!.class_loc}",
style: GoogleFonts.lato(
textStyle:
TextStyle(fontSize: 13, color:
Colors.grey[100]),
),
),
],

Page 43 of 65
),
),
SizedBox(width: 20,),
Container(
width: MediaQuery.of(context).size.width/2.5,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(
Icons.person_rounded,
color: Colors.grey[200],
size: 18,
),
SizedBox(width: 4),
Expanded(
child: Text(
"${course!.instructor}",
style: GoogleFonts.lato(
textStyle: TextStyle(fontSize: 13, color:
Colors.grey[100]),
),
),
),
],
),
),
],
),
],
),
),
Column(
children: [
Container(
margin: EdgeInsets.symmetric(horizontal: 10),
height: 60,
width: 0.5,
color: Colors.grey[200]!.withOpacity(0.7),
),
],
),
Column(
children: [
RotatedBox(
quarterTurns: 3,
child: Text(
'${course!.course_id}',
style: GoogleFonts.lato(
textStyle: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Colors.white),
),
),
),

Page 44 of 65
],
),
]),
),
);
}
}

iii. student_data.dart:
import 'package:app_cud/services/constants.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';

class StudentName extends StatelessWidget {


const StudentName({Key? key, required this.studentName}) : super(key: key);
final String studentName;

@override
Widget build(BuildContext context) {
return Row(
children: [
Text(
'Hi ',
style: Theme.of(context).textTheme.headlineSmall!.copyWith(
fontWeight: FontWeight.w200,
color: kTextWhiteColor,
),
),
Text(
studentName,
style: Theme.of(context).textTheme.headlineSmall!.copyWith(
fontWeight: FontWeight.w500,
color: kTextWhiteColor,
),
),
],
);
}
}

class StudentID extends StatelessWidget {


const StudentID({Key? key, required this.studentID}) : super(key: key);
final String studentID;

@override
Widget build(BuildContext context) {
return Text(
studentID,
style: Theme.of(context).textTheme.titleSmall!.copyWith(
fontSize: 14.0,
color: kTextWhiteColor,
),
);
}

Page 45 of 65
}

class StudentYear extends StatelessWidget {


const StudentYear({Key? key, required this.studentYear}) : super(key: key);
final String studentYear;

@override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 20,
decoration: BoxDecoration(
color: Get.isDarkMode?kDarkGreyColor:kOtherColor,
borderRadius: BorderRadius.circular(kDefaultPadding),
),
child: Center(
child: Text(
studentYear,
style: TextStyle(
fontSize: 12.0,
color: Get.isDarkMode?kTextWhiteColor:kTextBlackColor,
fontWeight: FontWeight.w200),
),
),
);
}
}

class StudentPicture extends StatelessWidget {


const StudentPicture({Key? key, required this.picAddress, required
this.onPress}) : super(key: key);
final String picAddress;
final VoidCallback onPress;

@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onPress,
child: CircleAvatar(
minRadius: 50.0,
maxRadius: 50.0,
backgroundColor: Get.isDarkMode?kSecondaryColorDark:kSecondaryColor,
backgroundImage:
AssetImage(picAddress),
),
);
}
}

iv. input_field:
import 'package:app_cud/services/constants.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get_core/src/get_main.dart';

Page 46 of 65
import 'package:get/get_navigation/get_navigation.dart';
import 'package:get/get_utils/get_utils.dart';

class MyInputField extends StatelessWidget {


final String title;
final String hint;
final TextEditingController? controller;
final Widget? widget;

const MyInputField({Key? key, required this.title, required this.hint,


this.controller, this.widget}) : super(key: key);

@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(top: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
fontSize: 16,
fontWeight: FontWeight.w400,
color: Get.isDarkMode?Colors.white:Colors.black,
),
),
Container(
height: 60,
margin: EdgeInsets.only(top: 8.0),
padding: EdgeInsets.only(left: 14),
decoration: BoxDecoration(
border: Border.all(
color: kContainerColor,
width: 1.0,
),
borderRadius: BorderRadius.circular(12)
),
child: Row(
children:[
Expanded(
child: TextFormField(
readOnly: widget==null?false:true,
autofocus: false,
cursorColor: Get.isDarkMode?
Colors.grey[100]:Colors.grey[700],
controller: controller,
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
fontSize: 16,
fontWeight: FontWeight.w400,
color: Get.isDarkMode?
Colors.grey[100]:Colors.grey[700],
),
decoration: InputDecoration(
hintText: hint,

Page 47 of 65
hintStyle:
Theme.of(context).textTheme.bodyLarge!.copyWith(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Get.isDarkMode?
Colors.grey[100]:Colors.grey[400],
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: context.theme.backgroundColor,
width: 0,
)
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: context.theme.backgroundColor,
width: 0,
)
),
),
),
),
widget==null?Container():Container(child: widget,)
]
),
)
],
),
);
}
}

v. course_button:
import 'package:app_cud/services/constants.dart';
import 'package:flutter/material.dart';

class DefaultButton extends StatelessWidget {


final VoidCallback onPress;
final String title;
final IconData iconData;

const DefaultButton(
{super.key,
required this.onPress,
required this.title,
required this.iconData});
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onPress,
child: Container(
margin: EdgeInsets.only(
left: kDefaultPadding,
right: kDefaultPadding,

Page 48 of 65
),
padding: EdgeInsets.only(right: kDefaultPadding),
width: double.infinity,
height: 60.0,
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [kSecondaryColor, kPrimaryColor],
begin: FractionalOffset(0.0, 0.0),
end: FractionalOffset(0.5, 0.0),
stops: [0.0, 1.0],
tileMode: TileMode.clamp,
),
borderRadius: BorderRadius.circular(kDefaultPadding)),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Spacer(),
Text(
title,
style: Theme.of(context).textTheme.titleSmall!.copyWith(
fontWeight: FontWeight.w500,
fontSize: 16.0,
color: kOtherColor,
),
),
Spacer(),
Icon(
iconData,
size: 30.0,
color: kOtherColor,
)
],
)),
);
}
}

class MainButton extends StatelessWidget {


const MainButton(
{Key? key,
required this.onPress,
required this.icon,
required this.title})
: super(key: key);
final VoidCallback onPress;
final String icon;
final String title;

@override
Widget build(BuildContext context) {
return InkWell(
onTap: onPress,
child: Container(
margin: EdgeInsets.only(top: 1),
width: MediaQuery.of(context).size.width / 2.5,

Page 49 of 65
height: MediaQuery.of(context).size.height / 14,
decoration: BoxDecoration(
color: kPrimaryColor,
borderRadius: BorderRadius.circular(kDefaultPadding / 2)),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
title,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleSmall,
),
],
),
),
);
}
}

vi. add_button.dart:
import 'package:app_cud/services/constants.dart';
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';

class AddButton extends StatelessWidget {


final String label;
final VoidCallback onPress;
const AddButton({Key? key, required this.label, required this.onPress}) :
super(key: key);

@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onPress,
child: Container(
width: 120,
height: 60,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Get.isDarkMode?kPrimaryColorDark:kPrimaryColor
),
child:Center(
child: Text(
label,
style: TextStyle(color: kTextWhiteColor),
),
)
),
);
}
}

Page 50 of 65
vii. calendar_widget.dart:
import 'package:app_cud/db/db_helper_course.dart';
import 'package:app_cud/services/constants.dart';
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';
import 'package:app_cud/models/course.dart';

class CalendarPage extends StatefulWidget {


@override
_CalendarPageState createState() => _CalendarPageState();

class _CalendarPageState extends State<CalendarPage> {


final ValueNotifier<CalendarView> _calendarView =
ValueNotifier(CalendarView.month);

@override
Widget build(BuildContext context) {
return Scaffold(
body: SfCalendar(
view: _calendarView.value,
allowedViews: [
CalendarView.month,
CalendarView.week,
CalendarView.day,
CalendarView.workWeek,
CalendarView.timelineDay,
CalendarView.timelineWeek,
CalendarView.timelineWorkWeek,
CalendarView.timelineMonth,
CalendarView.schedule],
showDatePickerButton: true,
dataSource: CoursesDataSource(_getDataSource()),
//onTap: _onCalendarTap,
cellBorderColor: Colors.transparent,
selectionDecoration: BoxDecoration(
color: Colors.transparent,
border:
Border.all(color: kPrimaryColor,
width: 2),
borderRadius: const BorderRadius.all(Radius.circular(15)),
shape: BoxShape.rectangle,
),
monthViewSettings: MonthViewSettings(showAgenda: true),
timeSlotViewSettings:
TimeSlotViewSettings(timelineAppointmentHeight: 100),
todayHighlightColor: kPrimaryColor,
),
);
}
}

Page 51 of 65
3. Services:
a. theme_services.dart:
import 'package:flutter/material.dart';
import 'package:get_storage/get_storage.dart';
import 'package:get/get.dart';

class ThemeServices{
final _box = GetStorage();
final _key = 'isDarkMode';

_saveThemeToBox(bool isDarkMode) => _box.write(_key, isDarkMode);

bool _loadThemeFromBox() => _box.read(_key)??false;


ThemeMode get theme => _loadThemeFromBox()?ThemeMode.dark:ThemeMode.light;
void switchTheme(){
Get.changeThemeMode(_loadThemeFromBox()?ThemeMode.light:ThemeMode.dark);
_saveThemeToBox(!_loadThemeFromBox());
}
}
b. routes.dart:
import 'package:app_cud/UI/add_task_bar.dart';
import 'package:app_cud/UI/assignment_screen.dart';
import 'package:app_cud/UI/calendar_screen.dart';
import 'package:app_cud/UI/home_screen.dart';
import 'package:app_cud/UI/login_screen.dart';
import 'package:app_cud/UI/my_profile.dart';
import 'package:app_cud/UI/splash_screen.dart';
import 'package:app_cud/UI/widgets/calendar_widget.dart';
import 'package:flutter/cupertino.dart';

Map<String, WidgetBuilder> routes = {


//all screens will be registered here like manifest in android
SplashScreen.routeName: (context) => SplashScreen(),
LoginScreen.routeName: (context) => LoginScreen(),
HomeScreen.routeName: (context) => HomeScreen(),
MyProfileScreen.routeName: (context) => MyProfileScreen(),
CalendarScreen.routeName: (context) => CalendarScreen(),
AssignmentScreen.routeName: (context) => AssignmentScreen(),
AddTaskPage.routeName: (context) => AddTaskPage(),

};
c. constants.dart:
import 'package:flutter/material.dart';

//Colors
const Color kPrimaryColor = Color(0xFFD50000);
const Color kPrimaryColorDark = Color(0xFFB60000);
const Color kSecondaryColor = Color(0xFFDB5B5B);
const Color kSecondaryColorDark = Color(0xFFBB3A3A);
const Color kTextBlackColor = Color(0xFF000000);
const Color kTextWhiteColor = Color(0xFFFFFFFF);
const Color kContainerColor = Color(0xFF777777);
const Color kOtherColor = Color(0xFFF4F6F7);

Page 52 of 65
const Color kBlackColor = Color(0xFF050505);
const Color kTextLightColor = Color(0xFFA5A5A5);
const Color kErrorBorderColor = Color(0xFFE74C3C);
const Color kDarkGreyColor = Color(0xFF303030);
const Color kBlueColor = Color(0xFF0066FF);
const Color kPinkColor = Color(0xFFFF076E);
const Color kOrangeColor = Color(0xFFFF4D00);
//default value
const kDefaultPadding = 20.0;

const sizedBox = SizedBox(


height: kDefaultPadding,
);

const kWidthSizedBox = SizedBox(


width: kDefaultPadding,
);

const kHalfSisedBox = SizedBox(


height: kDefaultPadding / 2,
);

const kHalfWhidthSisedBox = SizedBox(


width: kDefaultPadding / 2,
);

//validation for mobile


const String mobilePattern = r'(^(?:[+0]9)?[0-9]{10,12}$)';

//validation for email


const String emailPattern =
r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-
9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]
{2,}))$';
4. Models:
a. Course.dart:
class Course {
int? id;
String? name;
String? course_id;
String? day;
String? start_time;
String? end_time;
String? class_loc;
String? instructor;

Course({
this.id,
this.name,
this.course_id,
this.day,
this.start_time,
this.end_time,
this.class_loc,

Page 53 of 65
this.instructor,
});

Course.fromJson(Map<String, dynamic> json){


id = json["id"];
name = json["name"];
course_id = json["course_id"];
day = json["day"];
start_time = json["start_time"];
end_time = json["end_time"];
class_loc = json["class_loc"];
instructor = json["instructor"];
}

Map<String,dynamic> toJson(){
final Map<String,dynamic> data = new Map<String,dynamic>();
data['id'] = this.id;
data['name'] = this.name;
data['course_id'] = this.course_id;
data['day'] = this.day;
data['start_time'] = this.start_time;
data['end_time'] = this.end_time;
data['class_loc'] = this.class_loc;
data['instructor'] = this.instructor;
return data;
}
}
b. task.dart:
class Task {
int? id;
String? title;
String? note;
int? isCompleted;
String? startDate;
String? endDate;
String? startTime;
String? endTime;
int? color;
int? remind;

Task({
this.id,
this.title,
this.note,
this.isCompleted,
this.startDate,
this.endDate,
this.startTime,
this.endTime,
this.color,
this.remind,
});
Task.fromJson(Map<String, dynamic> json){
id = json["id"];
title = json["title"];

Page 54 of 65
note = json["note"];
isCompleted = json["isCompleted"];
startDate = json["startDate"];
endDate = json["endDate"];
startTime = json["startTime"];
endTime = json["endTime"];
color = json["color"];
remind = json["remind"];
}

Map<String,dynamic> toJson(){
final Map<String,dynamic> data = new Map<String,dynamic>();
data['id'] = this.id;
data['title'] = this.title;
data['startDate'] = this.startDate;
data['endDate'] = this.endDate;
data['note'] = this.note;
data['isCompleted'] = this.isCompleted;
data['startTime'] = this.startTime;
data['endTime'] = this.endTime;
data['color'] = this.color;
data['remind'] = this.remind;
return data;
}
}
5. db:
a. db_helper_course.dart:
import 'dart:io';

import 'package:flutter/services.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';

class DBHelperCourses {
static const _databaseName = 'courses.db';
static const _databaseVersion = 1;

DBHelperCourses._();
static final DBHelperCourses instance = DBHelperCourses._();

static Database? _database;

Future<Database> get database async {


if (_database != null) return _database!;

_database = await _openDatabase();


return _database!;
}

Future<Database> _openDatabase() async {


final databasePath = await getDatabasesPath();
final path = join(databasePath, _databaseName);

// Check if the database already exists in the app's assets directory

Page 55 of 65
if (!await databaseExists(path)) {
// Copy the database from assets to the device's local filesystem
ByteData data = await rootBundle.load(join("assets", _databaseName));
List<int> bytes = data.buffer.asUint8List(data.offsetInBytes,
data.lengthInBytes);
await File(path).writeAsBytes(bytes);
}

return await openDatabase(path);


}

Future<void> close() async {


final db = await database;
db.close();
}
}
b. db_helper.dart:
import 'package:app_cud/models/task.dart';
import 'package:sqflite/sqflite.dart';

class DBHelper{
static Database? _db;
static final int _version = 1;
static final String _tableName = "task";

static Future<void> initDb()async{


if (_db != null){
return;
}
try {
String _path = await getDatabasesPath() + 'task.db';
_db = await openDatabase(
_path,
version: _version,
onCreate: (db, version){
print("creating a new one");
return db.execute(
"CREATE TABLE $_tableName("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"title STRING, note TEXT,"
" startDate STRING, endDate STRING, "
"startTime STRING, endTime STRING, "
"remind INTEGER, "
"color INTEGER, isCompleted INTEGER)",
);
},
);
} catch (e) {
print(e);
}
}
static Future<int> insert(Task? task) async {
print("insert function called");
return await _db?.insert(_tableName, task!.toJson())??1;

Page 56 of 65
}

static Future<List<Map<String, dynamic>>> query() async {


print("query function called");
return await _db!.query(_tableName);
}

static delete(Task task) async {


return await _db!.delete(_tableName, where: 'id=?', whereArgs: [task.id]);
}

static update(int id) async {


return await _db!.rawUpdate('''
UPDATE task
SET isCompleted =?
WHERE id =?
''', [1, id]);
}

}
6. Controllers:
a. course_controllers.dart:
import 'package:app_cud/db/db_helper_course.dart';
import 'package:app_cud/models/course.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';

class CourseController extends GetxController {


var courses = <Course>[].obs;

@override
void onInit() {
super.onInit();
getCourses();
}

Future<void> getCourses() async {


try {
final db = await DBHelperCourses.instance.database;
final result = await db.query('courses');
if (result.isNotEmpty) {
courses.assignAll(result.map((data) =>
Course.fromJson(data)).toList());
}
} catch (e) {
print(e);
}
}
}
b. task_controllers.dart:
import 'package:app_cud/db/db_helper.dart';
import 'package:app_cud/models/task.dart';
import 'package:get/get.dart';

Page 57 of 65
class TaskController extends GetxController{
@override
void onReady(){
super.onReady();
}

var taskList = <Task>[].obs;

Future<int> addTask({Task? task}) async{


return await DBHelper.insert(task);
}

//get all the data from table


void getTasks() async {
List<Map<String, dynamic>> tasks = await DBHelper.query();
taskList.assignAll(tasks.map((data) => new Task.fromJson(data)).toList());
}

void delete(Task task){


DBHelper.delete(task);
getTasks();
}

void markTaskCompleted(int id) async {


await DBHelper.update(id);
getTasks();
}
}

7. dependencies:
cupertino_icons: ^1.0.2
google_fonts: ^4.0.3
flutter_svg: ^2.0.5
get_storage: ^2.1.1
get: ^4.6.5
flutter_local_notifications: ^13.0.0
flutter_native_timezone: ^2.0.0
sqflite: ^2.2.7
intl:
date_picker_timeline:
flutter_staggered_animations:
table_calendar:
syncfusion_flutter_calendar:
provider:

Technical details:
The student app was built using the Flutter framework, which allowed for cross-platform
development on both iOS and Android devices. The app was developed using Dart, a statically
typed programming language that is optimized for building user interfaces.
To manage user authentication and data storage, the app uses the SQLite package, a lightweight,
embedded database that runs locally on the user's device. SQLite was used to store user data such
Page 58 of 65
as assignments, notes, and task lists. The app also utilizes
the get_storage package for storing user preferences and
settings.
To implement the user interface, the app uses the
cupertino_icons package for icons, the google_fonts
package for custom fonts, and the flutter_svg package for
rendering scalable vector graphics.
To implement the calendar view, the app uses the
Syncfusion Flutter Calendar (SfCalendar) package, a third-
party package for Flutter that provides a customizable
calendar widget with support for multiple view modes.
The date_picker_timeline package was used for displaying
dates in a horizontal list.
The app also utilizes the flutter_staggered_animations
package for animating task and course tiles on screen, and
the provider package for state management.
In terms of testing, the app was tested using a combination
of unit tests and manual testing. Unit tests were used to test
individual functions and components of the app, while
manual testing was used to ensure overall functionality and
usability.

User interface:
The user interface of our university app is designed to be simple and
user-friendly, with a clear and intuitive layout that makes it easy for
students to access the information they need.
The app features a splash screen that displays the university logo and
it name.it stays visible for 5 seconds.
Once the splash screen fades, users are presented with a login screen
that in the top part displays the university logo and a brief message
welcoming students to the app, and the bottom part where they can
enter their credentials to access the app's features. The login screen
includes fields for the user's email and password.
Once the user logs in, they are taken to the app's homepage, which
displays the students name, ID, current semester, and the student’s
picture under these information’s there is three buttons that navigate
to different pages. the first button labeled timetable is set for the
current page, the second button is set to navigate to the calendar page

Page 59 of 65
and the third button is for the assignments page, this part of the page is constant through the
other pages.
Under this, you can find timetable of the courses the student is enrolled in. The timetable is
presented in a list format, with each course displayed as a separate tile in the list each tile shows
the course name and code, start and end time, classroom location and the instructor for the
course. The list is split into dates, allowing users to easily navigate to the date they are interested
in.
The calendar screen in our student app is designed to help students keep track of their
coursework and schedule. The main view of the calendar is a monthly view, and it also includes
a drop-down button that shows a list of views including a day view and a week view, this allows
users to switch between different views. This calendar displays all the student's courses,
assignments, and notes based on the selected day.
The assignments screen in our student app is designed to help students keep track of their
coursework and assignments. The screen displays a list of all upcoming assignments, with each
item in the list including the assignment title, note, due date, and any additional details.
The list of assignments is split by day, making it easy for students to see what they have due on a
specific day. Users can navigate between different days using a horizontal list of days, which
allows them to quickly jump to a specific day or scroll through the list of assignments.
Tapping on a specific assignment tile in the list brings up two choices, “task complete” which
will change the task from to-do to complete and “delete task” that will remove unwanted
assignments. For assignments, this includes information such as the assignment description, due
date, and any additional instructions or requirements. For notes, this includes the note title and
the full text of the note.

Page 60 of 65
In addition to displaying upcoming assignments, the screen also allows users to add their own
notes or assignments. Tapping on a "Add Task" button which opens a new screen called add
Task screen.
The add task page in our student app is designed to allow students to create new tasks or
assignments quickly and easily. The page includes input fields for the task title, task note, start
date and time, end date and time, and task color.

The task title and task note input fields include a validator that ensures the user does not leave
them empty. If the user tries to submit the form with empty title or note, an error message will be
displayed, prompting the user to enter the missing information.

Page 61 of 65
The start and end date and time input fields allow the user to set the date and time range for the
task. The user can select the start and end date from a calendar widget, and the start and end time

from a time picker widget. If the user sets the end date and time to be before the start date and
time, an error message will be displayed to remind the user to adjust the input.

Page 62 of 65
The task color input field allows the user to choose a color for the task tile, making it easy to
visually distinguish between different tasks or assignments.
Once the user has entered all the required information and selected a task color, they can submit
the form by tapping on a "Create Task" button at the bottom of the
page. If the user has left any required fields empty or set an invalid
date range, an error message will be displayed, prompting the user to
correct their input before submitting the form.

And finally, if you noticed throughout the three pages, they have a
similar top part that displays students name, ID, current semester,
and student’s picture. Clicking on the picture will send the user to
the profile page where the students info shows up.

Page 63 of 65
Two bugs:
first bug:

During the development of the splash screen, I encountered a bug where the university logo
would not load, and instead an error message would appear. To troubleshoot this issue, I decided
to run a debugging session and placed a breakpoint at the line of code where the image was being
loaded:
Image.asset('assets/images/splash.png',height: 200.0, width: 200.0,),
Upon further investigation, I discovered that the issue was becaused the assets path was not
included in the pubspec.yaml file. Once I updated the file to include the proper asset path, the
image loaded successfully.
Resolution:
To fix this bug, I updated the pubspec.yaml file to include the proper asset path for the university
logo. This allowed the image to load correctly on the splash screen, resolving the error message
that was previously being displayed.

Page 64 of 65
Second bug:

While working on the assignment screen in the university app, I noticed that the app was
displaying an error message saying "Null check operator was used on a null value" when I tried
to view the list of tasks.
To troubleshoot the issue, I used the debugging feature in the IDE to set a breakpoint at the
ListView.builder method and examine the behavior of the app. I discovered that the TaskTile
widget was set to null instead of the task list.
Resolution:
To fix the bug, I modified the code to ensure that the taskTile widget was set to the task list
instead of null. This involved checking the code responsible for initializing the task list and
making sure that it was properly passed to the TaskTile widget.
After implementing these changes, I retested the app using the debugging feature in your IDE
and confirmed that the issue had been resolved. The task list was now properly displayed on the
assignment screen, and the error message was no longer appearing.

Page 65 of 65

You might also like