토이 프로젝트를 시작한지 한달이 좀 넘었다.

라이버리의 버프를 받으며 이것저것 붙혀가며 앱을 구현하고 있지만

여럿 라이브러리를 적용해보고 또 나에게 맞게 코드를 커스텀 해보기도 하며

점점 완성시키고 있다.

한 70%? 정도 구현 됐다고 생각한다.

내년 2월까지는 꼭 앱 스토어에 출시까지 해보고싶다.

 

더불어 12월 중순부터는 GDG 송도 플러터 사이드프로젝트에 참여하게 되었다.
혼자 프로젝트를 진행할떄 정말 답답했는데 정말 잘됐다.

큰 성장이 기대된다.

 

 

토이프로젝트를 시작한지 벌써 몇주가 지났다.

생각만큼 진도가 나가진않아 답답할 뿐이다.

물어볼 사람도 없고 구글링에 한계가 있는거 같다.

마음맞는 사람을 만나 같이 팀을 꾸려서 완성시키고싶다.

지금까지 구현한 앱의 동작이다.

구현할게 산더미인데

우선적으로 내 머릴 제일 아프게하는것은

future/await이다.

해당 기능에 대해 대략적은 로직은 이해갔지만

막상 내 코드에 적용시키면 안된다....ㅜㅜ

구글링에 한계가 있다....

우선 다른것 부터 구현 중이긴 하다.

맥북 에어 m2가 왔다.....

첫 MAC OS인 만큼 적응이 너무 어렵다...

받자마자 뭐부터 만져야 할지도 몰랐다..

 

편하게 바꾸기보단 OS 그 자체를 느껴보고자

순정 상태로 사용해보려고 한다.

 

Flutter 설치를 끝내고 Ios 애뮬을 돌리는데 너무 심장이 떨렸다...

이렇게 빠르다니....

 

맥북은 천천히 익히도록 하고 밀린 Flutter 부터 학습해보자

이번에는 토이프로젝트를 조금씩 완성해보려고한다.

무엇을 할지 모른다면 역시 생각나는 작은것 부터 처리하는게 좋은거같다.

Splash 화면이다. 앱을 처음에 실행하면 나오는 로딩 페이지라고 생각하면 된다.

앱 로고가 아직 없기에 아이폰에 있는 내 이모지를 활용했다.

 

일단 첫번째로 기본 프로젝트를 만들어주자

 

https://pub.dev/packages/flutter_native_splash

위 페이지의 라이브러리를 이용하오니 설치 해주자.

pub get을 해주고

프로젝트 root 디렉토리에 flutter_native_splash.yaml 파일을 만들어주자

 

 

 

 

 

 

 

안에 들어갈 코드는 위 페이지에 나와있다.

코드중 내가 수정한 부분은 단 두줄이다.

백그라운드 컬러 및 로고 이미지 경로

기기의 모드(다크/라이트)에 따라 불러오는 로고를 다르게 설정할 수 있다. 이건 나중에 하도록 하겠다.

 

 

 

 

다음은 main.dart를 수정해보자

import 'package:flutter_native_splash/flutter_native_splash.dart';

를 import 해준뒤

메인 함수에 두줄을 추가한다.

해당 코드를 아래 위젯에 추가해준다.

해당 splash가 3초간 지속되고 첫 페이지로 들어가게 된다.

 

 

 

 

 

 

 

 

 

 

 

 

이로써 Splash 이미지는 구현 완료이다.

참 간단하지만 뭔가 있어보이는? 그런 라이브러리인거같다.

 

다음으로는 get을 이용해 하단 네비게이터바를 구현하여 다중 페이지를 구현하도록할것이다.

 

Getx를 배우고 있는데 노트북이 너무 버거워한다...

조만간 맥북으로 바꾸는것을 목표로 하고 잠시동안

가벼운 프로젝트만 돌리려고한다.

 

이번에는 앱 첫시작에 꼭 들어가는 Onboarding Page를 만들어보려고한다.

앱의 사용설명서 같은것이다.

역시나 외부 라이브러리를 이용할 예정이다.

라이브러리를 설치하는것은 여럿 안내를 했으니 스킵하도록 하겠다.

 

설치가 다됬으면 페이지에 쓰일 이미지를 준비해주자

이미지또한 yaml 파일에서 assets 시켜주자.

 

