Study/Development

[개발일지] flutter 앱개발 3주차

titann 2022. 9. 23. 16:57

<스파르타코딩 flutter 강의 수강일지>

 

3주차: 패키지 사용법 익히기 & 앱 기능 만들기

1. Flutter 패키지 관련 사이트

Pub.dev

플러터 패키지를 모아둔 사이트

https://pub.dev/

 

 

Dart packages

Pub is the package manager for the Dart programming language, containing reusable libraries & packages for Flutter, AngularDart, and general Dart programs.

pub.dev

 

Awsome Flutter

Flutter에 관련된 모든 자료를 모아둔 Github 문서

https://github.com/Solido/awesome-flutter#ui

 

GitHub - Solido/awesome-flutter: An awesome list that curates the best Flutter libraries, tools, tutorials, articles and more.

An awesome list that curates the best Flutter libraries, tools, tutorials, articles and more. - GitHub - Solido/awesome-flutter: An awesome list that curates the best Flutter libraries, tools, tuto...

github.com

 

Flutter Gems

카테고리별 플러터 패키지 추천

https://fluttergems.dev/

 

Flutter Gems - A Curated List of Dart & Flutter packages

Flutter Gems is a curated list of Dart & Flutter packages that are categorized based on functionality. Flutter Gems is also a visual alternative to pub.dev

fluttergems.dev

 

2. 패키지 설치하는 법(Terminal)

flutter pub add <설치하려는 패키지 이름>

 

*설치해본 패키지들

  • introduction_screen : 온보딩 화면 패키지
  • google_fonts : 폰트 패키지
  • table_calendar : 달력 위젯
  • provider : 상태 관리
  • intl : DateTime 형식 지정
  • shared_preferences : 데이터 저장

 

3. Onboarding Page

3페이지짜리 온보딩 페이지 기본 뼈대

body: IntroductionScreen(
        pages: [
          // 페이지1
          PageViewModel(
            title: "Title1",
            bodyWidget: Text("body1"),
          ),

          // 페이지2
          PageViewModel(
            title: "Title2",
            bodyWidget: Text("body2"),
          ),
        ],
        next: Text("Next", style: TextStyle(fontWeight: FontWeight.w600)),
        done: Text("Done", style: TextStyle(fontWeight: FontWeight.w600)),
        onDone: () {
          // When done button is press
        },
      ),

 

 

4. 기본 데이터 처리 기능 CRUD

  • Create
  • Read
  • Update
  • Delete

Provider Package 이용한 CRUD 샘플

  /// Diary 목록
  List<Diary> diaryList = [];

  /// 특정 날짜의 diary 조회
  List<Diary> getByDate(DateTime date) {
    return diaryList
        .where((diary) => isSameDay(date, diary.createdAt))
        .toList();
  }

  /// Diary 작성
  void create(String text, DateTime selectedDate) {
    DateTime now = DateTime.now();

    DateTime createdAt = DateTime(
      selectedDate.year,
      selectedDate.month,
      selectedDate.day,
      now.hour,
      now.minute,
      now.second,
    );

    Diary diary = Diary(
      text: text,
      createdAt: createdAt,
    );

    diaryList.add(diary);
    notifyListeners();
  }

  /// Diary 수정
  void update(DateTime createdAt, String newContent) {
    Diary diary =
        diaryList.firstWhere(((diary) => diary.createdAt == createdAt));
    diary.text = newContent;
    notifyListeners();
  }

  /// Diary 삭제
  void delete(DateTime createdAt) {
    diaryList.removeWhere((diary) => diary.createdAt == createdAt);

    notifyListeners();
  }

 

5. SharedPreferences에 커스텀 클래스를 저장

 

커스텀 클래스 → Map → jsonString → Map → 커스텀 클래스 변환

 

import 'dart:convert';

void main() {
  Diary diary = Diary("오늘 일기", DateTime.now());
  
  // 1) Diary ➡ Map
  Map<String, dynamic> map = diary.toJson();
  print("1) Diary ➡ Map : $map");
  
  // 2) Map ➡ jsonString
  String jsonString = jsonEncode(map);
  print("2) Map ➡ jsonString : $jsonString");
  
  // 3) jsonString ➡ Map
  Map<String, dynamic> jsonMap = jsonDecode(jsonString);
  print("3) jsonString ➡ Map : $jsonMap");
  
  // 4) Map ➡ Diary
  Diary parsedDiary = Diary.fromJson(jsonMap);
  print("4) Map ➡ Diary : $parsedDiary");
}

class Diary{
  String text;
  DateTime createdAt;
  
  Diary(this.text, this.createdAt);
 
  // Diary -> Map
  Map<String, dynamic> toJson() {
    return {
      "text": text,
      "createdAt": createdAt.toString(),
    };
  }
  
  // Map -> Diary
  factory Diary.fromJson(Map<String, dynamic> json) {
    return Diary(
      json["text"],
      DateTime.parse(json["createdAt"]),
    );
  }
}

 

실습과제에 사용한 코드

