[개발글쓰기 5일차] 와이어 프레임 작성하기 - 1

이혁진·2024년 3월 14일
0
post-thumbnail

오늘 작성해보자하는 것은 와이어 프레임 작성이다.

페이지 설계

데이터 가져오는 방식 설계

팀ID 로 TODO 를 가져오는 방식

  1. 사용자의 모든 팀 리스트를 가져온다.
  2. 해당하는 팀의 프로젝트를 가져온다.
  3. 프로젝트의 TODO 를 가져온다.

장점

  • 따로 정렬을 할 필요가 없다.

단점

  • API 요청 횟수가 많아진다.

예시 코드

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 으로 데이터 가져오기

데이터를 가져오는 알고리즘을 정하였으니 riverpod 을 이용해 어떻게 구현할지 chatGpt 를 통해 알아봤다.

GPT

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 작성

그래서 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 을 통해 계층적 구조에서 데이터를 불러오는 것을 살펴 보았다.

profile
앱 개발과 AI, 서비스에 관심이 많은 학생입니다.

0개의 댓글