티스토리 뷰

데이터 추가 및 삭제하기

 

SharedPreference 함수 추가하기

  // 키값을 통해 Json 리스트 불러오기
  static List<Map<String, dynamic>> getJsonList(String key) {
    List<String>? dataList = _prefs?.getStringList(key);
    return dataList
            ?.map((value) => json.decode(value) as Map<String, dynamic>)
            .toList() ?? [];
  }
  
  // Json 리스트 키에 저장하기
  static Future<bool> setJsonList(String key, List<Map<String, dynamic>> list) {
    List<String> dataList = list.map((map) => json.encode(map)).toList();
    return _prefs!.setStringList(key, dataList);
  }
  
  // 키값 삭제
  static Future<bool> remove(String key) async {
    return await _prefs!.remove(key);
  }

shared_preference_util.dart에 추가해준다.

 

운동기록을 출력하려면 List<Map<String, dynamic>>형태의 데이터를

키값에 저장하고 불러오는 작업이 필요하다.

 

 

List<Map<String, dynamic>> # [{}, {}, {}] < 이런 형태의 자료형이다.

참고로 Map<String,dynamic>은 json형태의 데이터를 의미한다.

 

List<map<string, dynamic>>  < 자료형이 뭔가 복잡해보이지만

리스트에 json 데이터가 들어간 자료형이라고 보면 된다.

 

 

  static Future<bool> setJsonList(String key, List<Map<String, dynamic>> list) {
    List<String> dataList = list.map((map) => json.encode(map)).toList();
    return _prefs!.setStringList(key, dataList);
  }

json 리스트를 키에 저장하는 함수이다.

SharedPreference에서 json 리스트를 저장하는 함수가 따로 없어

일단 setStringList로 List<String>형의 데이터를 추가해준다.

 

 

static List<Map<String, dynamic>> getJsonList(String key) {
    List<String>? dataList = _prefs?.getStringList(key);
    return dataList
            ?.map((value) => json.decode(value) as Map<String, dynamic>)
            .toList() ?? [];
  }

List<String>형의 데이터를 가진 키값을 dataList라는 변수에 저장하고

String형인 dataList의 요소를 json형으로 파싱을 진행하여 결과적으로 json리스트를 반환한다.

 

키값이 null일 때는 빈 배열을 반환하여 null 오류를 방지한다.

 

 

함수 작동 테스트

class HomeController with ChangeNotifier {
  List<Map<String, dynamic>> dataList = [];
  
  // 데이터리스트를 초기화한다.
  void initDataList() {
    dataList = SharedPreferencesUtil.getJsonList('dataList');
  }
  
  // 데이터리스트에 추가된 데이터를 키에 저장한다.
  void addData(data) {
    dataList.add(data);
    notifyListeners();

    SharedPreferencesUtil.setJsonList('dataList', dataList);
  }
  
  // 데이터리스트를 빈 배열로 초기화하고 키값을 지운다.
  void deleteData() {
    dataList.clear();
    notifyListeners();

    SharedPreferencesUtil.remove('dataList');
  }
}

작성한 함수가 잘 작동하는지 테스트를 하기 위해

데이터를 불러오는 함수, 추가하는 함수, 삭제하는 함수

총 3개의 함수를 HomeController에 작성해준다.

 

dataList의 변경사항을 화면에 바로 적용시키려면

notifyListner() 를 함수에 추가해줘야한다.

 

 

@override
  void initState() {
    super.initState();
    _controller.initDataList();
  }

initState에서 빈 배열을 'dataList'의 키값으로 초기화한다.

 

 

 

