1. 즐겨찾기 기능을 만들어보자 + (다중 위젯에 함수값 전달하는 방법)
- tabs.dart
import 'package:flutter/material.dart';
import 'package:meals/models/meal.dart';
import 'package:meals/screens/categories.dart';
import 'package:meals/screens/meals.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 _toggleMealFavoriteState(Meal meal) {
final isExisting = _favoriteMeals.contains(meal);
if (isExisting) {
setState(() {
_favoriteMeals.remove(meal);
});
_showInfoMessage('즐겨찾기에서 삭제 했습니다.');
} else {
setState(() {
_favoriteMeals.add(meal);
});
_showInfoMessage('즐겨찾기에 추가 되었습니다.');
}
}
void _selectPage(int index) {
setState(() {
_selectedPageIndex = index;
});
}
@override
Widget build(BuildContext context) {
Widget activePage = CategoriesScreen(
onToggleFavorite: _toggleMealFavoriteState,
);
var activePageTitle = 'Categories';
if (_selectedPageIndex == 1) {
activePage = MealsScreen(
meals: _favoriteMeals,
onToggleFavorite: _toggleMealFavoriteState,
);
activePageTitle = 'Your Favorites';
}
return Scaffold(
appBar: AppBar(
title: Text(activePageTitle),
),
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) _favoriteMeals 빈 리스트에 Meal모델값을 넣었다 빼는 토글 함수를 만들어주고 해당 함수를 다른 위젯에 넘겨주어
값을 넣을 수 있다. 해당 토글 함수를 보면 remove와 add에 setState가 들어가 있는데, 이는 해당 함수를 실행할 때 바로 값이 반영(리렌더링)되어 값이 삭제되거나 추가되어 화면에 바로 반영된다.
2) CategoryScreen과 MealScreen에 onToggleFavorite함수를 추가해주었다. 해당 함수에 _toggleMealFavoriteState함수를 전달해준다.
3) _showInfoMessage 함수를 통해 즐겨찾기 추가/삭제 시 스낵바 메시지가 나오게 설정하였다.
- categories.dart
import 'package:flutter/material.dart';
import 'package:meals/data/dummy_data.dart';
import 'package:meals/models/category.dart';
import 'package:meals/models/meal.dart';
import 'package:meals/widgets/category_grid_item.dart';
import 'package:meals/screens/meals.dart';
class CategoriesScreen extends StatelessWidget {
const CategoriesScreen({
super.key,
required this.onToggleFavorite,
});
final void Function(Meal meal) onToggleFavorite;
void _selectCategory(BuildContext context, Category category) {
final filteredMeals = dummyMeals
.where((meal) => meal.categories.contains(category.id))
.toList();
Navigator.of(context).push(
MaterialPageRoute(
builder: (ctx) => MealsScreen(
title: category.title,
meals: filteredMeals,
onToggleFavorite: onToggleFavorite,
),
),
); // Navigator.push(context, route)
}
@override
Widget build(BuildContext context) {
return GridView(
padding: const EdgeInsets.all(24),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 3 / 2,
crossAxisSpacing: 20,
mainAxisSpacing: 20,
),
children: [
// availableCategories.map((category) => CategoryGridItem(category: category)).toList()
for (final category in availableCategories)
CategoryGridItem(
category: category,
onSelectCategory: () {
_selectCategory(context, category);
},
)
],
);
}
}
1) onToggleFavorite 추가
- meals.dart
import 'package:flutter/material.dart';
import 'package:meals/models/meal.dart';
import 'package:meals/screens/meal_details.dart';
import 'package:meals/widgets/meal_item.dart';
class MealsScreen extends StatelessWidget {
const MealsScreen({
super.key,
this.title,
required this.meals,
required this.onToggleFavorite,
});
final String? title;
final List<Meal> meals;
final void Function(Meal meal) onToggleFavorite;
void selectMeal(BuildContext context, Meal meal) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (ctx) => MealDetailsScreen(
meal: meal,
onToggleFavorite: onToggleFavorite,
),
),
);
}
@override
Widget build(BuildContext context) {
Widget content = Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Uh oh ... nothing here!',
style: Theme.of(context).textTheme.headlineLarge!.copyWith(
color: Theme.of(context).colorScheme.onBackground,
),
),
const SizedBox(height: 16),
Text(
'Try selecting a different category!',
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
color: Theme.of(context).colorScheme.onBackground,
),
),
],
),
);
if (meals.isNotEmpty) {
content = ListView.builder(
itemCount: meals.length,
itemBuilder: (ctx, index) => MealItem(
meal: meals[index],
onSelectMeal: (meal) {
selectMeal(context, meal);
},
),
);
}
if (title == null) {
return content;
}
return Scaffold(
appBar: AppBar(
title: Text(title!),
),
body: content,
);
}
}
1) onToggleFavorite 추가
- meal_detail.dart
import 'package:flutter/material.dart';
import 'package:meals/models/meal.dart';
class MealDetailsScreen extends StatelessWidget {
const MealDetailsScreen({
super.key,
required this.meal,
required this.onToggleFavorite,
});
final Meal meal;
final void Function(Meal meal) onToggleFavorite;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(meal.title),
actions: [
IconButton(
onPressed: () {
onToggleFavorite(meal);
},
icon: const Icon(Icons.star),
)
],
),
body: SingleChildScrollView(
child: Column(
children: [
Image.network(
meal.imageUrl,
height: 300,
width: double.infinity,
fit: BoxFit.cover,
),
const SizedBox(height: 14),
Text(
'Ingredients',
style: Theme.of(context).textTheme.titleLarge!.copyWith(
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 14),
for (final ingredient in meal.ingredients)
Text(
ingredient,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onBackground,
),
),
const SizedBox(height: 24),
Text(
'Steps',
style: Theme.of(context).textTheme.titleLarge!.copyWith(
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 14),
for (final step in meal.steps)
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
child: Text(
step,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onBackground,
),
),
),
],
),
));
}
}
1) 세부화면2 appbar 상단 오른쪽에 onToggleFavorite를 실행하게 했다.
'코딩강의 > meals_app(플러터-유데미)' 카테고리의 다른 글
~174. Applying Filters (네비게이션 강의 부분 마지막) (0) | 2023.11.01 |
---|---|
~170. Replacing Screens (Instead of Pushing) (0) | 2023.10.31 |
~164. Adding Tab-based Navigation (0) | 2023.10.29 |
162. Adding Navigation to the MealDetails Screen (1) | 2023.10.28 |
161. Improving the MealItem Widget (0) | 2023.10.26 |