Flutter란?
Flutter는 Google에서 개발한 오픈소스 UI 소프트웨어 개발 툴킷입니다. 이 툴킷의 가장 큰 특징은 하나의 코드베이스로 iOS와 Android 모두에 네이티브 앱을 생성할 수 있다는 것입니다. 즉, 두 개의 다른 플랫폼에 대해 각각 코드를 작성할 필요 없이 Flutter 하나로 두 플랫폼 모두를 커버할 수 있습니다. Flutter의 이런 가능성은 그래픽 엔진과 위젯 기반의 UI 구성요소 덕분입니다. 위젯은 재사용 가능한 UI 요소로써, 앱의 화면을 구성하는 주요 구성요소입니다.
Dart란?
Flutter 앱을 개발할 때 사용하는 프로그래밍 언어는 Dart입니다. Dart는 Google이 개발한 객체 지향 프로그래밍 언어로, 처음에는 웹 개발을 목적으로 만들어졌습니다. 하지만 그 후로 그 영역은 확장되어 웹, 서버, 그리고 모바일 애플리케이션 개발에까지 넓혀졌습니다. Dart는 빠른 성능과 간결한 문법 구조를 가지며, 특히 Flutter와 함께 사용될 때 그 힘을 발휘합니다. Dart는 "Hot Reload" 기능을 통해 개발자가 코드 변경 사항을 즉시 앱에서 확인할 수 있게 도와줍니다.
Todo list 앱
Todo List 앱은 많은 앱 개발자들이 처음 학습할 때 만들어보는 기본적인 앱 중 하나입니다. 그 이유는 그 구조가 간단하면서도 사용자 인터랙션, 데이터 저장, UI 업데이트와 같은 주요 앱 개발 요소를 모두 포함하고 있기 때문입니다. 이번에는 Flutter와 Dart의 기본 개념과 위젯을 이해하고 사용하는 데 도움이 될 간단한 Todo List 앱을 만들어보겠습니다.
구현할 기능
- 교안의 Filtered List View를 사용하여 Todo List 표시: Filtered List View는 Flutter에서 제공하는 위젯 중 하나로, 사용자가 설정한 필터 조건에 따라 목록을 표시합니다. 이를 사용하면 할 일 목록을 더 유연하게 표시할 수 있습니다.
- Floated Button(+)를 이용하여 할 일을 추가: Flutter에서는 Floated Button이라는 원형의 버튼 위젯을 제공합니다. 이 버튼을 클릭하면 새로운 할 일을 입력하는 다이얼로그가 나타나게 됩니다.
- ListTile에 삭제 버튼 추가: 각 할 일 아이템 옆에는 삭제 버튼이 있어, 필요 없는 할 일을 쉽게 제거할 수 있습니다.
- ListTile의 좌측에 체크박스를 이용하여 완료 여부 표시: 사용자는 체크박스를 클릭하여 할 일의 완료 여부를 표시할 수 있습니다. 체크박스는 Flutter의 기본 위젯으로 제공됩니다.
- AppBar에 버튼을 추가하여 완료된 일만 볼 수 있도록 토글 기능 추가: AppBar는 앱의 상단에 위치한 바로, 주요 액션 버튼을 추가하여 사용자에게 더 많은 인터랙션을 제공할 수 있습니다. 여기서는 완료된 일만 보기 위한 토글 기능을 추가합니다.
- Hive를 사용하여 로컬 데이터베이스 연동: Hive 패키지를 사용하여 'mybox'라는 이름의 박스를 생성 및 열기를 진행합니다. 앱의 할 일 목록은 Hive 데이터베이스에 저장되므로 앱을 종료하고 다시 실행해도 데이터가 유지됩니다.
- CustomButton과 AddTaskDialog 위젯 구현: UI의 일관성을 위해 CustomButton이라는 사용자 정의 버튼 위젯을 구현하였으며, 할 일을 추가하기 위한 사용자 정의 다이얼로그인 AddTaskDialog도 구현하였습니다.
코드분석
1. Filtered List View를 사용하여 Todo List 표시
- HomePage 위젯: 이는 앱의 메인 화면을 나타내는 StatefulWidget입니다. 여기에는 할 일 목록이 표시되며, ListView.builder 위젯을 사용하여 목록의 아이템들을 동적으로 생성합니다.
- showCompletedTasksOnly 불리언 변수를 사용하여 완료된 할 일만 보여줄지를 결정합니다. 이 변수에 따라 목록이 필터링되어 표시됩니다.
// 홈 페이지 위젯. 할 일 목록을 보여주는 화면
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
// ignore: library_private_types_in_public_api
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
ToDoDatabase db = ToDoDatabase(); // 할 일 데이터베이스
bool showCompletedTasksOnly = false; // 완료된 할 일만 보여주는지 여부
final _taskController = TextEditingController(); // 새로운 할 일 입력을 위한 컨트롤러
// ... (생략된 다른 메서드들)
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('ToDo List App'),
actions: [
IconButton(
icon: Icon(showCompletedTasksOnly ? Icons.list : Icons.check),
onPressed: () {
setState(() {
showCompletedTasksOnly = !showCompletedTasksOnly; // 완료된 할 일만 보여주는 옵션을 토글
});
},
),
],
),
// 교안의 Filtered List View를 사용하여 Todo List 표시
body: ListView.builder(
itemCount: showCompletedTasksOnly // 완료된 할 일만 보여줄지 여부에 따라 목록 길이 결정
? db.toDoList.where((task) => task.isCompleted).length
: db.toDoList.length,
itemBuilder: (context, index) {
var task = showCompletedTasksOnly
? db.toDoList.where((task) => task.isCompleted).toList()[index]
: db.toDoList[index];
return ToDoItemWidget(
item: task,
onChanged: (value) {
setState(() {
task.isCompleted = value!;
db.updateDataBase(); // 변경된 데이터를 데이터베이스에 업데이트
});
},
onDelete: () {
setState(() {
db.toDoList.remove(task); // 할 일 목록에서 해당 아이템 삭제
db.updateDataBase(); // 변경된 데이터를 데이터베이스에 업데이트
});
},
);
},
),
// ... (생략된 다른 위젯들)
);
}
// ... (생략된 다른 메서드들)
}
2. Floated Button(+)를 이용하여 할 일을 추가
- _addTask 메서드: 이는 FloatingActionButton을 통해 호출됩니다. 이 메서드는 showDialog 함수를 사용하여 AddTaskDialog 위젯을 표시합니다. 사용자는 이 다이얼로그를 통해 새로운 할 일을 입력할 수 있습니다.
// Floated Button(+) 추가
floatingActionButton: FloatingActionButton(
onPressed: _addTask, // '+' 버튼 클릭 시 새로운 할 일 추가 함수 실행
child: const Icon(Icons.add), // 플로팅 버튼 아이콘
),
...
// 새로운 할 일을 추가하는 함수
void _addTask() {
showDialog(
context: context,
// Dialog를 사용하여 할일 입력
builder: (context) => AddTaskDialog(
controller: _taskController,
onSave: () {
setState(() {
var newTask = ToDoItem(name: _taskController.text); // 입력한 텍스트를 기반으로 새로운 할 일 생성
db.toDoList.add(newTask); // 할 일 목록에 새로운 아이템 추가
db.updateDataBase(); // 변경된 데이터를 데이터베이스에 업데이트
_taskController.clear(); // 입력 필드 초기화
});
Navigator.of(context).pop(); // 다이얼로그 닫기
},
onCancel: () {
_taskController.clear(); // 입력 필드 초기화
Navigator.of(context).pop(); // 다이얼로그 닫기
},
),
);
}
3. ListTile에 삭제 버튼과 좌측에 체그박스를 이용하여 완료 여부 표시
- ToDoItemWidget: 각 할 일 아이템을 표시하는 위젯입니다. 이 위젯은 할 일의 이름, 완료 여부 체크박스, 그리고 삭제 버튼을 포함합니다.
- 위의 ToDoItemWidget에서, Checkbox 위젯을 사용하여 사용자에게 할 일의 완료 여부를 표시하고, 토글할 수 있게 합니다.
class ToDoItemWidget extends StatelessWidget {
final ToDoItem item; // 할 일 아이템 정보
final ValueChanged<bool?> onChanged; // 체크박스 변경 시 실행될 함수
final VoidCallback onDelete; // 삭제 버튼 클릭 시 실행될 함수
const ToDoItemWidget({super.key, required this.item, required this.onChanged, required this.onDelete});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 25.0, vertical: 25),
child: Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.lightBlue[200],
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
// ListTile의 좌측에 체크박스를 표시하여 완료 여부 선택
Checkbox(
value: item.isCompleted,
onChanged: onChanged,
activeColor: Colors.black,
),
Text(
item.name,
style: TextStyle(
decoration: item.isCompleted
? TextDecoration.lineThrough // 완료된 할 일은 취소선 표시
: TextDecoration.none,
),
),
],
),
// ListTile에 삭제 버튼 추가
IconButton(
icon: const Icon(Icons.delete, color: Colors.red), // 삭제 아이콘
onPressed: onDelete,
)
],
),
),
);
}
}
4. AppBar에 버튼을 추가하여 완료된 일만 볼 수 있도록 토글 기능 추가
- AppBar에는 showCompletedTasksOnly 변수의 상태를 바꾸는 IconButton이 있습니다. 이를 통해 사용자는 완료된 할 일만 보기/전체 할 일 보기를 토글할 수 있습니다.
appBar: AppBar(
title: const Text('ToDo List App'),
actions: [
IconButton(
icon: Icon(showCompletedTasksOnly ? Icons.list : Icons.check),
onPressed: () {
setState(() {
showCompletedTasksOnly = !showCompletedTasksOnly; // 완료된 할 일만 보여주는 옵션을 토글
});
},
),
],
),
이 부분은 HomePage 위젯 내의 build 메서드 안에 위치하고 있습니다. IconButton을 사용하여 AppBar의 오른쪽 상단에 버튼을 추가하였고, showCompletedTasksOnly 변수의 상태에 따라 아이콘이 Icons.list 또는 Icons.check로 변경됩니다. 버튼을 누르면 showCompletedTasksOnly 변수의 상태가 반전됩니다.
5. Hive를 사용하여 로컬 데이터베이스 연동
- main 함수: 이 부분에서 앱이 시작될 때 Hive.initFlutter()를 호출하여 Hive 패키지를 초기화하고, Hive.openBox('mybox')를 사용하여 'mybox'라는 이름의 박스를 열거나 새로 생성합니다.
void main() async {
await Hive.initFlutter(); // Hive 패키지 초기화
await Hive.openBox('mybox'); // 'mybox'라는 이름의 박스를 열거나 새로 생성
runApp(const MyApp());
}
- ToDoDatabase 클래스: 이 클래스는 할 일 목록을 관리하고 Hive 데이터베이스와의 연동을 담당합니다. 이 클래스 내에서 loadData, createInitialData, 및 updateDataBase와 같은 메서드들을 사용하여 Hive와의 데이터 CRUD 작업을 수행합니다.
class ToDoDatabase {
List<ToDoItem> toDoList = []; // 할 일 목록을 저장하는 리스트
final _myBox = Hive.box('mybox'); // Hive 데이터베이스의 'mybox' 박스
/// 초기 데이터 생성
void createInitialData() {
toDoList = [
ToDoItem(name: "컴퓨터공학과 3학년", isCompleted: false),
ToDoItem(name: "20190531 박현빈", isCompleted: false),
ToDoItem(name: "10/19 오픈소스프로젝트 과제2", isCompleted: true),
];
}
void loadData() {
var loadedData = _myBox.get("TODOLIST") as List<dynamic>? ?? [];
toDoList = loadedData.map((item) {
if (item is Map) {
return ToDoItem.fromMap(item.cast<String, dynamic>());
}
return null; // 잘못된 데이터 타입에 대한 처리. 실제로 이런 데이터가 없다면 이 줄은 실행되지 않습니다.
}).where((item) => item != null).cast<ToDoItem>().toList();
}
/// 변경된 할 일 데이터를 데이터베이스에 업데이트
void updateDataBase() {
_myBox.put("TODOLIST", toDoList.map((e) => e.toMap()).toList()); // 'TODOLIST' 키에 리스트 데이터 저장
}
}
loadData 메서드는 Hive 박스에서 "TODOLIST" 키를 사용하여 데이터를 로드합니다. createInitialData 메서드는 초기 데이터를 생성하는 역할을 합니다. updateDataBase 메서드는 변경된 할 일 데이터를 Hive 박스에 업데이트합니다.
6. CustomButton과 AddTaskDialog 위젯 구현
- CustomButton: 일관된 UI를 위해 사용자 정의 버튼 위젯입니다. MaterialButton을 기반으로 하며, 필요한 파라미터로 버튼의 라벨과 클릭 시 실행될 함수를 받습니다.
class CustomButton extends StatelessWidget {
final String label; // 버튼의 라벨
final VoidCallback onPressed; // 버튼 클릭 시 실행될 함수
CustomButton({required this.label, required this.onPressed});
@override
Widget build(BuildContext context) {
return MaterialButton(
onPressed: onPressed,
child: Text(label),
color: Colors.blue, // 예시로 색상을 지정했습니다. 실제 디자인에 따라 변경 가능
textColor: Colors.white, // 예시로 색상을 지정했습니다. 실제 디자인에 따라 변경 가능
);
}
}
- AddTaskDialog: 사용자가 새로운 할 일을 추가할 때 나타나는 다이얼로그 위젯으로, 텍스트 입력 필드와 "저장" 및 "취소" 버튼을 포함합니다. 사용자가 "저장" 버튼을 클릭하면 입력한 텍스트를 다이얼로그의 결과로 반환합니다.
class AddTaskDialog extends StatefulWidget {
@override
_AddTaskDialogState createState() => _AddTaskDialogState();
}
class _AddTaskDialogState extends State<AddTaskDialog> {
late TextEditingController _controller; // 텍스트 입력 필드의 컨트롤러
@override
void initState() {
super.initState();
_controller = TextEditingController(); // 컨트롤러 초기화
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text("새로운 할 일 추가"),
content: TextField(
controller: _controller,
decoration: InputDecoration(labelText: "할 일"),
),
actions: [
CustomButton(
label: "취소",
onPressed: () {
Navigator.of(context).pop();
},
),
CustomButton(
label: "저장",
onPressed: () {
if (_controller.text.isNotEmpty) {
// 할 일 추가 로직이 여기에 들어갑니다.
Navigator.of(context).pop(_controller.text); // 입력한 텍스트를 결과로 반환
}
},
),
],
);
}
@override
void dispose() {
_controller.dispose(); // 리소스 해제
super.dispose();
}
}
결론
이 포스트를 통해 Flutter로 간단한 할 일 목록 앱을 만드는 방법을 탐색해 보았습니다. 우리는 Flutter의 기본 위젯들로 UI를 구성하고, Hive 데이터베이스를 통해 로컬 데이터 저장 기능을 구현하는 방법을 살펴보았습니다.
- UI 구성: Flutter의 위젯 기반 구조를 활용하여 사용자 친화적인 앱을 설계하였습니다. 사용자 정의 위젯인 CustomButton과 AddTaskDialog를 통해 UI의 일관성을 유지하고 코드 재사용성을 높였습니다.
- 데이터 관리: Hive 데이터베이스를 사용하여 앱의 데이터를 로컬에 안전하게 저장하였습니다. Hive는 효율적이고 빠른 접근 속도를 제공하기 때문에 이러한 간단한 앱에는 매우 적합한 선택이었습니다.
마지막으로, Flutter는 빠르게 앱을 개발하기 위한 강력한 프레임워크임을 다시 한번 확인하였습니다. 기본 위젯부터 데이터베이스 연동까지, Flutter는 모든 것을 간결하게 처리할 수 있게 해줍니다.
이 글을 통해 Flutter와 Hive에 대한 기초적인 이해를 얻으셨기를 바랍니다. 앞으로의 프로젝트에서 이 지식이 도움이 되기를 기대합니다. 더 궁금한 점이나 피드백이 있으시다면 댓글로 남겨주시기 바랍니다. 감사합니다!
전체코드
https://github.com/pakyoong/Flutter_ToDo_App.
GitHub - pakyoong/Flutter_ToDo_App: 2023 오픈소스프로젝트 Todo list 만들기(개인 과제)
2023 오픈소스프로젝트 Todo list 만들기(개인 과제). Contribute to pakyoong/Flutter_ToDo_App development by creating an account on GitHub.
github.com