플러터 강좌: 13.4 로또 앱 UI 개선

안녕하세요, 개발자 여러분! 이번 강좌에서는 로또 앱의 사용자 인터페이스(UI)를 개선하는 방법에 대해 알아보겠습니다. 이전 강좌에서는 로또 번호를 생성하고, 결과를 표시하는 기능을 구현했습니다. 이제 UI를 개선하여 사용자 경험을 더욱 향상시켜보겠습니다.

1. 프로젝트 설정 및 기존 코드 리뷰

우선, 기존 로또 앱 프로젝트를 다시 살펴보겠습니다. 기존 로또 앱은 번호 생성기 같은 기본적인 기능만을 가지고 있습니다. 앱을 실행하면, 무작위로 생성된 로또 번호가 화면에 표시됩니다.

기존 코드의 구조는 다음과 같습니다.


import 'package:flutter/material.dart';
import 'dart:math';

void main() => runApp(LottoApp());

class LottoApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Lotto App',
      home: LottoHome(),
    );
  }
}

class LottoHome extends StatefulWidget {
  @override
  _LottoHomeState createState() => _LottoHomeState();
}

class _LottoHomeState extends State {
  List lottoNumbers = [];

  void generateNumbers() {
    lottoNumbers = List.generate(6, (index) => Random().nextInt(45) + 1)..sort();
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('로또 번호 생성기')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              '생성된 로또 번호:',
              style: TextStyle(fontSize: 24),
            ),
            SizedBox(height: 20),
            Text(
              lottoNumbers.join(', '),
              style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
            ),
            SizedBox(height: 30),
            ElevatedButton(
              onPressed: generateNumbers,
              child: Text('번호 생성'),
            ),
          ],
        ),
      ),
    );
  }
}

이제 UI를 개선하기 위한 여러 가지 방법을 모색해 보겠습니다.

2. UI 개선 계획 수립하기

UI를 개선하기 위해 다음과 같은 요소를 고려할 수 있습니다:

  • 색상 구성 변경
  • 폰트 스타일 및 크기 조정
  • 번호를 더 돋보이게 하기 위해 카드 또는 그리드 레이아웃 적용
  • 버튼 디자인 개선
  • 애니메이션 효과 추가

2.1 색상 구성 변경

심플한 색상 배합이 아닌, 눈에 띄고 즐거운 색상 조합을 사용하여 사용자에게 기분 좋은 경험을 제공할 수 있습니다. 예를 들어, 배경을 밝은 색으로 하고, 텍스트를 대비되는 어두운 색으로 설정합니다.

2.2 폰트 스타일 및 크기 조정

더 매력적인 사용자 경험을 위해 다양한 폰트 스타일을 적용할 수 있습니다. 기본 폰트보다 더 독창적인 폰트를 사용하여 앱의 분위기를 바꿔보세요. Google Fonts를 활용하여 다양한 폰트를 쉽게 적용할 수 있습니다.

2.3 카드 또는 그리드 레이아웃 적용

로또 번호를 카드를 사용하여 출력하면 정보를 더 명확하게 전달할 수 있습니다. 또한, 그리드 레이아웃을 적용하여 로또 번호를 정렬해 보겠습니다.

2.4 버튼 디자인 개선

기본 버튼을 사용하기보다는, 선택 가능한 색상 조합 및 텍스트 스타일을 포함한 커스터마이징된 버튼을 만들어 버튼 눌림을 더욱 부각시켜야 합니다.

2.5 애니메이션 효과 추가

버튼이나 UI 요소에 애니메이션 효과를 추가하면 사용자 경험이 한층 개선됩니다. Flutter는 애니메이션을 손쉽게 구현할 수 있는 다양한 툴을 제공합니다.

3. UI 개선 구현하기

이제 위에서 세운 계획을 바탕으로 실제 코드를 수정해보겠습니다. 다음과 같은 요소를 수정하여 기존의 로또 앱을 개선합니다.

3.1 색상 구성 변경

먼저, 색상 구성을 변경하여 배경과 텍스트의 대비를 높입니다. main.dart 파일의 build 메서드를 수정합니다.


class LottoApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Lotto App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        scaffoldBackgroundColor: Colors.lightBlue[50],
        textTheme: TextTheme(
          bodyText1: TextStyle(color: Colors.grey[800]),
          bodyText2: TextStyle(color: Colors.black),
        ),
      ),
      home: LottoHome(),
    );
  }
}

3.2 폰트 스타일 및 크기 조정

다음으로 기본 텍스트 스타일을 변경합니다. Text 위젯에 style 속성을 추가하여 이전보다 더 매력적인 폰트를 적용할 수 있습니다.


