-
Flutter_17. GridView & Inkwell & FadeImage & Stack> Frontend/Flutter 2023. 5. 17. 17:35
0) 학습 내용
- GridView & Navigator.push
- Inkwell & BoxDecoration
- meals.dart setup
- FadeImage
- Stack & Positioned
- ModelItemTrait setup
1) GridView & Navigator.push
- Controls the layout of grid: default [top to bottom], [left to right ], [horizontally by 2 ]
<categories.dart>
body: GridView( padding: const EdgeInsets.all(24), // gridDelegate: controls the layout of Grid : top to bottom, left to right, horizontally 2 gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, // Row 별 item 갯수 childAspectRatio: 3 / 2, // item의 가로 세로 비율 => 3줄 가로 / 2줄 세로. crossAxisSpacing: 20, // Row item의 간격 mainAxisSpacing: 20, // Colum item의 간격 ), children: availableCategories // dummy data .map((category) => CategoryGridItem( category: category, onSelectCategory: () { _selectCategory(context, category); })) .toList(),
- 받은 dummy-category-item 하나당, CategoryItem 에 category-item 과 _selectCategory 함수를 보냄.
void _selectCategory(BuildContext context, Category category) { final filteredMeals = dummyMeals .where((meal) => meal.categories.contains(category.id)) .toList(); // Navigator.push(context, route) Navigator.of(context).push( MaterialPageRoute( builder: (ctx) => MealsScreens(title: category.title, meals: filteredMeals)), ); }
- Navigator.push() 메소드를 통해 화면전환 가능.
- MeaterialPageRoute( builder : (context) => 함께 보낼 내용 전달 )
2) Inkwel && BoxDecoration in category_grid_item.dart
<category_grid_item.dart >
// component for single item import 'package:flutter/material.dart'; import 'package:flutter08_meal/models/category.dart'; class CategoryGridItem extends StatelessWidget { const CategoryGridItem({required this.category, super.key, required this.onSelectCategory}); final Category category; final void Function() onSelectCategory; @override Widget build(BuildContext context) { // Inkwell: Event Listener with a Feedback return InkWell( onTap: onSelectCategory, splashColor: Theme.of(context).primaryColor, // visual tapping effect. borderRadius: BorderRadius.circular(16), child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), gradient: LinearGradient(colors: [ category.color.withOpacity(.55), category.color.withOpacity(.9), ], begin: Alignment.topLeft, end: Alignment.bottomRight)), child: Text( category.title, style: Theme.of(context) .textTheme .titleLarge! .copyWith(color: Theme.of(context).colorScheme.onBackground), ), ), ); } }
- Inkwell : Container 와 같이 별도의 Event-Listener ? 적용할 수 있는 widget.
- splashColor : 탭시, 효과
- BoxDecoration : 박스에 대한 꾸밈을 설정
3) meals.dart Setup (화면 전환 )
<meals.dart>
import 'package:flutter/material.dart'; import 'package:flutter08_meal/widgets/meal_item.dart'; import '../models/meal.dart'; class MealsScreens extends StatelessWidget { const MealsScreens({super.key, required this.title, required this.meals}); final String title; final List<Meal> meals; @override Widget build(BuildContext context) { Widget content = ListView.builder( itemCount: meals.length, itemBuilder: (ctx, index) => MealItem(meal: meals[index])); if (meals.isEmpty) { content = Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( 'No available Item', style: Theme.of(context) .textTheme .headlineLarge! .copyWith(color: Theme.of(context).colorScheme.onBackground), ), const SizedBox(height: 16), Text( 'Try Select a different category', style: Theme.of(context) .textTheme .bodyLarge! .copyWith(color: Theme.of(context).colorScheme.onBackground), ) ], ), ); } return Scaffold(appBar: AppBar(title: Text(title)), body: content); } }
- 받은 meals 의 갯수가, 비어있지 않은 경우 meal_item && 비어있는 경우 비어있다고 알려줌.
4) FadeInImage in meal_item.dart
import 'package:flutter/material.dart'; import 'package:flutter08_meal/models/meal.dart'; import 'package:flutter08_meal/widgets/meal_item_trait.dart'; import 'package:transparent_image/transparent_image.dart'; class MealItem extends StatelessWidget { const MealItem({super.key, required this.meal}); final Meal meal; String get getComplexityText { return meal.complexity.name[0].toUpperCase() + meal.complexity.name.substring(1); } String get getAffordability { return meal.affordability.name[0].toUpperCase() + meal.affordability.name.substring(1); } @override Widget build(BuildContext context) { return Card( margin: const EdgeInsets.all(8), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), clipBehavior: Clip.hardEdge, //overflow: hidden elevation: 2, child: InkWell( onTap: () {}, // stack: position multiple widgets above each other, but not above each other as column child: Stack( //stack ignores shape attribute. children: [ // display an Image in fade. // flutter pub add transparent_image FadeInImage( placeholder: MemoryImage(kTransparentImage), image: NetworkImage(meal.imageUrl), fit: BoxFit.cover, // fit height: 200, width: double.infinity, ), // Positioned widget: can give tlbr info to positioni the child. Positioned( bottom: 0, left: 0, right: 0, child: Container( color: Colors.black54, padding: const EdgeInsets.symmetric(horizontal: 44, vertical: 5), child: Column( children: [ Text( meal.title, maxLines: 2, textAlign: TextAlign.center, softWrap: true, overflow: TextOverflow.ellipsis, style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Colors.white), ), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ MealItemTrait(icon: Icons.schedule, label: '${meal.duration} min'), const SizedBox(width: 12), MealItemTrait(icon: Icons.work, label: getComplexityText), const SizedBox(width: 12), MealItemTrait(icon: Icons.work, label: getAffordability), ], ) ], ), ), ) ], ), ), ); } }
- FadeInImage =>
- 온라인에서 이미지를 다운로드할 때, 일정 시간이 걸려서 아무것도 안보이는데, 올바르지 않음
- 온라인에서 이미지를 다운로드할 때, 빈 칸이 보여지고, 이미지를 천천히 (fade-in) 해서 가져옴.
- Terminal: flutter pub add transparent_image
FadeInImage( placeholder: MemoryImage(kTransparentImage), image: NetworkImage(meal.imageUrl), fit: BoxFit.cover, // fit height: 200, width: double.infinity, ),
5) Stack & Positioned in meal_item.dart
- Stack: Column 과 Row는 각각 세로, 가로 방향으로 순서대로 배치하지만, 겹쳐있듯이, 원하는 대로 배치할 경우 사용
- Positioned: Stack은 단지 정해진 위치나 규칙 없이 위젯들을 배치하기에, 사용자가 직접 위치를 등록해야되는데, 이때 사용
Positioned( bottom: 0, left: 0, right: 0, child: Container( color: Colors.black54, padding: const EdgeInsets.symmetric(horizontal: 44, vertical: 5), child: Column( children: [ Text( meal.title, maxLines: 2, textAlign: TextAlign.center, softWrap: true, overflow: TextOverflow.ellipsis, style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Colors.white), ), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ MealItemTrait(icon: Icons.schedule, label: '${meal.duration} min'), const SizedBox(width: 12), MealItemTrait(icon: Icons.work, label: getComplexityText), const SizedBox(width: 12), MealItemTrait(icon: Icons.work, label: getAffordability), ], ) ], ), ), )
- 여기서 positioned widget 의 위치 시작점은 Stack 안에 있는 FadeImage 기준으로 잡히게 된다.
- 따라서, Positioned 의 값이 bottom: 0, left: 0, right: 0 이므로, FadeImage 기준으로 위치가 잡힘
6) ModalItemTrait.dart Setup
import 'package:flutter/material.dart'; class MealItemTrait extends StatelessWidget { const MealItemTrait({super.key, required this.icon, required this.label}); final IconData icon; final String label; @override Widget build(BuildContext context) { return Row( children: [ Icon( icon, size: 17, color: Colors.white, ), const SizedBox( width: 6, ), Text( label, style: const TextStyle(color: Colors.white), ) ], ); } }
'> Frontend > Flutter' 카테고리의 다른 글
Flutter_19. HTTP Requests (0) 2023.05.31 Flutter_18. App State_Provider(feat. Riverpod) (0) 2023.05.21 Flutter_16. Flutter Internals, Way of Render with Tree, Key, Mutating Values (0) 2023.05.16 Flutter_15. Building Responsive and adaptive User Interfaces (0) 2023.05.15 Flutter_14. Theme in Widget & Dark Mode & Named Constructor & For in (0) 2023.05.12