간단한 트리를 말하자면

OnboardingPage -> main page

이며 역으로 다시 이동도 가능하다.

 

코드와 같이 앱을 실행하면 온보딩 페이지가 뿌려지게

STL 위젯을 만들어준다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

OnBoardingPage이다.

위 라이브러리를 설치하게되면

IntroductionScreen();

을 불러올 수 있다.

 

 

 

 

 

기본적인 틀이다.

pages는 페이지의 리스트이다.

각 페이지를 구현하는 코드는 PageViewModel이며

한개당 하나의 페이지를 그린다.

나는 위젯의 관리를 위해 body 대신 bodywidget을 사용했다.

사진에는 안나와있지만

image: 도 들어가야한다.

 

 

 

 

 

 

decoration 부분은 여럿페이지에 한번에 적용될것이니 따로 분리시켜줬다.

위와같이 PageViewModel 코드를 여러개 작성하면 그만큼 페이지가 나타날것이다.

구성이 완료되었다면.

이 처럼 데코레이션 코드를 작성해주자.

 

 

 

 

 

 

 

 

 

 

 

모든 페이지를 봤다면. 이제 앱의 메인페이지로 진입할때 사용된다. 마지막 페이지에서 보여진다.
다음 페이지로 넘어가기 위한 버튼에 대한 설정이다. 마지막을 제외한 페이지에서 보여진다.
모든 페이지를 보지않고 Skip하는 경우가 많다. 나 역시 그렇다. 이럴때 사용되며 누르면 마지막 페이지로 넘어간다.
페이지 구분 점에 대한 설정 코드들이다. 하나하나 만저보면 어떤 기능인지 감이 올것이다.

추가로...

해당 코드는 페이지 넘김 에니메이션 효과들이다. 

 

 

마지막으로 Main Page에 대한 코드이다.

코드중 pushReplacement를 push 대신 사용한 이유는 죄측상단에 뒤로가기버튼을 안생기게 하기 위함이다.

 

 

오늘 배워볼 부분은 앱의 기초인 페이지 만들기이다.

그동안 main 페이지 하나만 만들어 그안에서만 기능을 구현하였다..

이번엔 여럿 페이지를 만들어 페이지간 이동을 해보자

 

페이지간 이동은 기본적으로 스택을 기본으로한다.

페이지 위에 다른페이지를 올려 화면에 띄우는 방식이다.

해당 부분은 유튜브에 영상이 많으니 한번 이해하고 오는것이 좋을꺼같다.

 

페이지는 screenA, B, C 총 3가지 이며 한 페이지에 다른 두개의 페이지에 넘어갈수 있도록 구현해볼것이다.

프로젝트/lib/  경로에 screenA, B, C 3가지 파일을 새롭게 만들어보자

 

main.dart 에는 위처럼 screenA를 홈으로 지정해준다.

이제 A,B,C 각 페이지 코드는 정말 비슷하므로 screenA를 예로 들겠다.

- appbar

- body

  - center

    - colum

      - ElevatedButton

      - ElevatedButton

이런 구조로 되어있다.

여기서 한가지 알아야할점이 있다면 좀 오래된 강의를 보면 처음보는 버튼 이름이 나와있을것이다.

위 ElevatedButton도 이전에는 RaisedButton였다.

아래 그림을 참고하자

출처 : https://dkswnkk.tistory.com/50#recentComments

Navigator  push코드를 보자

push - 화면 최상단에 띄우는것이다. (스택 맨위)

뒤쪽 MaterialPageRoute는 안드로이드에서 제공하는 에니메이션?? 이라고한다.

그래서 CupertinoPageRoute로 변경하여 실행해보면 화면 넘김 애니메이션이 좀 다르다.

pop - 화면을 지우는것이다.

예를 들어 A -> B -> A 의 경우 push를 2번( A -> B , B -> A) 이렇게 사용할수 있지만 이런경우 스택이 총 3개 쌓인다.
push 1회  pop 1회 이렇게 구성하면 B 화면을 push했다 pop 하기 때문에 스택이 1개 만 쌓이게 되는것이다.

Navigator 코드 첫번째 context는 왼쪽에 보이는 코드 위쪽 나와있는 context와 일치해야하며

두변째 context는 상관없다 따라서 _ 를 해도좋다.

 

 

 

 

