I cant seem to update or edit my Todo app - flutter

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.

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.

Related

SqfliteDatabaseException (DatabaseException(NOT NULL constraint failed: Note.Text (code 1299 SQLITE_CONSTRAINT_NOTNULL))

I was following this tutorial on Youtube on how to create a database in Flutter app, I literally followed all the instructions on the tutorial but I keep getting this exception whenever I try to click the save button to save a new note to the database which indicates that NOT NULL constraint failed.
here
here is what my files look like:
note_model.dart:
class Note {
final int? id;
final String title;
final String content;
const Note({required this.title, required this.content, this.id});
factory Note.fromJson(Map<String, dynamic> json) => Note(
id: json['id'],
title: json['title'],
content: json['content'],
);
Map<String, dynamic> toJson() => {
'id': id,
'title': title,
'content': content,
};
#override
String toString() {
return "id: $id \n title: $title \n content: $content \n";
}
}
database_help:
import 'package:dummy_database/models/note_model.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DatabaseHelper {
static const int _version = 1;
static const String _dbName = "Notes.db";
static Future<Database> _getDB() async {
return openDatabase(join(await getDatabasesPath(), _dbName),
onCreate: (db, version) async => await db.execute(
"CREATE TABLE Note(id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT NOT NULL);"),
version: _version);
}
static Future<int> addNote(Note note) async {
final db = await _getDB();
return await db.insert("Note", note.toJson(),
conflictAlgorithm: ConflictAlgorithm.replace);
}
static Future<int> updateNote(Note note) async {
final db = await _getDB();
return await db.update("Note", note.toJson(),
where: 'id = ?',
whereArgs: [note.id],
conflictAlgorithm: ConflictAlgorithm.replace);
}
static Future<int> deleteNote(Note note) async {
final db = await _getDB();
return await db.delete(
"Note",
where: 'id = ?',
whereArgs: [note.id],
);
}
static Future<List<Note>?> getAllNotes() async {
final db = await _getDB();
final List<Map<String, dynamic>> maps = await db.query("Note");
if (maps.isEmpty) {
return null;
}
return List.generate(maps.length, (index) => Note.fromJson(maps[index]));
}
}
***here is the file that causing the exception: ***
import 'dart:developer';
import 'package:dummy_database/models/note_model.dart';
import 'package:dummy_database/services/database_helper.dart';
import 'package:flutter/material.dart';
class NoteScreen extends StatelessWidget {
final Note? note;
NoteScreen({Key? key, this.note}) : super(key: key);
final titleController = TextEditingController();
final contentController = TextEditingController();
#override
Widget build(BuildContext context) {
if (note != null) {
titleController.text = note!.title;
contentController.text = note!.content;
}
return Scaffold(
appBar: AppBar(
title: Text(note == null ? 'Add a note' : 'Edit note'),
centerTitle: true,
),
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 30),
child: Column(
children: [
const Padding(
padding: EdgeInsets.only(bottom: 40),
child: Center(
child: Text(
'What are you thinking about?',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
),
),
Padding(
padding: const EdgeInsets.only(bottom: 40.0),
child: TextFormField(
controller: titleController,
maxLines: 1,
decoration: const InputDecoration(
hintText: 'Title',
labelText: 'Note title',
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.white,
width: 0.75,
),
borderRadius: BorderRadius.all(
Radius.circular(10.0),
))),
),
),
TextFormField(
controller: contentController,
decoration: const InputDecoration(
hintText: 'Type here the note',
labelText: 'Note content',
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.white,
width: 0.75,
),
borderRadius: BorderRadius.all(
Radius.circular(10.0),
))),
keyboardType: TextInputType.multiline,
onChanged: (str) {},
maxLines: 5,
),
const Spacer(),
Padding(
padding: const EdgeInsets.only(bottom: 20.0),
child: SizedBox(
height: 45,
width: MediaQuery.of(context).size.width,
child: ElevatedButton(
onPressed: () async {
final title = titleController.value.text;
final content = contentController.value.text;
log("title value is $title, \n content value is $content");
if (title.isEmpty || content.isEmpty) {
return;
}
final Note model =
Note(title: title, content: content, id: note?.id);
log(model.toString());
if (note == null) {
await DatabaseHelper.addNote(model);
} else {
await DatabaseHelper.updateNote(model);
}
Navigator.pop(context);
},
style: ButtonStyle(
shape: MaterialStateProperty.all(
const RoundedRectangleBorder(
side: BorderSide(
color: Colors.white,
width: 0.75,
),
borderRadius: BorderRadius.all(
Radius.circular(10.0),
)))),
child: Text(
note == null ? 'Save' : 'Edit',
style: const TextStyle(fontSize: 20),
)),
),
)
],
),
),
);
}
}

