Flutter (20) Calling UI

Huisu·2023년 3월 11일
0

Flutter

목록 보기
20/21
post-thumbnail
post-custom-banner

FirstOnboarding

  • 앱을 처음 시작했을 때 보이는 온보딩 페이지의 디자인은 다음과 같다
  • 먼저 새로운 플러터 프로젝트를 시작한다
  • 첫 페이지의 회원가입 부분을 FirstOnboardind 페이지로 지정하고 이를 stateless widget으로 만든다
  • scaffold의 body를 container로 반환하고 그 안에 세로인 column으로 위젯들을 엮어서 리턴
  • Text 위젯으로 리플로우 로고 띄우기
  • Icon 위젯으로 회원가입 전에 보이는 사용자 프로필 그리기
  • Textbutton 위젯으로 회원가입 버튼 만들기
    • onpressed 안에는 버튼을 눌렀을 때 어떤 행동을 취할 것인지 써야 하는데, 이는 추후에 다른 페이지를 만들고 네비게이터 추가
  • 구현 화면
  • firstonboarding.dart
    import 'package:callingapp/onboarding/second.dart';
    import 'package:flutter/material.dart';
    
    class FirstOnboarding extends StatelessWidget {
      const FirstOnboarding({Key? key}) : super(key: key);
    
      
      Widget build(BuildContext context) {
        return Scaffold(
          body: Container(
            alignment: Alignment.center,
            padding: EdgeInsets.symmetric(vertical: 100),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  "LIFLOW",
                  style: TextStyle(
                    fontWeight: FontWeight.w900,
                    fontSize: 50,
                    color: Colors.indigo
                  ),
                ),
                Text("\n\n"),
                Icon(Icons.person, size: 150,),
                Text("\n\n"),
                TextButton(
                    onPressed: () {
                      Navigator.push(context, MaterialPageRoute(builder: (context) => SecondOnboarding()));
                    },
                    child: Text(
                      "회원가입",
                      style: TextStyle(
                        fontWeight: FontWeight.w500,
                        fontSize: 25,
                        color: Colors.blue
                      ),
                    ),)
              ],
            ),
          ),
        );
      }
    }
  • 의문점 회원가입 버튼만 있고 왜 로그인 버튼 없는지 회원가입 후에 무슨 정보를 입력하고 어떤 회원가입을 진행하는지

