-
25일차 과제[2] - Todo 활용카테고리 없음 2023. 2. 28. 20:24
1. 다음의 공개된 API를 분석하고, 클래스를 활용하여 적용 후 해야할 일을 보여주는 앱을 다음과 같이 만드시오. https://jsonplaceholder.typicode.com/todos
예제 - 반드시 Todo 클래스를 만들고 Serialization을 진행할 수 있도록 하시오.
- AppBar는 다음의 조건을 따라 만들도록 하시오
- Blur 효과를 넣어 body의 내용이 흐릿하게 보여질 수 있도록 디자인하시오.
- Actions에는 다음의 기능이 포함되어있는 아이콘을 제작하시오
- Filter 아이콘 :
- 클릭시 아래서 필터를 설정할 수 있도록 시트 위젯이 켜진다.
- 필터가 적용되면 화면에 보이는 데이터의 종류가 바뀐다.
- (필터선택시 아래에서 올라오는 안내문구는 선택사항임)
- Refresh 아이콘 :
- 클릭시 네트워크에 데이터를 한 번 더 요청하여 리스트에 재적용한다.
- Filter 아이콘 :
- 각 Post를 보여주는 Widget은 다음의 조건을 따라 만들도록 하시오
- 완료된 상태의 Post라면, 초록색 배경에 체크버튼의 아이콘이 보여지도록 한다.
- Dismissable 위젯을 활용하여 옆으로 슬라이드 했을 때, 리스트에서 사라지도록 한다.
- 추가적으로, Dismissable 위젯의 key 속성이 의미하는 바를 정리하시오.
- 제공되는 소스코드를 활용할 수 있도록 하시오.
- widget/filter_bottom_sheet.dart
- 필터 아이콘 누를 시 하단에 출력되는 위젯입니다.
- enum에 대해 학습을 따로 진행하는 것을 추천드립니다.
import 'package:flutter/material.dart'; enum TodoFilter { all, completed, incompleted } class FilterBottomSheet extends StatefulWidget { const FilterBottomSheet( {Key? key, required this.filter, required this.onApply}) : super(key: key); final TodoFilter filter; final Function(TodoFilter) onApply; @override State<FilterBottomSheet> createState() => _FilterBottomSheetState(); } class _FilterBottomSheetState extends State<FilterBottomSheet> { onApply(TodoFilter filter) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Filter applied: $filter'), ), ); widget.onApply(filter); Navigator.pop(context); } @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(16), child: Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( title: const Text('All'), trailing: Checkbox( value: widget.filter == TodoFilter.all, onChanged: (value) { if (value == true) onApply(TodoFilter.all); }, ), ), ListTile( title: const Text('Completed'), trailing: Checkbox( value: widget.filter == TodoFilter.completed, onChanged: (value) { if (value == true) onApply(TodoFilter.completed); }, ), ), ListTile( title: const Text('InCompleted'), trailing: Checkbox( value: widget.filter == TodoFilter.incompleted, onChanged: (value) { if (value == true) onApply(TodoFilter.incompleted); }, ), ), ], ), ); } }
- widget/todo_card.dart(model 폴더에 todo클래스를 만들어놓을 것)
- widget/filter_bottom_sheet.dart
import 'package:flutter/material.dart'; import '../model/todo.dart'; class TodoCard extends StatelessWidget { const TodoCard({super.key, required this.todo}); final Todo todo; @override Widget build(BuildContext context) { return Dismissible( key: Key(todo.id.toString()), child: Container( margin: const EdgeInsets.all(8), decoration: BoxDecoration( color: todo.completed ? Colors.green.shade100 : null, border: todo.completed ? Border.all( color: Colors.green, ) : null, borderRadius: BorderRadius.circular(8), ), child: ListTile( title: Text( todo.title, style: TextStyle( color: todo.completed ? Colors.green : null, fontWeight: FontWeight.bold, ), ), trailing: todo.completed ? const Icon( Icons.check_circle, color: Colors.green, ) : null, ), ), ); } }
main.dart
import 'package:assigment25second/mainpage.dart'; import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: MainPage(), ); } }
mainpage.dart
import 'dart:ui'; import 'package:assigment25second/model/todo.dart'; import 'package:assigment25second/widget/filter_bottom_sheet.dart'; import 'package:assigment25second/widget/todo_card.dart'; import 'package:flutter/material.dart'; import 'package:dio/dio.dart'; class MainPage extends StatefulWidget { const MainPage({super.key}); @override State<MainPage> createState() => _MainPageState(); } class _MainPageState extends State<MainPage> { List<Todo> todos = []; TodoFilter todoFilter = TodoFilter.all; readTodos() async { var dio = Dio(); var url = 'https://jsonplaceholder.typicode.com/todos'; var res = await dio.get(url); if (res.statusCode == 200) { var data = List<Map<String, dynamic>>.from(res.data); setState(() => todos = data.map((e) => Todo.fromMap(e)).toList()); } } @override void initState() { super.initState(); readTodos(); } @override Widget build(BuildContext context) { return Scaffold( extendBodyBehindAppBar: true, appBar: AppBar( backgroundColor: Colors.transparent, foregroundColor: Colors.black, elevation: 0, flexibleSpace: ClipRRect( child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), child: Container(), ), ), title: Text('Todo App'), actions: [ IconButton( onPressed: (() { showModalBottomSheet( context: context, builder: ((context) => FilterBottomSheet( filter: todoFilter, onApply: ((value) { setState(() => todoFilter = value); })))); }), icon: Icon(Icons.filter_list)), IconButton( onPressed: (() { readTodos(); }), icon: Icon(Icons.refresh)) ]), body: ListView.builder( itemCount: filterMaker(todos).length, itemBuilder: ((context, index) { return TodoCard(todo: filterMaker(todos)[index]); }))); } List<Todo> filterMaker(List<Todo> value) { switch (todoFilter) { case TodoFilter.all: return value; case TodoFilter.completed: return value.where((element) => element.completed == true).toList(); case TodoFilter.incompleted: return value.where((element) => element.completed == false).toList(); } } }
FutureBuild사용해서 풀고있는데 계속 에러나와서 강의보고 따라해봤습니다.. 강의내용은 이해가 되긴했는데 난이도가 있네요.. FutureBulid사용해서 제가 하던대로 다시 풀어보겠습니다.
widget/todo_card.dart와 widget/filter_bottom_sheet.dart은 안건드림! 코드는 이해가는데 만들라하면 못만들거같네요
제가 풀어서 다시 제출하겠습니다
결과