Max
Max

Reputation: 1301

I can not understand why the data in the DB is not updated - Flutter

I use DB SQLite and I want to update the data, but when the data changes, nothing happens, only a warning pops up in the console (I attached it below). I tried different options, but still I can’t understand why I can’t update the data. Though create/delete works well without problems. And updating the data in the DB caused such difficulties. I would be grateful if you tell me what could be the reason.

database

 Future<int> updateTodo(Todo todo) async {
    Database db = await database;
    return await db
        .update(todoTable, todo.toMap(), where: 'id = ?', whereArgs: [todo.id]);
  }

edit_todo_screen

class EditTodoScreen extends StatelessWidget {
  final Todo todo;
  EditTodoScreen({Key? key, required this.todo}) : super(key: key);

  final _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    _controller.text = todo.description;

    return BlocBuilder<TodoBloc, TodoState>(builder: (context, state) {
      return Scaffold(
        appBar: AppBar(
          title: const Text('Edit Todo'),
        ),
        body: _body(context),
      );
    });
  }

  Widget _body(context) {
    return Padding(
      padding: const EdgeInsets.all(20.0),
      child: Column(
        children: [
          TextField(
            controller: _controller,
            autocorrect: true,
            decoration: const InputDecoration(hintText: 'Enter todo message'),
          ),
          const SizedBox(
            height: 10.0,
          ),
          InkWell(
            onTap: () {
              BlocProvider.of<TodoBloc>(context).add(
                UpdateTodos(
                  todo,
                  _controller.text,
                ),
              );
              Navigator.pop(context);
            },
            child: _updateBtn(context),
          )
        ],
      ),
    );
  }

todo_bloc

on<UpdateTodos>((event, emit) async {
  await todoRepository.updateTodo(event.todo);
  final List<Todo> _loadedTodoList = await todoRepository.getAllTodos();
  emit(TodoLoadedState(loadedUser: _loadedTodoList));
});

todo_event

class UpdateTodos extends TodoEvent {
  final Todo todo;

  const UpdateTodos(this.todo);
  @override
  List<Object> get props => [todo];
}

todo_list

class TodoList extends StatefulWidget {
  @override
  State<TodoList> createState() => _TodoListState();
}

class _TodoListState extends State<TodoList> {
  @override
  void initState() {
    super.initState();
    BlocProvider.of<TodoBloc>(context).add(LoadTodos());
  }

  @override
  void dispose() {
    BlocProvider.of<TodoBloc>(context).close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final TodoBloc todoBloc = context.read<TodoBloc>();
    return BlocBuilder<TodoBloc, TodoState>(builder: (context, state) {
      if (state is TodoEmptyState) {
        return const Center(
          child: Text(
            'No Todo',
            style: TextStyle(fontSize: 20.0),
          ),
        );
      }

      if (state is TodoLoadingState) {
        return const Center(child: CircularProgressIndicator());
      }

      if (state is TodoLoadedState) {
        return ListView.builder(
            physics: const NeverScrollableScrollPhysics(),
            shrinkWrap: true,
            itemCount: state.loadedUser.length,
            itemBuilder: (context, index) => ListTile(
                  title: Column(children: [
                    Text('${state.loadedUser[index].description}'),
                    Text('${state.loadedUser[index].id}'),
                  ]),
                  trailing: IconButton(
                    onPressed: () {
                      todoBloc.add(DeleteTodos(id: state.loadedUser[index].id));
                      // todoBloc.add(LoadTodos());
                    },
                    icon: const Icon(Icons.delete),
                  ),
                  onTap: () {
                    Navigator.push(
                      context,
                      MaterialPageRoute(
                          builder: (context) => EditTodoScreen(
                                todo: Todo(
                                    description:
                                        '${state.loadedUser[index].description}'),
                              )),
                    );

todo_model

class Todo {
  int? id;
  late String description;
  // bool isDone = false;

  Todo({this.id, required this.description});

  Map<String, dynamic> toMap() {
    final map = <String, dynamic>{};
    map['id'] = id;
    map['description'] = description;
    // map['isDone'] = isDone == false ? 0 : 1;
    return map;
  }

  Todo.fromMap(Map<String, dynamic> map) {
    id = map['id'];
    description = map['description'];
    // isDone = map['isDone'] == 0 ? false : true;
  }
}

todo_repository

class TodoRepository {
  final db = DBProvider();

  Future getAllTodos() => db.getTodos();

  Future insertTodo(Todo todo) => db.createTodo(todo);

  Future updateTodo(Todo todo) => db.updateTodo(todo);

  Future deleteTodo(int id) => db.deleteTodo(id);

  Future deleteAllTodo(int id) => db.deleteAllTodo(id);
}

When I click on the button, this is the warning in the console

enter image description here

Upvotes: 0

Views: 185

Answers (1)

Ramin
Ramin

Reputation: 935

return await db
        .update(todoTable, todo.toMap(), where: 'id = ?', whereArgs: [todo.id]);

The error you're getting happens here because todo id is null. This is what you're doing when routing to EditTodoScreen:

Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => EditTodoScreen(
          todo: Todo(
            description: '${state.loadedUser[index].description}',
          ),
        ),
      ),
    );

Notice the todo here:

todo: Todo(
      description: '${state.loadedUser[index].description}',
),

You're only passing description, but no id. This is why you get that error when trying to update.

You already have access to the todo you're trying to update so you can just pass that instead of creating a new one. Use this code for routing to EditTodoScreen:

Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => EditTodoScreen(
          todo: state.loadedUser[index],
        ),
      ),
    );

Upvotes: 1

Related Questions