SecondOnboarding

  • 리플로우의 말벗 찾기 기능으로 옮겨질 메뉴판들이 보이는 페이지를 구현하기 위해 새로운 페이지 생성

  • 첫 번째 온보딩 페이지에서 회원가입 누르면 해당 페이지로 이동할 수 있도록 네비게이터 생성하기

  • Container에 Column으로 엮어서 위젯 그리기

    • 현재 모든 버튼을 누르면 (onPressed) SecondOnboarding 페이지로 이동하게 되어 있지만 추후에 페이지를 추가로 구성하면 버튼에 맞는 페이지로 이동할 수 있도록 수정
  • secondonboarding.dart

    import 'package:flutter/material.dart';
    
    class SecondOnboarding extends StatefulWidget {
      const SecondOnboarding({Key? key}) : super(key: key);
    
      
      State<SecondOnboarding> createState() => _SecondOnboardingState();
    }
    
    class _SecondOnboardingState extends State<SecondOnboarding> {
      
      Widget build(BuildContext context) {
        return Scaffold(
          body: Container(
            alignment: Alignment.center,
            padding: EdgeInsets.symmetric(vertical: 100),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.start,
              children: <Widget>[
                Text(
                  "LIFLOW",
                  style: TextStyle(
                      fontWeight: FontWeight.w900,
                      fontSize: 50,
                      color: Colors.indigo
                  ),
                ),
                Text("\n\n"),
                Container(
                  width: 350,
                  height: 200,
                  margin: const EdgeInsets.all(10.0),
                  padding: const EdgeInsets.all(10.0),
                  decoration: BoxDecoration(
                    border: Border.all(
                      width: 3,
                      color: Colors.indigo,
                    ),
                    borderRadius: BorderRadius.circular(10.0),
                  ),
                  child: TextButton(
                    onPressed: () {
                      Navigator.push(context, MaterialPageRoute(builder: (context) => SecondOnboarding()));
                    },
                    child: Text(
                      "말벗 찾기",
                      style: TextStyle(
                          fontWeight: FontWeight.w600,
                          fontSize: 25,
                          color: Colors.indigo
                      ),
                    ),
                  ),
                ),
                Container(
                  width: 350,
                  height: 100,
                  alignment: Alignment.center,
                  margin: const EdgeInsets.all(10.0),
                  padding: const EdgeInsets.all(10.0),
                  decoration: BoxDecoration(
                    border: Border.all(
                      width: 3,
                      color: Colors.indigo,
                    ),
                    borderRadius: BorderRadius.circular(10.0),
                  ),
                  child: TextButton(
                    onPressed: () {
                      Navigator.push(context, MaterialPageRoute(builder: (context) => SecondOnboarding()));
                    },
                    child: Text(
                      "즐겨찾기",
                      style: TextStyle(
                          fontWeight: FontWeight.w600,
                          fontSize: 25,
                          color: Colors.indigo
                      ),
                    ),
                  ),
                ),
                Container(
                  width: 350,
                  height: 100,
                  alignment: Alignment.center,
                  margin: const EdgeInsets.all(10.0),
                  padding: const EdgeInsets.all(10.0),
                  decoration: BoxDecoration(
                    border: Border.all(
                      width: 3,
                      color: Colors.indigo,
                    ),
                    borderRadius: BorderRadius.circular(10.0),
                  ),
                  child: TextButton(
                    onPressed: () {
                      Navigator.push(context, MaterialPageRoute(builder: (context) => SecondOnboarding()));
                    },
                    child: Text(
                      "통계",
                      style: TextStyle(
                          fontWeight: FontWeight.w600,
                          fontSize: 25,
                          color: Colors.indigo
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
  • 구현 화면

    • 의문점 카드 크기를 통일하는 게 낫지 않나?

FindingPage

  • 말벗 찾기 기능을 이용하기 위해 finding page 생성

  • 키워드를 받아서 container에 실어 카드 형태로 반환하는 위젯 생성하기

    • 해당 그림에 나타나는 부분
    • String 형태의 keyword parameter를 받아서 해당 키워드를 텍스트로 써서 반환하는 keywordcard 위젯 생성
    • width, length, padding, alignment 를 통해 카드 사이즈와 정렬을 재구성
    • border의 박스 데코레이션을 통해 둥글둥글한 형태로 반환
    • 추후에 onpressed 버튼을 누르면 버튼 모양이 바뀌도록 코드 수정 예정
  • 키워드 카드를 row로 두 개 엮은 행을 column으로 엮어서 격자 모양으로 보일 수 있도록 그리기

  • findingpage.dart

    import 'package:flutter/material.dart';
    
    class FindingPage extends StatefulWidget {
      const FindingPage({Key? key}) : super(key: key);
    
      
      State<FindingPage> createState() => _FindingPageState();
    }
    
    class _FindingPageState extends State<FindingPage> {
      
      Widget build(BuildContext context) {
        return Scaffold(
          body: Container(
            alignment: Alignment.center,
            padding: EdgeInsets.symmetric(vertical: 100),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.start,
              children: <Widget>[
                Text(
                  "오늘의 관심사 (중복 가능)                    ",
                  style: TextStyle(
                      fontWeight: FontWeight.w700,
                      fontSize: 23,
                      color: Colors.black
                  ),
                ),
                Column(
                  children: <Widget>[
                    Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: <Widget>[
                        keywordcard(keyword: '경제',),
                        keywordcard(keyword: '건강',),
                      ],
                    ),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: <Widget>[
                        keywordcard(keyword: '문화',),
                        keywordcard(keyword: '여가',),
                      ],
                    ),
                    keywordcard(keyword: '종교')
                  ],
                ),
                Text('\n'),
                Text(
                  "오늘의 관심사 (중복 가능)                    ",
                  style: TextStyle(
                      fontWeight: FontWeight.w700,
                      fontSize: 23,
                      color: Colors.black
                  ),
                ),
                Column(
                  children: <Widget>[
                    Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: <Widget>[
                        keywordcard(keyword: '조용한',),
                        keywordcard(keyword: '외향적인',),
                      ],
                    ),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: <Widget>[
                        keywordcard(keyword: '밝은',),
                        keywordcard(keyword: '재미있는',),
                      ],
                    ),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: <Widget>[
                        keywordcard(keyword: '차분한',),
                        keywordcard(keyword: '믿음직한',),
                      ],
                    ),
                  ],
                ),
                Container(
                  width: 185,
                  height: 65,
                  alignment: Alignment.center,
                  margin: const EdgeInsets.all(3.0),
                  padding: const EdgeInsets.all(8.0),
                  decoration: BoxDecoration(
                    color: Colors.indigo,
                    borderRadius: BorderRadius.circular(10.0),
                  ),
                  child: TextButton(
                    onPressed: () {
                    },
                    child: Text(
                      "등록 완료하기",
                      style: TextStyle(
                          fontWeight: FontWeight.w600,
                          fontSize: 20,
                          color: Colors.white
                      ),
                    ),
                  ),
                )
              ],
            ),
          ),
        );
      }
    }
    
    class keywordcard extends StatelessWidget {
      final String keyword;
    
      const keywordcard({
        Key? key,
        required this.keyword,
      }) : super(key: key);
    
      
      Widget build(BuildContext context) {
        return Container(
          width: 185,
          height: 67,
          alignment: Alignment.center,
          margin: const EdgeInsets.all(2.0),
          padding: const EdgeInsets.all(10.0),
          decoration: BoxDecoration(
            border: Border.all(
              width: 3,
              color: Colors.indigo,
            ),
            borderRadius: BorderRadius.circular(30.0),
          ),
          child: TextButton(
            onPressed: () {
            },
            child: Text(
              keyword,
              style: TextStyle(
                  fontWeight: FontWeight.w600,
                  fontSize: 20,
                  color: Colors.indigo
              ),
            ),
          ),
        );
      }
    }
  • 의문점

    두 번째 카테고리에 오늘의 관심사보다 오늘의 기분이 더 맞지 않을지!!

Recommand Page

  • 키워드에 맞는 말벗 추천해 주는 페이지

  • 추천으로 넘어가기 전에 waiting page 구현을 위해 파일 생성

    • 아래 그림과 같은 페이지 구현
  • findingpage에서 등록 완료하기 버튼을 눌렀을 때 waitingpage로 이동할 수 있도록 네비게이터 생성

  • 안내문을 줄 단위로 작성할 위젯 생성하기

  • 해당 위젯을 column으로 엮고 container에 border decoration 설정하여 구분선 그리기

  • waiting.dart

    import 'package:flutter/material.dart';
    
    class WaitingPage extends StatelessWidget {
      const WaitingPage({Key? key}) : super(key: key);
    
      
      Widget build(BuildContext context) {
        return Scaffold(
          body:
            Container(
              padding: const EdgeInsets.only(left: 50, right: 50, top: 100, bottom: 100),
              child: Container(
                alignment: Alignment.center,
                decoration: BoxDecoration(
                  border: Border.all(
                    width: 3,
                    color: Colors.indigo,
                  ),
                  borderRadius: BorderRadius.circular(30.0),
                ),
               child: Column(
                 mainAxisAlignment: MainAxisAlignment.center,
                 children: <Widget>[
                   notice(line: '제공자'),
                   notice(line: '전화를 희망하시면'),
                   notice(line: '우측 하단의'),
                   notice(line: '좋아요를'),
                   notice(line: '눌러 주세요'),
                   notice(line: ' '),
                   notice(line: '연결을 위하여'),
                   notice(line: '일정 시간이'),
                   notice(line: '소요될 수 있습니다'),
                   notice(line: ' '),
                   notice(line: '(아무 곳이나 누르세요)'),
                 ],
               ),
              ),
            )
        );
      }
    }
    
    class notice extends StatelessWidget {
      final String line;
      const notice({
        Key? key,
        required this.line
      }) : super(key: key);
    
      
      Widget build(BuildContext context) {
        return TextButton(
          onPressed: () {
          },
          child: Center(
            child: Text(
              line,
              style: TextStyle(
                  fontWeight: FontWeight.w700,
                  fontSize: 20,
                  color: Colors.black,
              ),
            ),
          ),
        );
      }
    }
  • 구현 화면

    • 의문점 아무곳이나 누르는 것보다 다음 단계로 가기를 누르는 게 더 정확한 유아이가 아닌가?
  • 누를 때마다 다음 추천 페이지로 이동할 수 있도록 네비게이터 생성

  • 제공자를 추천하는 ServicePage 생성하기

  • 아래의 페이지 구현

  • 이런 사람은 어때요 부분을 구현하기 위해서 container와 text를 column과 row로 엮어서 나열하기

    • 까만 부분에 프로필 삽입
    • 빨간 부분에 특징 삽입
  • textstyle과 border decoration으로 디테일한 설정값 마치기 (글자 크기, 두께, 박스가 둥글거리는 정도)

  • 프로필에 이미지 사진을 첨가하기 위해 assets 파일 생성

  • 해당 폴더에 파일별로 이미지를 정리

  • pubyaml파일에서 assets에 대한 접근성을 허용한 뒤 pub get

  • 프로필 넣는 container에 image 삽입

  • 이런 사람입니다 페이지 구현을 위해 container 세 개를 엮어 원 모양으로 된 위젯을 row 형태로 반환하기

  • 같은 형식으로 container 세 개를 row로 엮은 위젯을 반환하고 양쪽 끝에는 추천과 비추천 아이콘을 삽입

  • findingpage.dart에서 등록 완료하기 버튼과 동일한 형식으로 textbutton 생성

  • service.dart

    import 'package:flutter/material.dart';
    
    class ServiceOne extends StatefulWidget {
      const ServiceOne({Key? key}) : super(key: key);
    
      
      State<ServiceOne> createState() => _ServiceOneState();
    }
    
    class _ServiceOneState extends State<ServiceOne> {
      
      Widget build(BuildContext context) {
        return Scaffold(
          body: Container(
            alignment: Alignment.center,
            padding: EdgeInsets.symmetric(vertical: 100),
            child: Column(
              children: <Widget>[
                Text("이런 사람은 어때요?",
                  style: TextStyle(
                      fontWeight: FontWeight.w900,
                      fontSize: 25,
                      color: Colors.black
                  ),),
                Container(
                  padding: EdgeInsets.all(20),
                  height: 200,
                  child: Row(
                    children: <Widget>[
                      Container(
                        child: Image.asset('assets/images/profile_two.png'),
                        decoration: BoxDecoration(
                          border: Border.all(
                            width: 5,
                            color: Colors.indigo,
                          ),
                          borderRadius: BorderRadius.circular(50.0),
                        ),
                        width: 160,
                        height: 160,
                      ),
                      Container(
                        margin: const EdgeInsets.all(30),
                        child:
                          Text("23/여\n\n고민 상담\n\n자격증 미보유",
                            style: TextStyle(
                                fontWeight: FontWeight.w900,
                                fontSize: 15,
                                color: Colors.black
                            ),
                          ),
                      )
                    ],
                  ),
                ),
                Text("이런 사람입니다!                             ",
                  style: TextStyle(
                      fontWeight: FontWeight.w900,
                      fontSize: 25,
                      color: Colors.black
                  ),),
                Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    Container(
                      alignment: Alignment.center,
                      child: Text("활발한",
                        style: TextStyle(
                            fontWeight: FontWeight.w500,
                            color: Colors.black
                        ),),
                      margin: const EdgeInsets.all(10),
                      width: 100,
                      height: 100,
                      decoration: BoxDecoration(
                        border: Border.all(
                          width: 15,
                          color: Colors.indigo,
                        ),
                        borderRadius: BorderRadius.circular(70.0),
                      ),
                    ),
                    Container(
                      alignment: Alignment.center,
                      child: Text("친절한",
                        style: TextStyle(
                            fontWeight: FontWeight.w500,
                            color: Colors.black
                        ),),
                      margin: const EdgeInsets.all(10),
                      width: 100,
                      height: 100,
                      decoration: BoxDecoration(
                        border: Border.all(
                          width: 15,
                          color: Colors.indigo,
                        ),
                        borderRadius: BorderRadius.circular(70.0),
                      ),
                    ),
                    Container(
                      alignment: Alignment.center,
                      child: Text("따뜻한",
                        style: TextStyle(
                            fontWeight: FontWeight.w500,
                            color: Colors.black
                        ),),
                      margin: const EdgeInsets.all(10),
                      width: 100,
                      height: 100,
                      decoration: BoxDecoration(
                        border: Border.all(
                          width: 15,
                          color: Colors.indigo,
                        ),
                        borderRadius: BorderRadius.circular(70.0),
                      ),
                    )
                  ],
                ),
                Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    Container(
                      alignment: Alignment.center,
                      child: Icon(Icons.thumb_up_alt_rounded, size: 80, color: Colors.indigo,),
                      margin: const EdgeInsets.all(10),
                      width: 100,
                      height: 100,
                    ),
                    Container(
                      alignment: Alignment.center,
                      margin: const EdgeInsets.all(10),
                      width: 100,
                      height: 100,
                    ),
                    Container(
                      alignment: Alignment.center,
                      child: Icon(Icons.thumb_down_alt_rounded, size: 80, color: Colors.indigo,),
                      margin: const EdgeInsets.all(10),
                      width: 100,
                      height: 100,
                    )
                  ],
                ),
                Container(
                  width: 350,
                  height: 65,
                  alignment: Alignment.center,
                  margin: const EdgeInsets.all(3.0),
                  padding: const EdgeInsets.all(8.0),
                  decoration: BoxDecoration(
                    color: Colors.indigo,
                    borderRadius: BorderRadius.circular(10.0),
                  ),
                  child: TextButton(
                    onPressed: () {},
                    child: Text(
                      "추천 중단하기",
                      style: TextStyle(
                          fontWeight: FontWeight.w600,
                          fontSize: 20,
                          color: Colors.white
                      ),
                    ),
                  ),
                )
              ],
            ),
          ),
        );
      }
    }
  • 의문점

    이런 사람은 어때요와 이런 사람입니다 정렬을 굳이 안 맞추는 이유가 뭔지

    사용자에게 엄지 아이콘과 추천 중단하기 중 엄지 아이콘이 전화 걸리는 기능이라고 인식될 가능성

    추천 중단하기는 시스템 종료 느낌이지 않나 싶음

post-custom-banner

0개의 댓글