void main() async {
  // 메인 함수에 추가하여야 할 코드
  WidgetsFlutterBinding.ensureInitialized();
  SharedPreferences prefs = await SharedPreferences.getInstance();
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) => DiaryService(prefs)),
      ],
      child: const MyApp(),
    ),
  );
}
import 'dart:convert';
import 'dart:developer';

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:table_calendar/table_calendar.dart';

class Diary {
  String text; // 내용
  DateTime createdAt; // 작성 시간

  Diary({
    required this.text,
    required this.createdAt,
  });

  Map<String, dynamic> toJson() {
    return {"text": text, "createdAt": createdAt.toString()};
  }

  factory Diary.fromJson(Map<String, dynamic> jsonMap) {
    return Diary(
        text: jsonMap['text'], createdAt: DateTime.parse(jsonMap['createdAt']));
  }
}

class DiaryService extends ChangeNotifier {
  SharedPreferences prefs;
  DiaryService(this.prefs) {
    List<String> stringDiaryList = prefs.getStringList('diaryList') ?? [];
    for (String stringDiary in stringDiaryList) {
      // stringDiary ==> Diary
      Map<String, dynamic> jsonMap = jsonDecode(stringDiary);
      Diary diary = Diary.fromJson(jsonMap);
      diaryList.add(diary);
    }
  }

  /// Diary 목록
  List<Diary> diaryList = [];

  /// 특정 날짜의 diary 조회
  List<Diary> getByDate(DateTime date) {
    return diaryList
        .where((diary) => isSameDay(date, diary.createdAt))
        .toList();
  }

  /// Diary 작성
  void create(String text, DateTime selectedDate) {
    DateTime now = DateTime.now();

    DateTime createdAt = DateTime(
      selectedDate.year,
      selectedDate.month,
      selectedDate.day,
      now.hour,
      now.minute,
      now.second,
    );

    Diary diary = Diary(
      text: text,
      createdAt: createdAt,
    );

    diaryList.add(diary);
    print(inspect(diary));
    notifyListeners();
    _saveDiaryList();
  }

  /// Diary 수정
  void update(DateTime createdAt, String newContent) {
    Diary diary =
        diaryList.firstWhere(((diary) => diary.createdAt == createdAt));
    diary.text = newContent;
    notifyListeners();
    _saveDiaryList();
  }

  /// Diary 삭제
  void delete(DateTime createdAt) {
    diaryList.removeWhere((diary) => diary.createdAt == createdAt);

    notifyListeners();
    _saveDiaryList();
  }

  /// Diary prefs 저장
  void _saveDiaryList() {
    List<String> stringDiaryList = [];

    for (Diary diary in diaryList) {
      Map<String, dynamic> jsonMap = diary.toJson();
      String stringDiary = jsonEncode(jsonMap);
      stringDiaryList.add(stringDiary);
    }

    prefs.setStringList('diaryList', stringDiaryList);
  }
}

 

알아둬야 할 것들

1. Where() 메소드

where()

배열 요소를 필터링한다.

final numbers = <int>[1, 2, 3, 5, 6, 7];

// 5보다 작은 요소 반환
var result = numbers.where((x) => x < 5); // (1, 2, 3)

// 5보다 큰 요소 반환
result = numbers.where((x) => x > 5); // (6, 7)

// 짝수 반환
result = numbers.where((x) => x.isEven); // (2, 6)

 

firstWhere()

요소를 반복하고 주어진 조건의 첫번째 요소를 반환한다.

*lastWhere(): 마지막 요소 반환

// 6으로 나눴을 때, 나머지가 0인 것 반환
var result = await Stream.fromIterable([1, 3, 4, 9, 12])
    .firstWhere((element) => element % 6 == 0, orElse: () => -1);
print(result); // 12

// 찾고자 하는 값이 없을 때
result = await Stream.fromIterable([1, 2, 3, 4, 5])
    .firstWhere((element) => element % 6 == 0, orElse: () => -1);
print(result); // -1

 

removeWhere()

주어진 조건에 맞는 모든 요소를 제거

final numbers = <String>['one', 'two', 'three', 'four'];

// 글자수가 3인 요소 제거
numbers.removeWhere((item) => item.length == 3);
print(numbers); // [three, four]

 

 

2. Null Safety

e1!       // e1이 null 인 경우 에러 발생
e2 ?? e3  // e2가 null 일 경우 e3을 사용
x ??= e4  // x가 null 일 경우 `x = e4`.
e5?.foo() // e5가 null이 아니면, e5에 `foo`함수 실행
[...? e6] // e6이 null이 아니면, e6을 list로 spread하여 확장

 

 

 

오류 탐험

DateTime을 받아올 때, 순서를 잘못넣어서 오류가 생겼다.

day 데이터가 year자리에 들어간 것 때문에, 계속 화면에 나타나지 않았었다.

순서에 유의하도록 해야겠다.

 

커스텀 클래스의 인스턴스 내용을 살펴  inspect()를 사용한다.

print(inspect(diary));
반응형