본문 바로가기
스파르타/Flutter

[Flutter] 마이메모 #3 완성 - ( week 3 )

by bakcoding_sparta 2023. 4. 22.

메모 개별 조회

이제 DetailPage에서 별도로 memoList 값을 전달받아서 사용할 필요 없이 memoService를 통해서 데이터를 가져다가 사용하면 된다.

 

class DetailPage extends StatelessWidget {
  DetailPage({super.key, required this.index});
  //DetailPage({super.key, required this.memoList, required this.index});

  //final List<String> memoList;
  final int index;

  TextEditingController contentController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    //contentController.text = memoList[index];
    MemoService memoService = context.read<MemoService>();
    Memo memo = memoService.memoList[index];

    contentController.text = memo.content;

불필요한 변수를 지우고 생성자에서도 제거해 준다.

마찬가지로 build 함수 내부에서 사용 중이던 memoList는 제거하고 MemoService를 사용하기 위한 객체를 선언한다.

그리고 해당 서비스에 선언된 메모리스트를 가져온다.

 

Consumer위젯 또는 context.read <class>를 사용하면 서비스를 가져올 수 있는데 두 방식의 차이는 Consumer의 경우 화면을 새로 그려주어야 할 필요가 있을 때이고 단순히 값만 가지고 와서 사용할 때는 context.read를 사용하면 된다.

 

아직 에러가 남은 부분인 메모를 삭제하고 텍스트를 추가하는 부분의 코드는 잠시 주석처리를 한다.

 

메모 페이지 이동

Navigator.push(
                          context,
                          MaterialPageRoute(
                            builder: (_) => DetailPage(
                              index: index,
                            ),
                          ),
                        );

이전에는 정보를 전달하기위해서 memoList의 정보도 넘겼지만 이제는 필요가 없어졌다.

 

메모 작성

서비스의 데이터를 수정하기 위해서는 모든 데이터가 서비스에서만 처리되도록 해당 서비스에서 함수를 정의하고 함수를 통해서 데이터에 접근하는 방식으로 값을 수정해주어야 한다.

 

class MemoService extends ChangeNotifier {
  List<Memo> memoList = [
    Memo(content: '장보기 목록: 사과, 양파'), // 더미(dummy) 데이터
    Memo(content: '새 메모'), // 더미(dummy) 데이터
  ];

  createMemo({required String content}) {
    Memo memo = Memo(content: content);
    memoList.add(memo);
  }
}

content 문자열을 받아서 Memo를 생성하고 memoList에 추가하는 함수이다.

 

이제 플로팅 버튼을 클릭할 때 새로운 빈 메모가 생성되도록 한다.

onPressed: () {
              // + 버튼 클릭시 메모 생성 및 수정 페이지로 이동
              memoService.createMemo(content: '');
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (_) => DetailPage(
                    index: memoService.memoList.length - 1,
                  ),
                ),
              );
            },

index 부분의 코드를 살펴보면 memoSevice의 memoList 길이에 1만큼 뺀 값을 index로 한다. 

즉 새로 추가된 메모의 인덱스값을 구하는 공식이다.

 

이 상태에서도 여전히 버튼을 클릭했을 때 데이터상에 메모가 추가되어도 화면이 갱신되지 않는데 createMemo함수가 호출될 때 정보를 갱신해 주는 함수인 notifyListeners를 호출해주어야 한다.

  createMemo({required String content}) {
    Memo memo = Memo(content: content);
    memoList.add(memo);
    notifyListeners(); // Consumer<MemoService>의 builder 부분을 호출해서 화면 새로고침
  }
}

 

이번에는 작성된 메모의 내용을 수정해서 반영하는 기능을 구현한다.

메모를 새로 생성할 때와는 다르게 기존 메모를 수정하기 위해서는 바꾸게 되는 메모의 리스트상의 인덱스 정보가 필요하다.

 

해당 함수도 역시 서비스 내에서 선언해 준다.

  updateMemo({required int index, required String content}) {
    Memo memo = memoList[index];
    memo.content = content;
    notifyListeners();
  }

 

메모의 값을 새로운 값으로 변경하고 화면을 갱신하는 함수를 호출한다.

해당 함수를 값을 입력하는 곳에서 호출하여 값이 변경될 때마다 갱신되도록 한다.

onChanged: (value) {
            // 텍스트필드 안의 값이 변할 때
            memoService.updateMemo(index: index, content: value);

에뮬레이터에서 확인해 보면 값이 변경된 게 즉시 반영된다.

 

메모 삭제

삭제 또한 데이터를 관리하는 서비스에서 함수를 추가해 주고 해당 함수를 적절한 위치에서 호출해서 사용하도록 한다.

  deleteMemo({required int index}) {
    memoList.removeAt(index);
    notifyListeners();
  }

인덱스를 입력받고 리스트에서 해당 인덱스의 메모를 제거하고 화면을 갱신한다.

이 함수는 휴지통 아이콘을 누르고 뜨는 다이어로그에서 확인 버튼을 눌렀을 때 호출된다.

onPressed: () {
                          memoService.deleteMemo(index: index);
                          Navigator.pop(context); // 팝업 닫기
                          Navigator.pop(context); // HomePage 로 가기
                        },

 

 

Provider의 핵심은 공유할 변수를 포함한 데이터를 변형시키는 기능들을 모두 서비스에서 선언하고 해당 서비스를 위젯 트리 최상위에 선언하고 서비스를 통해서 변수의 값을 사용하거나 함수를 데이터를 변경하는 기능을 호출하도록 하는 것이다.

 

리팩토링

showDialog

showDialog 함수 부분을 별도로 빼내어서 코드의 가독성을 높이도록한다.

리팩터의 Extract Method 기능을 사용해서 함수를 따로 빼낸다.

onPressed: () {
              // 삭제 버튼 클릭시
              showDeleteDialog(context, memoService);
            },

실제 함수는 코드 하단에 자동으로 빼내어진다.

Future<dynamic> showDeleteDialog(BuildContext context, MemoService memoService) {
    return showDialog(

 

빼내어진 함수는 자동완성으로 선언이 되었는데 여기서 Future<dynamic> 부분은 필요가 없기 때문에 void로 변경하고 반환이 없기 때문에 그 아래 return도 제거해준다.

  void showDeleteDialog(BuildContext context, MemoService memoService) {
    showDialog(

불필요한 부분을 정리한것이기 때문에 기능은 문제없이 작동한다.