Skip to content
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
53 changes: 39 additions & 14 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import 'package:flutter/material.dart';
import 'package:myshop_app/providers/auth.dart';
import 'package:myshop_app/providers/orders.dart';
import 'package:myshop_app/screens/auth_screen.dart';
import 'package:myshop_app/screens/products_overview_screen.dart';
import 'package:provider/provider.dart';

import './models/custom_colors.dart';
import './models/router.dart';
import './providers/cart.dart';
import './providers/products_provider.dart';
import './screens/products_overview_screen.dart';

void main() => runApp(MyApp());

Expand All @@ -15,23 +17,46 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (ctx) => ProductsProvider()),
ChangeNotifierProvider(create: (ctx) => Auth()),
ChangeNotifierProxyProvider<Auth, ProductsProvider>(
update: (ctx, auth, previousProducts) {
return ProductsProvider(auth.token, auth.userId, previousProducts == null ? [] : previousProducts.items);
},
// ProductsProvider(auth.token ),
create: (_) => ProductsProvider(
null,
null,
[],
),
),
ChangeNotifierProvider(create: (ctx) => Cart()),
ChangeNotifierProvider(create: (ctx) => Orders()),
ChangeNotifierProxyProvider<Auth, Orders>(
create: (_) => Orders(
null,
null,
[],
),
update: (ctx, auth, previousOrder) =>
Orders(auth.token, auth.userId, previousOrder == null ? [] : previousOrder.orders)),
// ChangeNotifierProvider(create: (ctx) => Orders()),
],
child: MaterialApp(
title: 'MyShop',
theme: ThemeData(
primarySwatch: primaryTheme,
accentColor: accentTheme,
canvasColor: canvasTheme,
fontFamily: 'Lato',
),
child: Consumer<Auth>(
builder: (context, auth, _) => MaterialApp(
title: 'MyShop',
theme: ThemeData(
primarySwatch: primaryTheme,
accentColor: accentTheme,
canvasColor: canvasTheme,
fontFamily: 'Lato',
),

///First screen to show
initialRoute: ProductsOverviewScreen.routeName,
onGenerateRoute: Routers.generateRoute,
///First screen to show
initialRoute: firstScreen(auth),
onGenerateRoute: Routers.generateRoute,
),
),
);
}

String firstScreen(Auth auth) => auth.isAuth ? ProductsOverviewScreen.routeName : AuthScreen.routeName;
}
4 changes: 4 additions & 0 deletions lib/models/constants.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
class Constants {

static const url = 'https://shop-app-4ed38-default-rtdb.firebaseio.com';
// String authUrl =
// 'https://identitytoolkit.googleapis.com/v1/accounts:$urlSegment?key=AIzaSyDDS9QGht-1JD8bUjcqP-0BQ0cgibbacAQ';
// static const signInUrl = 'https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=AIzaSyDDS9QGht-1JD8bUjcqP-0BQ0cgibbacAQ';
}
7 changes: 5 additions & 2 deletions lib/models/router.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:myshop_app/screens/auth_screen.dart';