Flutter dropdown with getX not working properly on some devices

I'm using getx for state management,here's the video of the problem
https://drive.google.com/file/d/1tm2M46pkXVnGuf4vyh9rNs2HY2TdtBD8/view?usp=sharing
here is my code
class ActivitiesController extends GetxController {
late List<String> statusList = ["All", "Approved", "Unapproved"];
var selectedStatus = "Approved".obs;
}
#override
ActivitiesController get controller => Get.put(ActivitiesController());
in view:
const RequiredText(text: "Status"),
const SizedBox(height: Constants.defaultPadding / 2),
Obx(
() => GlobalDropDownContainer(
hintText: "All",
items: controller.statusList.toList(),
onChange: (value) {
controller.selectedStatus(value);
},
selectedValue: controller.selectedStatus.value,
)),
Here is the "GlobalDropDownContainer" code
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class GlobalDropDownContainer extends StatelessWidget {
final String hintText;
final List<String> items;
final double? width;
final Color? isNotValid;
final Function(String?) onChange;
final String? selectedValue;
const GlobalDropDownContainer({
Key? key,
required this.hintText,
required this.items,
this.width,
this.isNotValid,
required this.onChange,
this.selectedValue,
}) : super(key: key);
#override
Widget build(BuildContext context) {
Size size = Get.size;
return Container(
width: width != null ? (size.width * width!) : Get.width,
padding: const EdgeInsets.symmetric(horizontal: 10),
decoration: BoxDecoration(
border: Border.all(
width: 1,
color: isNotValid ??
Theme.of(context).colorScheme.onSurface.withOpacity(0.5),
),
color:Theme.of(context).colorScheme.background,
borderRadius: BorderRadius.circular(5)),
child: DropdownButton<String>(
dropdownColor: Theme.of(context).colorScheme.background,
value: selectedValue != null && selectedValue!.isNotEmpty
? selectedValue
: null,
isExpanded: true,
underline: const SizedBox(),
hint: Text(
hintText,
style: const TextStyle(
color: Color(0xFF666666),
),
),
style: TextStyle(
color: Theme.of(context).colorScheme.onBackground,
),
items: items.map((String value) {
return DropdownMenuItem<String>(
value: value != null && value.isNotEmpty ? value : null,
child: Text(
value,
style: TextStyle(
color: Theme.of(context).colorScheme.onBackground),
),
);
}).toList(),
onChanged: onChange,
));
}
}
I searched for a day and i didn't find anything, i tried debug the code but it gives no warning or error. Can Anyone help me?
Try this one
Make an instance of a controller, which i presumed u have done already.
final controller = Get.put(yourgetxcontrollername());
The Widget code
Obx(
() => DropdownButton<String>(
isExpanded: true,
value: controller.selectedStatus.value,
icon: const Icon(Icons.arrow_drop_down),
iconSize: 24,
elevation: 16,
style: const TextStyle(
color: Colors.blue,
fontSize: 14,
),
onChanged: (value) {
controller.selectedStatus(
value,
);
},
items: controller.statusList.map<DropdownMenuItem<String>>(
(String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: const TextStyle(
color: Colors.black,
),
),
);
},
).toList(),
),
)

How to filter a listview with buttons?

