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

<스파르타코딩 flutter 강의 수강일지>
3주차: 패키지 사용법 익히기 & 앱 기능 만들기
1. Flutter 패키지 관련 사이트
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
카테고리별 플러터 패키지 추천
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));