앱을 구동해보면 앱바 왼쪽에 뒤로가기 버튼이 생기는것을 볼 수 있다.

이게 pop역활을 하는것이다.

따라서 앱바가 있는경우 pop을 구현할 필요는 없다..

 

이제 다중 페이지를 쉽게 구현하기 위해 initialRoute를 사용하여 구현해보자

main.dart의 코드이다.

home: 대신 initinalRoute를 사용했고

첫 페이지는 / 를 사용해야한다.

 

initinalRoute: '/'

/ 페이지를 home으로 지정하겠다. 이다.

 

아래 routes 는 앱에서 사용될 페이지들을

지정해주는 코드이다.

A, B, C 차례대로 /, /b, /c  이렇게

이름으로 지정해줬다.

 

 

 

 

 

 

 

 

 

 

Navigator.pushNamed
를 이용하여 작성하면 된다.

이번 시간에는 버튼을 학습했다.

단순한 버튼은 아니고 클릭하면 메시지가 뜨게 하는 버튼이다.

Material은 기본적으로 snackbar를 제공하여 메시지를 띄울수있지만 안이쁘다...

난 이쁜걸 원한다...

바로 Toast라는 것이다.

먼저 설치 방법부터 알아보자

1. pubspec.yaml 수정

프로젝트 파일중 pubspec.yaml에 들어가

fluttertoast: ^8.0.9를 넣어준다.

그 다음 위쪽 Pub get을 눌러 load 해준다.

 

2, main.dart 수정

import 'package:fluttertoast/fluttertoast.dart';

맨 윗줄에 해당 문구 삽입

 

이렇게 진행 하면 사용준비는 완료이다.

 

이번 시간에 만들 예제이다.

appbar - 버튼 3개 텍스트 1개

body - 텍스트 1개 버튼2개

로 구성되어있다.

 

appbar에 있는 버튼은 Toast 기본 설정으로 띄울것이며

body에 있는 Custom Button은 Toast-Custom을 이용

Success Button는 motion-toast를 사용할것이다.

*motion-toast는 별도의 설정이 필요하다... 검색 ㄱㄱ

 

appBar: AppBar(
  title: Center(child: Text("appBar & Toast Test")),
  elevation: 0.0,
  leading: IconButton(
      icon: Icon(Icons.menu),
      onPressed: () {
        showToast('이것은 메뉴입니다.');
      }),
  actions: [
    IconButton(
      icon: Icon(Icons.shopping_cart),
      onPressed: () {
        showToast('이것은 장바구니입니다.');
      },
    ),
    IconButton(
      icon: Icon(Icons.search_rounded),
      onPressed: () {
        showToast('이것은 검색입니다.');
      },
    ),
  ],
),

 

leading - appbar 맨 왼쪽에 자리잡는 위젯이다.

action - appbar 맨 오른쪽에 자리잡는 위젯이다.

 

