Add basic CRUD with sqlite
This commit is contained in:
parent
03eaa16842
commit
82ef2bf87c
|
@ -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
103
lib/gym_set.dart
Normal 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]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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"))
|
||||||
}
|
}
|
||||||
|
|
27
pubspec.lock
27
pubspec.lock
|
@ -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"
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user