본문 바로가기
스파르타/Flutter

[Flutter] 샘플 만들기 - (week 1)

by bakcoding_sparta 2023. 4. 5.

Login Page

위젯을 다루면서 예제를 따라서 만들어본다.

예제는 로그인 페이지로 상단에 텍스트를 띄우고 그 안에 공간에서 이미지와 이메일과 비밀번호는 텍스트 입력을 받을 수 있는 요소와 로그인을 진행할 버튼으로 구성되어 있다. 여기서 실제 로그인 기능은 구현하지 않고 보이는 것들만 만들어본다.

 

우선 main.dart 파일의 코드를 살펴본다.

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

  @override
  Widget build(BuildContext context) {
  	// MaterialApp 또한 위젯이다
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      // 화면에 보여지는 부분
      home: Scaffold(
        appBar: AppBar(),
      ),
    );
  }
}

여기서 MaterialApp 또한 하나의 위젯이다. 이 위젯 안에 Scaffold라는 위젯이 들어가 있는 구조로 볼 수 있다.

내부의 home 내용은 앱을 실행했을 때 화면에 보이는 부분으로 내용들을 지워보면 내부의 코드가 어떤 구조였는지 짐작할 수 있다. 

처음. appBar 제거, home 제거

 

Scaffold

비계라는 뜻이다.

비계는 건설현장에서 건물을 지을 때 인부들이 작업하기 위해 설치하는 발판이나 통로를 말한다.

Flutter에서 사용되는 Scaffold도 마찬가지로 앱에 화면을 그리기 위한 틀로 사용하는 위젯으로 볼 수 있다.

 

Scaffold 안에 자녀 위젯을 통해서 화면을 구성한다.

 

Scaffold(
	appBar : widget, // 상단 바
    body : widget, // 화면 중앙 가장 큰 면적
    bottomNavigationBar : widget // 하단 바
    floatingActionButton : widget // 하단 화면 최상위에 떠있는 버튼
),

 

Scaffold 다루기

Scaffold의 속성을 사용해서 화면을 변경시켜 본다.

Text

body 영역에 Text를 넣어본다.

home: Scaffold(
        appBar: AppBar(),
        body: Text("Hello Flutter!"),
      ),

 

* Hot Reload : 앱을 한번 실행시킨 후에 코드를 수정하면 바로바로 반영되는데 이 기능을 Hot Reload라고 부른다.

변경 사항들이 즉각적으로 반영되기 때문에 빠르게 개발을 진행할 수 있게 도와준다.

 

입력한 코드의 "Hello Flutter" 뒤에 콤마를 찍고 ctrl + space를 누르면 Text 위젯에서 사용할 수 있는 속성들의 목록을 볼 수 있다.

 

우선 텍스트를 꾸미기 위해서 style 속성을 사용해 본다.

위젯에서 사용되는 속성이 어떤 타입을 필요로 하는지 확인하고 싶을 때는 해당 코드 위에 마우스를 올리면 볼 수 있다.

style 속성을 다루기 위해서 해당 속성에 필요한 타입이 TextStyle이라는 것을 확인했다.

다시 TextStyle을 입력하고 괄호를 열어보면 사용할 수 있는 속성들의 목록을 볼 수 있다.

여기서 폰트의 크기를 수정하기 위해서 fontSize를 사용해 보는데 마찬가지로 어떤 타입을 필요로 하는지 확인이 필요하다.

fontSize는 실수인 double 타입을 받는다. 일단 실수를 아무거나 넣어서 확인해 본다.

home: Scaffold(
        appBar: AppBar(),
        body: Text(
          "Hello Flutter!",
          style: TextStyle(fontSize: 60),
        ),
      ),

폰트의 크기가 확실히 커진걸 학인할 수 있다.

 

Column

예제에서는 여러 개의 요소들이 세로로 나열이 되어있었다.

이걸 구현하기 위해서 column위젯을 사용하여 Text위젯을 감싸주는 구조를 만들어야 한다.

(가로의 경우 row를 사용하면 된다.)

 

직접 코드를 작성해도 되는 부분이지만 쉽고 빠르게 작업을 진행할 수 있도록 제공해 주는 편의기능은 활용을 잘할수록 생산성을 증가시킬 수 있다.

 

column을 사용하기 위해서 Text 위젯을 클릭하면 왼쪽에 노란색 전구가 뜨는 걸 확인할 수 있다. 이 기능을 Refactor라 부르며 해당 위젯을 다른 위젯으로 감싸거나 추출할 수 있도록 도와주는 기능을 한다.

리팩터를 실행하면 다음과 같이 코드가 변경된다.

home: Scaffold(
        appBar: AppBar(),
        body: Column(
          children: [
            Text(
              "Hello Flutter!",
              style: TextStyle(fontSize: 60),
            ),
          ],
        ),
      ),

 

 

