skip to Main Content

the following situation: I want to make a ToDo list with a Cubit. That the ToDo list does not disappear after leaving the app, I found the package hydrated Bloc. When I created the HydratedCubit with the 2 override methods for the FromJson and ToJson, creating them Inside the ToDoState and executed it, I got an exception, "Converting object to an encodable object failed: Instance of ‘Todo’". After research I found out that JSON can’t convert such a model and you have to overwrite these models with working From and To functions first. I tried this with the package "json serializable". Here I had to ask myself in which class I have to add the "@JsonSerializable()", inside the Todo model or the todo state, in which the error appears? I decided to add the @ for my todostate because adding it on the Todo model didn’t change anything. After this package created me the "todo_list_state.g.dart" I don’t get the error anymore, BUT: It also doesn’t save the list, looks like HydratedCubit is being "ignored". Here a few code snippets:

First, I show you the Todo Model:

Uuid uuid = const Uuid();

class Todo extends Equatable {
  final String id;
  final String desc;
  final double menge;

  Todo( {
    String? id,
    required this.desc,
    required this.menge,

  }) : id = id ?? uuid.v4();

  
}

I left out the to string and props…
As I mentioned before, I tried to add the JsonSerializableGenerator inside this class, but this didn’t change something.
Here is my TodoListState:

part of 'todo_list_cubit.dart';

@JsonSerializable()
class TodoListState extends Equatable {
  final List<Todo> todos;

  factory TodoListState.initial() {
    return TodoListState(todos: []);
  }

  Map<String, dynamic>toMap() => _$FoodListStateToMap(this, food);

  factory FoodListState.fromMap(Map<String, dynamic> map) => _$FoodListStateFromMap(map);

  }

Here are the generated functions inside ‘todo_list_state.g.dart’:

TodoListState _$TodoListStateFromMap(Map<String, dynamic> map) {
    return TodoListState (
        todo: map['todo'] as List<TodoModel>,
    );
}

Map<String, dynamic> _$TodoListStateToMap(TodoListState instance,  List<TodoModel> todo) {
    return {
        'todo': todo,
    };
}

At last here my Cubit functions where I override the From and To:

  @override
  TodoListState? fromJson(Map<String, dynamic> json) {
    return TodoListState.fromMap(json);
  }

  @override
  Map<String, dynamic>? toJson(TodoListStatestate) {
    return state.toMap();
  }

So does anybody know why my Hydrated Cubit doesn’t save my list? Or do I have to convert the Model but just dont know how?… Appreciate your help. Thanks

2

Answers


  1. Both model and states need to have toJson and fromJson methods. So the approach was right with using JsonSerializable. But converting objects to and from json is a very important thing to understand how to do without codegen tools. Those can be boilerplate and not always converting correctly complex objects. When doing it manually, it’s also easier to debug. So in your case, additions to the model would be:

    class Todo extends Equatable {
      final String id;
      final String desc;
      final double menge;
    
      Todo( {
        String? id,
        required this.desc,
        required this.menge,
    
      }) : id = id ?? uuid.v4();
      factory Todo.fromJson(Map<String, dynamic> json){
        return Todo(id: json['id'],
                   desc: json['desc'],
                   menge: json['menge'],);
        }
      
      Map<String, dynamic> toJson() {
        return {
          'id': id,
          'desc': desc,
          'menge': menge,
        };
      }
    }
    

    Then, in your states (again, I am not going to use JsonSerializable):

    class TodoListState extends Equatable {
      final List<Todo> todos;
    
      TodoListState({this.todos = const <Todo>[]});
    
      factory TodoListState.fromJson(Map<String, dynamic> json) {
        var todos = <Todo>[];
        for (var item in json['todos']) {
          todos.add(Todo.fromJson(item));
        }
        return TodoListState(todos: todos);
      }
    
      Map<String, dynamic> toJson() {
        var jsonTodoList = <Map<String, dynamic>>[];
        for (var item in todos) {
          jsonTodoList.add(item.toJson());
        }
        return {
          'todos': jsonTodoList,
        };
      }
    }
    

    Then in your Cubit nothing changes

      @override
      TodoListState? fromJson(Map<String, dynamic> json) {
        return TodoListState.fromJson(json);
      }
    
      @override
      Map<String, dynamic>? toJson(TodoListState state) {
        return state.toJson();
      }
    

    Except for you may want to add all sorts of null safety checks around the conversions, because I omitted all aspects not related to the topic of the question.

    Login or Signup to reply.
  2. Persisting single instance of todo

    TodoCubit.dart

    class TodoCubit extends HydratedBloc<TodoCubit, TodoState> {
      TodoCubit() : super(ToDoInitial());
    
      @override
      TodoState? fromJson(Map<String, dynamic> json) {
        return TodoState.fromJson(json);
      }
    
      @override
      Map<String, dynamic>? toJson(TodoState state) {
        return state.toJson();
      }
    
      saveTodo(String todo, String date) {
        emit(TodoState(presentTodo: Todo(todo: todo, day: date)));
      }
    }
    
    

    ToDoState.dart

    
    class TodoState extends Equatable {
      final Todo presentTodo;
    
      const TodoState({required this.presentTodo});
      factory TodoState.fromJson(Map<String, dynamic> json) {
        return TodoState(presentTodo: Todo(todo: json['todo'], day: json['day']));
      }
      Map<String, dynamic> toJson() =>
          {'todo': presentTodo.todo, 'day': presentTodo.day};
    
      @override
      List<Object?> get props => [presentTodo];
    }
    
    class Todo {
      String todo;
      String day;
      Todo({required this.todo, required this.day});
      @override
      String toString() {
        return "todo: " + todo + " day: " + day;
      }
    }
    
    class ToDoInitial extends TodoState {
      ToDoInitial() : super(presentTodo: Todo(todo: '', day: ''));
    }
    

    Home.dart

                  return Center(
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        const SizedBox(height: 10),
                        Text(state.presentTodo.toString()),
                        ElevatedButton(
                          style: Constants.kElevatedButtonStyle,
                          onPressed: () {
                            context
                                .read<TodoCubit>()
                                .saveTodo('Drink Water ', 'Tuesday');
                          },
                          child: const Text(
                            'Add Dummy Todo',
                            style: TextStyle(
                                fontSize: 18, fontWeight: FontWeight.bold),
                          ),
                        ),
                      ],
                    ),
                  );
    

    main.dart

    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
      HydratedBloc.storage = await HydratedStorage.build(
        storageDirectory: await getTemporaryDirectory(),
      );
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({Key? key}) : super(key: key);
      @override
      Widget build(BuildContext context) {
        return BlocProvider(
          create: (context) => TodoCubit(),
          child: MaterialApp(
            debugShowCheckedModeBanner: false,
            theme: ThemeData(
              appBarTheme: const AppBarTheme()
                  .copyWith(backgroundColor: const Color.fromRGBO(18, 26, 28, 1)),
              primarySwatch: Colors.blue,
            ),
            home: const Home(),
          ),
        );
      }
    }
    
    

    Output:

    enter image description here

    To store multiple instance of todo that is list of todos refer this example

    @Credit for the example : @Olga P

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search