import '../screens/cart_screen.dart';
import '../screens/edit_product_screen.dart';
Expand All @@ -16,10 +17,12 @@ Arguments are used to pass the information to that route
class Routers {
static Route<dynamic> generateRoute(RouteSettings setting) {
switch (setting.name) {
case AuthScreen.routeName:
return MaterialPageRoute<dynamic>(builder: (BuildContext context) => AuthScreen());
case ProductsOverviewScreen.routeName:
return MaterialPageRoute<dynamic>(builder: (BuildContext context) => ProductsOverviewScreen());
case ProductDetailScreen.routeName:
final argument = setting.arguments as String;
final argument = setting.arguments as String?;
return MaterialPageRoute<dynamic>(
builder: (BuildContext context) => ProductDetailScreen(productDetailArgs: argument));
case CartScreen.routeName:
Expand All @@ -29,7 +32,7 @@ class Routers {
case UserProductsScreen.routeName:
return MaterialPageRoute<dynamic>(builder: (BuildContext context) => UserProductsScreen());
case EditProductScreen.routeName:
final args = setting.arguments as EditProductArguments;
final args = setting.arguments as EditProductArguments?;
return MaterialPageRoute<dynamic>(
builder: (BuildContext context) => EditProductScreen(editProductarguments: args));
default:
Expand Down
82 changes: 82 additions & 0 deletions lib/providers/auth.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import 'dart:async';
import 'dart:convert';

import 'package:flutter/widgets.dart';
import 'package:http/http.dart' as http;
import 'package:myshop_app/models/http_exception.dart';

class Auth with ChangeNotifier {
String? _token;
DateTime? _expiryDate;
String? _userId;
Timer? _authTimer;

bool get isAuth {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please put short description

return token != null;
}

String? get token {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

document this method

if (_expiryDate != null && _expiryDate!.isAfter(DateTime.now()) && _token != null) {
return _token;
}
return null;
}

String? get userId {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

document

return _userId;
}

Future<void> _authenticate(String? email, String? password, String urlSegment) async {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

final url = Uri.parse(
'https://identitytoolkit.googleapis.com/v1/accounts:$urlSegment?key=AIzaSyDDS9QGht-1JD8bUjcqP-0BQ0cgibbacAQ');

try {
final response = await http.post(
url,
body: json.encode(
{
'email': email,
'password': password,
'returnSecureToken': true,
},
),
);
final dynamic responseData = json.decode(response.body);
if (responseData['error'] != null) {
throw HttpException((responseData['error']['message']).toString());
}
_token = responseData['idToken'].toString();
_userId = responseData['localId'].toString();
_expiryDate = DateTime.now().add(
Duration(
seconds: int.parse(
responseData['expiresIn'].toString(),
),
),
);

notifyListeners();
} catch (error) {
rethrow;
}
}

Future<void> signUp(String? email, String? password) async {
return _authenticate(email, password, 'signUp');
}

Future<void> login(String? email, String? password) async {
return _authenticate(email, password, 'signInWithPassword');
}

void logout() {
_token = null;
_userId = null;
_expiryDate = null;
if (_authTimer != null) {
_authTimer?.cancel();
_authTimer = null;
}
notifyListeners();
}
Comment on lines +64 to +81
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please document all methods

}
32 changes: 16 additions & 16 deletions lib/providers/cart.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@ import 'package:flutter/foundation.dart';
class CartItem {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

document this class and its methods

final String id;
final String title;
final int quantity;
final double price;
final int? quantity;
final double? price;

CartItem({
@required this.id,
@required this.title,
@required this.quantity,
@required this.price,
required this.id,
required this.title,
required this.quantity,
required this.price,
});
}

class Cart with ChangeNotifier {
///Map for cart items
Map<String, CartItem> _items = {};
Map<String?, CartItem> _items = {};

///getter of _items
Map<String, CartItem> get items => {..._items};
Map<String?, CartItem> get items => {..._items};

///Counts the number of _items in the cart
int get itemCount => _items.length;
Expand All @@ -28,15 +28,15 @@ class Cart with ChangeNotifier {
double get totalAmount {
var total = 0.0;
_items.forEach((key, cartItem) {
total += cartItem.price * cartItem.quantity;
total += cartItem.price! * cartItem.quantity!;
});
return total;
}

///Function for adding items in the cart
void addItem(
String productId,
double price,
String? productId,
double? price,
String title,
) {
if (_items.containsKey(productId)) {
Expand All @@ -48,7 +48,7 @@ class Cart with ChangeNotifier {
id: existingCartItem.id,
title: existingCartItem.title,
price: existingCartItem.price,
quantity: existingCartItem.quantity + 1,
quantity: existingCartItem.quantity! + 1,
));
} else {
///if there is no existing productId, a new item will be added
Expand All @@ -68,25 +68,25 @@ class Cart with ChangeNotifier {
}

///Removes an item in the cart
void removeItem(String productId) {
void removeItem(String? productId) {
_items.remove(productId);
notifyListeners();
}

///This function is used by the snackbar when the UNDO button is pressed
void removeSingleItem(String productId) {
void removeSingleItem(String? productId) {
if (!_items.containsKey(productId)) {
///If the productId is not present in the cart items, it will return nothing
return;
}
if (_items[productId].quantity > 1) {
if (_items[productId]!.quantity! > 1) {
///If the productId is present in the cart items, when the UNDO button is pressed, it will deduct the recently added item
_items.update(productId, (existingCartItem) {
return CartItem(
id: existingCartItem.id,
title: existingCartItem.title,
price: existingCartItem.price,
quantity: existingCartItem.quantity - 1,
quantity: existingCartItem.quantity! - 1,
);
});
} else {
Expand Down
26 changes: 15 additions & 11 deletions lib/providers/orders.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,49 +8,53 @@ import './cart.dart';

class OrderItem {
final String id;
final double amount;
final double? amount;
final List<CartItem> products;
final DateTime dateTime;

OrderItem({
@required this.id,
@required this.amount,
@required this.products,
@required this.dateTime,
required this.id,
required this.amount,
required this.products,
required this.dateTime,
});
}

///List of _orders
class Orders with ChangeNotifier {
List<OrderItem> _orders = [];
final String? authToken;
final String? userId;

Orders(this.authToken, this.userId, this._orders);

///getter of _orders
List<OrderItem> get orders {
return [..._orders];
}

Future<void> fetchAndSetOrders() async {
const url = '${Constants.url}/orders.json';
final url = Uri.parse('${Constants.url}/orders/$userId.json?auth=$authToken');

final List<OrderItem> loadedOrders = [];
final response = await http.get(url);
final extractedData = json.decode(response.body) as Map<String, dynamic>;
final extractedData = json.decode(response.body) as Map<String, dynamic>?;
if (extractedData == null) {
return;
}
extractedData.forEach((String orderId, dynamic orderData) {
loadedOrders.add(
OrderItem(
id: orderId,
amount: orderData['amount'] as double,
amount: orderData['amount'] as double?,
dateTime: DateTime.parse(orderData['dateTime'].toString()),
products: (orderData['products'] as List<dynamic>)
.map(
(dynamic item) => CartItem(
id: item['id'].toString(),
title: item['title'].toString(),
quantity: item['quantity'] as int,
price: item['price'] as double,
quantity: item['quantity'] as int?,
price: item['price'] as double?,
),
)
.toList(),
Expand All @@ -63,7 +67,7 @@ class Orders with ChangeNotifier {

///Function for adding an order
Future<void> addOrder(List<CartItem> cartProducts, double total) async {
const url = '${Constants.url}/orders.json';
final url = Uri.parse('${Constants.url}/orders/$userId.json?auth=$authToken');
final timestamp = DateTime.now();
try {
final response = await http.post(url,
Expand Down
Loading