코딩강의/meals_app(플러터-유데미)
~170. Replacing Screens (Instead of Pushing)
김마드
2023. 10. 31. 11:05
1. side drawer를 추가해주고, drawer에서 화면 이동을 구현해보자.
2. filter 화면을 구현해보자.
- tabs.dart
import 'package:flutter/material.dart';
import 'package:meals/models/meal.dart';
import 'package:meals/screens/categories.dart';
import 'package:meals/screens/filters.dart';
import 'package:meals/screens/meals.dart';
import 'package:meals/widgets/main_drawer.dart';
class TabsScreen extends StatefulWidget {
const TabsScreen({super.key});
@override
State<TabsScreen> createState() {
return _TabsScreenState();
}
}
class _TabsScreenState extends State<TabsScreen> {
int _selectedPageIndex = 0;
final List<Meal> _favoriteMeals = [];
void _showInfoMessage(String message) {
ScaffoldMessenger.of(context).clearSnackBars();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
),
);
}
void _toggleMealFavoriteStatus(Meal meal) {
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!');
});
}
}
void _selectPage(int index) {
setState(() {
_selectedPageIndex = index;
});
}
void _setScreen(String identifier) {
Navigator.of(context).pop();
if (identifier == 'filters') {
Navigator.of(context).push(
MaterialPageRoute(
builder: (ctx) => const FiltersScreen(),
),
);
}
}
@override
Widget build(BuildContext context) {
Widget activePage = CategoriesScreen(
onToggleFavorite: _toggleMealFavoriteStatus,
);
var activePageTitle = 'Categories';
if (_selectedPageIndex == 1) {
activePage = MealsScreen(
meals: _favoriteMeals,
onToggleFavorite: _toggleMealFavoriteStatus,
);
activePageTitle = 'Your Favorites';
}
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: 'Favorites',
),
],
),
);
}
}
1) appBar 밑에 drawer추가 및 MainDrawer 위젯 추가해주었다. onSelectScreen함수에 _setScreen함수 연동(화면 이동)
이 때, _setScreen의 인수값으로 'filter'값이 쓰이면 filterScreen으로 이동하고, 다른 값이 오면 그냥 pop(뒤로가기)가 쓰인다. pop이 맨처음에 쓰이는 이유는 filter갔다가 뒤로가기하면 drawer가 그대로 열려있어 닫아주어야 하기 때문이다.
- main_drawer.dart(new)
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
.withOpacity(0.8),
],
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.onBackground,
),
title: Text(
'Meals',
style: Theme.of(context).textTheme.titleSmall!.copyWith(
color: Theme.of(context).colorScheme.onBackground,
fontSize: 24,
),
),
onTap: () {
onSelectScreen('meals');
},
),
ListTile(
leading: Icon(
Icons.settings,
size: 26,
color: Theme.of(context).colorScheme.onBackground,
),
title: Text(
'Filters',
style: Theme.of(context).textTheme.titleSmall!.copyWith(
color: Theme.of(context).colorScheme.onBackground,
fontSize: 24,
),
),
onTap: () {
onSelectScreen('filters');
},
),
],
),
);
}
}
1) 플러터 내장 위젯인 ListTile이 쓰인다.
- 결과 화면
- filters.dart(new)
import 'package:flutter/material.dart';
// import 'package:meals/screens/tabs.dart';
// import 'package:meals/widgets/main_drawer.dart';
class FiltersScreen extends StatefulWidget {
const FiltersScreen({super.key});
@override
State<FiltersScreen> createState() {
return _FiltersScreenState();
}
}
class _FiltersScreenState extends State<FiltersScreen> {
var _glutenFreeFilterSet = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Your Filters'),
),
// drawer: MainDrawer(
// onSelectScreen: (identifier) {
// Navigator.of(context).pop();
// if (identifier == 'meals') {
// Navigator.of(context).pushReplacement(
// MaterialPageRoute(
// builder: (ctx) => const TabsScreen(),
// ),
// );
// }
// },
// ), Replace가 쓰이는 이유는 push는 그대로 스택이 쌓이는 반면, Replace는 초기화를 해준다. 그래서 안드로이드폰에서 Replace를 사용하게 되면 더 뒤로 갈 곳이 없을때 앱이 아래로 사라져버린다. (잘 사용안함)
body: Column(children: [
SwitchListTile(
value: _glutenFreeFilterSet,
onChanged: (isChecked) {
setState(() {
_glutenFreeFilterSet = isChecked;
});
},
title: Text(
'Gluten-free',
style: Theme.of(context).textTheme.titleLarge!.copyWith(
color: Theme.of(context).colorScheme.onBackground,
),
),
subtitle: Text(
'Only include gluten-free meals.',
style: Theme.of(context).textTheme.labelMedium!.copyWith(
color: Theme.of(context).colorScheme.onBackground,
),
),
activeColor: Theme.of(context).colorScheme.tertiary,
contentPadding: const EdgeInsets.only(left: 34, right: 22),
),
]),
);
}
}
1) 여기서도 MainDrawer위젯을 추가해줄 수 있지만, 안하는 식으로 구현했다.
2) 플러터 내장 위젯인 SwitchListTile를 이용해 스위치 버튼을 구현 할 수 있다.
- 결과 화면