Add basic CRUD with sqlite

This commit is contained in:
Brandon Presley 2023-04-07 15:56:17 +12:00
parent 03eaa16842
commit 82ef2bf87c
7 changed files with 206 additions and 84 deletions

View File

@ -1,13 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:fmassive/home_page.dart'; import 'package:fmassive/gym_set.dart';
class EditGymSetPage extends StatefulWidget { class EditGymSetPage extends StatefulWidget {
final GymSet gymSet; final GymSet gymSet;
EditGymSetPage({required this.gymSet}); const EditGymSetPage({required this.gymSet, super.key});
@override @override
_EditGymSetPageState createState() => _EditGymSetPageState(); createState() => _EditGymSetPageState();
} }
class _EditGymSetPageState extends State<EditGymSetPage> { class _EditGymSetPageState extends State<EditGymSetPage> {
@ -16,17 +16,18 @@ class _EditGymSetPageState extends State<EditGymSetPage> {
final TextEditingController _weightController = TextEditingController(); final TextEditingController _weightController = TextEditingController();
late GymSet _editedGymSet; late GymSet _editedGymSet;
late GymSetDatabaseHelper _db;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// Initialize the edited GymSet object with the values from the input GymSet object _db = GymSetDatabaseHelper.instance;
_editedGymSet = GymSet( _editedGymSet = GymSet(
id: widget.gymSet.id,
name: widget.gymSet.name, name: widget.gymSet.name,
reps: widget.gymSet.reps, reps: widget.gymSet.reps,
weight: widget.gymSet.weight, weight: widget.gymSet.weight,
created: DateTime.now()); created: DateTime.now());
// Set the text controller values
_nameController.text = _editedGymSet.name; _nameController.text = _editedGymSet.name;
_repsController.text = _editedGymSet.reps.toString(); _repsController.text = _editedGymSet.reps.toString();
_weightController.text = _editedGymSet.weight.toString(); _weightController.text = _editedGymSet.weight.toString();
@ -35,9 +36,15 @@ class _EditGymSetPageState extends State<EditGymSetPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(title: const Text('Edit Gym Set'), actions: [
title: Text('Edit Gym Set'), IconButton(
), onPressed: () async {
await _db.delete(_editedGymSet);
if (!mounted) return;
Navigator.pop(context, _editedGymSet);
},
icon: const Icon(Icons.delete))
]),
body: Padding( body: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Column( child: Column(
@ -76,7 +83,13 @@ class _EditGymSetPageState extends State<EditGymSetPage> {
), ),
), ),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
onPressed: () { onPressed: () async {
print('edited gym set id: ${_editedGymSet.id}');
if (_editedGymSet.id != null)
await _db.update(_editedGymSet);
else
await _db.insert(_editedGymSet);
if (!mounted) return;
Navigator.pop(context, _editedGymSet); Navigator.pop(context, _editedGymSet);
}, },
child: const Icon(Icons.check), child: const Icon(Icons.check),

103
lib/gym_set.dart Normal file
View File

@ -0,0 +1,103 @@
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class GymSet {
int? id; // added id for SQLite
String name;
int reps;
int weight;
DateTime created;
GymSet(
{this.id,
required this.name,
required this.reps,
required this.weight,
required this.created});
}
class GymSetDatabaseHelper {
static final _databaseName = "gym_set_database.db";
static final _databaseVersion = 1;
static final table = 'gym_sets';
static final columnId = '_id';
static final columnName = 'name';
static final columnReps = 'reps';
static final columnWeight = 'weight';
static final columnCreated = 'created';
GymSetDatabaseHelper._privateConstructor();
static final GymSetDatabaseHelper instance =
GymSetDatabaseHelper._privateConstructor();
static Database? _database;
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}
_initDatabase() async {
String path = join(await getDatabasesPath(), _databaseName);
return await openDatabase(path,
version: _databaseVersion, onCreate: _onCreate);
}
Future _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE $table (
$columnId INTEGER PRIMARY KEY,
$columnName TEXT NOT NULL,
$columnReps INTEGER NOT NULL,
$columnWeight INTEGER NOT NULL,
$columnCreated TEXT NOT NULL
)
''');
}
Future<int> insert(GymSet gymSet) async {
Database db = await instance.database;
return await db.insert(table, {
columnName: gymSet.name,
columnReps: gymSet.reps,
columnWeight: gymSet.weight,
columnCreated: gymSet.created.toIso8601String()
});
}
Future<List<GymSet>> getAll() async {
Database db = await instance.database;
List<Map<String, dynamic>> result = await db.query(table);
return result
.map((gymSetMap) => GymSet(
id: gymSetMap[columnId],
name: gymSetMap[columnName],
reps: gymSetMap[columnReps],
weight: gymSetMap[columnWeight],
created: DateTime.parse(gymSetMap[columnCreated]),
))
.toList();
}
Future<int> update(GymSet gymSet) async {
Database db = await instance.database;
return await db.update(
table,
{
columnName: gymSet.name,
columnReps: gymSet.reps,
columnWeight: gymSet.weight,
columnCreated: gymSet.created.toIso8601String()
},
where: '$columnId = ?',
whereArgs: [gymSet.id]);
}
Future<int> delete(GymSet gymSet) async {
Database db = await instance.database;
return await db
.delete(table, where: '$columnId = ?', whereArgs: [gymSet.id]);
}
}

View File

@ -1,10 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:fmassive/edit_set.dart'; import 'package:fmassive/edit_set.dart';
import 'package:fmassive/gym_set.dart';
class HomePage extends StatelessWidget { class HomePage extends StatelessWidget {
HomePage({super.key}); HomePage({super.key});
final List<Map<String, dynamic>> _pageData = [ final List<Map<String, dynamic>> routes = [
{'title': 'Home', 'icon': Icons.home}, {'title': 'Home', 'icon': Icons.home},
{'title': 'Plans', 'icon': Icons.calendar_today}, {'title': 'Plans', 'icon': Icons.calendar_today},
{'title': 'Best', 'icon': Icons.star}, {'title': 'Best', 'icon': Icons.star},
@ -17,19 +18,19 @@ class HomePage extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(_pageData[0]['title']), title: Text(routes[0]['title']),
), ),
drawer: Drawer( drawer: Drawer(
child: ListView.builder( child: ListView.builder(
itemCount: _pageData.length, itemCount: routes.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return ListTile( return ListTile(
leading: Icon(_pageData[index]['icon']), leading: Icon(routes[index]['icon']),
title: Text(_pageData[index]['title']), title: Text(routes[index]['title']),
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
Navigator.pushNamed( Navigator.pushNamed(
context, '/${_pageData[index]['title'].toLowerCase()}'); context, '/${routes[index]['title'].toLowerCase()}');
}, },
); );
}, },
@ -42,130 +43,105 @@ class HomePage extends StatelessWidget {
} }
} }
class GymSet {
String name;
int reps;
int weight;
DateTime created;
GymSet(
{required this.name,
required this.reps,
required this.weight,
required this.created});
}
class GymSetPage extends StatefulWidget { class GymSetPage extends StatefulWidget {
const GymSetPage({Key? key}) : super(key: key); const GymSetPage({Key? key}) : super(key: key);
@override @override
_GymSetPageState createState() => _GymSetPageState(); createState() => GymSetPageState();
} }
class _GymSetPageState extends State<GymSetPage> { class GymSetPageState extends State<GymSetPage> {
final List<GymSet> _gymSets = [ late GymSetDatabaseHelper db;
GymSet( List<GymSet> sets = [];
name: "Bench press",
reps: 10,
weight: 50,
created: DateTime(2022, 1, 1)),
GymSet(
name: "Bench press",
reps: 8,
weight: 60,
created: DateTime(2022, 1, 2)),
GymSet(
name: "Bench press",
reps: 6,
weight: 70,
created: DateTime(2022, 1, 3)),
GymSet(
name: "Shoulder press",
reps: 12,
weight: 40,
created: DateTime(2022, 1, 4)),
GymSet(
name: "Shoulder press",
reps: 15,
weight: 35,
created: DateTime(2022, 1, 5)),
];
List<GymSet> _searchResults = []; List<GymSet> searchResults = [];
final TextEditingController _searchController = TextEditingController(); final TextEditingController searchController = TextEditingController();
void _searchGymSets(String searchQuery) { void search(String searchQuery) {
List<GymSet> results = []; List<GymSet> results = [];
if (searchQuery.isEmpty) { if (searchQuery.isEmpty) {
results = _gymSets; results = sets;
} else { } else {
for (int i = 0; i < _gymSets.length; i++) { for (int i = 0; i < sets.length; i++) {
if (_gymSets[i].reps.toString().contains(searchQuery) || if (sets[i].reps.toString().contains(searchQuery) ||
_gymSets[i].weight.toString().contains(searchQuery) || sets[i].weight.toString().contains(searchQuery) ||
_gymSets[i].created.toString().contains(searchQuery) || sets[i].created.toString().contains(searchQuery) ||
_gymSets[i].name.contains(searchQuery)) { sets[i].name.contains(searchQuery)) {
results.add(_gymSets[i]); results.add(sets[i]);
} }
} }
} }
setState(() { setState(() {
_searchResults = results; searchResults = results;
});
}
void reset() async {
print('Resetting...');
final data = await db.getAll();
setState(() {
sets = data;
searchResults = data;
}); });
} }
@override @override
void initState() { void initState() {
super.initState(); super.initState();
db = GymSetDatabaseHelper.instance;
// Initialize the search results to all the gym sets reset();
_searchResults = _gymSets;
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
automaticallyImplyLeading: false,
title: TextField( title: TextField(
controller: _searchController, controller: searchController,
decoration: const InputDecoration( decoration: const InputDecoration(
hintText: 'Search Gym Sets', hintText: 'Search Gym Sets',
border: InputBorder.none, border: InputBorder.none,
), ),
onChanged: (searchQuery) { onChanged: (searchQuery) {
_searchGymSets(searchQuery); search(searchQuery);
}, },
), ),
), ),
body: ListView.builder( body: ListView.builder(
itemCount: _searchResults.length, itemCount: searchResults.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return ListTile( return ListTile(
title: Text( title: Text(
'${_searchResults[index].name}: ${_searchResults[index].reps}x${_searchResults[index].weight}kg'), '${searchResults[index].name}: ${searchResults[index].reps}x${searchResults[index].weight}kg'),
onTap: () { onTap: () async {
Navigator.push( await Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) =>
EditGymSetPage(gymSet: _searchResults[index]), EditGymSetPage(gymSet: searchResults[index]),
), ),
); );
reset();
}); });
}, },
), ),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
onPressed: () { onPressed: () async {
Navigator.push( await Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) => EditGymSetPage(
EditGymSetPage(gymSet: _searchResults[0]), gymSet: GymSet(
name: '',
reps: 0,
weight: 0,
created: DateTime.now())),
), ),
); );
reset();
}, },
child: const Icon(Icons.add))); child: const Icon(Icons.add)));
} }

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:fmassive/best_page.dart'; import 'package:fmassive/best_page.dart';
import 'package:fmassive/edit_set.dart'; import 'package:fmassive/edit_set.dart';
import 'package:fmassive/gym_set.dart';
import 'package:fmassive/home_page.dart'; import 'package:fmassive/home_page.dart';
import 'package:fmassive/plans_page.dart'; import 'package:fmassive/plans_page.dart';
import 'package:fmassive/settings_page.dart'; import 'package:fmassive/settings_page.dart';

View File

@ -5,6 +5,8 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import sqflite
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
} }

View File

@ -116,7 +116,7 @@ packages:
source: hosted source: hosted
version: "1.8.0" version: "1.8.0"
path: path:
dependency: transitive dependency: "direct main"
description: description:
name: path name: path
sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b
@ -136,6 +136,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.1" version: "1.9.1"
sqflite:
dependency: "direct main"
description:
name: sqflite
sha256: "500d6fec583d2c021f2d25a056d96654f910662c64f836cd2063167b8f1fa758"
url: "https://pub.dev"
source: hosted
version: "2.2.6"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: "963dad8c4aa2f814ce7d2d5b1da2f36f31bd1a439d8f27e3dc189bb9d26bc684"
url: "https://pub.dev"
source: hosted
version: "2.4.3"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
@ -160,6 +176,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.0" version: "1.2.0"
synchronized:
dependency: transitive
description:
name: synchronized
sha256: "33b31b6beb98100bf9add464a36a8dd03eb10c7a8cf15aeec535e9b054aaf04b"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
@ -186,3 +210,4 @@ packages:
version: "2.1.4" version: "2.1.4"
sdks: sdks:
dart: ">=2.19.5 <3.0.0" dart: ">=2.19.5 <3.0.0"
flutter: ">=3.3.0"

View File

@ -34,6 +34,8 @@ dependencies:
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
path: ^1.8.2
sqflite: ^2.2.6
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: