미새문지

flutter 헤드라인 페이지 UI 구현 본문

개발 TIL

flutter 헤드라인 페이지 UI 구현

문미새 2024. 11. 23. 01:39
728x90

메인페이지인 헤드라인 페이지의 UI를 구현했다.

 

일단 피그마에 기획한대로 구현은 했는데, 좀 걱정되는 부분이 있다. 블로그 포스트 자체를 iframe으로 화면으로 가져와 보여줄 예정이였는데, 인기포스트나 다른 포스트들의 이미지와 게시글 주제 같은 내용은 어떻게 가져올런지 궁금해졌다.

 

코드 구조에 대해 찾아보다가 router를 사용하면 공용 위젯을 main.dart에 사용하기 어렵다고 해서 새로 공용 인터페이스인 baseScreen이라는 위젯을 하나 만들어서 appBar와 bottomBar를 추가해주고 body부분만 인자로 받는걸로 만들어줬다.

import 'package:defeefront/widgets/footer.dart';
import 'package:defeefront/widgets/header.dart';
import 'package:flutter/material.dart';

class BaseScreen extends StatelessWidget {
  final Widget child;

  const BaseScreen({required this.child, super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: Header(),
      body: SingleChildScrollView(
        child: child,
      ),
      bottomNavigationBar: Footer(),
    );
  }
}

 

그리고 초기에 만들어놓은 헤드라인 페이지의 Scaffold 위젯을 없애고 body부분만 작성했다.

import 'package:defeefront/screens/headline/widgets/category.dart';
import 'package:defeefront/screens/headline/widgets/other_post.dart';
import 'package:defeefront/screens/headline/widgets/popular.dart';
import 'package:defeefront/screens/headline/widgets/post_content.dart';
import 'package:defeefront/widgets/basescreen.dart';
import 'package:defeefront/widgets/footer.dart';
import 'package:defeefront/widgets/header.dart';
import 'package:flutter/material.dart';

class Headline extends StatelessWidget {
  const Headline({super.key});

  @override
  Widget build(BuildContext context) {
    return const BaseScreen(
      child: Padding(
        padding: EdgeInsets.symmetric(horizontal: 30.0),
        child: Column(
          children: [
            // 카테고리
            Category(),
            // 인기 포스트
            Popular(),
            // 포스트 컨텐츠
            PostContent(),
            // 하단 나머지 포스트
            OtherPost(),
          ],
        ),
      ),
    );
  }
}

 

카테고리의 경우 지금은 단순히 UI를 위해 5개 고정으로 해놨는데 아마 나중에 사용자가 설정한 값으로 동적으로 변경될 예정이다.

import 'package:flutter/material.dart';

class Category extends StatelessWidget {
  const Category({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 70,
      width: double.infinity,
      alignment: Alignment.center,
      child: DefaultTextStyle(
        style: const TextStyle(
          fontSize: 13,
          color: Color.fromARGB(255, 92, 92, 92),
        ),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: const [
            Text("최신 포스트"),
            Text("BACKEND"),
            Text("FRONTEND"),
            Text("DEVOPS"),
            Text("CS"),
          ],
        ),
      ),
    );
  }
}

 

가장 최상단의 게시글은 가장 인기 있는 게시글이 출력된다. 해당 부분이 게시글의 이미지와 주제를 어떻게 가져올지 고민하던 부분인데, 만약 서버에서 해당 값을 못가져온다한다면 다른 방식으로 뜯어고칠 예정

import 'package:flutter/material.dart';

class Popular extends StatelessWidget {
  const Popular({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 350,
      width: double.infinity,
      alignment: Alignment.center,
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Container(
            width: double.infinity,
            height: 250,
            decoration: BoxDecoration(
              color: Colors.grey[300],
              borderRadius: BorderRadius.circular(6),
            ),
            child: Image(
              image: AssetImage('assets/images/dummy.png'),
              fit: BoxFit.contain,
            ),
          ),
          SizedBox(height: 10),
          Text(
            "재로그",
            style: TextStyle(fontSize: 13, fontWeight: FontWeight.normal),
          ),
          Text(
            "SOLID - SRP와 OCP",
            style: TextStyle(fontSize: 23, fontWeight: FontWeight.normal),
          ),
          SizedBox(height: 10),
          Divider(
            color: Colors.grey,
            thickness: 1,
          ),
        ],
      ),
    );
  }
}

 