Widget _buildPage(BuildContext context) {
    return SingleChildScrollView(
      child: Column(

화면 오버플로우를 방지하기 위해 

Column을 SingleChildScrollView로 감싸준다.

 

 

  Row(
    mainAxisAlignment: MainAxisAlignment.spaceBetween,
    children: [
      CommonButton(
        width: 140,
        text: '데이터 추가',
        onPressed: () {
          _controller.addData({
            'day': 1,
            'ex1': '풀업',
            'ex2': '푸쉬업',
            'ex1List': [10, 10, 10],
            'ex2List': [10, 10, 10]
          });
        },
      ),
      CommonButton(
        width: 140,
        text: '데이터 삭제',
        onPressed: () {
          _controller.deleteData();
        },
      )
    ],
  );

화면 맨 아래에 문자열 형태의 데이터리스트와 데이터 추가 및 삭제 버튼을 추가한다.

 

 

 

홈화면으로 라우팅 될 떄 dataList는 키값으로 초기화되므로

새로고침을 해도 dataList의 변경사항은 그대로 유지된다.

 

여기까지 했다면 리스트뷰 출력을 위한 준비는 끝이났다.

 

 

 

운동기록 출력 리스트뷰 

 

3항 연산자의 활용

Center(
	child: _controller.dataList.isEmpty
    	? Column(
	        children: [ // 운동기록이 없을 때 처리 ]
          )
        : SizedBox(
        	child: // 운동기록 출력
          )
)

3항 연산자를 이용해 리스트가 비어있는지의 여부에 따라 다른 화면을 출력해준다.

 

 

 

개요

 

운동 데이터별로 구분 선이 있어야 하고 스크롤은 비활성화 상태여야한다.

이 조건을 만족한 상태에서 최근 3일의 운동기록을 보여줘야 한다. 

 

 

기본조건 만족

SizedBox(
  height: _controller.returnListViewHeight(), // 데이터 개수에 따른 리스트뷰 높이 설정
  child: ListView.separated(
      physics: const NeverScrollableScrollPhysics(), // 스크롤 비활성화
      itemBuilder: (context, index) {
        final data = _controller.dataList.length > 3 // 운동기록이 3일을 넘어가면
            ? _controller.dataList[
                index + _controller.dataList.length - 3] // 최근 3일부터 출력
            : _controller.dataList[index];

데이터마다 구분선을 주기 위해서 ListView.separated 위젯을 사용해줬다.

 

여기서 index는 0 ~ 운동일수의 범위에 해당하는 값이다.

운동기록이 3일을 넘어갈 때와아닐 때의 데이터를 다른값으로 할당해준다. 

 

 

double returnListViewHeight() {
    if (dataList.length == 1) {
      return 63;
    } else if (dataList.length == 2) {
      return 140;
    } else {
      return 217;
    }
  }

운동일수가 1일이거나 2일일때도 고려해야한다.

SizedBox의 height 프로퍼티에는 double형 값이 들어가야한다.

 

 

separatorBuilder:
	(BuildContext context, int index) {
  return const Column(
	children: [
	  Divider(
		color: Color(0xFFEAEAEA),
		height: 4,
	  )
    ],
  );
},

구분선은 separatorBuilder 프로퍼티를 이용해서 줄 수 있다.

 

 

 

데이터 출력

int sum1 = (data['ex1'][0] +
	data['ex1'][1] +
	data['ex1'][2]);

int sum2 = data['ex2'][0] +
	data['ex2'][1] +
	data['ex2'][2];

코드의 가독성을 위해 필요한 변수들은 리스트뷰 내에서 선언해준다. 

 

 

 

  return ListTile(
    contentPadding: const EdgeInsets.only(left: 2),
    title: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          children: [
            Text(
              '${data['day']}일차',
              style: TextStyles.b3Medium,
            ),
          ],
        ),
        const SizedBox(
          height: 2,
        ),
      ],
    ),
    subtitle: Column(
      children: [
        Row(
          children: [
            Text(
              '${data['ex1_name']}',
              style: TextStyles.b4Medium,
            ),
            const SizedBox(
              width: 8,
            ),
            Text(
              '${data['ex1'].join('  ')}',
              style: TextStyles.b4Regular,
            ),
            const SizedBox(
              width: 5,
            ),
            Text(
              '($sum1개)',
              style: TextStyles.b4Regular.copyWith(
                  color: context.isLight
                      ? LightModeColors.dark3
                      : DarkModeColors.dark3),
            )
          ],
        ),
        Row(
          children: [
            Text(
              '${data['ex2_name']}',
              style: TextStyles.b4Medium,
            ),
            const SizedBox(
              width: 8,
            ),
            Text(
              '${data['ex2'].join('  ')}',
              style: TextStyles.b4Regular,
            ),
            const SizedBox(
              width: 5,
            ),
            Text(
              '($sum2개)',
              style: TextStyles.b4Regular.copyWith(
                  color: context.isLight
                      ? LightModeColors.dark3
                      : DarkModeColors.dark3),
            )
          ],
        ),
      ],
    ),
  );

리스트뷰는 ListTile위젯을 리턴한다.

 

data['키']로 해당 운동일자의 데이터에서 원하는 키값을 가지고 올 수 있다.

 

 

콘솔창 오류메시지 해결

itemCount: _controller.dataList.length > 3
	? 3
	: _controller.dataList.length

리스트뷰의 itemCount 프로퍼티로 반환할 수 있는 ListTile위젯의 수를 제한할 수 있다.

 

3으로 주거나 운동일수로 값을 줘도 화면상에서 문제는 발생하지 않지만

두 경우의 값을 처리해주지 않으면 보이지 않는 곳에서 index range error가 뜨게된다.

 

 

 

테스트

 

 

테스트를 위해 임시적으로 버튼을 통해서 값을 추가 혹은 제거를 하였다.

 

 
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함