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
을 정의합니다. 이 위젯은 앱의 탭 바에 표시되는 탭 항목을 나타냅니다. 상세히 설명드리겠습니다.
라이브러리와 패키지 임포트:
flutter/material.dart
와 font_awesome_flutter
패키지가 사용됩니다.NavTab 위젯:
NavTab
은 StatelessWidget
이므로, 위젯의 속성은 변경되지 않습니다.text
, isSelected
, icon
, 그리고 onTap
.멤버 변수:
text
: 탭의 레이블로 사용됩니다.isSelected
: 현재 탭이 선택된 상태인지 여부를 나타내는 불리언 값입니다.icon
: 탭에 표시될 아이콘의 데이터입니다.onTap
: 탭을 클릭했을 때 수행될 작업입니다.build 메서드:
Expanded
위젯: 탭 바에 여러 탭이 있을 때, 모든 탭이 동일한 공간을 차지하도록 합니다. 또한 터치가 가능한 공간을 가득 채워 범위를 넓혀줍니다. GestureDetector
: 이 위젯은 탭을 클릭할 수 있게 해줍니다.Container
: AnimatedOpacity
를 포함하고 있으며 배경색이 검은색으로 설정되어 있습니다.AnimatedOpacity
: 선택된 탭의 투명도는 1(완전 불투명)이고, 선택되지 않은 탭의 투명도는 0.6입니다. 이를 통해 사용자에게 현재 어떤 탭이 선택되었는지 시각적으로 표시해줍니다.Column
: 아이콘과 텍스트를 수직으로 배열합니다. FaIcon
을 사용하여 Font Awesome 아이콘을 표시하고, Gaps.v5
로 아이콘과 텍스트 사이에 간격을 줍니다.이 위젯은 탭 바의 각 탭을 나타내며, 현재 선택된 탭과 그렇지 않은 탭을 다르게 표시해줍니다.
AnimatedOpacity
와 같은 애니메이션 위젯들은 종종 헷갈릴 수 있는 부분입니다. AnimatedOpacity
는 내부적으로 상태를 관리하지만, 그 상태는 NavTab
위젯의 상태가 아닙니다. 다시 말해, AnimatedOpacity
는 상태를 가진다고 해서 그것이 NavTab
전체의 상태를 의미하는 것은 아닙니다.
AnimatedOpacity
내부에서는 애니메이션의 현재 상태, 즉 현재의 투명도 값을 관리하기 위해 상태를 가질 수 있습니다. 그러나 NavTab
위젯 자체의 isSelected
값은 부모 위젯에서 받아오는 것이며, 그 값이 변경되면 부모 위젯에서 NavTab
위젯을 다시 구성합니다.
구체적으로 설명하자면:
NavTab
의 isSelected
프로퍼티가 변경될 때, 부모 위젯에서 NavTab
을 다시 구성하게 됩니다.isSelected
값에 따라 AnimatedOpacity
의 opacity
값이 1 또는 0.6으로 설정됩니다.AnimatedOpacity
는 현재 투명도에서 목표 투명도까지 부드럽게 변화하는 애니메이션을 시작합니다. 이러한 변화를 관리하기 위해 AnimatedOpacity
내부에서는 상태를 가질 수 있습니다.NavTab
전체의 상태와는 별개입니다. 따라서 NavTab
은 여전히 StatelessWidget
으로 정의됩니다.결론적으로, NavTab
위젯 자체는 상태를 관리하지 않습니다. 모든 입력값은 프로퍼티로 받아오며, 위젯이 다시 그려질 필요가 있을 때는 부모 위젯에서 그려집니다. 이러한 특성 때문에 NavTab
은 StatelessWidget
입니다.