A new Flutter project.
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
For help getting started with Flutter development, view the online documentation, which offers tutorials, samples, guidance on mobile development, and a full API reference.
- Home Page
- Detail Book Page
- Add Book Page
- Edit Book Page
-- Result
- Delete Book Page
book_collection_app_flutter/lib/widget/book_card.dart
Lines 7 to 64 in 17d7d87
class BookCard extends StatelessWidget { | |
final Book book; | |
final Function? update; | |
const BookCard(this.book, { | |
this.update, | |
super.key | |
}); | |
@override | |
Widget build(BuildContext context) { | |
return GestureDetector( | |
onTap: () async { | |
await Navigator.of(context).push( | |
MaterialPageRoute( | |
builder: (context) => BookDetailPage(bookId: book.id!,) | |
) | |
); | |
if(update != null) update!(); | |
}, | |
child: Card( | |
child: Padding( | |
padding: const EdgeInsets.fromLTRB(10, 10, 10, 10), | |
child: Column( | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: [ | |
// add small image | |
Container( | |
width: double.infinity, | |
height: 200, | |
decoration: BoxDecoration( | |
image: DecorationImage( | |
image: NetworkImage(book.bookImage), | |
fit: BoxFit.cover, | |
), | |
), | |
), | |
Text( | |
DateFormat.yMd().format(book.createdTime), | |
style: const TextStyle( | |
color: Colors.black38, | |
fontWeight: FontWeight.w500, | |
), | |
), | |
const SizedBox(height: 5,), | |
Text( | |
book.title, | |
style: const TextStyle( | |
fontSize: 20, | |
fontWeight: FontWeight.bold, | |
), | |
), | |
], | |
), | |
), | |
), | |
); | |
} | |
} |
book_collection_app_flutter/lib/pages/books_page.dart
Lines 8 to 98 in 17d7d87
class BookPage extends StatefulWidget { | |
const BookPage({super.key}); | |
@override | |
State<BookPage> createState() => _BookPageState(); | |
} | |
class _BookPageState extends State<BookPage> { | |
late List<Book> books; | |
bool isLoading = false; | |
@override | |
void initState() { | |
super.initState(); | |
refreshBooks(); | |
} | |
void refreshBooks() async { | |
setState(() => isLoading = true); | |
List<Book> list = await BookDatabase.instance.readAllBooks(); | |
setState(() { | |
isLoading = false; | |
books = list; | |
}); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: const Text( | |
'Books', | |
style: TextStyle( | |
color: Colors.white, | |
fontSize: 28, | |
), | |
), | |
centerTitle: true, | |
backgroundColor: Colors.transparent, | |
), | |
floatingActionButton: FloatingActionButton( | |
onPressed: () async { | |
await Navigator.of(context).push( | |
MaterialPageRoute( | |
builder: (context) => const AddEditBookPage(), | |
) | |
); | |
refreshBooks(); | |
}, | |
child: const Icon(Icons.add), | |
), | |
body: Column( | |
children: [ | |
const CircleAvatar( | |
backgroundImage: AssetImage('assets/profile.webp'), | |
), | |
Padding( | |
padding: const EdgeInsets.all(10), | |
child: Center( | |
child: isLoading ? const CircularProgressIndicator() | |
: books.isEmpty ? const Text( | |
'No Books', | |
style: TextStyle( | |
color: Colors.white | |
), | |
) | |
: buildBooks() | |
), | |
), | |
], | |
), | |
); | |
} | |
Widget buildBooks() => StaggeredGrid.count( | |
crossAxisCount: 2, | |
mainAxisSpacing: 2, | |
crossAxisSpacing: 2, | |
children: List.generate(books.length, (index) { | |
Book book = books[index]; | |
return StaggeredGridTile.fit( | |
crossAxisCellCount: 1, | |
child: BookCard(book, update: refreshBooks,), | |
); | |
}), | |
); | |
} |
book_collection_app_flutter/lib/widget/book_card.dart
Lines 32 to 42 in 17d7d87
// add small image | |
Container( | |
width: double.infinity, | |
height: 200, | |
decoration: BoxDecoration( | |
image: DecorationImage( | |
image: NetworkImage(book.bookImage), | |
fit: BoxFit.cover, | |
), | |
), | |
), |
book_collection_app_flutter/lib/db/book_database.dart
Lines 66 to 82 in 17d7d87
Future<Book> readBook(int id) async { | |
try { | |
final db = await instance.database; | |
final maps = await db.query( | |
tableBooks, | |
columns: BookFields.values, | |
where: '${BookFields.id} = ?', | |
whereArgs: [id], | |
); | |
if (maps.isEmpty) throw Exception('ID $id is not found'); | |
return Book.fromJson(maps[0]); | |
} catch(error) { | |
throw Exception("Error in reading book"); | |
} | |
} |
book_collection_app_flutter/lib/db/book_database.dart
Lines 36 to 134 in 17d7d87
Future _createDB(Database db, int version) async { | |
try { | |
const idType = 'INTEGER PRIMARY KEY AUTOINCREMENT'; | |
const textType = 'TEXT NOT NULL'; | |
await db.execute(''' | |
CREATE TABLE $tableBooks ( | |
${BookFields.id} $idType, | |
${BookFields.title} $textType, | |
${BookFields.description} $textType, | |
${BookFields.createdTime} $textType, | |
${BookFields.bookImage} $textType | |
) | |
'''); | |
} catch (error) { | |
throw Exception("Error in creating database"); | |
} | |
} | |
Future<Book> create(Book note) async { | |
try { | |
final db = await instance.database; | |
final id = await db.insert(tableBooks, note.toJson()); | |
return note.copy(id: id); | |
} catch (error) { | |
throw Exception("Error in creating book"); | |
} | |
} | |
Future<Book> readBook(int id) async { | |
try { | |
final db = await instance.database; | |
final maps = await db.query( | |
tableBooks, | |
columns: BookFields.values, | |
where: '${BookFields.id} = ?', | |
whereArgs: [id], | |
); | |
if (maps.isEmpty) throw Exception('ID $id is not found'); | |
return Book.fromJson(maps[0]); | |
} catch(error) { | |
throw Exception("Error in reading book"); | |
} | |
} | |
Future<List<Book>> readAllBooks() async { | |
try { | |
final db = await instance.database; | |
final result = await db.query(tableBooks); | |
return result.map((note) => Book.fromJson(note)).toList(); | |
} catch (error) { | |
throw Exception("Error in reading all books"); | |
} | |
} | |
Future<int> update(Book note) async { | |
try { | |
final db = await instance.database; | |
print(note.toJson()); | |
return await db.update( | |
tableBooks, | |
note.toJson(), | |
where: '${BookFields.id} = ?', | |
whereArgs: [note.id], | |
); | |
} catch (error) { | |
throw Exception("Error in updating book"); | |
} | |
} | |
Future<int> delete(int id) async { | |
try { | |
final db = await instance.database; | |
return await db | |
.delete(tableBooks, where: '${BookFields.id} = ?', whereArgs: [id]); | |
} catch (error) { | |
throw Exception("Error in deleting book"); | |
} | |
} | |
Future close() async { | |
try { | |
final db = await instance.database; | |
db.close(); | |
} catch (error) { | |
throw Exception("Error in closing database"); | |
} | |
} | |
} |
- Container
- FloatingActionButton
- Column
- CircleAvatar
- Padding
- Staggergrid
book_collection_app_flutter/lib/pages/books_page.dart
Lines 38 to 84 in 17d7d87
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: const Text( | |
'Books', | |
style: TextStyle( | |
color: Colors.white, | |
fontSize: 28, | |
), | |
), | |
centerTitle: true, | |
backgroundColor: Colors.transparent, | |
), | |
floatingActionButton: FloatingActionButton( | |
onPressed: () async { | |
await Navigator.of(context).push( | |
MaterialPageRoute( | |
builder: (context) => const AddEditBookPage(), | |
) | |
); | |
refreshBooks(); | |
}, | |
child: const Icon(Icons.add), | |
), | |
body: Column( | |
children: [ | |
const CircleAvatar( | |
backgroundImage: AssetImage('assets/profile.webp'), | |
), | |
Padding( | |
padding: const EdgeInsets.all(10), | |
child: Center( | |
child: isLoading ? const CircularProgressIndicator() | |
: books.isEmpty ? const Text( | |
'No Books', | |
style: TextStyle( | |
color: Colors.white | |
), | |
) | |
: buildBooks() | |
), | |
), | |
], | |
), | |
); | |
} |