반응형
250x250
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- ssh
- MySQL
- nodejs
- 영어
- AWS
- mariadb
- Kibana
- docker
- JavaScript
- app
- Python
- Ai
- 바보
- unity
- sample
- Windows
- ChatGPT
- s3
- JS
- React
- Linux
- MSSQL
- 유니티
- error
- API
- elasticsearch
- 구글
- build
- 설정
Archives
- Today
- Total
가끔 보자, 하늘.
Flutter 로 앱 개발 및 릴리즈 - 05. Project 페이지 본문
오늘은 프로젝트 탭 페이지를 만들어 보겠습니다.
트리구조 형태에 드래그 앤 드랍으로 일정은 손쉽게 조정할 수 있게 할 컴포넌트가 필요한데 공개된 컴포넌트는 적당한게 없네요. 기본으로 제공되는 TreeNodeWidget을 활용해 개발할 것을 Gemini가 추천을 해주네요.
이 프로젝트는 개발자를 위한 ToDo 앱을 개발하는 것이며, 일정을 프로젝트 별로 관리하도록 합니다. 상세한 데이터 구조는 뒤에서 다시 논의하고 오늘은 이름만 가진 임시 데이터 구조를 만들어 보겠습니다. lib/project_item.dart 파일을 생성하고 아래 코드를 추가합니다.
Gemin에게 요청: 트리 형태로 출력하고 하단에는 +, - 등의 조작 버튼을 만들어줘 트리 영역은 스크롤 가능하고 하단의 조작 버튼은 고정되게 해줘. 트리 내 모두 객체는 드래그 & 드랍으로 위치를 조정할 수 있어.
import 'package:flutter/material.dart';
class ProjectItem {
String id; // 고유 id
String name; // 일정 이름
List<ProjectItem> children; // 자식 일정
ProjectItem({required this.id, required this.name, this.children = const []});
}
lib/tree_node_widget.dart 파일을 생성하고 아래 코드를 추가합니다.
import 'package:flutter/material.dart';
import 'project_item.dart'; // ProjectItem 모델 임포트
class TreeNodeWidget extends StatelessWidget {
final ProjectItem item;
final Function(String sourceId, String targetId) onDragComplete;
const TreeNodeWidget({
super.key,
required this.item,
required this.onDragComplete,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
// Draggable 위젯: 이 위젯을 드래그할 수 있도록 합니다.
Draggable<String>(
data: item.id, // 드래그될 때 전달할 데이터
feedback: Material( // 드래그 중에 표시될 위젯
elevation: 4.0,
child: Container(
padding: const EdgeInsets.all(8.0),
child: Text(item.name),
),
),
child: DragTarget<String>( // DragTarget 위젯: 다른 Draggable 위젯을 받을 수 있도록 합니다.
onAccept: (data) {
// 드래그된 데이터(sourceId)가 이 타겟(targetId)에 드롭되었을 때 호출됩니다.
onDragComplete(data, item.id);
},
builder: (context, candidateData, rejectedData) {
// 드래그 중일 때 타겟의 모양을 변경할 수 있습니다.
return Container(
decoration: BoxDecoration(
border: candidateData.isNotEmpty
? Border.all(color: Colors.blueAccent, width: 2.0)
: null,
),
padding: const EdgeInsets.all(8.0),
margin: const EdgeInsets.symmetric(vertical: 4.0),
child: Row(
children: [
const Icon(Icons.folder), // 트리 항목 아이콘 (예시)
const SizedBox(width: 8.0),
Text(item.name),
],
),
);
},
),
),
// 자식 노드를 재귀적으로 표시
Padding(
padding: const EdgeInsets.only(left: 16.0),
child: Column(
children: item.children.map((child) {
return TreeNodeWidget(
item: child,
onDragComplete: onDragComplete,
);
}).toList(),
),
),
],
);
}
}
이제 lib/project_page.dart를 아래와 같이 생성합니다. 화면 하단에는 Node 추가 삭제를 위한 버튼을 추가합니다.
import 'package:flutter/material.dart';
import 'project_item.dart'; // ProjectItem 모델 임포트
import 'tree_node_widget.dart'; // TreeNodeWidget 임포트
class ProjectPage extends StatefulWidget {
const ProjectPage({super.key});
@override
State<ProjectPage> createState() => _ProjectPageState();
}
class _ProjectPageState extends State<ProjectPage> {
// 예시 데이터입니다. 이번에는 drag & drop 시 실제 변경이 일어나지는 않습니다.
List<ProjectItem> _projectItems = [
ProjectItem(id: '1', name: 'Project 1', children: [
ProjectItem(id: '1.1', name: 'Subproject 1.1'),
ProjectItem(id: '1.2', name: 'Subproject 1.2', children: [
ProjectItem(id: '1.2.1', name: 'Task 1.2.1'),
]),
]),
ProjectItem(id: '2', name: 'Project 2'),
];
// 드래그 앤 드롭 완료 시 호출될 함수
void _handleDragComplete(String sourceId, String targetId) {
setState(() {
// 여기에서 _projectItems 리스트를 업데이트하는 로직을 구현합니다.
// sourceId를 가진 항목을 찾아서 targetId를 가진 항목의 자식으로 이동시키는 로직이 필요합니다.
// 트리 구조에서의 드래그 앤 드롭 로직은 복잡하며, 부모-자식 관계 업데이트, 순서 변경 등을 고려해야 합니다.
print('Dragged $sourceId to $targetId');
// 실제 데이터 업데이트 로직을 추가해야 합니다.
// 예시: 간단히 로그만 출력합니다.
});
}
// 하단 버튼 클릭 시 실행될 함수들 (예시)
void _addItem() {
// 항목 추가 로직 구현
print('Add button clicked');
}
void _removeItem() {
// 항목 삭제 로직 구현
print('Remove button clicked');
}
// 필요한 다른 조작 버튼 함수들을 여기에 추가합니다.
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Projects'),
),
body: Column(
children: [
Expanded( // 트리 영역이 남은 공간을 모두 차지하고 스크롤 가능하도록 합니다.
child: SingleChildScrollView( // 트리 영역 스크롤 가능
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: _projectItems.map((item) {
return TreeNodeWidget(
item: item,
onDragComplete: _handleDragComplete,
);
}).toList(),
),
),
),
),
// 하단 고정 조작 버튼 영역
Container(
padding: const EdgeInsets.all(8.0),
color: Colors.grey[200], // 버튼 영역 배경색 (예시)
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: _addItem, // 항목 추가 함수 연결
child: const Icon(Icons.add),
),
ElevatedButton(
onPressed: _removeItem, // 항목 삭제 함수 연결
child: const Icon(Icons.remove),
),
// 필요한 다른 조작 버튼을 여기에 추가합니다.
// ElevatedButton(
// onPressed: () {
// // 다른 기능
// print('Another button clicked');
// },
// child: const Icon(Icons.edit),
// ),
],
),
),
],
),
);
}
}
대부분의 주요 코드는 아직 작동하지 않고 이벤트 정보를 출력만 합니다. 이제 navi_bar.dart코드를 아래와 같이 수정합니다.
import 'package:flutter/material.dart';
import 'calendar_page.dart';
import 'project_page.dart';
.
.
final List<Widget> _pages = <Widget>[
const CalendarPage(),
const ProjectPage(),
.
.
.
실행하면 아래와 같은 화면을 확인 할 수 있습니다.
기본 Widget이라 보기는 좀 썰렁 하네요. 첫 술에 배부를 수는 없으니까요 ^^
다음에는 Setting Page를 구성해 기본 틀을 모두 완료해 보겠습니다. :)
반응형
'개발 이야기 > 개발 및 서비스' 카테고리의 다른 글
Flutter 로 앱 개발 및 릴리즈 - 06. Settings 페이지 (0) | 2025.05.14 |
---|---|
🎮 외노자의 강제 휴식과 AI 프로젝트 - MDM 기반 애니메이션 생성 및 학습 툴 개발 (1) | 2025.05.13 |
Flutter 로 앱 개발 및 릴리즈 - 04. Calendar 페이지 (0) | 2025.05.12 |
Flutter 로 앱 개발 및 릴리즈 - 03. Flutter Compoent와 Native Component (1) | 2025.05.09 |
Flutter 로 앱 개발 및 릴리즈 - 02. 폴더 구조 정리 (1) | 2025.05.08 |