오늘 작성해보자하는 것은 와이어 프레임 작성이다.
예시 코드
var teamList = getTeamLists()
for( team in teamList ) {
var Projects = getProject(team.id);
for(project in projects) {
var todos = getTodos(project.id);
for(todo in todos) {
TodoCustormCumponent(todo);
}
}
}
데이터를 가져오는 알고리즘을 정하였으니 riverpod 을 이용해 어떻게 구현할지 chatGpt 를 통해 알아봤다.
Flutter와 Riverpod를 사용하여 위와 같은 알고리즘을 구현하려면, 다음과 같이 여러 단계로 나누어 생각해볼 수 있습니다. Riverpod를 사용하여 상태 관리를 할 때, 비동기 데이터를 가져오는 과정을 StreamProvider, FutureProvider 또는 StateNotifierProvider를 통해 처리할 수 있습니다. 여기서는 각 팀, 프로젝트, 그리고 할 일(todo) 목록을 가져오는 예제를 간단하게 구현해 보겠습니다.
이 예제에서는 FutureProvider
를 사용합니다. 실제 데이터를 받아오는 부분은 간략화하여 처리했습니다. 실제 프로젝트에서는 서버나 데이터베이스에서 데이터를 받아올 것입니다.
다음은 Riverpod를 사용하여 팀 목록, 프로젝트 목록, 그리고 할 일 목록을 가져오는 간단한 예제 코드입니다:
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(
ProviderScope(child: MyApp()),
);
}
final teamListProvider = FutureProvider<List<Team>>((ref) async {
// 여기서는 가짜 데이터를 생성합니다. 실제로는 서버나 데이터베이스에서 데이터를 가져옵니다.
return [Team(id: 'team1'), Team(id: 'team2')];
});
final projectListProvider = FutureProvider.family<List<Project>, String>((ref, teamId) async {
// teamId를 기반으로 프로젝트 목록을 가져옵니다.
return [Project(id: 'project1'), Project(id: 'project2')]; // 가짜 데이터
});
final todoListProvider = FutureProvider.family<List<Todo>, String>((ref, projectId) async {
// projectId를 기반으로 할 일 목록을 가져옵니다.
return [Todo(), Todo()]; // 가짜 데이터
});
class MyApp extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
// 앱의 기본 구성
return MaterialApp(
home: Scaffold(
body: Center(
// 팀 목록을 표시합니다.
child: ref.watch(teamListProvider).when(
data: (teamList) => ListView.builder(
itemCount: teamList.length,
itemBuilder: (context, index) {
// 각 팀에 대한 정보를 표시합니다.
return TeamWidget(team: teamList[index]);
},
),
loading: () => CircularProgressIndicator(),
error: (error, stack) => Text('Error: $error'),
),
),
),
);
}
}
class TeamWidget extends ConsumerWidget {
final Team team;
TeamWidget({required this.team});
Widget build(BuildContext context, WidgetRef ref) {
// 프로젝트 목록을 가져옵니다.
final projectList = ref.watch(projectListProvider(team.id));
return projectList.when(
data: (projects) => Column(
children: projects.map((project) => ProjectWidget(project: project)).toList(),
),
loading: () => CircularProgressIndicator(),
error: (e, _) => Text('Error loading projects'),
);
}
}
class ProjectWidget extends ConsumerWidget {
final Project project;
ProjectWidget({required this.project});
Widget build(BuildContext context, WidgetRef ref) {
// 할 일 목록을 가져옵니다.
final todoList = ref.watch(todoListProvider(project.id));
return todoList.when(
data: (todos) => Column(
children: todos.map((todo) => TodoCustomComponent(todo)).toList(),
),
loading: () => CircularProgressIndicator(),
error: (e, _) => Text('Error loading todos'),
);
}
}
// 가짜 모델 클래스
class Team {
final String id;
Team({required this.id});
}
class Project {
final String id;
Project({required this.id});
}
class Todo {}
//
정리해보자면
1. 각 팀, 프로젝트, todos 를 가져오는 provider
를 작성한다.
2. 팀, 프로젝트, todos 컴포넌트를 작성하여 Consumer
를 이용해 컴포넌트가 build 할 때 데이터를 호출한다.
그래서 provider 을 이렇게 작성하였다.
import 'package:front/core/assiners/entity.dart/assiner.dart';
import 'package:front/core/const/enum.dart';
import 'package:front/core/project/domain/entities/project_entity.dart';
import 'package:front/core/team/domain/team_entity.dart';
import 'package:front/core/todo/entity/todo_entity.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'provider.g.dart';
@riverpod
Future<List<TeamEntity>> teamList(TeamListRef ref) async {
return [
TeamEntity(
id: 1,
name: 'Team 1',
inviteLink: 'abc',
teamLeader: '홍길동',
createdAt: DateTime.now(),
),
TeamEntity(
id: 2,
name: 'Team 2',
inviteLink: 'def',
teamLeader: '곽철용',
createdAt: DateTime.now(),
)
];
}
@riverpod
Future<List<ProjectEntity>> projectList(ProjectListRef ref, int teamId) async {
return [
ProjectEntity(
name: '프로젝트명',
description: 'description',
managerId: 2,
projectState: ProjectStateEnum.onProgress,
projectId: 1,
),
ProjectEntity(
name: '프로젝트명',
description: 'description',
managerId: 2,
projectState: ProjectStateEnum.before,
projectId: 2,
),
];
}
@riverpod
Future<List<TodoEntity>> todoList(TodoListRef ref, int projectId) async {
return [
TodoEntity(
assigners: [Assiner(name: '홍길동'), Assiner(name: '곽철용')],
description: 'description',
title: '제안서 1차 완성하기',
dueDate: DateTime.now(),
),
TodoEntity(
assigners: [Assiner(name: '배용만'), Assiner(name: '곽주팔')],
description: 'description',
title: '제안서 2차 완성하기',
dueDate: DateTime.now(),
),
];
}
일단 현재 와이어 프레임만 작성하는 중이므로 임시 데이터를 provider 로 전달하는 방식으로 작성하였다.
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:front/app/domain/presentation/todo/component/todo_custom_component.dart';
import 'package:front/app/domain/presentation/todo/provider.dart';
import 'package:front/core/project/domain/entities/project_entity.dart';
import 'package:front/core/team/domain/team_entity.dart';
class TodoScreen extends ConsumerWidget {
const TodoScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
// 앱의 기본 구성
var dateTimeNow = DateTime.now();
return Scaffold(
appBar: AppBar(
title: const Text('Todo List'),
),
body: SingleChildScrollView(
child: Center(
// 팀 목록을 표시합니다.
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'오늘',
style: TextStyle(fontSize: 14),
),
Text(
'${dateTimeNow.year}.${dateTimeNow.month}.${dateTimeNow.day}, (${getKoreanWeekday(dateTimeNow)})',
style: const TextStyle(fontSize: 14),
),
const Divider(),
ref.watch(teamListProvider).when(
data: (teamList) => Column(
children: teamList
.map((team) => TeamWidget(team: team))
.toList(),
),
loading: () => const CircularProgressIndicator(),
error: (error, stack) => Text('Error: $error'))
])),
),
));
}
String getKoreanWeekday(DateTime date) {
var weekdays = <String>['월요일', '화요일', '수요일', '목요일', '금요일', '토요일', '일요일'];
// DateTime의 weekday는 1부터 시작하기 때문에, 인덱스에 맞추기 위해 1을 빼줍니다.
return weekdays[date.weekday - 1];
}
}
class TeamWidget extends ConsumerWidget {
const TeamWidget({super.key, required this.team});
final TeamEntity team;
@override
Widget build(BuildContext context, WidgetRef ref) {
// 프로젝트 목록을 가져옵니다.
var projectList = ref.watch(projectListProvider(team.id));
return Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
team.name,
style: const TextStyle(fontSize: 14),
),
projectList.when(
data: (projects) => Column(
children: projects
.map((project) => Padding(
padding: const EdgeInsets.only(top: 4.0, bottom: 4.0),
child: ProjectWidget(project: project),
))
.toList(),
),
loading: () => const CircularProgressIndicator(),
error: (e, _) => const Text('Error loading projects'),
),
const Divider()
],
);
}
}
class ProjectWidget extends ConsumerWidget {
const ProjectWidget({super.key, required this.project});
final ProjectEntity project;
@override
Widget build(BuildContext context, WidgetRef ref) {
// 할 일 목록을 가져옵니다.
var todoList = ref.watch(todoListProvider(project.projectId));
return Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
project.name,
style: const TextStyle(fontSize: 14),
),
todoList.when(
data: (todos) => Column(
children: todos
.map((todo) => Padding(
padding: const EdgeInsets.only(top: 4.0, bottom: 4.0),
child: TodoCustomComponent(
todo: todo,
),
))
.toList(),
),
loading: () => const CircularProgressIndicator(),
error: (e, _) => const Text('Error loading todos'),
)
]);
}
}
위젯의 경우는 각 컴포넌트들이 build 할 때 데이터를 호출하게 작성하였다.
Flutter Riverpod 을 통해 계층적 구조에서 데이터를 불러오는 것을 살펴 보았다.