Column 위젯의 children 속성은 중괄호 즉, 배열로 선언할 수 있는데 이는 내부에 요소가 여러 개 들어갈 수 있다는 걸 의미한다.

 

home: Scaffold(
        appBar: AppBar(),
        body: Column(
          children: [
            Text(
              "Hello Flutter!",
              style: TextStyle(fontSize: 60),
            ),
            Text(
              "Hello Flutter!",
              style: TextStyle(fontSize: 60),
            ),
            Text(
              "Hello Flutter!",
              style: TextStyle(fontSize: 60),
            ),
            Text(
              "Hello Flutter!",
              style: TextStyle(fontSize: 60),
            ),
          ],
        ),
      ),

 

TextField

예제의 이메일과 비밀번호를 입력받는 공간이 세로로 나열되어 있는 걸 구현해 본다.

home: Scaffold(
        appBar: AppBar(),
        body: Column(
          children: [
            Text(
              "Hello Flutter!",
              style: TextStyle(fontSize: 28),
            ),
            TextField(),
          ],
        ),
      ),

 

입력이 잘되는 걸 확인할 수 있다. 이제 TextField에 email 입력칸이라는 걸 표시하기 위해야 한다.

TextField 위젯의 속성 중 decoration은 TextField를 꾸밀 수 있다.

 

decoration에 사용되는 타입은 InputDecoration으로 여기서 사용할 수 있는 속성인 labelText를 사용한다.

home: Scaffold(
        appBar: AppBar(),
        body: Column(
          children: [
            Text(
              "Hello Flutter!",
              style: TextStyle(fontSize: 28),
            ),
            TextField(
              decoration: InputDecoration(
                labelText: "email",
              ),
            ),
          ],
        ),
      ),

이제 그 아래에 비밀번호를 추가해 주면 되는데 비밀번호 역시 이메일과 동일한 구조로 만들어 준다. 

비밀번호의 경우 이메일과 달리 보이면 안 되는 값이기 때문에 속성을 추가해서 보이지 않도록 한다.

해당 속성은 TextField의 obscureText를 사용하면 되는데 이 속성은 bool 값을 받는다. 이 기능을 사용할 것이기 때문에 true로 설정해 준다.

home: Scaffold(
        appBar: AppBar(),
        body: Column(
          children: [
            Text(
              "Hello Flutter!",
              style: TextStyle(fontSize: 28),
            ),
            TextField(
              decoration: InputDecoration(
                labelText: "email",
              ),
            ),
            TextField(
              obscureText: true,
              decoration: InputDecoration(
                labelText: "password",
              ),
            ),
          ],
        ),
      ),

 

Button

Flutter에서는 이벤트를 발생시키는 Elevated Button, Text Button, Icon Button 버튼들을 제공한다.

왼에서부터 elevated, text, icon button

 

// 위로 올라와 있는 듯한 버튼
ElevatedButton(
  onPressed: () {},
  child: Text('Elevated Button'),
),

// 텍스트 버튼
TextButton(
  onPressed: () {},
  child: Text('Text Button'),
),

// 아이콘 버튼
IconButton(
  onPressed: () {},
  icon: Icon(Icons.add),
),

여기서 onPressed 속성에 눌렀을 때 발생시킬 이벤트를 설정한다.

onPressed는 함수를 받으며 Dart에서 일반적인 함수의 문법은 

add_func (a, b) {
	return a + b;
}

이런 형태이다.

 

 

예제의 로그인 버튼을 만들기 위해서 Elevated Button을 사용한다.

ElevatedButton(onPressed: onPressed, child: child)

Elevated Button은 필수적으로 onPressed(함수)와 child(위젯)를 필요로 한다.

ElevatedButton(
              onPressed: () {
                print("On Click Login");
              },
              child: Text("Login"),
            ),

print는 콘솔창에 로그를 보여주는 함수로 Login 버튼을 클릭하면 콘솔에서 On Click Login이 출력되는 걸 확인할 수 있다.

 

AppBar

이제 예제처럼 appBar 영역에 텍스트를 넣어주는 작업을 해준다.

appBar에서 AppBar 위젯 안에 아까 만든 텍스트를 옮겨 담아준다. 

appBar: AppBar(
          title: Text(
            "Hello Flutter!",
            style: TextStyle(fontSize: 28),
          ),
        ),

여기서 예제의 경우 iOS를 기준으로 만들어졌는데 iOS는 AppBar의 텍스트가 기본적으로 중앙 정렬이고 안드로이드의 경우 좌측정렬이다. 따라서 동일하게 동작하게 만들기 위해서 정렬하는 코드를 추가해 준다.

appBar: AppBar(
          centerTitle: true,
          title: Text(
            "Hello Flutter!",
            style: TextStyle(fontSize: 28),
          ),
        ),

 

Padding

여백을 주는 위젯이다.

