메모 앱 기능 개발 중 한 가지 문제가 생겼다.

메모 데이터 수정이 발생한 경우 수정된 메모만 다시 랜더링 한다는 의도와는 다르게 모든 메모들이 다시 랜더링 되는 현상이 발생했다.

이를 해결하기 위해 여러 방법을 시도한 결과, 데이터를 저장하는 형식을 바꾸는 방법으로 해결했다.

 

1. 기존 방법 - Future Builder 사용

DB에서 메모 데이터를 받아오는 방식이기 때문에, 데이터를 모두 받은 이후에 랜더링 할 수 있도록 Future Bulider 를 사용했는데

해당 클래스의 future 파라미터에 넣는 함수는 데이터를 반환해야 한다.

Future<List<Map<String, dynamic>>> _requestDateTodoList() async {
    final data = await DatabaseHelper.instance.readDateTodo(_memoDay);
    return data;
  }
  
FutureBuilder(
    future: _requestDateTodoList(),
    builder: (context, snapshot) {
    // `snapshot`에서 데이터가 준비될 때까지 기다림
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const CircularProgressIndicator();

    // 데이터를 모두 받은 이후 아래 코드 진행
    } else {
      final List<Map<String, dynamic>> items = snapshot.data!;
      return Expanded(
        child: MemoList(
          items: items,
          onItemUpdated: _requestDateTodoList,
        ),
      );
    }
  },
)

MemoList 위젯 내 데이터가 변경되면 DB 값 업데이트와 함께 _requestDateTodoList 가 다시 요청된다.

이 때, 해당 함수는 모든 데이터를 반환하므로 모든 UI가 수정된다.

 

2. 수정 방법 - ValueNotifier

ValueNotifier를 이용하면, 데이터 중 변경된 값 만을 감지하여 UI에 새로 랜더링 된다.

단, 해당 방식을 사용하면, 최초 화면에서 데이터를 불러오는 작업을 하지 않기 때문에 초기 랜더링 시 데이터를 받아올 수 있도록

initState 에서 메모를 불러오는 함수를 추가해주어야 한다.

builder의 3번째 파라미터는 다시 빌드하지 않을 위젯을 지정할 수 있다.

final ValueNotifier<List<Map<String, dynamic>>> _todoListNotifier =
      ValueNotifier([]);


@override
void initState() {
    super.initState();
    _requestDateTodoList();
}

ValueListenableBuilder<List<Map<String, dynamic>>>(
    valueListenable: _todoListNotifier,
    builder: (context, items, _) {
        return Expanded(
          child: MemoList(items: items, updatedItem: _requestDateTodoList),
        );
      }
    )

 

데이터를 받아온 후 일부 변경 사항에 따른 개별 데이터의 UI 변경이 필요한 경우 ValueListenableBuilder 가 유용해 보이고

전체 데이터 UI 변경이 필요하거나, 데이터 로딩 중이나 읽어오기 실패 등의 상태 변화를 감지해야 하는 경우에는

Future Builder 를 사용하는 것이 더 유용해 보인다.