body: Container(
  height: double.infinity,
  width: double.infinity,
  child: Column(
    mainAxisAlignment: MainAxisAlignment.center,
    crossAxisAlignment: CrossAxisAlignment.center,
    children: [
      const Text("Toast 메시지 예제", style: TextStyle(fontSize: 20,),
      ),
      const SizedBox(height: 10,),
      Container(width: 200, height: 50,
        child: ElevatedButton(
          onPressed: () {_showToast();},
          child: const Text("Custom Button", style: TextStyle(fontSize: 20,),),
        ),
      ),
      const SizedBox(height: 10,),
      Container(width: 200, height: 50,
        child: ElevatedButton(
          onPressed: () {_displaySuccessToast(context);},
          child: const Text("Success Toast", style: TextStyle(fontSize: 20,),),
        ),
      ),
height: double.infinity,
width: double.infinity,

mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,

이 두가지는 항상 쓰려고한다. 왜냐하면 위젯들이 센터로 자리잡을 수 있게 한다.

중간중간 SizeBox를 통해 위젯마다 공간을 주었다.

 

이제 Toast 부분을 살펴보자

 

void showToast(String msg2) {
  Fluttertoast.showToast(
    msg: msg2,
    toastLength: Toast.LENGTH_SHORT,
    gravity: ToastGravity.BOTTOM,
    backgroundColor: Colors.red,
    textColor: Colors.white,
  );
}
onPressed: () {
  showToast('이것은 장바구니입니다.');
},

이것은 appbar에 들어간 Toast 기본 버튼이다.

Fluttertoast.showToast를 이용하면 쉽게 구현가능하다.

두번째 코드는 호출 코드이다.

코드와 같이 String을 넣어줄수있다.

 

late FToast fToast;

@override
void initState() {
  super.initState();
  fToast = FToast();
  fToast.init(context);
}
_showToast() {
  Widget toast = Container(
    padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
    decoration: BoxDecoration(
      borderRadius: BorderRadius.circular(25.0),
      color: Colors.redAccent,
    ),
    child: Row(
      mainAxisSize: MainAxisSize.min,
      children: const [
        Icon(Icons.check, color: Colors.white,),
        SizedBox(
          width: 10.0,
        ),
        Text('이것은 커스텀 버튼입니다.', style: TextStyle(color: Colors.white,),),
      ],
    ),
  );

  fToast.showToast(
    child: toast,
    gravity: ToastGravity.BOTTOM,
    toastDuration: Duration(seconds: 1),
  );
}
onPressed: () {
_showToast();
},

Toast를 커스텀 할 수 있는 코드이다.

기본으로 해도 충분히 깔끔하게 나오지만 좀 더 아쉬우면 해당 기능으로 구현해보자.

여럿 값을 만져주면서 어떤 부분이 어떻게 변경되는지 쉽게 확인가능하다.

 

import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:motion_toast/motion_toast.dart';

void main() {
  runApp(const appBar());
}

class appBar extends StatelessWidget {
  const appBar({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'appBar & Toast',
      theme: ThemeData(primarySwatch: Colors.lightGreen),
      home: firstpage(),
    );
  }
}

class firstpage extends StatefulWidget {
  const firstpage({Key? key}) : super(key: key);

  @override
  State<firstpage> createState() => _firstpageState();
}

class _firstpageState extends State<firstpage> {

  late FToast fToast;

  @override
  void initState() {
    super.initState();
    fToast = FToast();
    fToast.init(context);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: const Color(0xFF69abce),
        appBar: AppBar(
          title: Center(child: Text("appBar & Toast Test")),
          elevation: 0.0,
          leading: IconButton(
              icon: Icon(Icons.menu),
              onPressed: () {
                showToast('이것은 메뉴입니다.');
              }),
          actions: [
            IconButton(
              icon: Icon(Icons.shopping_cart),
              onPressed: () {
                showToast('이것은 장바구니입니다.');
              },
            ),
            IconButton(
              icon: Icon(Icons.search_rounded),
              onPressed: () {
                showToast('이것은 검색입니다.');
              },
            ),
          ],
        ),
        body: Container(
          height: double.infinity,
          width: double.infinity,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              const Text("Toast 메시지 예제", style: TextStyle(fontSize: 20,),
              ),
              const SizedBox(height: 10,),
              Container(width: 200, height: 50,
                child: ElevatedButton(
                  onPressed: () {_showToast();},
                  child: const Text("Custom Button", style: TextStyle(fontSize: 20,),),
                ),
              ),
              const SizedBox(height: 10,),
              Container(width: 200, height: 50,
                child: ElevatedButton(
                  onPressed: () {_displaySuccessToast(context);},
                  child: const Text("Success Toast", style: TextStyle(fontSize: 20,),),
                ),
              ),
            ],
          ),
        ));
  }

  void showToast(String msg2) {
    Fluttertoast.showToast(
      msg: msg2,
      toastLength: Toast.LENGTH_SHORT,
      gravity: ToastGravity.BOTTOM,
      backgroundColor: Colors.red,
      textColor: Colors.white,
    );
  }

  void _displaySuccessToast(context) {
    MotionToast.success(
      title: Text("Success"),
      description: Text("This is Success Toast"),
    ).show(context);
  }

  _showToast() {
    Widget toast = Container(
      padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(25.0),
        color: Colors.redAccent,
      ),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: const [
          Icon(Icons.check, color: Colors.white,),
          SizedBox(
            width: 10.0,
          ),
          Text('이것은 커스텀 버튼입니다.', style: TextStyle(color: Colors.white,),),
        ],
      ),
    );

    fToast.showToast(
      child: toast,
      gravity: ToastGravity.BOTTOM,
      toastDuration: Duration(seconds: 1),
    );
  }
}

 

+ Recent posts