-1

enter image description hereI 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.

enter image description here

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.

enter image description here

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.

enter image description here

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..

Tobsig
  • 33
  • 1
  • 9

2 Answers2

0

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.

QasimSiddiqui
  • 70
  • 1
  • 9
  • Hi Qasim, Thank you for your help, Im still busy implementing. – Tobsig Jun 01 '22 at 17:10
  • Qasim, I have created the final Todo todo, (and the constructor) on the EditPage, but now on the CustomerCard page at onpressed to EditPage(updateFunction: updateItem) I need to insert the required 'todo', but is also need the identifier. Do you have any idea what the identifier of todo would be? The identifier of updateFunction is updateItem.. Thanks for the help.. – Tobsig Jun 01 '22 at 17:14
  • @Tobsig Hey, So i updated the answer, I hope this is what you were asking for. – QasimSiddiqui Jun 01 '22 at 21:18
  • HI Qasim, thank you for all your help so far. I'm struggling to get to populate current data to the editPage. For some reason passing the data 'todo: anotherTodo' does not populate the current data onto the editPage. There are no code errors, but when I open the app and click on the edit icon, the edipage opens normally with hintexts, but no current data.. – Tobsig Jun 02 '22 at 17:23
  • @Tobsig did you initialize the TextEditingController with the data on the EditPage like this `final nameController = TextEditingController(text: todo.name);` ? – QasimSiddiqui Jun 02 '22 at 18:11
  • Hi Qasim, I did not, many thanks. the todo in todo.name gives the following error: 'the instance member 'todo' can't be assessed in an initializer.' – Tobsig Jun 02 '22 at 18:40
  • Can you add an image of the error? – QasimSiddiqui Jun 02 '22 at 21:55
  • Hi Qasim. I have updated the initial question and added an image of the editpage with the error messages in the textController. – Tobsig Jun 03 '22 at 08:58
  • @Tobsig I updated the answer. I hope this solves the issue. – QasimSiddiqui Jun 03 '22 at 15:42
  • Hi Qasim. creating the initState gave error messages. I managed to resolve it though in that I used your initial solution (final textController = TextEditingControllertext: todo.title); but I added late before final. That resolved the issue as far as passing the information to my editpage. The problem I am now having is that when I amend the information it does not update my DB. I know you mentioned in need to do onChanged, however can't seem to figure to manage.. – Tobsig Jun 05 '22 at 17:54
  • Hi Qasim, thanks. I noticed an error in my line " updateFunction.updateTodo(myTodo). The error is: the method 'updateTodo' is not defined for the type Function. – Tobsig Jun 06 '22 at 12:54
0

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.

Tobsig
  • 33
  • 1
  • 9