현재 email과 password 텍스트가 왼쪽에 딱 붙어 있는데 여기에 여백을 주어서 공간을 조금 떨어뜨려주도록 한다.

padding: const EdgeInsets.all(8.0),

Scaffold의 body 속성에 위젯들의 배치는 아래의 그림처럼 되어있다. 

여기에 Column위젯을 Padding 위젯으로 감싸주면 여백이 생기게 된다.

Padding의 속성으로 여백을 주는 방식을 정할 수 있다.

// 전방위 모두 동일하게 적용
EdgeInsets.all(8)
// 특정 방위만 적용
EdgeInsets.only(
  left: 8,
  right: 8,
)
// 위아래, 좌우 적용
EdgeInsets.symmetric(
	vertical: 8,
	horizontal: 8,
)

기본으로 설정된 8.0의 값을 조절해 준다.

padding: const EdgeInsets.all(16),

하위 위젯들에 여백이 적용된 것을 확인할 수 있다.

 

Container

이번엔 버튼의 크기를 조절해 본다. ElevatedButton위젯 자체에는 width 속성이 없기 때문에 부모 위젯의 크기를 이용해 조절할 수 있다.

 

Container 위젯을 사용해 ElevatedButton을 감싸준다. 이 Container는 Box형태의 기본적인 위젯이다.

Container(
  width: 200, // 폭
  height: 200, // 높이
  color: Colors.amber, // 박스 색상
  child: Text("I Love Flutter!"), // 자식 위젯
),

넓이, 높이, 색상, 그리고 자식을 속성으로 둘 수 있어 다양하게 활용이 가능하여 많이 사용되는 위젯이다.

 

Container의 부모인 Column의 크기에 최대로 맞추게 되면 자식인 ElevatedButton의 크기도 함께 늘어나게 된다.

 
 
              Container(
                // 부모 위젯이 가지는 width의 최대치
                // double.infinity 무한대
                width: double.infinity,
                child: ElevatedButton(
                  onPressed: () {
                    print("On Click Login");
                  },
                  child: Text("Login"),
                ),
              ),

그리고 TextField와 버튼이 붙어있기 때문에 이를 띄워주기로 한다.

여백을 주기 위해서 Container에 margin 속성을 사용한다.

// EdgeInsets.only(left, right, top, bottom)
margin: EdgeInsets.only(top: 24),

padding과 margin의 여백에는 차이가 있다.

Container를 기준으로 테두리인 border를 기준으로 안쪽이 Padding, 바깥쪽이 margin이다.

 

Image

이제 이미지를 추가해 준다.

이미지는 Image 위젯을 사용해서 불러올 수 있으며 외부에서 이미지를 가져올 수 있다.

Image.network("URL"),

Image.network를 사용해서 URL로 된 이미지를 보여줄 수 있다.

이렇게 하면 원본 이미지의 크기가 들어가게 되는데 TextField의 값을 입력하기 위해서 누르게 되면 가상 키보드가 활성화되는데 이러면 되면 위젯들의 크기가 넘쳐버리는 오버플로우 문제가 생긴다.

이 문제를 해결하기 위해서는 가상키보드가 활성화될 때 화면이 스크롤이 되도록 변경해야 한다.

이러한 문제는 사용자의 입력을 받는 화면의 경우에서 고려해야 될 문제로 현재 개발 중인 에뮬레이터 상에서 괜찮아 보이더라도 기기마다 높이와 폭이 다르기 때문에 다른 기기에서는 문제가 될 수 있다.

 

이러한 문제를 해결하는데 도움이 되는 패키지가 있다.

device_preview

이 패키지는 다양한 기기와 상황에서 어떻게 화면이 보이는지 확인할 수 있는 기능을 제공한다.

 

widget

현재 문제를 해결하기 위해서는 Padding 영역을 전체 스크롤되도록 해야 한다.

스크롤이 되도록 하기 위해서 Padding을 widget으로 감싸준다.

 

그리고 widget의 이름을 할당해주어야 하는데 하위항목의 하나의 위젯을 스크롤 가능하게 만들어야 하므로 해당 기능을 제공하는 SingleChildScrollView를 사용한다.

 

이제 TextField를 클릭했을 때 가상키보드가 활성화되어도 스크롤이 되면서 오버플로우 문제가 해결된다.

 

다시 Image 위젯으로 돌아가서 이제 이미지의 크기를 조절해 준다.

Image 위젯은 width 속성을 가지고 있기 때문에 이를 통해서 크기를 조절해 줄 수 있다.

Image.network(
       "URL",
       width: 81,
),

 

이미지도 마찬가지로 다른 위젯들과 간격을 조절해 주도록 한다.

Padding(
    padding: const EdgeInsets.all(32),
    child: Image.network(
        "URL",
         width: 81,
     ),
)

 

최종 완성된 로그인 페이지