중간의 포스트 컨텐츠의 경우 맞춤형으로 몇 개의 게시글이 출력되고 드래그해서 확인할 수 있으며, 전체 포스트 버튼을 클릭하면 포스트 페이지로 넘어가질 예정이다. 플러터에서 드래그 슬라이드 기능은 처음보는 기능이라 지피티한테 도움받았다.

import 'package:flutter/material.dart';

class PostContent extends StatelessWidget {
  const PostContent({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        SizedBox(
          height: 100,
          child: ListView(
            scrollDirection: Axis.horizontal,
            children: [
              SlideItem(),
              SlideItem(),
              SlideItem(),
              SlideItem(),
            ],
          ),
        ),
        const SizedBox(height: 15),
        ElevatedButton(
          onPressed: () {
            print("Button clicked!");
          },
          style: ElevatedButton.styleFrom(
              minimumSize: const Size(double.infinity, 55),
              backgroundColor: Color.fromARGB(1, 186, 186, 186)),
          child: const Text(
            "전체 포스트 컨텐츠",
            style: TextStyle(color: Colors.white),
          ),
        ),
        SizedBox(height: 10),
        Divider(
          color: Colors.grey,
          thickness: 1,
        ),
      ],
    );
  }
}

class SlideItem extends StatelessWidget {
  const SlideItem({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
        width: 380,
        margin: const EdgeInsets.only(right: 20),
        padding: const EdgeInsets.all(16),
        decoration: BoxDecoration(
          color: Color.fromARGB(139, 129, 129, 129),
          borderRadius: BorderRadius.circular(12),
        ),
        alignment: Alignment.center,
        child: const Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Text(
                  "미새문지",
                  style: TextStyle(
                      fontSize: 20,
                      color: Colors.white,
                      fontWeight: FontWeight.bold),
                ),
                SizedBox(width: 5),
                Image(
                  width: 20,
                  image: AssetImage("assets/images/velog.png"),
                )
              ],
            ),
            Text(
              "소프트웨어 품질 관리",
              style: TextStyle(fontSize: 15, color: Colors.white),
            )
          ],
        ));
  }
}

지피티 코드를 보니까 반복되는 게시글을 하나의 위젯으로 만들어 반복시키는데, 나중에 따로 위젯 파일로 빼서 import할 예정이다.

슬라이드는  ListView라는 위젯을 사용했다. 이 위젯은 width값이 화면을 넘어가면 스크롤이 발생하며 슬라이드가 가능해진다. 스크롤 방향은 horizintal로 가로 스크롤이 가능하게 지정했다.

 

하단의 다른 게시글들의 경우 인기 게시글 UI를 가져와서 나중에 데이터에 따라 map처럼 가져오면 될 것 같다.

작업하면서 리액트 props처럼 인자 보내는 방식도 확인했는데, 플러터는 값을 const와 final로 지정하더라.

두 개의 차이점은 사용점에 있는데,

const의 경우, 값이 컴파일에 고정되거나 재사용 가능한 불변성의 위젯을 생성할때 사용되고,

final의 경우, 값이 실행 상태에 따라 달라지고 생성자를 통해 동적인 데이터를 전달할 때 사용된다.

 

const와  final 차이점

특성 const final
값 변경 여부 값이 컴파일 타임에 결정되어야 함 값이 런타임에 할당되면 불변
초기화 시점 컴파일 타임에 고정된 값을 가져야 함 한 번 초기화되면 값이 바뀌지 않음
사용 예시 상수로 고정된 값 (e.g., 숫자, 문자열) 런타임 값이나 객체를 나타낼 때 주로 사용

 

강의도 주로 있는 구현 강의가 아니라 flutter의 아키텍쳐와 로직 등, 상황 별 강의를 구매했다. 코드는 익숙해지면 되는데, 특정 상황에서의 로직과 발생 원인같은건 알기가 어려워서 이쪽 위주로 학습하려고 한다.

일단 프로젝트는 이미 시작했으니 최대한 구글링과 지피티를 통해 작업하고 틈틈히 강의를 들으면서 flutter에 익숙해지려고 한다.

728x90