I'm trying to develop a restaurant menu app and I need that when I click one of the text buttons below it filters the items according to which button I tap. When I built the "meal" object, I created an attribute called "idDiaSem", and I have 5 different IDs that I need to display 5 different lists of items based on the ID I choose.
Here's the concept:
Code:
Expanded(
child: ListView.builder(
itemCount: dayOfWeekMeals.length,
itemBuilder: (ctx, index) {
return Card(
elevation: 0,
margin: (index == dayOfWeekMeals.length - 1)
? const EdgeInsets.only(bottom: 0, left: 20, right: 20)
: const EdgeInsets.only(left: 20, right: 20),
child: ClipRRect(
borderRadius: (index == 0)
? const BorderRadius.only(
topLeft: Radius.circular(50),
topRight: Radius.circular(50),
)
: (index == dayOfWeekMeals.length - 1)
? const BorderRadius.only(
bottomLeft: Radius.circular(50),
bottomRight: Radius.circular(50),
)
: BorderRadius.circular(0),
child: MealItem(dayOfWeekMeals[index]),
),
);
},
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: () {},
child: Text(
'Segunda \n____________',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 11,
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.bold),
),
),
GestureDetector(
onTap: () {},
child: const Text(
'Terça \n____________',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 11),
),
),
GestureDetector(
onTap: () {},
child: const Text(
'Quarta \n____________',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 11),
),
),
GestureDetector(
onTap: () {},
child: const Text(
'Quinta \n____________',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 11),
),
),
GestureDetector(
onTap: () {},
child: const Text(
'Sexta \n____________',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 11),
),
),
],
),
],
),
class Meal {
final String id;
final String descricao;
final List<String> ingredients;
final List<String> idDiaSem;
final String imageUrl;
const Meal({
required this.id,
required this.descricao,
required this.ingredients,
required this.idDiaSem,
required this.imageUrl,
});
}
How can I create the filter? I need the button to change the filter when it's pressed.
Filter it with List.where(predicate) in the list of meals and the predicate checks if any of the weekdays match the idDiaSem. And then assign it to the dayOfWeekMeals variable.
It's going to be like the below snippet:
final dayOfWeekMeals = meals
.where((meal) => meal.idDiaSem.any((idDiaSem) => idDiaSem == _idDiaSem))
.toList();
Here's the result:
Here's a complete minimal-reproducible-example:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var _idDiaSem = 'seg';
#override
Widget build(BuildContext context) {
final dayOfWeekMeals = meals
.where((meal) => meal.idDiaSem.any((idDiaSem) => idDiaSem == _idDiaSem))
.toList();
return Scaffold(
appBar: AppBar(
title: const Text('Gastronomia'),
),
body: Column(
children: [
Expanded(
child: ListView.builder(
itemCount: dayOfWeekMeals.length,
itemBuilder: (ctx, index) {
return Card(
elevation: 0,
margin: (index == dayOfWeekMeals.length - 1)
? const EdgeInsets.only(bottom: 0, left: 20, right: 20)
: const EdgeInsets.only(left: 20, right: 20),
child: ClipRRect(
borderRadius: (index == 0)
? const BorderRadius.only(
topLeft: Radius.circular(50),
topRight: Radius.circular(50),
)
: (index == dayOfWeekMeals.length - 1)
? const BorderRadius.only(
bottomLeft: Radius.circular(50),
bottomRight: Radius.circular(50),
)
: BorderRadius.circular(0),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(dayOfWeekMeals[index].descricao),
),
),
);
},
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SemanaButton(
'Segunda',
onPressed: () => setState(() => _idDiaSem = 'seg'),
selected: _idDiaSem == 'seg',
),
SemanaButton(
'Terça',
onPressed: () => setState(() => _idDiaSem = 'ter'),
selected: _idDiaSem == 'ter',
),
SemanaButton(
'Quarta',
onPressed: () => setState(() => _idDiaSem = 'qua'),
selected: _idDiaSem == 'qua',
),
SemanaButton(
'Quinta',
onPressed: () => setState(() => _idDiaSem = 'qui'),
selected: _idDiaSem == 'qui',
),
SemanaButton(
'Sexta',
onPressed: () => setState(() => _idDiaSem = 'sex'),
selected: _idDiaSem == 'sex',
),
],
),
],
),
);
}
}
class SemanaButton extends StatelessWidget {
final String text;
final bool? selected;
final VoidCallback onPressed;
const SemanaButton(
this.text, {
Key? key,
required this.onPressed,
this.selected,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onPressed,
child: Text(
'$text \n____________',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 11,
color:
selected == true ? Theme.of(context).colorScheme.primary : null,
fontWeight: selected == true ? FontWeight.bold : null,
),
),
);
}
}
class Meal {
final String id;
final String descricao;
final List<String> ingredients;
final List<String> idDiaSem;
final String imageUrl;
const Meal({
required this.id,
required this.descricao,
required this.ingredients,
required this.idDiaSem,
required this.imageUrl,
});
}
var id = 0;
final meals = [
Meal(
id: '${++id}',
descricao: 'Feijão Tropeiro',
ingredients: [],
idDiaSem: ['seg'],
imageUrl: '',
),
Meal(
id: '${++id}',
descricao: 'Feijoada',
ingredients: [],
idDiaSem: ['sex'],
imageUrl: '',
),
Meal(
id: '${++id}',
descricao: 'Batata Doce Caramelada',
ingredients: [],
idDiaSem: ['seg'],
imageUrl: '',
),
Meal(
id: '${++id}',
descricao: 'Cubos Suínos ao Molho Escuro',
ingredients: [],
idDiaSem: ['seg'],
imageUrl: '',
),
Meal(
id: '${++id}',
descricao: 'Enrolado de Salsicha',
ingredients: [],
idDiaSem: ['seg'],
imageUrl: '',
),
Meal(
id: '${++id}',
descricao: 'Doce e Fruta',
ingredients: [],
idDiaSem: ['seg', 'ter', 'qua', 'qui', 'sex'],
imageUrl: '',
),
Meal(
id: '${++id}',
descricao: 'Buffet de Saladas',
ingredients: [],
idDiaSem: ['seg', 'ter', 'qua', 'qui', 'sex'],
imageUrl: '',
),
];
Well, here is the recipe for the required logic:
Define a variable to contain the id that you would to filter the meals according to.
int _filterId;
When any filter button is pressed, just set the value of the _filterId inside a setState() block:
onTap: () {
setState(() => _filterId = 0); // Take care of the ID
},
At the begining of the build() method, Define a List of Meal that would contain the filtered meals in the future and set it to the dayOfWeekMeals list if the _filterId is null and apply the filter if it is not null, like the following:
List<Meal> _filteredMeals = _filterId == null ? dayOfWeekMeals : dayOfWeekMeals.where((meal) => meal.idDiaSem.contains(_filterId).toList();
Use the _filteredMeals list in the ListView instead of dayOfWeekMeals, for example:
itemCount: _filteredMeals.length,
MealItem(_filteredMeals[index])
NOTE: Don't forget to replace the others.
Side Note: You can start with an initial filter by just starting the variable _filterId with an initial id of the filter.

How to create edit function in a Sqlite database

I have created an app where a company can add the contact details of its customers. The data is saved on Sqlite. It is very similar to a To Do app. On the ListTile you get a location icon (not functional yet), the name of the company, then a delete icon (which works fine).The add button works fine and all new companies are listed fine and the details of the company.
Once you click on a company name to open the tile, you see all the company details, BUT, at the bottom I have an Edit icon. I just cannot seem to find my edit functionality to work.
I have found some helping code, but it does not work and as I am fairly new to programming I just don't understand what I am doing wrong. When I click my edit icon, it opens the input page where one would normally insert new details (see below). What it is supposed to do is to open the input page, but with the existing company details so that it can be edited.
Here is my code:
I keep my Sqfilte code in my a Library page:
Library
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
//Customer
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;
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',
); //this will order the list by id in descending order
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,
),
);
}
}
Then, on the code below (customercard) is where the ListTile with all the new company data is converted into a card. The edit icon is also seen here which onpressed will go to Edit Page. I am now having a error here as I don't know what code to insert here with the 'updateFunction'.
customercard
import 'package:flutter/material.dart';
import 'library.dart';
import 'package:test_sqlite/editpage.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 insertFunction;
final Function deleteFunction;
final Function updateFunction;
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.insertFunction,
required this.deleteFunction,
required this.updateFunction,
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(updateFunction: addItem),);
},
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.deleteFunction(anotherTodo);
},
icon: const Icon(
Icons.delete,
color: Colors.red,
size: 20,
),
Then the editpage which is the input page where one goes to when pressing the edit button. On this page one should see all the existing company data so one can edit it, but it only opens a new input page with hint text.
editpage
import 'package:flutter/material.dart';
import 'library.dart';
import 'package:flutter/cupertino.dart';
class EditPage extends StatelessWidget {
final textController = TextEditingController();
final nameController = TextEditingController();
final phoneController = TextEditingController();
final faxController = TextEditingController();
final emailController = TextEditingController();
final streetController = TextEditingController();
final cityController = TextEditingController();
final townController = TextEditingController();
final codeController = TextEditingController();
final Function updateFunction;
// DatabaseConnect updateFunction = DatabaseConnect();
EditPage({required this.updateFunction, Key? key, todo}) : 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),
),
),
TextField(
controller: nameController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: ' Contact Name & Surname',
hintStyle: TextStyle(color: Colors.grey),
),
),
TextField(
controller: phoneController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: ' Contact Number',
hintStyle: TextStyle(color: Colors.grey),
),
),
TextField(
controller: faxController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: 'Fax Number',
hintStyle: TextStyle(color: Colors.grey),
),
),
TextField(
controller: emailController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: 'Email Address',
hintStyle: TextStyle(color: Colors.grey),
),
),
TextField(
controller: streetController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: 'Street Name',
hintStyle: TextStyle(color: Colors.grey),
),
),
TextField(
controller: cityController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: ' City',
hintStyle: TextStyle(color: Colors.grey),
),
),
TextField(
controller: townController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: 'Town',
hintStyle: TextStyle(color: Colors.grey),
),
),
TextField(
controller: codeController,
autofocus: true,
textAlign: TextAlign.left,
decoration: const InputDecoration(
hintText: ' Code',
hintStyle: TextStyle(color: Colors.grey),
),
),
GestureDetector(
onTap: () {
Navigator.pop(context);
var myTodo = 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);
updateFunction.updateTodo(myTodo);
},
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(
'Add',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
Then, I have a customer page where you will see the following functions: addItem, deleteItem and updateItem, which in essence is contained in 'insertFunction, deleteFunction and updateFunction.
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,
deleteFunction: deleteItem,
updateFunction: updateItem,
),
],
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.lightBlueAccent,
child: const Icon(Icons.add),
onPressed: () {
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) => CustomerProfile(insertFunction: addItem),
);
}
PS: I really need help. I have been stucked here for more than a month now. I have watch youtube channels, read stack overflow and the document, but I simply can't figure this out. Any help will really be greatly appreciated..
What I was able to understand from going through your question is that, basically you want to make the editPage screen be populated with the current data and then allow user to modify it (as it should be).
In the CustomerCard screen you have the data, and you want to be able to modify it. The simplest solution, I can see is to just pass this data to your EditPage widget.
class EditPage extends StatelessWidget {
final Todo todo; // <- Add this variable
final late TextEditingController textController; // Do this to add initial value to controller
final late TextEditingController nameController;
// ... and the rest of the TextEditingControllers
EditPage({required this.updateFunction, Key? key,
required this.todo // <- Add this
}) : super(key: key);
#override
void initState() {
textController = TextEditingController(text: todo.title);
nameController = TextEditingController(text: todo.name);
// .. and add the rest of the fields
super.initState();
}
#override
Widget build(BuildContext context) {
...
}
And when you call the editPage, you just pass the todo to it. Here's how you can do it:
EditPage(
updateFunction: addItem,
todo: anotherTodo, // This is the todo you are creating in the customerCard
)
This will populate your current data to the editPage. Then you can call your update method to update this data with the new values. To get new values add the onChanged function on the TextFields.
Also you have a bug here in the editPage:
GestureDetector(
onTap: () {
// Navigator.pop(context); // <- This should be called at the end
var myTodo = 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);
updateFunction.updateTodo(myTodo);
Navigator.pop(context); // <- Here
},
Hope this helps.
I have managed to finally resolve the problem.
My updateFunction and updateItem function was actually working just fine all along. 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.

The method 'getUsers' was called on null. Receiver: null Tried calling: getUsers()

I am getting error on red screen right after i run my app. My first screen is login screen. I want to simply register the user and login the user. but when i use the getUsers() or getLogin() I get the above error. I am fed up of this error. Searched everywhere but m not able to find any working solutions. Please can u please write the code which i need to add. Please help me.
UserLogin.dart
import 'dart:ui';
import 'package:customer/models/registerUser.dart';
import 'package:customer/screens/UserRegistration.dart';
import 'package:customer/screens/people_list.dart';
import 'package:customer/services/db_service.dart';
import 'package:customer/utils/form_helper.dart';
import 'package:flutter/material.dart';
import 'ForgotPassword.dart';
class UserLogin extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return UserLoginState();
}
}
class UserLoginState extends State<UserLogin>{
final reguser = RegisterUser();
String name,_password,_email;
var _formKey=GlobalKey<FormState>();
RegisterUser model;
DBService dbService;
var _minimumPadding = 5.0;
TextEditingController usernameController=TextEditingController();
TextEditingController passwordController=TextEditingController();
bool isHiddenPassword=true;
final scaffoldKey = new GlobalKey<ScaffoldState>();
void _showSnackBar(String text) {
scaffoldKey.currentState.showSnackBar(new SnackBar(
content: new Text(text),
));
}
void _togglePasswordView() {
setState(() {
isHiddenPassword = !isHiddenPassword;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text('Customer Tracking System'),),
body:_fetchData(),
);
}
Widget _fetchData(){
return FutureBuilder<List<RegisterUser>>(
future: dbService.getUsers(),
builder:
(BuildContext context, AsyncSnapshot<List<RegisterUser>> userDetails) {
if (userDetails.hasData) {
return _loginUI(userDetails.data);
}
return CircularProgressIndicator();
},
);
}
Widget _loginUI(List<RegisterUser> userDetails){
TextStyle textStyle = Theme.of(context).textTheme.title;
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;
Form(
key: _formKey,
child: Container(
child: Padding(
padding: EdgeInsets.all(_minimumPadding * 2),
child: ListView
(
children: <Widget>[
Text("Login".toUpperCase(),
style: TextStyle(
fontSize: 40.0, fontWeight: FontWeight.bold),
textAlign: TextAlign.center),
SizedBox(height: height * 0.08,),
Divider(),
Padding(
padding: EdgeInsets.only(
top: _minimumPadding * 3,
bottom: _minimumPadding),
child: TextFormField(
controller: usernameController,
style: textStyle,
validator: (String value) {
if (value.isEmpty) {
return 'Please Enter Name';
}
return null;
},
onSaved: (String value) {
_email = value;
},
decoration: InputDecoration(
// filled: true,
//fillColor: Colors.white,
prefixIcon: Icon(Icons.person),
labelText: 'Username',
hintText: 'Username',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(25.0)
)
)
)),
Padding(
padding: EdgeInsets.only(
top: _minimumPadding * 3,
bottom: _minimumPadding),
child: TextFormField(
style: textStyle,
obscureText: isHiddenPassword,
controller: passwordController,
validator: (String value) {
if (value.isEmpty) {
return 'Please Enter Name';
}
return null;
},
onSaved: (String value) {
_password = value;
},
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock),
suffixIcon: InkWell(
onTap: _togglePasswordView,
child: Icon(Icons.visibility)),
labelText: 'Password',
hintText: 'Password',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(25.0)
)
)
)
),
InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ForgotPassword()));
},
child: Text(
"Forgot Password ?",
style: TextStyle(
fontSize: 18,
color: Colors.purpleAccent,
//fontWeight: FontWeight.bold,
letterSpacing: 1.7),
textAlign: TextAlign.right,
),
),
SizedBox(
height: height * 0.08,
),
GestureDetector(
onTap: () {
_submit();
},
child: Container(
padding:
EdgeInsets.symmetric(horizontal: 26, vertical: 20),
decoration: BoxDecoration(
//gradient: new LinearGradient(
//colors: [Colors.purple, Colors.purpleAccent]),
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
blurRadius: 4,
//color: Colors.purpleAccent,
offset: Offset(2, 2))
]),
child: Text(
"Login".toUpperCase(),
style: TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.bold,
letterSpacing: 1.7),
textAlign: TextAlign.center,
),
),
),
SizedBox(
height: height * 0.05,
),
SizedBox(
height: height * 0.05,
),
Row(
children: <Widget>[
Expanded(
child: Text("Not yet registered?",
style: TextStyle(
fontSize: 25.0)
)
),
GestureDetector(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => UserRegistration()));
},
child: Center(
child: Container(
padding:
EdgeInsets.symmetric(
horizontal: 26, vertical: 10),
decoration: BoxDecoration(
//gradient: new LinearGradient(
// colors: [Colors.purple, Colors.purpleAccent]),
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
blurRadius: 4,
//color:,
offset: Offset(2, 2))
]),
child: Text(
"Register".toUpperCase(),
style: TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.bold,
letterSpacing: 1.7),
textAlign: TextAlign.center,
),
),
),
)
],
),
],
)),
),
);
}
bool validateAndSave() {
final form = _formKey.currentState;
if (form.validate()) {
form.save();
return true;
}
return false;
}
void _submit(){
final form = _formKey.currentState;
var res;
if (validateAndSave()) {
setState(() {
//getLogin(_email, _password);
res=dbService.getLogin(_email, _password).then((value) {
if(res!=0){
FormHelper.showMessage(
context,
"Login",
"Login Successfull",
"Ok",
() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => People_List(),
),
);
},
);}
else {
FormHelper.showMessage(
context,
"Login",
"Login not Successfull",
"Ok", () {}
);
}
});
});
}
}
}
RegisterUser.dart
import 'model.dart';
class RegisterUser extends Model {
static String table = 'userDetails';
int id;
String firstName;
String lastName;
String mobileNum;
String emailId;
String address;
String userType;
String password;
RegisterUser({
this.id,
this.firstName,
this.lastName,
this.mobileNum,
this.emailId,
this.address,
this.userType,
this.password
});
static RegisterUser fromMap(Map<String, dynamic> map) {
return RegisterUser(
id: map["id"],
firstName: map['firstName'].toString(),
lastName: map['lastName'],
mobileNum: map['mobileNum'],
emailId: map['emailId'],
address: map['address'],
userType: map['userType'],
password: map['password']
);
}
Map<String, dynamic> toMap() {
Map<String, dynamic> map = {
'id': id,
'firstName': firstName,
'lastName': lastName,
'mobileNum': mobileNum,
'emailId': emailId,
'address': address,
'userType':userType,
'password':password
};
if (id != null) {
map['id'] = id;
}
return map;
}
}
db_service.dart
import 'package:customer/models/registerUser.dart';
import 'package:customer/utils/database_helper.dart';
Future<List<RegisterUser>> getUsers() async {
await DB.init();
List<Map<String, dynamic>> userDetails = await DB.query(RegisterUser.table);
return userDetails.map((item) => RegisterUser.fromMap(item)).toList();
}
Future<List<RegisterUser>> getLogin(String email, String password) async {
await DB.init();
List<Map<String, dynamic>> res = await DB.rawQuery("SELECT * FROM userDetails WHERE emailId = '$email' and password = '$password'");
if (res.length > 0) {
return res.map((item) => RegisterUser.fromMap(item)).toList();
//return new User.fromMap(res.first);
}
return null;
}
You need a line with dbService = something before you can do dbService.getUsers() because just saying DbService dbService; initializes it to be null.
It's like a paper with no phone number on it and you are trying to call someone with it. You need to write down a phone number on it.
I finally got the answer to my question.
I had to just instantiate the DBService.
i.e DBService dbService=new DBService();
class UserLoginState extends State<UserLogin>{
final reguser = RegisterUser();
String name,_password,_email;
var _formKey=GlobalKey<FormState>();
RegisterUser model;
DBService dbService=new DBService();
...