1. Before Start
- Drawer
Flutter에서 **Drawer(드로어)**는 화면의 왼쪽(또는 오른쪽)에서 슬라이드하여 나타나는 패널로, 보통 내비게이션 메뉴를 표시하는 데 사용됩니다. 드로어는 앱의 다양한 섹션이나 기능으로 쉽게 접근할 수 있도록 해줍니다.
- ListTile
- SwitchListTile
2. Prodjct
2.1 Main Drawer 정의(part 01) - DrawerHeader
Drawer는 Scaffold의 dreawer option으로 들어가게 된다. 물론, 그 방대한 Drawer Widget을 option에 집어넣게 된다면, 가독성을 저하시키는 원인이 되므로, 우리는 일단 이를 ./widgets/main_drawer.dart에 집어넣는다.
import 'package:flutter/material.dart';
class MainDrawer extends StatelessWidget{
const MainDrawer({super.key});
@override
Widget build(BuildContext context) {
return Drawer(
child: Column(
children: [
//Drawer-Column-(1)DrawerHeader
DrawerHeader(
padding:const EdgeInsets.all(20) ,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context).colorScheme.primaryContainer,
Theme.of(context)
.colorScheme
.primaryContainer
.withAlpha((0.9*255).toInt()),]
,begin:Alignment.topLeft
,end:Alignment.bottomRight
)
) ,
child: Row(
children: [
Icon(Icons.fastfood,size:48,color:Theme.of(context).colorScheme.primary,),
const SizedBox(width:18),
Text("Cooking up!",style:Theme.of(context).textTheme.titleLarge!.copyWith(
color:Theme.of(context).colorScheme.primary,
)),
//Drawer-Column-(2)DrawerHeader
],
),
),
],
)
);
}
}
2.2 Main Drawer 정의(part 02) ListTitle
여러가지 정보를 알맞게 표현해주는 Widget이 있는데,이를 바로 ListTitle이라고 한다. 많은 옵션들이 있고, 이를 MainDrawer에 한번 써보도록 하자.
import 'package:flutter/material.dart';
class MainDrawer extends StatelessWidget{
const MainDrawer({super.key});
@override
Widget build(BuildContext context) {
return Drawer(
child: Column(
children: [
//Drawer-Column-(1)DrawerHeader
DrawerHeader(
padding:const EdgeInsets.all(20) ,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context).colorScheme.primaryContainer,
Theme.of(context)
.colorScheme
.primaryContainer
.withAlpha((0.9*255).toInt()),]
,begin:Alignment.topLeft
,end:Alignment.bottomRight
)
) ,
child: Row(
children: [
Icon(Icons.fastfood,size:48,color:Theme.of(context).colorScheme.primary,),
const SizedBox(width:18),
Text("Cooking up!",style:Theme.of(context).textTheme.titleLarge!.copyWith(
color:Theme.of(context).colorScheme.primary,
))
],
),
),
//Drawer-Column-(2)DrawerHeader
ListTile(
leading: Icon(Icons.restaurant,size:26,color:Theme.of(context).colorScheme.onSurface,
),
title:Text('Meals',
style:Theme.of(context).textTheme.titleSmall!.copyWith(
color:Theme.of(context).colorScheme.primary,
fontSize:24,
)
),
onTap: (){},
),
//Drawer-Column-(3)
ListTile(
leading: Icon(Icons.settings,size:26,color:Theme.of(context).colorScheme.onSurface,
),
title:Text('Filters',
style:Theme.of(context).textTheme.titleSmall!.copyWith(
color:Theme.of(context).colorScheme.primary,
fontSize:24,
)
),
onTap: (){},
),
],
)
);
}
}
2.2 Main Drawer 정의(part 02) FiterScreen
마지막 main_drawer section에 올려놓은 것은 바로 'filter'항목이였다.그렇다면,이를 세분하 할 수 있는 예시를 통해서 filter를 항목을 조정해보자.
step 01) ./screens/filters.dart에 다가, FilterScreen StateFulWidget을 만들어 준다. 그리고 아래와 같이 항목을 만들어 준다.
import 'package:flutter/material.dart';
class FilterScreen extends StatefulWidget{
@override
State<StatefulWidget>createState(){
return _FilterScreen();
}
}
class _FilterScreen extends State<FilterScreen>{
var _glutenFreeFilterSet=false;
@override
Widget build(BuildContext context) {
return Scaffold(
//Scaffold-appBar
appBar:AppBar(
title:const Text("Your filter"),
),
//Scaffold-body
body:Column(
children: [
//value:boolean, onChanged:activation funciton if true
//title: message appears
//activeColor: choose the color if switch is activated
//SwitchListTile(01)
SwitchListTile(
value:_glutenFreeFilterSet ,
onChanged: (bool isChecked){
setState((){
_glutenFreeFilterSet=isChecked;
});
},
//SwitchListTile-title
title:Text(
'Glutten-free',
style:Theme.of(context).textTheme.titleLarge!.copyWith(
color:Theme.of(context).colorScheme.onSurface,)),
//SwitchListTile-subtitle
subtitle:Text(
'Only include glute-free meals.',
style:Theme.of(context).textTheme.labelMedium!.copyWith(
color:Theme.of(context).colorScheme.onSurface,
)
),
//active color-color if switch is activated
activeColor: Theme.of(context).colorScheme.tertiary,
contentPadding: const EdgeInsets.only(left:34,right:22),
),
],
)
);
}
}
step 02) 우리가 더 추가적인 SwitchListTile을 집어넣기 전에, 이 filter.dart가 과연 제대로 작동한지 보고 싶다. 이를 위해선, ./screens/tabs.dart에 가서 아래와 같이 코드를 추가한다.
// ./screens/tabs.dart
//new function
void _setScreen(String identifier){
if(identifier=='filters'){
Navigator.of(context).push(
MaterialPageRoute(builder: (ctx)=>const FilterScreen())
);
}else{
Navigator.of(context).pop();
}
@override build function 부분 위에다가 윗 코드를 정의한다. 그리고 이를 동일한 file안에 있는, Scaffold-drawer option안의 우리가 지정한 MainDrawer widget class의 옵션으로 넣어야 한다. 하지만, MainDrawer는 현재 function을 받아줄 매개변수가 없다. 이를 추가하기 위해, 다시
./widget/main_drawer.dart 로 들어가서 function을 받아줄 매개변수를 정의한다.
// ./widget/main_drawer.dart
ListTile(
leading: Icon(
Icons.settings,
size: 26,
color: Theme.of(context).colorScheme.onSurface,
),
title: Text(
'Filters',
style: Theme.of(context).textTheme.titleSmall!.copyWith(
color: Theme.of(context).colorScheme.onSurface,
fontSize: 24,
),
),
onTap: () {
onSelectScreen('filters');
},
),
./widget/main_drawer.dart
import 'package:flutter/material.dart';
class MainDrawer extends StatelessWidget {
const MainDrawer({super.key, required this.onSelectScreen});
final void Function(String identifier) onSelectScreen;
@override
Widget build(BuildContext context) {
return Drawer(
child: Column(
children: [
DrawerHeader(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context).colorScheme.primaryContainer,
Theme.of(context)
.colorScheme
.primaryContainer
.withAlpha((0.8*255).toInt()),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Row(
children: [
Icon(
Icons.fastfood,
size: 48,
color: Theme.of(context).colorScheme.primary,
),
const SizedBox(width: 18),
Text(
'Cooking Up!',
style: Theme.of(context).textTheme.titleLarge!.copyWith(
color: Theme.of(context).colorScheme.primary,
),
),
],
),
),
ListTile(
leading: Icon(
Icons.restaurant,
size: 26,
color: Theme.of(context).colorScheme.onSurface,
),
title: Text(
'Meals',
style: Theme.of(context).textTheme.titleSmall!.copyWith(
color: Theme.of(context).colorScheme.onSurface,
fontSize: 24,
),
),
onTap: () {
onSelectScreen('meals');
},
),
ListTile(
leading: Icon(
Icons.settings,
size: 26,
color: Theme.of(context).colorScheme.onSurface,
),
title: Text(
'Filters',
style: Theme.of(context).textTheme.titleSmall!.copyWith(
color: Theme.of(context).colorScheme.onSurface,
fontSize: 24,
),
),
onTap: () {
onSelectScreen('filters');
},
),
],
),
);
}
}
./screens/tabs.dart 는 아래와 같다.
import 'package:flutter/material.dart';
import 'package:meals/screens/categories.dart';
import 'package:meals/screens/filters.dart';
import 'package:meals/screens/meals.dart';
import 'package:meals/models/meal.dart';
import 'package:meals/widget/main_drawer.dart';
import 'package:meals/widget/meal_item.dart';
//하나의 route에서 다른 route로 이동을 해야하므로, 우리는
//StatelessWidget이 아닌 StatefulWidget를 사용한다.
class TabsScreen extends StatefulWidget{
const TabsScreen({super.key});
@override
State<TabsScreen>createState(){
return _TabsScreen();
}
}
class _TabsScreen extends State<TabsScreen>{
int _selectedPageIndex=0;
final List<Meal>_favoriteMeals=[];
/*
그렇다면, snackBar라는 option을 통해서 우리가 전달하고자 하는 메시지를
인자로 주고, 그걸 snackBar에 보여주는 것을 목표로 한다.
1) _favoriteMeals에 meal을 추가할때,
"Marked as a favorite"메세지 보내기
2) _favoriteMeals에서 meal을 삭제할때,
"Meal is no longer a favorite"메시지 보내기
*/
void _showInfoMessage(String message){
ScaffoldMessenger.of(context).clearSnackBars();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
),
);
}
void _toggleMealFavoriteStatus(Meal meal){
//parameter로 받은 meal이 현재 _favoriteMeal에 있는지 체크
final isExisting=_favoriteMeals.contains(meal);
if(isExisting){
setState(() {
_favoriteMeals.remove(meal);
});
_showInfoMessage("Meal is no longer a favorite");
}else{
setState(() {
_favoriteMeals.add(meal);
});
_showInfoMessage("Marked as a favorite");
}
}
var activePageTitle="Categories";
void _selectPage(int index){
setState((){
_selectedPageIndex=index;
});
}
//new function
void _setScreen(String identifier){
if(identifier=='filters'){
Navigator.of(context).push(
MaterialPageRoute(builder: (ctx)=>const FilterScreen())
);
}else{
Navigator.of(context).pop();
}
}
@override
Widget build(BuildContext context){
Widget activePage=CategoriesScreen(ontoggleFavorite:_toggleMealFavoriteStatus,);
if(_selectedPageIndex==1){
activePage=MealsScreen(
meals:_favoriteMeals,
ontoggleFavorite:_toggleMealFavoriteStatus,);
activePageTitle="Your favorite";
}
return Scaffold(
appBar:AppBar(
title:Text(activePageTitle),
),
drawer: MainDrawer(
onSelectScreen:_setScreen,
),
body:activePage,
bottomNavigationBar:BottomNavigationBar(
onTap:_selectPage,
currentIndex: _selectedPageIndex,
items:const [
BottomNavigationBarItem(icon:Icon(Icons.set_meal),label:"Categories"),
BottomNavigationBarItem(icon:Icon(Icons.star),label:"Favorite"),
]
),
);
}
}
2.3 Navigator Pop (다른 방법)
이번에는 drawer를 Filter안에다가 넣는 방법도 알아보도록하자 .
step 01) User가 'Your filter'를 진입을 했으면, 다시 TabScreen화면으로 전환시킨다.
step 02) TabScreen으로 전환된 뒤에, Meals를 입력한다면 MealScreen으로 전환.
class FilterScreen extends StatefulWidget{
const FilterScreen({super.key});
@override
State<StatefulWidget>createState(){
return _FilterScreen();
}
}
class _FilterScreen extends State<FilterScreen>{
var _glutenFreeFilterSet=false;
@override
Widget build(BuildContext context) {
return Scaffold(
//Scaffold-appBar
appBar:AppBar(
title:const Text("Your filter"),
),
drawer:MainDrawer(onSelectScreen: (identifier){
//현재 filter context를 지워버리고.
Navigator.of(context).pop();
//만약 User가 Meals를 선택한다면, tabScreen으로 돌아가게 한다.
if(identifier=="meals"){
Navigator
.of(context)
.push(MaterialPageRoute(builder: (ctx)=>const TabsScreen()));
}
},)
,//Scaffold-body
Filter에다가 drawer를 쓰지 않고 싶다면, 위의 drawer를 지워버리면 됨.
'Front-End > Flutter_Project_03_ToDo APP' 카테고리의 다른 글
8.Udemy 강의를 통한 Meals Project (0) | 2025.01.27 |
---|---|
6. Udemy 강의를 통한 Meals Project (0) | 2025.01.20 |
5. Udemy 강의를 통한 Meals Project (0) | 2025.01.19 |