Created
June 17, 2022 11:05
-
-
Save koboolean/2bc4a15cad1f9a71c82beb4fdd133fa5 to your computer and use it in GitHub Desktop.
flutter diary project
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:convert'; | |
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; | |
List<Diary> diaryList = []; | |
DiaryService(this.prefs) { | |
List<String> strintDiaryList = prefs.getStringList("diaryList") ?? []; | |
for (String s in strintDiaryList) { | |
Map<String, dynamic> jsonMap = jsonDecode(s); | |
Diary diary = Diary.fromJson(jsonMap); | |
diaryList.add(diary); | |
} | |
} | |
List<Diary> getByDate(DateTime date) { | |
return diaryList | |
.where((diary) => isSameDay(date, diary.createdAt)) | |
.toList(); | |
} | |
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(); | |
savePreferences(); | |
} | |
void update(DateTime createdAt, String newContent) { | |
Diary diary = diaryList.firstWhere((diary) => diary.createdAt == createdAt); | |
diary.text = newContent; | |
notifyListeners(); | |
savePreferences(); | |
} | |
void delete(DateTime createdAt) { | |
diaryList.removeWhere((diary) => diary.createdAt == createdAt); | |
notifyListeners(); | |
savePreferences(); | |
} | |
void savePreferences() { | |
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/cupertino.dart'; | |
import 'package:flutter/material.dart'; | |
import 'package:intl/intl.dart'; | |
import 'package:provider/provider.dart'; | |
import 'package:table_calendar/table_calendar.dart'; | |
import 'diary_service.dart'; | |
class HomePage extends StatefulWidget { | |
const HomePage({Key? key}) : super(key: key); | |
@override | |
State<HomePage> createState() => _HomePageState(); | |
} | |
class _HomePageState extends State<HomePage> { | |
CalendarFormat calendarFormat = CalendarFormat.month; | |
DateTime selectedDate = DateTime.now(); | |
TextEditingController createControl = TextEditingController(); | |
TextEditingController updateControl = TextEditingController(); | |
@override | |
Widget build(BuildContext context) { | |
return Consumer<DiaryService>( | |
builder: (context, diaryService, child) { | |
List<Diary> diaryList = diaryService.getByDate(selectedDate); | |
return Scaffold( | |
// 키보드가 올라올 때 화면 밀지 않도록 만들기(overflow 방지) | |
resizeToAvoidBottomInset: false, | |
body: SafeArea( | |
child: Column( | |
children: [ | |
/// 달력 | |
TableCalendar( | |
firstDay: DateTime.utc(2010, 10, 16), | |
lastDay: DateTime.utc(2030, 3, 14), | |
focusedDay: selectedDate, | |
calendarFormat: calendarFormat, | |
onFormatChanged: (format) { | |
// 달력 형식 변경 | |
setState(() { | |
calendarFormat = format; | |
}); | |
}, | |
eventLoader: (date) { | |
// 각 날짜에 해당하는 diaryList 보여주기 | |
return diaryService.getByDate(date); | |
}, | |
calendarStyle: CalendarStyle( | |
// today 색상 제거 | |
todayTextStyle: TextStyle(color: Colors.black), | |
todayDecoration: BoxDecoration( | |
color: Colors.white, | |
shape: BoxShape.circle, | |
), | |
), | |
selectedDayPredicate: (day) { | |
return isSameDay(selectedDate, day); | |
}, | |
onDaySelected: (_, focusedDay) { | |
setState(() { | |
selectedDate = focusedDay; | |
}); | |
}, | |
), | |
Divider(height: 1), | |
/// 선택한 날짜의 일기 목록 | |
Expanded( | |
child: diaryList.isEmpty | |
? Center( | |
child: Text( | |
"한 줄 일기를 작성해주세요.", | |
style: TextStyle( | |
color: Colors.grey, | |
fontSize: 18, | |
), | |
), | |
) | |
: ListView.separated( | |
itemCount: diaryList.length, | |
itemBuilder: (context, index) { | |
// 역순으로 보여주기 | |
int i = diaryList.length - index - 1; | |
Diary diary = diaryList[i]; | |
return ListTile( | |
/// text | |
title: Text( | |
diary.text, | |
style: TextStyle( | |
fontSize: 24, | |
color: Colors.black, | |
), | |
), | |
/// createdAt | |
trailing: Text( | |
DateFormat('kk:mm').format(diary.createdAt), | |
style: TextStyle( | |
fontSize: 12, | |
color: Colors.grey, | |
), | |
), | |
/// 클릭하여 update | |
onTap: () { | |
showUpdateDialog(diaryService, diary); | |
}, | |
/// 꾹 누르면 delete | |
onLongPress: () { | |
showDeleteDialog(diaryService, diary); | |
}, | |
); | |
}, | |
separatorBuilder: (BuildContext context, int index) { | |
// item 사이에 Divider 추가 | |
return Divider(height: 1); | |
}, | |
), | |
), | |
], | |
), | |
), | |
/// Floating Action Button | |
floatingActionButton: FloatingActionButton( | |
child: Icon(Icons.create), | |
backgroundColor: Colors.indigo, | |
onPressed: () { | |
showCreateDialog(diaryService); | |
}, | |
), | |
); | |
}, | |
); | |
} | |
void createDiary(DiaryService diaryService) { | |
// 앞뒤 공백 삭제 | |
String newText = createControl.text.trim(); | |
if (newText.isNotEmpty) { | |
diaryService.create(newText, selectedDate); | |
createControl.text = ""; | |
} | |
} | |
void updateDiary(DiaryService diaryService, Diary diary) { | |
// 앞뒤 공백 삭제 | |
String updatedText = updateControl.text.trim(); | |
if (updatedText.isNotEmpty) { | |
diaryService.update( | |
diary.createdAt, | |
updatedText, | |
); | |
} | |
} | |
void showCreateDialog(DiaryService diaryService) { | |
showDialog( | |
context: context, | |
builder: (context) { | |
return AlertDialog( | |
title: Text("일기 작성"), | |
content: TextField( | |
controller: createControl, | |
autofocus: true, | |
// 커서 색상 | |
cursorColor: Colors.indigo, | |
decoration: InputDecoration( | |
hintText: "한 줄 일기를 작성해주세요.", | |
// 포커스 되었을 때 밑줄 색상 | |
focusedBorder: UnderlineInputBorder( | |
borderSide: BorderSide(color: Colors.indigo), | |
), | |
), | |
onSubmitted: (_) { | |
// 엔터 누를 때 작성하기 | |
createDiary(diaryService); | |
Navigator.pop(context); | |
}, | |
), | |
actions: [ | |
/// 취소 버튼 | |
TextButton( | |
onPressed: () => Navigator.pop(context), | |
child: Text( | |
"취소", | |
style: TextStyle(color: Colors.indigo), | |
), | |
), | |
/// 작성 버튼 | |
TextButton( | |
onPressed: () { | |
createDiary(diaryService); | |
Navigator.pop(context); | |
}, | |
child: Text( | |
"작성", | |
style: TextStyle(color: Colors.indigo), | |
), | |
), | |
], | |
); | |
}, | |
); | |
} | |
/// 수정 다이얼로그 보여주기 | |
void showUpdateDialog(DiaryService diaryService, Diary diary) { | |
showDialog( | |
context: context, | |
builder: (context) { | |
updateControl.text = diary.text; | |
return AlertDialog( | |
title: Text("일기 수정"), | |
content: TextField( | |
autofocus: true, | |
controller: updateControl, | |
// 커서 색상 | |
cursorColor: Colors.indigo, | |
decoration: InputDecoration( | |
hintText: "한 줄 일기를 작성해 주세요.", | |
// 포커스 되었을 때 밑줄 색상 | |
focusedBorder: UnderlineInputBorder( | |
borderSide: BorderSide(color: Colors.indigo), | |
), | |
), | |
onSubmitted: (v) { | |
// 엔터 누를 때 수정하기 | |
updateDiary(diaryService, diary); | |
Navigator.pop(context); | |
}, | |
), | |
actions: [ | |
/// 취소 버튼 | |
TextButton( | |
child: Text( | |
"취소", | |
style: TextStyle( | |
fontSize: 18, | |
color: Colors.indigo, | |
), | |
), | |
onPressed: () => Navigator.pop(context), | |
), | |
/// 수정 버튼 | |
TextButton( | |
child: Text( | |
"수정", | |
style: TextStyle( | |
fontSize: 18, | |
color: Colors.indigo, | |
), | |
), | |
onPressed: () { | |
// 수정하기 | |
updateDiary(diaryService, diary); | |
Navigator.pop(context); | |
}, | |
), | |
], | |
); | |
}, | |
); | |
} | |
/// 삭제 다이얼로그 보여주기 | |
void showDeleteDialog(DiaryService diaryService, Diary diary) { | |
showDialog( | |
context: context, | |
builder: (context) { | |
updateControl.text = diary.text; | |
return AlertDialog( | |
title: Text("일기 삭제"), | |
content: Text('"${diary.text}"를 삭제하시겠습니까?'), | |
actions: [ | |
TextButton( | |
child: Text( | |
"취소", | |
style: TextStyle( | |
fontSize: 18, | |
color: Colors.indigo, | |
), | |
), | |
onPressed: () => Navigator.pop(context), | |
), | |
/// Delete | |
TextButton( | |
child: Text( | |
"삭제", | |
style: TextStyle( | |
fontSize: 18, | |
color: Colors.indigo, | |
), | |
), | |
onPressed: () { | |
diaryService.delete(diary.createdAt); | |
Navigator.pop(context); | |
}, | |
), | |
], | |
); | |
}, | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'package:provider/provider.dart'; | |
import 'package:shared_preferences/shared_preferences.dart'; | |
import 'diary_service.dart'; | |
import 'home_page.dart'; | |
void main() async { | |
WidgetsFlutterBinding.ensureInitialized(); | |
// sharedPreferences 인스턴스 불러오기 | |
SharedPreferences prefs = await SharedPreferences.getInstance(); | |
runApp( | |
MultiProvider( | |
providers: [ | |
ChangeNotifierProvider(create: (context) => DiaryService(prefs)), | |
], | |
child: const MyApp(), | |
), | |
); | |
} | |
class MyApp extends StatelessWidget { | |
const MyApp({Key? key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
debugShowCheckedModeBanner: false, | |
home: HomePage(), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment