별도의 Widget 추출하기

샤워실의 바보·2024년 2월 10일
0
post-thumbnail
post-custom-banner
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';

import '../../../constants/gaps.dart';

class NavTab extends StatelessWidget {
  const NavTab({
    super.key,
    required this.text,
    required this.isSelected,
    required this.icon,
    required this.onTap,
  });

  final String text;
  final bool isSelected;
  final IconData icon;
  final Function onTap;

  
  Widget build(BuildContext context) {
    return Expanded(
      child: GestureDetector(
        onTap: () => onTap(),
        child: Container(
          color: Colors.black,
          child: AnimatedOpacity(
            duration: const Duration(milliseconds: 300),
            opacity: isSelected ? 1 : 0.6,
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                FaIcon(
                  icon,
                  color: Colors.white,
                ),
                Gaps.v5,
                Text(
                  text,
                  style: const TextStyle(
                    color: Colors.white,
                  ),
                )
              ],
            ),
          ),
        ),
      ),
    );
  }
}
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:tiktok_clone/constants/sizes.dart';
import 'package:tiktok_clone/features/main_navigation/widgets/nav_tab.dart';

class MainNavigationScreen extends StatefulWidget {
  const MainNavigationScreen({super.key});

  
  State<MainNavigationScreen> createState() => _MainNavigationScreenState();
}

class _MainNavigationScreenState extends State<MainNavigationScreen> {
  int _selectedIndex = 0;

  void _onTap(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      bottomNavigationBar: BottomAppBar(
        color: Colors.black,
        child: Padding(
          padding: const EdgeInsets.all(Sizes.size12),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              NavTab(
                text: "Home",
                isSelected: _selectedIndex == 0,
                icon: FontAwesomeIcons.house,
                onTap: () => _onTap(0),
              ),
              NavTab(
                text: "Discover",
                isSelected: _selectedIndex == 1,
                icon: FontAwesomeIcons.magnifyingGlass,
                onTap: () => _onTap(1),
              ),
              NavTab(
                text: "Inbox",
                isSelected: _selectedIndex == 3,
                icon: FontAwesomeIcons.message,
                onTap: () => _onTap(3),
              ),
              NavTab(
                text: "Profile",
                isSelected: _selectedIndex == 4,
                icon: FontAwesomeIcons.user,
                onTap: () => _onTap(4),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

이 코드는 NavTab이라는 이름의 StatelessWidget을 정의합니다. 이 위젯은 앱의 탭 바에 표시되는 탭 항목을 나타냅니다. 상세히 설명드리겠습니다.

  1. 라이브러리와 패키지 임포트:

    • 필요한 라이브러리와 패키지를 임포트합니다. 여기서는 flutter/material.dartfont_awesome_flutter 패키지가 사용됩니다.
  2. NavTab 위젯:

    • NavTabStatelessWidget이므로, 위젯의 속성은 변경되지 않습니다.
    • 해당 위젯은 여러 매개변수를 필요로 합니다: text, isSelected, icon, 그리고 onTap.
  3. 멤버 변수:

    • text: 탭의 레이블로 사용됩니다.
    • isSelected: 현재 탭이 선택된 상태인지 여부를 나타내는 불리언 값입니다.
    • icon: 탭에 표시될 아이콘의 데이터입니다.
    • onTap: 탭을 클릭했을 때 수행될 작업입니다.
  4. build 메서드:

    • Expanded 위젯: 탭 바에 여러 탭이 있을 때, 모든 탭이 동일한 공간을 차지하도록 합니다. 또한 터치가 가능한 공간을 가득 채워 범위를 넓혀줍니다.
    • GestureDetector: 이 위젯은 탭을 클릭할 수 있게 해줍니다.
    • Container: AnimatedOpacity를 포함하고 있으며 배경색이 검은색으로 설정되어 있습니다.
    • AnimatedOpacity: 선택된 탭의 투명도는 1(완전 불투명)이고, 선택되지 않은 탭의 투명도는 0.6입니다. 이를 통해 사용자에게 현재 어떤 탭이 선택되었는지 시각적으로 표시해줍니다.
    • Column: 아이콘과 텍스트를 수직으로 배열합니다. FaIcon을 사용하여 Font Awesome 아이콘을 표시하고, Gaps.v5로 아이콘과 텍스트 사이에 간격을 줍니다.

이 위젯은 탭 바의 각 탭을 나타내며, 현재 선택된 탭과 그렇지 않은 탭을 다르게 표시해줍니다.

AnimatedOpacity와 같은 애니메이션 위젯들은 종종 헷갈릴 수 있는 부분입니다. AnimatedOpacity는 내부적으로 상태를 관리하지만, 그 상태는 NavTab 위젯의 상태가 아닙니다. 다시 말해, AnimatedOpacity는 상태를 가진다고 해서 그것이 NavTab 전체의 상태를 의미하는 것은 아닙니다.

AnimatedOpacity 내부에서는 애니메이션의 현재 상태, 즉 현재의 투명도 값을 관리하기 위해 상태를 가질 수 있습니다. 그러나 NavTab 위젯 자체의 isSelected 값은 부모 위젯에서 받아오는 것이며, 그 값이 변경되면 부모 위젯에서 NavTab 위젯을 다시 구성합니다.

구체적으로 설명하자면:

  1. NavTabisSelected 프로퍼티가 변경될 때, 부모 위젯에서 NavTab을 다시 구성하게 됩니다.
  2. isSelected 값에 따라 AnimatedOpacityopacity 값이 1 또는 0.6으로 설정됩니다.
  3. AnimatedOpacity는 현재 투명도에서 목표 투명도까지 부드럽게 변화하는 애니메이션을 시작합니다. 이러한 변화를 관리하기 위해 AnimatedOpacity 내부에서는 상태를 가질 수 있습니다.
  4. 그러나, 이러한 내부 상태는 NavTab 전체의 상태와는 별개입니다. 따라서 NavTab은 여전히 StatelessWidget으로 정의됩니다.

결론적으로, NavTab 위젯 자체는 상태를 관리하지 않습니다. 모든 입력값은 프로퍼티로 받아오며, 위젯이 다시 그려질 필요가 있을 때는 부모 위젯에서 그려집니다. 이러한 특성 때문에 NavTabStatelessWidget입니다.

profile
공부하는 개발자
post-custom-banner

0개의 댓글