Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Saif Harith #5

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added Assets/Images/placeholder.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
52 changes: 5 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,5 @@
# Task 3 - Book Store App

Task resolution process:

- Fork the repo
- Clone the forked repo to your local machine
- Resolve the task
- Commit your solution
- Push to GitHub
- create a pull request



### Task 3:
Build a book store mobile application. The store contain a list of available books with the ability to add custom books to the store as well as view, order them.

* Don't use pre-made widgets (packges).
* It's ok to use icons packages.
* Use Getx for state management.

## UI
### Main Page
The main page should contain a list of the available books. When tapping on a book, the second page (Book Details Page) will be navigated. Use Scrollable view for the list of books. ALso there is a simple search on the top of the page.


### Cart Page (Optional)
This page can view the ordered books.

### Details Page
This have the details of the book which is:
- Book name
- Author
- Desciption
- Image
- Rate (constant)

### Add Page
It is the page where you can add new books to the store.

## FLow
1. Build the UI (from figma design).
2. seperate the widgets to small and organized files.
3. Build models that contain the book data (You can build one model or more).
4. Try to seperate the logic (Functions and data) from the UI.

## Figma Design
https://www.figma.com/file/4BGkiFfTPT7b8K9pyFTBD6/Online-Book-Store-App-(2019)-(Community)?node-id=0%3A1
![2022-08-25 21_20_14-Android Emulator - Pixel_5_API_33_5554](https://user-images.githubusercontent.com/58166633/203349049-f2809ae9-7707-4609-8427-544b9ca50940.png)
![2022-08-25 21_20_23-Android Emulator - Pixel_5_API_33_5554](https://user-images.githubusercontent.com/58166633/203349053-e3c412f6-2acd-4554-b16b-5a7eb7c33433.png)
![2022-08-25 21_20_41-Android Emulator - Pixel_5_API_33_5554](https://user-images.githubusercontent.com/58166633/203349056-d0173ea0-4cdb-4470-bfe4-629277e5b664.png)
![2022-08-25 21_21_05-Android Emulator - Pixel_5_API_33_5554](https://user-images.githubusercontent.com/58166633/203349060-9d89bec5-fd40-473e-b057-36a8fb99997f.png)
![2022-08-25 21_25_15-Android Emulator - Pixel_5_API_33_5554](https://user-images.githubusercontent.com/58166633/203349064-960764a2-8522-4de9-bcf2-a44d79850c93.png)
112 changes: 112 additions & 0 deletions lib/Add Book/add_book_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
// ignore: depend_on_referenced_packages
import 'package:get/get.dart';

import 'package:book_app/Models/books_model.dart';
import 'package:book_app/Reusable_Widgets/star_rating.dart';
import 'package:book_app/Reusable_Widgets/title.dart';
import '../Reusable_Widgets/MainButton.dart';
import '../Reusable_Widgets/customTextField.dart';

import './textfiled_vars.dart';
import 'clear_textfields.dart';
import 'validating/validating_and_error_msgs.dart';
import 'validating/validating_textfields.dart';

class AddBookPage extends StatelessWidget {
AddBookPage({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
Rx<int> rate = Rx<int>(0);

// String imageLink = "https://api.lorem.space/image/book?w=150&h=224";
addButton() {
try {
validator();
Books.addToAllBooks(
Book(
title: titleTextfields.text,
author: authorTextfields.text,
imageLink: imageLinkTextfields.text,
price: double.parse(priceTextfields.text),
rate: rate.value.toDouble(),
description: descriptionTextfields.text),
);
FocusScope.of(context).unfocus();
clearTextfields();
if (kDebugMode) {
Book lastBook = Books().allBooks[Books().allBooks.length - 1];
print(
"title:${lastBook.title}\nauthor:${lastBook.author}\ndescription:${lastBook.description}\nprice:\$${lastBook.price}\nrate:${lastBook.rate}");
}
} catch (e) {}
}

return SingleChildScrollView(
child: Container(
height: 800,
width: double.maxFinite,
child: Column(
// mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 20),
const CustomTitle(title: "Add Book"),
const SizedBox(height: 40),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Padding(
padding: const EdgeInsets.only(bottom: 0),
child: Column(
children: [
CustomTextField(
hintText: "Book's Name",
validator: titleValidator,
errmsg: titleErrMsg,
textEditingController: titleTextfields),
CustomTextField(
validator: authorValidator,
errmsg: authorErrMsg,
hintText: "Author's Name",
textEditingController: authorTextfields),
CustomTextField(
hintText: "Price",
textInputType: TextInputType.number,
validator: priceValidator,
errmsg: priceErrMsg,
maxLines: 1,
textEditingController: priceTextfields),
CustomTextField(
validator: imageLinkValidator,
errmsg: imageLinkErrMsg,
hintText: "Image Link",
maxLines: 1,
textEditingController: imageLinkTextfields),
CustomTextField(
hintText: "Description",
validator: descriptionValidator,
errmsg: descriptionErrMsg,
maxLines: 5,
textEditingController: descriptionTextfields),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: StarRating(rate: rate)),

// -----------------------
MainButton(
buttonFunction: addButton,
buttonTitle: "Add",
),
],
),
),
),
const Spacer()
],
),
),
);
}
}
9 changes: 9 additions & 0 deletions lib/Add Book/clear_textfields.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'package:book_app/Add%20Book/textfiled_vars.dart';

void clearTextfields(){
titleTextfields.clear();
authorTextfields.clear();
imageLinkTextfields.clear();
priceTextfields.clear();
descriptionTextfields.clear();
}
7 changes: 7 additions & 0 deletions lib/Add Book/textfiled_vars.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import 'package:flutter/material.dart';

TextEditingController titleTextfields = TextEditingController();
TextEditingController authorTextfields = TextEditingController();
TextEditingController imageLinkTextfields = TextEditingController();
TextEditingController priceTextfields = TextEditingController();
TextEditingController descriptionTextfields = TextEditingController();
12 changes: 12 additions & 0 deletions lib/Add Book/validating/validating_and_error_msgs.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import 'package:get/get.dart';

Rx<bool> titleValidator = Rx<bool>(false);
Rx<bool> authorValidator = Rx<bool>(false);
Rx<bool> priceValidator = Rx<bool>(false);
Rx<bool> imageLinkValidator = Rx<bool>(false);
Rx<bool> descriptionValidator = Rx<bool>(false);
Rx<String> titleErrMsg = Rx<String>("");
Rx<String> authorErrMsg = Rx<String>("");
Rx<String> priceErrMsg = Rx<String>("");
Rx<String> imageLinkErrMsg = Rx<String>("");
Rx<String> descriptionErrMsg = Rx<String>("");
43 changes: 43 additions & 0 deletions lib/Add Book/validating/validating_textfields.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import '../textfiled_vars.dart';
import 'validating_and_error_msgs.dart';

void validator() {
if (titleTextfields.text.isEmpty) {
titleValidator.value = true;
titleErrMsg.value = "Book's at least must be 1 character";
// return null;
} else if (titleTextfields.text.length > 25) {
titleValidator.value = true;
titleErrMsg.value = "Book's must be less than 25";
} else
titleValidator.value = false;

if (authorTextfields.text.isEmpty) {
authorValidator.value = true;
authorErrMsg.value = "Author's name at least must be 1 character";
} else if (authorTextfields.text.length > 25) {
authorValidator.value = true;
authorErrMsg.value = "author must be less than 25";
} else
authorValidator.value = false;

if (priceTextfields.text.isEmpty) {
priceValidator.value = true;
priceErrMsg.value = "Price is required field";
} else if (double.tryParse(priceTextfields.text) == null) {
priceValidator.value = true;
priceErrMsg.value = "Please Enter valid price value";
} else
priceValidator.value = false;

if (imageLinkTextfields.text.isEmpty) {
imageLinkValidator.value = true;
imageLinkErrMsg.value = "Image is required field";
} else
imageLinkValidator.value = false;

if (titleValidator.value ||
authorValidator.value ||
priceValidator.value ||
imageLinkValidator.value) return null;
}
97 changes: 97 additions & 0 deletions lib/BookPage/book_main.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import 'package:book_app/BookPage/image_container.dart';
import 'package:flutter/material.dart';

import '../Models/books_model.dart';
import '../Reusable_Widgets/MainButton.dart';
import '../Reusable_Widgets/subButton.dart';
import '../Reusable_Widgets/title.dart';
import 'title_and_rate.dart';

class BookPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
dynamic bookInfo = ModalRoute.of(context)?.settings.arguments;
print(bookInfo);
return Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 25),
child: Container(
width: double.maxFinite,
height: double.maxFinite,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Align(
alignment: Alignment.topLeft,
child: IconButton(
onPressed: () => Navigator.pop(context),
icon: Icon(Icons.arrow_back)),
),
ImageContainer(imageLink: bookInfo['imageLink']),
TitleAndRate(
title: bookInfo['title'],
author: bookInfo['author'],
rate: bookInfo['rate'].toInt()),
Column(
children: [
Text(
bookInfo['description'],
style: TextStyle(color: Colors.grey),
),
SizedBox(
height: 10,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: SubButton(
buttonFunction: () => null,
buttonTitle: "Preview",
height: 50,
icon: Icons.bar_chart_rounded,
),
),
SizedBox(
width: 10,
),
Expanded(
child: SubButton(
buttonFunction: () => null,
buttonTitle: "Reviews",
height: 50,
icon: Icons.chat_outlined,
),
)
],
),
],
),
StatefulBuilder(
builder: (BuildContext context, setState) {
return MainButton(
buttonFunction: () {
setState(
() => bookInfo['isCart'] = !bookInfo['isCart']);

for (var element in Books().allBooks) {
if (element.title == bookInfo['title']) {
element.isCart = bookInfo['isCart'];
}
}
},
buttonTitle: bookInfo['isCart']
? "Remove from Cart"
: "Buy Now For \$${bookInfo['price']}",
);
},
),
],
),
),
),
),
);
}
}
33 changes: 33 additions & 0 deletions lib/BookPage/image_container.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';

class ImageContainer extends StatelessWidget {
final String imageLink;
const ImageContainer({
Key? key,
required this.imageLink,
}) : super(key: key);

@override
Widget build(BuildContext context) {
return CachedNetworkImage(
width: 210,
height: 310,
fit: BoxFit.fill,
imageUrl: imageLink,
placeholder: (context, url) {
return const Center(
child: SizedBox(
width: 30,
height: 30,
child: CircularProgressIndicator(),
),
);
},
errorWidget: (context, url, error) => Image.asset(
"Assets/Images/placeholder.jpg",
fit: BoxFit.fill,
),
);
}
}
Loading