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

[Flutter] 숙제 - ( week 1 )

by bakcoding_sparta 2023. 4. 10.

지금까지 배운 것을 토대로 예시를 따라 만들어 본다.

 

 

사용한 위젯들 

  • Scaffold
  • AppBar
  • Text
  • IconButton
  • Column
  • Padding
  • TextField
  • Icon
  • Divider
  • Expanded
  • ListView.builder
  • Card
  • Stack
  • Image.network
  • Container
  • Text

 

Title

우선 타이틀을 만들어준다. 

타이틀은 왼쪽으로 정렬된 텍스트와 흰 배경으로 되어있고 오른쪽 끝에는 아이콘이 하나 들어가 있다.

1. 텍스트를 넣고 색상은 검은색 사이즈도 수정해 준다.

            title: Text(
              'Movie Reviews',
              style: TextStyle(
                fontSize: 25,
                color: Colors.black,
              ),
            )

기본적으로 앱바의 색상이 파란색으로 되어있고 아래에는 그림자도 들어가 있는데 이 부분을 조절해 준다.

          appBar: AppBar(
            elevation: 0,
            backgroundColor: Colors.white,

그림자 부분에 대해서 찾아보니 제거하는 코드는 elevation 값을 0으로 주면 된다.

 

2. 아이콘을 넣어본다.

아이콘을 넣는 방법은 두 가지가 있다.

leading : 왼쪽에 배치

actions : 오른쪽에 배치

            leading: IconButton(
              icon: Icon(Icons.menu),
              onPressed: () {},
            ),
            actions: [
              IconButton(
                onPressed: () {},
                icon: Icon(Icons.person_outline),
              ),
            ],

iconTheme: IconThemeData(color: Colors.black),

그리고 기본적으로 아이콘의 색상은 흰색이기 때문에 색상도 검은색으로 변경해야 현재 앱바에서 볼 수 있다.

예시에는 오른쪽에만 아이콘을 넣기 때문에 leading은 제거해 준다.

사용되는 Icon은 Icons 중 person_outline이다. 기능은 따로 없기 때문에 onPressed에 들어가는 함수를 비워둔다.

아이콘을 미리 볼 수 있는 페이지

Icons API

 

 

Body

이제 body 쪽 위젯을 만들어 본다. 

1. TextField

텍스트 필드에 외곽선을 수정하기 위해서 enalbedBorder 속성을 사용해주어야 한다.

TextField(
            decoration: InputDecoration(
              enabledBorder: OutlineInputBorder(
                borderSide: BorderSide(color: Colors.black, width: 1),
              ),
              label: Text('영화 제목을 검색해주세요.'),
            ),
          )

외곽선을 수정하고 라벨에 텍스트도 넣어준다.

양 옆에 여백이 필요해 보인다.

TextField를 Padding으로 감싸주고 여백을 준다.

body: Padding(
            padding: const EdgeInsets.all(8.0),
            child: TextField(
              decoration: InputDecoration(
                enabledBorder: OutlineInputBorder(
                  borderSide: BorderSide(color: Colors.black, width: 1),
                ),
                label: Text('영화 제목을 검색해주세요.'),
              ),
            ),
          ),

이제 텍스트 필드의 오른쪽에 아이콘을 넣어줘야 한다. 

그냥 Icon을 넣으면 텍스트 필드 외부에 왼쪽에 생성되는데 내부에 생성하려면 suffixIcon을 사용해야 한다.

                  icon: Icon(Icons.search_rounded),
                  suffixIcon: Icon(Icons.search_rounded)),

2. Image List

스크롤이 가능한 이미지들이 세로로 정렬되어 있다.

Container로 리스트를 감싸고 내부에 리스트는 ListView.builder를 사용해서 생성한다.

우선 이미지부터 띄우는 데 사용할 소스는 예제 코드에서 이 부분만 가져다가 사용한다.

 

리스트를 만들기 위해서 영화 클래스를 만들고 타이틀과 URL 속성을 선언해 준다.

그리고 이 클래스를 리스트에 넣고 불러와서 쓰도록 한다. 

 

class Movie {
  String movieTitle = 'title';
  String movieURL = 'url';
  Movie(String title, String url) {
    movieTitle = title;
    movieURL = url;
  }
}

var movieList = [
  Movie('탑건: 매버릭', 'https://i.ibb.co/sR32PN3/topgun.jpg'),
  Movie('범죄도시2', 'https://i.ibb.co/2czdVdm/The-Outlaws.jpg'),
  Movie('헤어질 결심', 'https://i.ibb.co/gM394CV/Decision-to-Leave.jpg'),
  Movie('브로커', 'https://i.ibb.co/MSy1XNB/broker.jpg'),
];

 

