플러터(Flutter) 앱에서 쉽게 사용할 수 있는 의존성 주입 패키지 Get_it는 코드의 결합도를 낮추고 유지보수를 쉽게 할 수 있어 개발자들에게 편의성을 제공한다. 이번 글에서는 지금까지 만들었던 앱에서 기본적인 플러터 CRUD 기능 중 Create, Read을 구현하는 방법에 대해 작성한다.

플러터 CRUD 목차
Get_it 패키지 소개
Get_it은 Flutter 앱에서 의존성 주입을 관리하는 효과적인 플러그인이다. 데이터베이스 클래스를 전역에서 사용하도록 등록하면, Get_it을 통해 어디서든 동일한 데이터베이스 인스턴스를 손쉽게 가져와 사용할 수 있다. 이는 코드의 일관성을 유지하고 개발을 간편하게 만들어준다.
아래 코드에서 GetIt.I.registerSingleton을 사용하여 LocalDatabase클래스를 애플리케이션 전역에 등록하였다.
import 'database/drift_database.dart';
import 'package:get_it/get_it.dart';
// 애플리케이션 진입점인 main 함수
Future<void> main() async {
...
// LocalDatabase 인스턴스 생성
final database = LocalDatabase();
// GetIt을 사용하여 LocalDatabase를 애플리케이션 전역에서 Singleton으로 등록
GetIt.I.registerSingleton<LocalDatabase>(database);
// 앱 실행
runApp(MyApp());
}플러터 CRUD 기능
플러터 CRUD(Create, Read, Update, Delete)중에 이번 포스트에서 Create, Read에 위한 기능을 구현하고 다음 글에서 Update, Delete에 대해 작성하겠다.
입력값 저장 기능
입력 위젯에서 입력한 값을 save을 눌렀을 때 가져와야 된다. 클래스 내부에 TextEditingController 인스턴스 변수를 선언하여 클래스의 모든 메서드에서 접근 가능하도록 하면 모든 위젯에서 contentController 인스턴스 변수를 사용할 수 있다.
class _DiarySheetState extends State<DiarySheet> {
// 텍스트 입력을 관리하는 TextEditingController 인스턴스 변수 선언
TextEditingController contentController = TextEditingController();
...
// 다중 행 입력을 처리하는 TextFormField 위젯 설정
TextFormField(
// contentController 인스턴스 변수 사용
controller: contentController,
keyboardType: TextInputType.multiline,
maxLines: 25,
decoration: InputDecoration(
hintText: '입력해주세요.',
// 만약 입력값이 없다면 hintText 표시
),
);
...
}ElevatedButton에서 onPressed할때 _saveDiaryEntry 함수가 실행되며 debugPrint()로 콘솔에 메시지를 출력된다.
debugPrint는 개발 모드에서만 동작하며, 릴리스 모드에서는 아무런 동작을 하지 않는다. 이는 앱의 성능에 부담을 주지 않으면서 디버깅 정보를 출력할 수 있도록 도와준다.
ElevatedButton(
onPressed: () {
_saveDiaryEntry(date);
},
child: Text('Save'),
),
...
void _saveDiaryEntry(DateTime date) async {
debugPrint(contentController.text);
}
save 버튼을 클릭하면 입력값이 콘솔에 출력된다.

