Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
P
PaperChase
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
h703249754
PaperChase
Commits
7e291a8b
Commit
7e291a8b
authored
Apr 27, 2025
by
Aryan Patel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Aryan-4/27/25
parent
9cee33a8
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
471 additions
and
385 deletions
+471
-385
project.pbxproj
ios/Runner.xcodeproj/project.pbxproj
+3
-3
book_detail_page.dart
lib/book_detail_page.dart
+127
-67
chat_page.dart
lib/chat_page.dart
+110
-51
inbox.dart
lib/inbox.dart
+201
-249
main.dart
lib/main.dart
+17
-11
mybooks.txt
lib/mybooks.txt
+0
-0
post.dart
lib/post.dart
+2
-0
profile.dart
lib/profile.dart
+11
-4
No files found.
ios/Runner.xcodeproj/project.pbxproj
View file @
7e291a8b
...
...
@@ -499,7 +499,7 @@
"$(inherited)"
,
"@executable_path/Frameworks"
,
);
PRODUCT_BUNDLE_IDENTIFIER
=
com.IOSPhoneTesting.paperchaseApp
;
PRODUCT_BUNDLE_IDENTIFIER
=
com.IOSPhoneTesting
2
.paperchaseApp
;
PRODUCT_NAME
=
"$(TARGET_NAME)"
;
SWIFT_OBJC_BRIDGING_HEADER
=
"Runner/Runner-Bridging-Header.h"
;
SWIFT_VERSION
=
5.0
;
...
...
@@ -682,7 +682,7 @@
"$(inherited)"
,
"@executable_path/Frameworks"
,
);
PRODUCT_BUNDLE_IDENTIFIER
=
com.IOSPhoneTesting.paperchaseApp
;
PRODUCT_BUNDLE_IDENTIFIER
=
com.IOSPhoneTesting
2
.paperchaseApp
;
PRODUCT_NAME
=
"$(TARGET_NAME)"
;
SWIFT_OBJC_BRIDGING_HEADER
=
"Runner/Runner-Bridging-Header.h"
;
SWIFT_OPTIMIZATION_LEVEL
=
"-Onone"
;
...
...
@@ -705,7 +705,7 @@
"$(inherited)"
,
"@executable_path/Frameworks"
,
);
PRODUCT_BUNDLE_IDENTIFIER
=
com.IOSPhoneTesting.paperchaseApp
;
PRODUCT_BUNDLE_IDENTIFIER
=
com.IOSPhoneTesting
2
.paperchaseApp
;
PRODUCT_NAME
=
"$(TARGET_NAME)"
;
SWIFT_OBJC_BRIDGING_HEADER
=
"Runner/Runner-Bridging-Header.h"
;
SWIFT_VERSION
=
5.0
;
...
...
lib/book_detail_page.dart
View file @
7e291a8b
...
...
@@ -2,20 +2,25 @@ import 'package:flutter/material.dart';
import
'package:cloud_firestore/cloud_firestore.dart'
;
import
'package:firebase_auth/firebase_auth.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:paperchase_app/chat_page.dart'
;
import
'colors.dart'
;
import
'NavBar.dart'
;
class
BookDetailsPage
extends
StatelessWidget
{
final
Map
<
String
,
dynamic
>
book
;
final
String
bookId
;
const
BookDetailsPage
({
super
.
key
,
required
this
.
book
});
const
BookDetailsPage
({
super
.
key
,
required
this
.
book
,
required
this
.
bookId
});
@override
Widget
build
(
BuildContext
context
)
{
final
bool
isDarkMode
=
Theme
.
of
(
context
).
brightness
==
Brightness
.
dark
;
final
currentUser
=
FirebaseAuth
.
instance
.
currentUser
;
final
isMyBook
=
currentUser
?.
uid
==
book
[
'userId'
];
final
title
=
book
[
'title'
]
??
'No title available'
;
final
author
=
book
[
'author'
]
??
'No author available'
;
final
isbn
=
book
[
'isbn'
]
??
'No ISBN available'
;
...
...
@@ -28,6 +33,7 @@ class BookDetailsPage extends StatelessWidget {
return
Scaffold
(
appBar:
AppBar
(
automaticallyImplyLeading:
true
,
iconTheme:
IconThemeData
(
color:
isDarkMode
?
kDarkBackground
:
kLightBackground
,
),
...
...
@@ -42,7 +48,7 @@ class BookDetailsPage extends StatelessWidget {
),
),
),
drawer:
const
NavBar
(),
body:
SingleChildScrollView
(
child:
Padding
(
padding:
const
EdgeInsets
.
all
(
16.0
),
...
...
@@ -68,12 +74,31 @@ class BookDetailsPage extends StatelessWidget {
style:
TextStyle
(
fontSize:
18
,
fontWeight:
FontWeight
.
bold
)),
Text
(
description
,
style:
const
TextStyle
(
fontSize:
16
)),
const
SizedBox
(
height:
24
),
if
(!
isMyBook
&&
currentUser
!=
null
)
if
(
isMyBook
&&
currentUser
!=
null
)
SizedBox
(
width:
double
.
infinity
,
child:
ElevatedButton
(
onPressed:
()
=>
_confirmAndDeleteBook
(
context
,
bookId
),
style:
ElevatedButton
.
styleFrom
(
backgroundColor:
Colors
.
red
,
padding:
const
EdgeInsets
.
symmetric
(
vertical:
16
),
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
12
),
),
),
child:
const
Text
(
'Delete Book'
,
style:
TextStyle
(
fontSize:
18
,
color:
Colors
.
white
),
),
),
)
else
if
(!
isMyBook
&&
currentUser
!=
null
)
SizedBox
(
width:
double
.
infinity
,
child:
ElevatedButton
(
onPressed:
()
=>
_contactSeller
(
context
,
book
[
'userId'
],
title
),
_contactSeller
(
context
,
book
,
bookId
),
style:
ElevatedButton
.
styleFrom
(
backgroundColor:
kPrimaryColor
,
padding:
const
EdgeInsets
.
symmetric
(
vertical:
16
),
...
...
@@ -86,7 +111,9 @@ class BookDetailsPage extends StatelessWidget {
style:
TextStyle
(
fontSize:
18
,
color:
Colors
.
white
),
),
),
)
else
if
(
currentUser
==
null
)
Center
(
child:
TextButton
(
...
...
@@ -127,8 +154,9 @@ class BookDetailsPage extends StatelessWidget {
);
}
Future
<
void
>
_contactSeller
(
BuildContext
context
,
String
sellerId
,
String
bookTitle
)
async
{
Future
<
void
>
_contactSeller
(
BuildContext
context
,
Map
<
String
,
dynamic
>
book
,
String
bookId
)
async
{
final
currentUser
=
FirebaseAuth
.
instance
.
currentUser
;
if
(
currentUser
==
null
)
{
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
...
...
@@ -137,69 +165,101 @@ class BookDetailsPage extends StatelessWidget {
return
;
}
try
{
final
sellerId
=
book
[
'userId'
];
// 📌 This is the user who posted the book
final
isBuyer
=
currentUser
.
uid
!=
sellerId
;
final
rolePrefix
=
isBuyer
?
'buyer'
:
'seller'
;
final
users
=
[
currentUser
.
uid
,
sellerId
]..
sort
();
final
chatRoomId
=
users
.
join
(
'_'
)
;
final
chatRoomId
=
"
${rolePrefix}
_
${bookId}
_
${users.join('_')}
"
;
final
existingChat
=
await
FirebaseFirestore
.
instance
.
collection
(
'chats'
)
.
doc
(
chatRoomId
)
try
{
final
sellerDoc
=
await
FirebaseFirestore
.
instance
.
collection
(
'users'
)
.
doc
(
sellerId
)
.
get
();
final
sellerName
=
sellerDoc
.
exists
?
"
${sellerDoc['first_name']}
${sellerDoc['last_name']}
"
:
"Unknown Seller"
;
final
chatRef
=
FirebaseFirestore
.
instance
.
collection
(
'chats'
).
doc
(
chatRoomId
);
final
chatData
=
{
'users'
:
users
,
'bookId'
:
bookId
,
'bookTitle'
:
book
[
'title'
],
'lastMessage'
:
'Hi! Is this book still available?'
,
'lastMessageTime'
:
FieldValue
.
serverTimestamp
(),
'bookTitle'
:
bookTitle
,
'createdAt'
:
FieldValue
.
serverTimestamp
(),
'participants'
:
{
currentUser
.
uid
:
true
,
sellerId:
true
,
},
'sellerId'
:
sellerId
,
'buyerId'
:
isBuyer
?
currentUser
.
uid
:
null
,
// null if seller is messaging
};
final
existingChat
=
await
chatRef
.
get
();
if
(
existingChat
.
exists
)
{
await
FirebaseFirestore
.
instance
.
collection
(
'chats'
)
.
doc
(
chatRoomId
)
.
update
({
await
chatRef
.
update
({
'lastMessage'
:
chatData
[
'lastMessage'
],
'lastMessageTime'
:
chatData
[
'lastMessageTime'
],
});
}
else
{
await
FirebaseFirestore
.
instance
.
collection
(
'chats'
)
.
doc
(
chatRoomId
)
.
set
(
chatData
);
await
chatRef
.
set
(
chatData
);
}
await
FirebaseFirestore
.
instance
.
collection
(
'chats'
)
.
doc
(
chatRoomId
)
.
collection
(
'messages'
)
.
add
({
await
chatRef
.
collection
(
'messages'
).
add
({
'senderId'
:
currentUser
.
uid
,
'message'
:
'Hi! Is this book still available?'
,
'timestamp'
:
FieldValue
.
serverTimestamp
(),
'read'
:
false
,
});
if
(
context
.
mounted
)
{
Navigator
.
pushReplacementNamed
(
context
,
'/inbox'
);
Navigator
.
push
(
context
,
MaterialPageRoute
(
builder:
(
context
)
=>
StrictChatPage
(
chatId:
chatRoomId
,
otherUserName:
sellerName
,
currentUserId:
currentUser
.
uid
,
sellerId:
sellerId
,
),
),
);
}
catch
(
e
)
{
debugPrint
(
'Error starting chat:
$e
'
);
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
const
SnackBar
(
content:
Text
(
'Chat started with the seller
'
)),
const
SnackBar
(
content:
Text
(
'Failed to contact seller. Please try again.
'
)),
);
}
}
catch
(
e
)
{
debugPrint
(
"Error contacting seller:
$e
"
);
}
void
_confirmAndDeleteBook
(
BuildContext
context
,
String
bookId
)
async
{
final
shouldDelete
=
await
showDialog
<
bool
>(
context:
context
,
builder:
(
context
)
=>
AlertDialog
(
title:
const
Text
(
'Confirm Deletion'
),
content:
const
Text
(
'Are you sure you want to delete this book?'
),
actions:
[
TextButton
(
onPressed:
()
=>
Navigator
.
pop
(
context
,
false
),
child:
const
Text
(
'Cancel'
)),
TextButton
(
onPressed:
()
=>
Navigator
.
pop
(
context
,
true
),
child:
const
Text
(
'Delete'
)),
],
),
);
if
(
shouldDelete
==
true
)
{
await
FirebaseFirestore
.
instance
.
collection
(
'books'
).
doc
(
bookId
).
delete
();
if
(
context
.
mounted
)
{
Navigator
.
pop
(
context
);
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
const
SnackBar
(
content:
Text
(
'Failed to contact seller. Please try again.'
)),
const
SnackBar
(
content:
Text
(
'Book removed successfully'
)),
);
}
}
}
String
_formatPrice
(
dynamic
price
)
{
if
(
price
==
null
)
return
'0.00'
;
if
(
price
is
num
)
return
price
.
toStringAsFixed
(
2
);
...
...
lib/chat_page.dart
View file @
7e291a8b
...
...
@@ -2,26 +2,23 @@ import 'package:flutter/foundation.dart';
import
'package:flutter/material.dart'
;
import
'package:firebase_auth/firebase_auth.dart'
;
import
'package:cloud_firestore/cloud_firestore.dart'
;
import
'package:paperchase_app/book_detail_page.dart'
;
import
'colors.dart'
;
class
StrictChatPage
extends
StatefulWidget
{
final
String
chatId
;
//final String bookId;
final
String
otherUserName
;
final
List
<
String
>
predefinedMessages
;
final
String
currentUserId
;
final
String
sellerId
;
const
StrictChatPage
({
Key
?
key
,
required
this
.
chatId
,
required
this
.
otherUserName
,
this
.
predefinedMessages
=
const
[
"Is this still available?"
,
"When can we meet?"
,
"I'll take it"
,
"Thanks!"
,
"Hello"
,
"Can you hold it for me?"
,
"What's your lowest price?"
,
],
required
this
.
currentUserId
,
required
this
.
sellerId
,
})
:
super
(
key:
key
);
@override
...
...
@@ -32,6 +29,32 @@ class _StrictChatPageState extends State<StrictChatPage> {
final
ScrollController
_scrollController
=
ScrollController
();
final
TextEditingController
_messageController
=
TextEditingController
();
String
?
_bookTitle
;
String
?
_bookId
;
List
<
String
>
get
predefinedMessages
{
final
currentUser
=
FirebaseAuth
.
instance
.
currentUser
;
final
email
=
currentUser
?.
email
??
"your email"
;
if
(
widget
.
currentUserId
==
widget
.
sellerId
)
{
return
[
"Yes, it's still available."
,
"Thanks!"
,
"How about we meet this weekend?"
,
"That a deal!"
,
"Yes, I will hold it"
,
"Contact me at
$email
"
];
}
else
{
return
[
"Is this still available?"
,
"When can we meet?"
,
"I'll take it"
,
"Thanks!"
,
"Can you hold it for me?"
,
"Contact me at
$email
"
,
];
}
}
@override
void
initState
()
{
...
...
@@ -56,6 +79,7 @@ class _StrictChatPageState extends State<StrictChatPage> {
if
(
doc
.
exists
)
{
setState
(()
{
_bookTitle
=
doc
.
data
()?[
'bookTitle'
]
as
String
?;
_bookId
=
doc
.
data
()?[
'bookId'
]
as
String
?;
});
}
}
catch
(
e
)
{
...
...
@@ -111,7 +135,8 @@ class _StrictChatPageState extends State<StrictChatPage> {
final
backgroundColor2
=
isDarkMode
?
kLightBackground
:
kDarkBackground
;
final
textColor
=
isDarkMode
?
kDarkText
:
kLightText
;
final
textColor2
=
isDarkMode
?
kLightText
:
kDarkText
;
final
messageBackgroundOther
=
isDarkMode
?
Colors
.
grey
[
800
]
:
Colors
.
grey
[
200
];
final
messageBackgroundOther
=
isDarkMode
?
Colors
.
grey
[
800
]
:
Colors
.
grey
[
200
];
return
Scaffold
(
appBar:
AppBar
(
...
...
@@ -176,7 +201,8 @@ class _StrictChatPageState extends State<StrictChatPage> {
final
text
=
data
[
'message'
]
as
String
?
??
''
;
final
senderId
=
data
[
'senderId'
]
as
String
?
??
''
;
final
currentUser
=
FirebaseAuth
.
instance
.
currentUser
;
final
isMe
=
currentUser
!=
null
&&
senderId
==
currentUser
.
uid
;
final
isMe
=
currentUser
!=
null
&&
senderId
==
currentUser
.
uid
;
return
Container
(
margin:
const
EdgeInsets
.
symmetric
(
vertical:
4
),
...
...
@@ -187,7 +213,10 @@ class _StrictChatPageState extends State<StrictChatPage> {
children:
[
Container
(
constraints:
BoxConstraints
(
maxWidth:
MediaQuery
.
of
(
context
).
size
.
width
*
0.75
,
maxWidth:
MediaQuery
.
of
(
context
)
.
size
.
width
*
0.75
,
),
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
16
,
...
...
@@ -237,27 +266,57 @@ class _StrictChatPageState extends State<StrictChatPage> {
fontSize:
14
,
),
),
// Only show the Confirmed button if the current user is the seller
if
(
widget
.
currentUserId
==
widget
.
sellerId
)
SizedBox
(
width:
double
.
infinity
,
child:
ElevatedButton
(
onPressed:
()
=>
_confirmAndCompletePurchase
(
context
),
style:
ElevatedButton
.
styleFrom
(
backgroundColor:
Colors
.
lightGreenAccent
,
padding:
const
EdgeInsets
.
symmetric
(
vertical:
16
),
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
12
),
),
),
child:
const
Text
(
'Confirmed!'
,
style:
TextStyle
(
fontSize:
18
,
color:
kLightText
),
),
),
),
if
(
widget
.
currentUserId
==
widget
.
sellerId
)
const
SizedBox
(
height:
12
),
Wrap
(
spacing:
12
,
runSpacing:
12
,
children:
widget
.
predefinedMessages
.
map
((
msg
)
{
children:
predefinedMessages
.
map
((
msg
)
{
return
ActionChip
(
label:
Text
(
msg
),
onPressed:
()
=>
_sendMessage
(
msg
),
backgroundColor:
backgroundColor
,
labelStyle:
TextStyle
(
color:
textColor
,
),
labelStyle:
TextStyle
(
color:
textColor
,),
);
}).
toList
(),
),
const
SizedBox
(
height:
12
),
],
),
),
),
],
),
);
}
void
_confirmAndCompletePurchase
(
BuildContext
context
)
async
{
await
FirebaseFirestore
.
instance
.
collection
(
'books'
).
doc
(
_bookId
).
delete
();
//await FirebaseFirestore.instance.collection('chats').doc(widget.chatId).collection('messages').doc().delete();
await
FirebaseFirestore
.
instance
.
collection
(
'chats'
).
doc
(
widget
.
chatId
).
delete
();
Navigator
.
pop
(
context
);
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
const
SnackBar
(
content:
Text
(
'Transaction completed!'
)),
);
}
}
\ No newline at end of file
lib/inbox.dart
View file @
7e291a8b
This diff is collapsed.
Click to expand it.
lib/main.dart
View file @
7e291a8b
...
...
@@ -3,9 +3,6 @@ import 'package:flutter/material.dart';
import
'package:firebase_auth/firebase_auth.dart'
;
import
'package:firebase_core/firebase_core.dart'
;
import
'firebase_options.dart'
;
import
'package:http/http.dart'
as
http
;
import
'dart:convert'
;
import
'package:url_launcher/url_launcher.dart'
;
import
'login.dart'
;
import
'signup.dart'
;
import
'profile.dart'
;
...
...
@@ -14,7 +11,6 @@ import 'inbox.dart';
import
'package:firebase_app_check/firebase_app_check.dart'
;
import
'colors.dart'
;
import
'utils.dart'
;
import
'home.dart'
;
import
'package:shared_preferences/shared_preferences.dart'
;
import
'NavBar.dart'
;
import
'book_detail_page.dart'
;
...
...
@@ -185,7 +181,8 @@ class _HomePageState extends State<HomePage> {
}).
toList
();
setState
(()
{
_books
=
filteredBooks
.
map
((
doc
)
=>
doc
.
data
()).
toList
();
_books
=
filteredBooks
;
});
}
catch
(
e
)
{
print
(
"Error searching books:
$e
"
);
...
...
@@ -202,7 +199,8 @@ class _HomePageState extends State<HomePage> {
.
get
();
setState
(()
{
_books
=
snapshot
.
docs
.
map
((
doc
)
=>
doc
.
data
()).
toList
();
_books
=
snapshot
.
docs
;
});
}
catch
(
e
)
{
print
(
"Error fetching recent books:
$e
"
);
...
...
@@ -357,21 +355,29 @@ String _filterBy = 'Latest Posted'; // Default filter option
itemCount:
_books
.
length
,
itemBuilder:
(
context
,
index
)
{
final
book
=
_books
[
index
];
final
bookId
=
book
.
id
;
final
data
=
book
.
data
()
as
Map
<
String
,
dynamic
>;
final
title
=
book
.
data
()[
'title'
]
??
"Unknown Title"
;
final
author
=
book
.
data
()[
'author'
]
??
"No author available"
;
final
thumbnail
=
book
.
data
()[
'imageUrl'
]
??
"https://via.placeholder.com/50"
;
final
price
=
book
.
data
()[
'price'
];
final
title
=
book
[
'title'
]
??
"Unknown Title"
;
final
author
=
book
[
'author'
]
??
"No author available"
;
final
thumbnail
=
book
[
'imageUrl'
]
??
"https://via.placeholder.com/50"
;
return
ListTile
(
leading:
Image
.
network
(
thumbnail
,
width:
50
,
height:
50
,
fit:
BoxFit
.
cover
),
title:
Text
(
title
),
subtitle:
Text
(
author
),
subtitle:
Text
(
'
$author
-
\$
$price
-
${book.data()['condition'] ?? 'Condition not available'}
'
),
onTap:
()
{
if
(
_isLoggedIn
)
{
Navigator
.
push
(
context
,
MaterialPageRoute
(
builder:
(
context
)
=>
BookDetailsPage
(
book:
book
),
// Pass book data
builder:
(
context
)
=>
BookDetailsPage
(
book:
book
.
data
()
as
Map
<
String
,
dynamic
>,
bookId:
bookId
),
// Pass book data
),
);
}
else
{
...
...
lib/mybooks.
dar
t
→
lib/mybooks.
tx
t
View file @
7e291a8b
File moved
lib/post.dart
View file @
7e291a8b
...
...
@@ -7,6 +7,7 @@ import 'package:image_picker/image_picker.dart';
import
'package:cloud_firestore/cloud_firestore.dart'
;
import
'package:firebase_storage/firebase_storage.dart'
;
import
'colors.dart'
;
import
'NavBar.dart'
;
class
PostBookPage
extends
StatefulWidget
{
@override
...
...
@@ -137,6 +138,7 @@ Future<String?> uploadImageToImgur(File imageFile) async {
Widget
build
(
BuildContext
context
)
{
final
bool
isDarkMode
=
Theme
.
of
(
context
).
brightness
==
Brightness
.
dark
;
return
Scaffold
(
drawer:
NavBar
(),
appBar:
AppBar
(
iconTheme:
IconThemeData
(
color:
isDarkMode
?
kDarkBackground
:
kLightBackground
,
...
...
lib/profile.dart
View file @
7e291a8b
...
...
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import
'package:firebase_auth/firebase_auth.dart'
;
import
'package:cloud_firestore/cloud_firestore.dart'
;
import
'package:image_picker/image_picker.dart'
;
import
'package:paperchase_app/book_detail_page.dart'
;
import
'package:permission_handler/permission_handler.dart'
;
import
'dart:io'
;
import
'colors.dart'
;
...
...
@@ -155,7 +156,9 @@ class ProfilePage extends StatelessWidget {
return
const
Center
(
child:
CircularProgressIndicator
());
}
final
books
=
booksSnapshot
.
data
?.
docs
??
[];
final
books
=
booksSnapshot
.
data
!.
docs
;
if
(
books
.
isEmpty
)
{
return
Text
(
...
...
@@ -182,6 +185,7 @@ class ProfilePage extends StatelessWidget {
physics:
const
NeverScrollableScrollPhysics
(),
itemCount:
books
.
length
,
itemBuilder:
(
context
,
index
)
{
final
doc
=
books
[
index
];
final
book
=
books
[
index
].
data
()
as
Map
<
String
,
dynamic
>;
return
Card
(
color:
isDarkMode
?
Colors
.
grey
[
900
]
:
Colors
.
white
,
...
...
@@ -204,11 +208,14 @@ class ProfilePage extends StatelessWidget {
fontWeight:
FontWeight
.
bold
,
),
),
onTap:
()
{
Navigator
.
push
Named
(
Navigator
.
push
(
context
,
'/book_details'
,
arguments:
books
[
index
].
id
,
MaterialPageRoute
(
builder:
(
context
)
=>
BookDetailsPage
(
book:
book
,
bookId:
doc
.
id
),
),
);
},
),
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment