본문 바로가기

Flutter-플러터/플러터 공부

Flutter Bloc 공부 2 Todo 어플 만들기

반응형

 

 

Flutter Bloc 로 Todo 어플 만들기 

 

1. state는 atomic 하게 만들기 

2. state는 별도의 클래스로 

3. copyWith 함수로 항상 새로운 상태를 만들기 

4. Equtable을 사용해서 해당 클래스가 서로 같은지 비교하게 하기 

 

 

Todo 모델입니다. 

uuid 를 만들어서 할일이 생성 될 때마다  고유의 id값을 만들어줍니다. 

Filter는 지금 할일이 해야 하는 할일인지, 끝난일인지, 모든 값인지 보여줍니다. 

Equtable 패키지를 이용해서 앞으로 Todo의 클래스와 이후 Cubit안에 쓰여질 클래스의 값이 일치하는지 도와줍니다. 

import 'package:equatable/equatable.dart';
import 'package:uuid/uuid.dart'; // Import Equatable package

enum Filter { all, active, completed }

Uuid uuid = Uuid();

class Todo extends Equatable {
  // Extend Equatable

  final String id;
  final String desc;
  final bool completed;

  Todo({String? id, required this.desc, this.completed = false})
      : id = id ?? Uuid().v4(); // Ensure uuid is used directly

  // Override props getter
  @override
  List<Object?> get props =>
      [id, desc, completed]; // Ensure it returns a list of all properties

  @override
  String toString() => "Todo(id: $id, desc:$desc, completed: $completed)";
}

 

 

만들어야할 큐빗은 총 5개입니다. 

 

1. todo_list  -> todo 리스트
2. todo_search  ->검색기능
3. todo_filter -> active, all,completed 인지 

4.filtered_todo -> 검색되어진 todo

6.active_todo_counter -> 해야할 일이 얼마나 남았는지 

 

기본 사용법은 

 

1. 각 Cubit의 State 값을 만든다. 

 

// ignore_for_file: public_member_api_docs, sort_constructors_first
part of 'todo_serach_cubit.dart';

class TodoSearchState extends Equatable {
  final String searchItem;
  TodoSearchState({
    required this.searchItem,
  });
//todo 검색 기능의 상태 초기 값은 ""
  factory TodoSearchState.initial() {
    return TodoSearchState(searchItem: "");
  }
  @override
  List<Object> get props => [searchItem];

  @override
  String toString() => 'TodoSearchState(searchItem: $searchItem)';

  TodoSearchState copyWith({
    String? searchItem,
  }) {
    return TodoSearchState(
      searchItem: searchItem ?? this.searchItem,
    );
  }
}

 

2. state의 상태값을 이용해서 emit(state.copyWith) 함수를 이용해서 새로운 상태값으로 변경한다. 

 

import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';

part 'todo_serach_state.dart';

class TodoSerachCubit extends Cubit<TodoSearchState> {
  TodoSerachCubit() : super(TodoSearchState.initial());

  void setSearchTerm(String newSearchTerm) {
    emit(state.copyWith(searchItem: newSearchTerm));
  }
}

 

 


 

 

검색되어진 값들은 Stream으로 관리한다. 

1. 필터링 되어진 Todos를 담을 변수를 만든다. 

// ignore_for_file: public_member_api_docs, sort_constructors_first
part of 'filter_todos_cubit.dart';

class FilterTodosSate extends Equatable {
  final List<Todo> filtredTodos;
  FilterTodosSate({
    required this.filtredTodos,
  });

  factory FilterTodosSate.initial() {
    return FilterTodosSate(filtredTodos: []);
  }
  @override
  List<Object> get props => [filtredTodos];

  @override
  String toString() => 'FilterTodosSate(filtredTodos: $filtredTodos)';

  FilterTodosSate copyWith({
    List<Todo>? filtredTodos,
  }) {
    return FilterTodosSate(
      filtredTodos: filtredTodos ?? this.filtredTodos,
    );
  }
}

 

 

2.각 큐빗의 state의 상태값을 Stream으로 받아오는 변수를 선언하고, 그 값을 emit,copyWith 함수로 새로운 상태로 값을 생성한다. 

 

// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';

import 'package:bloc_todo/cubits/todo_filter/todo_filter_cubit.dart';
import 'package:bloc_todo/cubits/todo_list/todo_list_cubit.dart';
import 'package:bloc_todo/cubits/todo_search/todo_serach_cubit.dart';
import 'package:bloc_todo/models/todo_model.dart';

part 'filter_todos_state.dart';

class FilterTodosCubit extends Cubit<FilterTodosSate> {
  late StreamSubscription todoFilterSubscription;
  late StreamSubscription todoListSubscription;
  late StreamSubscription todoSearchSubscription;
  final TodoFilterCubit todoFilterCubit;
  final TodoSerachCubit todoSerachCubit;
  final TodoListCubit todoListCubit;

  FilterTodosCubit({
    required this.todoFilterCubit,
    required this.todoSerachCubit,
    required this.todoListCubit,
  }) : super(FilterTodosSate.initial()) {
    //해당 값이 변할 때 setFiltedTodos의 함수가 실행이 됨
    todoFilterSubscription =
        todoFilterCubit.stream.listen((TodoFilterState todoFilterState) {
      setFilteredTodos();
    });

    todoListSubscription =
        todoListCubit.stream.listen((TodoListState todoListState) {
      setFilteredTodos();
    });

    todoSearchSubscription =
        todoSerachCubit.stream.listen((TodoSearchState todoSearchState) {
      setFilteredTodos();
    });
  }
//검색을 통해 필터링 되는 함수
  void setFilteredTodos() {
    //필터링 되는 리스트를 만든다.
    List<Todo> _filteredTodos;

    switch (todoFilterCubit.state.filter) {
      case Filter.active:
        _filteredTodos = todoListCubit.state.todos
            .where((Todo todo) => !todo.completed)
            .toList();
        break;
      case Filter.completed:
        _filteredTodos = todoListCubit.state.todos
            .where((Todo todo) => todo.completed)
            .toList();
        break;
      case Filter.all:
      default:
        _filteredTodos = todoListCubit.state.todos;
        break;
    }

    if (todoSerachCubit.state.searchItem.isNotEmpty) {
      _filteredTodos = _filteredTodos
          .where((Todo todo) => todo.desc
              .toLowerCase()
              .contains(todoSerachCubit.state.searchItem))
          .toList();
    }

    //여기서 만들어진 _filteredTodos는 새로운 상태로 filteredTodos의 값이 됨
    emit(state.copyWith(filtredTodos: _filteredTodos));
  }

  @override
  Future<void> close() {
    todoFilterSubscription.cancel();
    todoListSubscription.cancel();
    todoSearchSubscription.cancel();
    return super.close();
  }
}

 

 

 


큐빗이 많아지니까 

한꺼번에 export 할 수 있는 cubits.dart 파일을 만든다. 

 

//여기에 값

export 'todo_filter/todo_filter_cubit.dart';
export 'todo_search/todo_serach_cubit.dart';
export 'todo_list/todo_list_cubit.dart';
export 'active_todo_counter/active_todo_counter_cubit.dart';
export 'filter_todos/filter_todos_cubit.dart';

 

728x90
반응형