이번에 사용할 ListView.builder를 사용하면 반복되는 부분을 쉽게 생성할 수 있다.

ListView.builder의 itemCount를 사용해서 반복 횟수를 정하고 itemBuilder 속성에서 반복할 동작에 대해서 정의할 수 있다.

그리고 return으로 그려줄 위젯을 반환해야 하는데 일반적으로 리스트에 많이 사용하는 Card 위젯을 쓰도록 한다.

 

구조는 Container 안에서 Column으로 리스트를 생성한다. 

Container(
              height: 100,
              child: ListView.builder(
                  itemCount: movieList.length,
                  itemBuilder: (BuildContext context, int index) {
                    String title = movieList[index].movieTitle;
                    String url = movieList[index].movieURL;
                    print(title + url);
                    return Card(
                      child: Image.network(
                        url,
                        width: double.infinity,
                        height: 200,
                        fit: BoxFit.cover,
                      ),
                    );
                  }),
            )

리스트는 movieList의 길이만큼 생성되도록 하고 생성할 Card는 텍스트도 넣어야 하기 때문에 children이 필요하지만 일단 이미지만 추가하고 테스트한다.

 

이미지의 크기는 가로를 영역 최대치로 하고 높이는 200 정도로 한다. fit : BoxFit.cover 속성으로 영역 안에 비율을 유지해서 이미지를 채우도록 한다.

 

실행해 보면 에러가 발생하면서 리스트가 생성되지 않는다.

원인을 검색해 보니 ListView.builder를 사용할 경우 높이가 무한대로 잡히기 때문에 Container의 높이를 결정하지 않으면 이러한 이유로 에러가 발생한다. 그래서 임의로 height 값을 준 결과 제대로 동작하는 걸 확인했다.

Container(
              height: 500,

하지만 실행환경마다 높이가 다르기 때문에 이 부분을 동적으로 제어할 수 있는 위젯인 Flexible 또는 Expanded를 사용한다. 두 위젯은 반드시 Row 또는 Column의 하위 위젯에서만 사용이 가능한데 그 이유는 Row나 Column을 확장하여 메인 축에서 사용 가능한 공간을 채우는 방식이기 때문이다. 동일한 동작을 하며 Flexible의 경우 설정이 가능한 값들이 더 다양하다는 차이만 있다.

 

여기서는 자세한 설정은 필요 없어 보이므로 Expand를 사용한다. 

Expanded(
              child: ListView.builder(
                  itemCount: movieList.length,
                  itemBuilder: (BuildContext context, int index) {
                    String title = movieList[index].movieTitle;
                    String url = movieList[index].movieURL;
                    print(title + url);
                    return Card(
                      child: Image.network(
                        url,
                        width: double.infinity,
                        height: 200,
                        fit: BoxFit.cover,
                      ),
                    );
                  }),
            )

리스트가 잘 그려지며 스크롤도 문제없이 동작한다.

 

이제 영화의 제목을 이미지 위에 추가해 주도록 한다. 여기서 Container를 사용해서 이미지 위에 텍스트를 표시하는 방법을 찾아보니 이런 경우 Stack을 활용하는 게 더 간단했다.

 

return Card(
                      child: Stack(alignment: Alignment.center, children: [
                        Image.network(
                          url,
                          width: double.infinity,
                          height: 200,
                          fit: BoxFit.cover,
                        ),
                        Text(
                          title,
                          style: TextStyle(
                            color: Colors.white,
                            fontSize: 30,
                          ),
                        ),
                      ]),
                    );

Card 내부에 Stack으로 이미지와 텍스트 위젯을 그린다. 텍스트는 잘 보이도록 색상과 크기를 설정한다.

 

 

텍스트가 잘 보이지 않는 것 같아서 다시 예제를 보니 이미지가 약간 어둡게 처리된 걸 발견했다.

이미지를 어둡게 해주는 속성을 추가한다. 이미지 위젯의 색상에 값을 주고 ColorBlendMode : BlendMode.darken을 넣어주면 이미지가 어둡게 표현된다.

                        Image.network(
                          url,
                          color: Colors.black45,
                          colorBlendMode: BlendMode.darken,
                          width: double.infinity,
                          height: 200,
                          fit: BoxFit.cover,
                        ),

black의 값은 적당한 45로 했다. 색상 뒤에 숫자는 투명도를 뜻한다.

 

 

최종 결과