My customer app (whereby you add customer details to a list) successfully adds and deletes data to the data base and sets the state properly. But I cannot get my edit/update function to work.
When I click my edit icon, it successfully shows the details of the existing data, but when I change the detail (text) it does not update to set the state with the new data.
The functions insertTodo
, deleteTodo
and updateTodo
are the problem:
Library
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class Todo {
int? id;
final String title;
final String name;
final String phone;
final String fax;
final String email;
final String street;
final String city;
final String town;
final String code;
bool isExpanded;
Todo({
this.id,
required this.title,
required this.name,
required this.phone,
required this.fax,
required this.email,
required this.street,
required this.city,
required this.town,
required this.code,
this.isExpanded = false,
});
Map<String, dynamic> toMap() {
return {
'id': id,
'title': title,
'name': name,
'phone': phone,
'fax': fax,
'email': email,
'street': street,
'city': city,
'town': town,
'code': code,
'isExpanded': isExpanded ? 1 : 0,
};
}
@override
String toString() {
return 'Todo(id : $id, title : $title, name : $name, phone : $phone, fax: $fax, email:
$email, street: $street, city: $city, town: $town, code: $code, isExpanded :
$isExpanded,)';
}
}
class DatabaseConnect {
Database? _database;
Future<Database> get database async {
final dbpath = await getDatabasesPath();
const dbname = 'todo.db';
final path = join(dbpath, dbname);
_database = await openDatabase(path, version: 1, onCreate: _createDB);
return _database!;
}
Future<void> _createDB(Database db, int version) async {
await db.execute('''
CREATE TABLE todo(
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
name TEXT,
phone TEXT,
fax TEXT,
email TEXT,
street TEXT,
city TEXT,
town TEXT,
code TEXT,
isExpanded INTEGER
)
''');
}
Future<void> insertTodo(Todo todo) async {
final db = await database;
await db.insert(
'todo',
todo.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
Future<void> deleteTodo(Todo todo) async {
final db = await database;
await db.delete(
'todo',
where: 'id == ?',
whereArgs: [todo.id],
);
}
Future<void> updateTodo(Todo todo) async {
final db = await database;
await db.update(
'todo',
todo.toMap(),
where: 'id = ?',
whereArgs: [todo.id],
);
}
Future<List<Todo>> getTodo() async {
final db = await database;
List<Map<String, dynamic>> items = await db.query(
'todo',
orderBy: 'title ASC',
);
return List.generate(
items.length,
(i) => Todo(
id: items[i]['id'],
title: items[i]['title'],
name: items[i]['name'],
phone: items[i]['phone'],
fax: items[i]['fax'],
email: items[i]['email'],
street: items[i]['street'],
city: items[i]['city'],
town: items[i]['town'],
code: items[i]['code'],
isExpanded: items[i]['isExpanded'] == 1 ? true : false,
),
);
}
Future<List<Todo>> searchContacts(String keyword) async {
final db = await database;
List<Map<String, dynamic>> items =
await db.query('todo', where: 'title LIKE ?', whereArgs: ['$keyword%']);
return List.generate(
items.length,
(i) => Todo(
id: items[i]['id'],
title: items[i]['title'],
name: items[i]['name'],
phone: items[i]['phone'],
fax: items[i]['fax'],
email: items[i]['email'],
street: items[i]['street'],
city: items[i]['city'],
town: items[i]['town'],
code: items[i]['code'],
isExpanded: items[i]['isExpanded'] == 1 ? true : false,
),
);
}
}
Following is my Customer page, where the functions addItem
, deleteItem
, updateItem
are called that link to the database and also the page where I setState
(the mainframe the app goes back to after any action is taken). It portrays the list of data after a new customer detail gets added, existing detail deleted and new info updated:
Customer
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import '../customerlist.dart';
import '../library.dart';
import '../user_input.dart';
class Customer extends StatefulWidget {
const Customer({Key? key}) : super(key: key);
@override
_CustomerState createState() => _CustomerState();
}
class _CustomerState extends State<Customer> {
var db = DatabaseConnect();
void addItem(Todo todo) async {
await db.insertTodo(todo);
setState(() {});
}
void deleteItem(Todo todo) async {
await db.deleteTodo(todo);
setState(() {});
}
void updateItem(Todo todo) async {
await db.updateTodo(todo);
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
centerTitle: false,
title: const Padding(
padding: EdgeInsets.all(50.0),
child: Text(
'My Clients',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w600,
color: Colors.black),
),
),
backgroundColor: Colors.white,
elevation: 0,
actions: [
IconButton(
onPressed: () {
Navigator.of(context).pushReplacementNamed('/searchPage');
},
icon: const Icon(
Icons.search,
color: Colors.black,
),
),
]),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Text(
'Company Name',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
),
),
CustomerList(
// insertFunction: addItem,
tobie: deleteItem,
stella: updateItem,
),
],
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.lightBlueAccent,
child: const Icon(Icons.add),
onPressed: () {
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) => CustomerProfile(insertFunction: addItem),
);
}),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
);
}
}
The next code is my CustomerList where my ListviewBuilder
checks for customer data to display. If there are, passes the detail to my CustomerCard page (and populate into a card/ListTile). Also in my CustomerList it passes my additem through function Stella
from Customer, through CustomerList, through CustomerCard, to my EditPage:
CustomerList
import 'package:flutter/material.dart';
import 'library.dart';
import 'customercard.dart';
class CustomerList extends StatelessWidget {
final Function tobie;
final Function stella;
final db = DatabaseConnect();
CustomerList(
{required this.stella,
required this.tobie,
Key? key})
: super(key: key);
@override
Widget build(BuildContext context) {
return Expanded(
child: FutureBuilder(
future: db.getTodo(),
initialData: const [],
builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
var data = snapshot
.data; // this is the data we have to show. (list of todo)
var datalength = data!.length;
return datalength == 0
? const Center(
child: Text('no data found'),
)
: ListView.builder(
itemCount: datalength,
itemBuilder: (context, i) => CustomerCard(
id: data[i].id,
title: data[i].title,
name: data[i].name,
phone: data[i].phone,
fax: data[i].fax,
email: data[i].email,
street: data[i].street,
city: data[i].city,
town: data[i].town,
code: data[i].code,
isExpanded: data[i].isExpanded,
// insertFunction: insertFunction,
janFunction: tobie,
simbaFunction: stella,
),
);
}),
);
}
}
Following is my CustomerCard code, which populates the data into a Card and ListTile. Once again, my updateItem is passed through (simbaFunction:stella):
CustomerCard
import 'package:flutter/material.dart';
import 'library.dart';
import 'package:test_sqlite/edit_input.dart';
class CustomerCard extends StatefulWidget {
final int id;
final String title;
final String name;
final String phone;
final String fax;
final String email;
final String street;
final String city;
final String town;
final String code;
bool isExpanded;
final Function janFunction;
final Function simbaFunction;
CustomerCard(
{required this.id,
required this.title,
required this.name,
required this.phone,
required this.fax,
required this.email,
required this.street,
required this.city,
required this.town,
required this.code,
required this.isExpanded,
required this.janFunction,
required this.simbaFunction,
Key? key})
: super(key: key);
@override
_CustomerCardState createState() => _CustomerCardState();
}
class _CustomerCardState extends State<CustomerCard> {
var db = DatabaseConnect();
@override
Widget build(BuildContext context) {
var anotherTodo = Todo(
id: widget.id,
title: widget.title,
name: widget.name,
phone: widget.phone,
fax: widget.fax,
email: widget.email,
street: widget.street,
city: widget.city,
town: widget.town,
code: widget.code,
isExpanded: widget.isExpanded);
return Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Theme(
data: Theme.of(context).copyWith(dividerColor: Colors.transparent),
child: ExpansionTile(
initiallyExpanded: false,
title: Text(
widget.title,
style: const TextStyle(
//fontWeight: FontWeight.bold,
fontSize: 16,
),
),
children: [
ListTile(
leading: const Icon(
Icons.person,
size: 20,
color: Colors.teal,
),
visualDensity: const VisualDensity(vertical: -3),
title: Text(
widget.name,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black,
),
),
),
ListTile(
leading: const Icon(
Icons.phone,
size: 20,
color: Colors.teal,
),
visualDensity: const VisualDensity(vertical: -4),
title: Text(
widget.phone,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black,
),
),
),
ListTile(
leading: const Icon(
Icons.report,
size: 20,
color: Colors.teal,
),
visualDensity: const VisualDensity(vertical: -4),
title: Text(
widget.fax,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black,
),
),
),
ListTile(
leading: const Icon(
Icons.email,
size: 20,
color: Colors.teal,
),
visualDensity: const VisualDensity(vertical: -4),
title: Text(
widget.email,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black,
),
),
),
ListTile(
leading: const Icon(
Icons.place,
size: 20,
color: Colors.teal,
),
visualDensity: const VisualDensity(vertical: -4),
title: Text(
widget.street,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black,
),
),
),
ListTile(
leading: const Icon(
Icons.place,
size: 20,
color: Colors.teal,
),
visualDensity: const VisualDensity(vertical: -4),
title: Text(
widget.city,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black,
),
),
),
ListTile(
leading: const Icon(
Icons.place,
size: 20,
color: Colors.teal,
),
visualDensity: const VisualDensity(vertical: -4),
title: Text(
widget.town,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black,
),
),
),
ListTile(
leading: const Icon(
Icons.code,
size: 20,
color: Colors.teal,
),
visualDensity: const VisualDensity(vertical: -4),
title: Text(
widget.code,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black,
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () {
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) => EditPage(
nalaFunction: widget.simbaFunction,
variable: anotherTodo,
edit: '',
edit1: '',
edit2: '',
edit3: '',
edit4: '',
edit5: '',
edit6: '',
edit7: '',
edit8: '',
),
//updateFunction,
//),
);
},
style: ElevatedButton.styleFrom(
shape: const StadiumBorder(),
primary: Colors.white,
elevation: 0,
padding: const EdgeInsets.symmetric(
horizontal: 2, vertical: 2),
),
child: const Icon(
Icons.edit,
size: 20,
color: Colors.grey,
),
),
),
],
),
],
leading: const IconButton(
icon: Icon(
Icons.place,
color: Colors.blue,
size: 20,
),
onPressed: null,
alignment: Alignment.center,
),
trailing: IconButton(
onPressed: () {
widget.janFunction(anotherTodo);
},
icon: const Icon(
Icons.delete,
color: Colors.red,
size: 20,
),
),
),
),
],
),
);
}
}
Lastly, my EditPage. When I open it, the existing customer data in the SQLite database automatically displays correctly. Each Textfield has its own onChanged. Once I press the Elevated button, the bottomModalSheet closes properly (pop), but the new data does not actually update. This is the problem.
EditPage
import 'package:flutter/material.dart';
import 'library.dart';
import 'package:flutter/cupertino.dart';
class EditPage extends StatelessWidget {
final Todo variable;
final Function nalaFunction;
String edit;
String edit1;
String edit2;
String edit3;
String edit4;
String edit5;
String edit6;
String edit7;
String edit8;
late final textController = TextEditingController(text: variable.title);
late final nameController = TextEditingController(text: variable.name);
late final phoneController = TextEditingController(text: variable.phone);
late final faxController = TextEditingController(text: variable.fax);
late final emailController = TextEditingController(text: variable.email);
late final streetController = TextEditingController(text: variable.street);
late final cityController = TextEditingController(text: variable.city);
late final townController = TextEditingController(text: variable.town);
late final codeController = TextEditingController(text: variable.code);
EditPage(
{Key? key,
required this.nalaFunction,
required this.variable,
required this.edit,
required this.edit1,
required this.edit2,
required this.edit3,
required this.edit4,
required this.edit5,
required this.edit6,
required this.edit7,
required this.edit8,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(30.0),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30.0),
topRight: Radius.circular(30.0),
),
),
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
title: const Padding(
padding: EdgeInsets.all(15.0),
child: Text(
'Client Details',
style: TextStyle(color: Colors.black, fontSize: 24),
),
),
leading: GestureDetector(
onTap: () {
Navigator.of(context).pushReplacementNamed('/homePage');
},
child: const Icon(
Icons.arrow_back,
color: Colors.black,
),
),
),
body: SingleChildScrollView(
child: Container(
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextField(
controller: textController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: 'Company Name',
hintStyle: TextStyle(color: Colors.grey),
),
onChanged: (String? value) {
edit = value!;
},
),
TextField(
controller: nameController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: ' Contact Name & Surname',
hintStyle: TextStyle(color: Colors.grey),
),
onChanged: (String? value1) {
edit1 = value1!;
},
),
TextField(
controller: phoneController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: ' Contact Number',
hintStyle: TextStyle(color: Colors.grey),
),
onChanged: (String? value2) {
edit2 = value2!;
},
),
TextField(
controller: faxController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: 'Fax Number',
hintStyle: TextStyle(color: Colors.grey),
),
onChanged: (String? value3) {
edit3 = value3!;
},
),
TextField(
controller: emailController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: 'Email Address',
hintStyle: TextStyle(color: Colors.grey),
),
onChanged: (String? value4) {
edit4 = value4!;
},
),
TextField(
controller: streetController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: 'Street Name',
hintStyle: TextStyle(color: Colors.grey),
),
onChanged: (String? value5) {
edit5 = value5!;
},
),
TextField(
controller: cityController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: ' City',
hintStyle: TextStyle(color: Colors.grey),
),
onChanged: (String? value6) {
edit6 = value6!;
},
),
TextField(
controller: townController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: 'Town',
hintStyle: TextStyle(color: Colors.grey),
),
onChanged: (String? value7) {
edit7 = value7!;
},
),
TextField(
controller: codeController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: ' Code',
hintStyle: TextStyle(color: Colors.grey),
),
onChanged: (String? value8) {
edit8 = value8!;
},
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
var valueTodo = Todo(
title: edit,
name: edit1,
phone: edit2,
fax: edit3,
email: edit4,
street: edit5,
city: edit6,
town: edit7,
code: edit8,
isExpanded: false);
nalaFunction(valueTodo);
},
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 10, 0, 0),
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(15),
),
padding: const EdgeInsets.symmetric(
horizontal: 25, vertical: 10),
child: const Text(
'Update',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
),
],
),
),
),
),
);
}
}
I think the problem is either:
- My function in my database is incorrect.
- Somehow the data (updateItem) in Customer page does not link with updateTodo in the database.
- The updateItem data is passed through all the widgets to EditPage incorrectly.
- My code in EditPage (ElevatedButton onPressed is wrong).
- The argument (edit '', edit1 '' and so on) in CustomerCard page that route to EditPage is wrong.
CodePudding user response:
I have managed to finally resolve the problem. I have replaced my ElevatedButton with a GestureDetector and also removed onChaged from my TextFields, thus also removing all my edit parameters.
My updateFunction and updateItem function was actually working just fine all along. I previously used GestureDetector with onTap and a variable (updateTodo) with my textEditingControllers, but since I could not make this work, I tried ElevateButton with the onChanged, which also did not work. So, moving back to GestureDetector, I realized that I never assigned an 'id' to my variable ' updateTodo'. I only had my TextEditingController. Now, in my Sql Database, the database is updated using 'Where: id', but since I did not assign an 'id' to my text, I effectively returned Null everytime I updated.
Here is the snippet of code for GetureDetector before the fix,
GestureDetector(
onTap: () {
Navigator.pop(context);
var updateTodo = Todo(
title: textController.text,
name: nameController.text,
phone: phoneController.text,
fax: faxController.text,
email: emailController.text,
street: streetController.text,
city: cityController.text,
town: townController.text,
code: codeController.text,
isExpanded: false);
nalaFunction(updateTodo);
},
and here the same code after the fix:
GestureDetector(
onTap: () {
Navigator.pop(context);
var updateTodo = Todo(
id: id,
title: textController.text,
name: nameController.text,
phone: phoneController.text,
fax: faxController.text,
email: emailController.text,
street: streetController.text,
city: cityController.text,
town: townController.text,
code: codeController.text,
isExpanded: false);
nalaFunction(updateTodo);
},
and the code for passing the 'id' in CustomerCard class through to EditPage:
class CustomerCard extends StatefulWidget {
final int id;
final String title;
final String name;
final String phone;
final String fax;
final String email;
final String street;
final String city;
final String town;
final String code;
bool isExpanded;
final Function janFunction;
final Function simbaFunction;
CustomerCard(
{required this.id,
required this.title,
required this.name,
required this.phone,
required this.fax,
required this.email,
required this.street,
required this.city,
required this.town,
required this.code,
required this.isExpanded,
and ..
Row(mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () {
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) => EditPage(
nalaFunction: widget.simbaFunction,
variable: anotherTodo,
id: widget.id,
),
//updateFunction,
//),
);
},
and passing the 'id' through to the EditPage so that it can be reached by the GestureDetector on the same page:
class EditPage extends StatelessWidget {
final Todo variable;
final Function nalaFunction;
final int id;
late final textController = TextEditingController(text: variable.title);
late final nameController = TextEditingController(text: variable.name);
late final phoneController = TextEditingController(text: variable.phone);
late final faxController = TextEditingController(text: variable.fax);
late final emailController = TextEditingController(text: variable.email);
late final streetController = TextEditingController(text: variable.street);
late final cityController = TextEditingController(text: variable.city);
late final townController = TextEditingController(text: variable.town);
late final codeController = TextEditingController(text: variable.code);
EditPage({Key? key,
required this.nalaFunction,
required this.variable,
required this.id,
}) : super(key: key);
This resolved my problem and my app now update new customer data perfectly.