Text(
  '생성된 로또 번호:',
  style: TextStyle(
    fontSize: 28,
    fontWeight: FontWeight.bold,
    fontFamily: 'Roboto',
  ),
),

3.3 그리드 레이아웃 적용

로또 번호를 그리드 형태로 표시하도록 변경합니다. 아래 코드를 추가하면 각 번호를 카드로 감싸 더욱 시각적으로 보기 좋게 만듭니다.


GridView.builder(
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 3,
    childAspectRatio: 1,
    crossAxisSpacing: 10,
    mainAxisSpacing: 10,
  ),
  itemCount: lottoNumbers.length,
  itemBuilder: (context, index) {
    return Card(
      color: Colors.yellowAccent,
      child: Center(
        child: Text(
          lottoNumbers[index].toString(),
          style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold),
        ),
      ),
    );
  },
),

3.4 버튼 디자인 개선

버튼 디자인을 개선하기 위해 ElevatedButton에 속성을 추가하여 스타일을 변경합니다.


ElevatedButton(
  onPressed: generateNumbers,
  style: ElevatedButton.styleFrom(
    primary: Colors.blue,
    onPrimary: Colors.white,
    padding: EdgeInsets.symmetric(horizontal: 25, vertical: 15),
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
  ),
  child: Text('번호 생성', style: TextStyle(fontSize: 20)),
),

3.5 애니메이션 효과 추가

버튼 클릭 시 애니메이션 효과를 적용하기 위해 AnimatedContainer를 사용합니다. 버튼을 누르면 애니메이션 효과가 활성화됩니다.


AnimatedContainer(
  duration: Duration(milliseconds: 200),
  decoration: BoxDecoration(
    color: buttonPressed ? Colors.green : Colors.blue,
    borderRadius: BorderRadius.circular(30),
  ),
  child: ElevatedButton(
    onPressed: () {
      setState(() {
        buttonPressed = !buttonPressed;
      });
      generateNumbers();
    },
    child: Text('번호 생성', style: TextStyle(fontSize: 20)),
  ),
),

4. 최종 코드

위의 모든 코드를 통합하여 다음과 같이 최종 코드를 작성하겠습니다.


import 'package:flutter/material.dart';
import 'dart:math';

void main() => runApp(LottoApp());

class LottoApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Lotto App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        scaffoldBackgroundColor: Colors.lightBlue[50],
        textTheme: TextTheme(
          bodyText1: TextStyle(color: Colors.grey[800]),
          bodyText2: TextStyle(color: Colors.black),
        ),
      ),
      home: LottoHome(),
    );
  }
}

class LottoHome extends StatefulWidget {
  @override
  _LottoHomeState createState() => _LottoHomeState();
}

class _LottoHomeState extends State {
  List lottoNumbers = [];
  bool buttonPressed = false;

  void generateNumbers() {
    lottoNumbers = List.generate(6, (index) => Random().nextInt(45) + 1)..sort();
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('로또 번호 생성기')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('생성된 로또 번호:', style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold, fontFamily: 'Roboto')),
            SizedBox(height: 20),
            Expanded(
              child: GridView.builder(
                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 3,
                  childAspectRatio: 1,
                  crossAxisSpacing: 10,
                  mainAxisSpacing: 10,
                ),
                itemCount: lottoNumbers.length,
                itemBuilder: (context, index) {
                  return Card(
                    color: Colors.yellowAccent,
                    child: Center(
                      child: Text(
                        lottoNumbers[index].toString(),
                        style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold),
                      ),
                    ),
                  );
                },
              ),
            ),
            SizedBox(height: 30),
            ElevatedButton(
              onPressed: () {
                setState(() {
                  buttonPressed = !buttonPressed;
                });
                generateNumbers();
              },
              style: ElevatedButton.styleFrom(
                primary: Colors.blue,
                onPrimary: Colors.white,
                padding: EdgeInsets.symmetric(horizontal: 25, vertical: 15),
                shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
              ),
              child: Text('번호 생성', style: TextStyle(fontSize: 20)),
            ),
          ],
        ),
      ),
    );
  }
}

5. 마무리 및 향후 개선 방향

지금까지 로또 앱의 UI를 개선하는 방법에 대해 알아보았습니다. 다양한 방법으로 앱의 디자인을 개선함으로써 사용자의 경험을 향상시킬 수 있었습니다. 앞으로는 추가적인 기능, 예를 들어 로또 번호의 역사 데이터 시각화 또는 사용자가 이전에 생성한 번호의 기록 보관 기능 등을 추가하여 로또 앱을 더욱 풍부하게 만들어 갈 수 있습니다.

이번 강좌가 개발자 여러분께 도움이 되기를 바라며, 다음 강좌에서 또 다른 주제로 찾아오도록 하겠습니다. 끝까지 읽어 주셔서 감사합니다!