데이터베이스 연동 기능
입력폼에서 값을 가져왔다면 Sqlite를 사용하여 저장해야 된다. GetIt 패키지를 사용하여 로컬 데이터베이스에 contentController.text 값을 저장하였다.
void _saveDiaryEntry(DateTime date) async {
try {
// 새로운값 생성
await GetIt.I<LocalDatabase>().createSchedule(
DiaryDbCompanion(
content: Value(contentController.text), //날짜별 내용
date: Value(date), //선택한 날짜
create: Value(DateTime.now())), //생성일자
);
// 저장 성공 시 메시지 출력 (선택적)
debugPrint('Diary entry saved successfully.');
} catch (e) {
// 저장 실패 시 에러 메시지 출력 (선택적)
debugPrint('Failed to save diary entry: $e');
}
}TextFormField 위젯
날짜별 데이터 유무에 따라 위젯이 다르도록 보여줘야 된다. 해당 날짜 DB에 데이터가 있다면 위젯에서 데이터를 보여주며 데이터가 없다면 빈 위젯을 보여주도록 구현한다.
아래 코드의 _buildDiaryEntrie함수에 두가지 조건의 widget을 리턴할 수 있도록 구현하였다.
Container(
// Container의 정렬 속성을 설정합니다.
alignment: Alignment.center,
// Container의 여백(padding)을 설정합니다.
padding: EdgeInsets.all(20),
// Container 안에 포함된 위젯들을 세로로 나열하는 Column 위젯을 사용
child: Column(
children: [
// _buildDiaryEntries() 메서드로부터 반환된 위젯을 추가
_buildDiaryEntries(),
// "Save"라는 텍스트를 갖는 ElevatedButton을 추가
ElevatedButton(
// 버튼이 클릭되었을 때 실행될 콜백 함수를 설정
onPressed: () {
// _saveDiaryEntry(date) 메서드를 호출하여 일정을 저장
_saveDiaryEntry(date);
},
// 버튼에 표시될 텍스트를 설정
child: Text('Save'),
),
],
),
)GetIt.I().watchSchedules(date)을 이용하여 DB값을 조회하고 데이터가 비어있는 경우 TextFormField 위젯을 그대로 반환하고 데이터가 있는 경우 생성일자로 정렬하고 최근 값(diaryDataList.last.content)을 가져온다.
Widget _buildDiaryEntries() {
// StreamBuilder를 사용하여 일기 데이터의 변화를 감지하고 업데이트하는 위젯을 반환
return StreamBuilder<List<DiaryDbData>>(
// watchSchedules(date)로부터 반환된 일기 데이터의 스트림을 감시
stream: GetIt.I<LocalDatabase>().watchSchedules(date),
builder: (context, snapshot) {
// 데이터 로딩 중일 때
if (snapshot.connectionState == ConnectionState.waiting) {
// 로딩 상태를 나타내는 CircularProgressIndicator를 반환
return CircularProgressIndicator();
}
// 데이터 로딩이나 에러가 없을 때
else if (snapshot.hasError) {
// 에러 상태를 나타내는 텍스트를 반환
return Text('Error: ${snapshot.error}');
}
// 데이터가 성공적으로 로딩되었을 때
else {
// 로드된 일기 데이터 목록을 가져옴
final diaryDataList = snapshot.data ?? [];
// 데이터 생성 일자로 정렬
diaryDataList.sort((a, b) => a.create.compareTo(b.create));
// 일기 데이터가 비어있는 경우
if (diaryDataList.isEmpty) {
// TextFormField를 반환하고, 입력 컨트롤러를 사용하여 텍스트를 입력
return TextFormField(
controller: contentController,
keyboardType: TextInputType.multiline,
maxLines: 25,
decoration: InputDecoration(
hintText: '입력해주세요.',
// 일기 데이터가 없을 때만 hintText를 표시
),
);
}
// 일기 데이터가 존재하는 경우
else {
// 마지막 내용으로 초기화된 TextFormField를 반환
return TextFormField(
controller: getControllerText(diaryDataList.last.content),
keyboardType: TextInputType.multiline,
maxLines: 25,
decoration: InputDecoration(
hintText: '입력해주세요.',
// 일기 데이터가 없을 때만 hintText를 표시
),
);
}
}
},
);
}
...getControllerText함수는 주어진 텍스트로 초기화된 TextEditingController를 반환한다.
// 주어진 텍스트로 초기화된 TextEditingController를 반환하는 함수
TextEditingController getControllerText(String text) {
// TextEditingController를 생성하고 주어진 텍스트로 초기화
contentController = TextEditingController(text: text);
// 초기화된 TextEditingController를 반환
return contentController;
}마무리
지금까지 일기장 앱에서 날짜별 데이터를 저장하는 기능을 구현하였습니다. 다음 포스팅에서는 사용자가 입력한 데이터를 데이터베이스에서 삭제하는 기능을 구현하도록 하겠습니다. 읽어주셔서 감사합니다.
전체 코드는 링크에서 확인할 수 있습니다.