이전 포스팅에서 예고했던 대로, Flutter로 간단한 TodoList App을 만들어 볼 것이다.
이번 포스팅은 TodoListApp의 기본 UI를 구성해보도록 하겠다.
위와 같은 UI를 작성할 것이며, BottomNavigationBar의 아이템을 클릭하면 현재의 아이템도 변경되도록 작업 할 것이다.
위 UI를 기반으로 기능을 붙여나가는 방식으로 진행 할 것이다.
1. 루트 페이지 만들기
class TabItem {
String _title;
IconData _icon;
TabItem(String title, IconData icon) {
_title = title;
_icon = icon;
}
String getTitle() => _title;
IconData getIcon() => _icon;
}
BottomNavigationBar의 TabItem은 타이틀(Label)과 아이콘을 갖는다.
위와 같은 클래스를 작성하자.
void main() {
runApp(TodoListApp());
}
class TodoListApp extends StatefulWidget {
@override
State<StatefulWidget> createState() => _TodoListAppState();
}
class _TodoListAppState extends State<TodoListApp> {
final List<TabItem> _tabItems = [
TabItem("Todo", Icons.clear),
TabItem("In Progress", Icons.loop),
TabItem("Done", Icons.check),
];
int _currentTabIndex = 0;
@override
Widget build(BuildContext context) {
}
}
그리고 main.dart를 위와 같이 변경하자.
현재 선택한 아이템의 Index의 상태를 갖는 StatefulWidget이 루트 페이지가 된다.
탭 아이템들은 final로 리스트로 갖도록 정의한다.
이제 _TodoListAppState의 build 함수를 구현한다.
class _TodoListAppState extends State<TodoListApp> {
...
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter TodoList',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Scaffold(
appBar: AppBar(title: Text(_tabItems[_currentTabIndex].getTitle())),
)
);
}
}
Scaffold를 사용하도록 한다.
appBar를 생성하고, 현재 선택된 탭의 인덱스로 데이터를 가져오고 Title을 설정해준다.
class _TodoListAppState extends State<TodoListApp> {
...
@override
Widget build(BuildContext context) {
return MaterialApp(
...
home: Scaffold(
...
bottomNavigationBar: _createBottomNavigationBar(),
)
);
}
Widget _createBottomNavigationBar() {
return BottomNavigationBar(
items: _tabItems.map((tabItem) =>
BottomNavigationBarItem(icon: Icon(tabItem.getIcon()), label: tabItem.getTitle()),
).toList(),
currentIndex: _currentTabIndex,
onTap: (int index) => {
_onTabClick(index)
},
);
}
void _onTabClick(int index) {
setState(() {
_currentTabIndex = index;
});
}
}
이제 위와 같이 작성하여 BottomNavigationBar를 생성해준다.
List<TabItem>을 List<BottomNavigationBarItem>으로 변경하기 위해 map 메서드를 사용했다.
BottomNavigationBar의 onTap을 구현해준다. onTap은 선택된 아이템의 index를 반환해준다.
_onTabClick에 index를 넘기고 setState() 함수를 사용하여 _currentTabIndex의 상태를 변경해준다.
이제 여기까지 완성되었을 것이다. 탭을 클릭하면 클릭된 탭으로 이동되는 것을 확인할 수 있다.
2. TodoList 페이지 만들기
class TodoListPage extends StatefulWidget {
@override
State<StatefulWidget> createState() => _TodoListPageState();
}
class _TodoListPageState extends State<TodoListPage> {
@override
Widget build(BuildContext context) {
return _createTodoList();
}
...
}
todo_list.dart 파일을 만들고 위 처럼 기본적인 StatefulWidget의 뼈대를 만든다.
추후에 목록이 추가되고 삭제되고 변경되면서 상태가 변경되는 Page이기 때문에 StatefulWidget으로 작성한다.
build함수에선 _createTodoList() 함수를 호출하여 화면을 그리도록 할 것이다.
class _TodoListPageState extends State<TodoListPage> {
@override
Widget build(BuildContext context) {
return _createTodoList();
}
Widget _createTodoList() {
return ListView.separated(
itemCount: 10,
itemBuilder: (BuildContext context, int index) {
return _createTodoCard();
},
separatorBuilder: (BuildContext context, int index) {
return Divider(
thickness: 8.0,
height: 8.0,
color: Colors.transparent,
);
},
);
}
...
}
_createTodoList 함수를 구현한다. ListView는 ListView.separated를 사용하여 생성한다.
현재 아이템은 없으므로 임의로 10을 준다.
그리고 itemBuilder를 구현하는데, _crateTodoCard() 함수를 호출하도록 한다.
separatorBuilder는 간단하게 8.0px의 Divider를 갖도록 구현하였다.
class _TodoListPageState extends State<TodoListPage> {
@override
Widget build(BuildContext context) {
return _createTodoList();
}
...
Widget _createTodoCard() {
return Card(
elevation: 4.0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(8.0))),
child: Container(
padding: EdgeInsets.all(16.0),
child: _createTodoItemRow()
),
);
}
...
}
createTodoCard 함수를 구현한다.
Card Widget을 사용하여 귀퉁이가 약간 둥근 형태의 카드를 만든다.
child로 16px의 패딩을 갖는 Container를 가진다
내부 Container는 _createTodoItemRow를 호출하여 child를 생성한다.
class _TodoListPageState extends State<TodoListPage> {
@override
Widget build(BuildContext context) {
return _createTodoList();
}
...
Widget _createTodoItemRow() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_createTodoItemContentWidget(),
Icon(Icons.keyboard_arrow_right, color: Colors.blue)
],
);
}
...
}
_createTodoItemRow 함수를 구현한다.
Row를 사용하여 좌측엔 _createTodoItemContentWidget을, 우측엔 오른쪽 화살표 모양의 아이콘을 갖는 Row를 반환한다.
class _TodoListPageState extends State<TodoListPage> {
@override
Widget build(BuildContext context) {
return _createTodoList();
}
...
Widget _createTodoItemContentWidget() {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Todo Item Title",
style: TextStyle(fontSize: 24.0, color: Colors.blue)),
Divider(
thickness: 8.0,
height: 8.0,
color: Colors.transparent,
),
Text("2021.01.18",
style: TextStyle(fontSize: 18.0, color: Colors.blueGrey))
],
);
}
}
_createTodoItemContentWidget 함수를 구현한다.
Column을 사용하여 구현한다. 위는 Todo Item의 Title을, 아래에는 Todo Item의 생성 날짜를 나타내는 Text로 구성한다.
여기까지 잘 따라왔다면 위와 같은 TodoList가 보여질 것이다.
현재까지의 코드는 아래 Github에 저장되어 있다.
Github : github.com/DuItDDu/Flutter-Codelabs/tree/master/Flutter-TodoList/flutter_todolist
아직은 UI만 존재할 뿐이다.
이를 바탕으로 정말 TodoItem을 입력하여 추가하고 상태를 변경해보자.
더 나아가서 SQLite를 연동하여 데이터베이스 처리까지 할 것이며, Flutter에서 권장하는 아키텍처 패턴도 다뤄 볼 것이다.
'개발 > Flutter' 카테고리의 다른 글
Flutter - 10. TodoList App (3) Database를 연동해보자 (SQLite, sqflite) (0) | 2021.01.26 |
---|---|
Flutter - 9. TodoList App (2) Todo Item 추가하기 (AlertDialog, FloatingActionButton) (4) | 2021.01.25 |
Flutter - 7. Widget? Scaffold(AppBar, BottomNavigationBar, FloatingActionButton, Drawer) (0) | 2021.01.17 |
Flutter - 6. Widget? GridView (1) | 2021.01.13 |
Flutter - 5. Widget? ListView (0) | 2021.01.12 |