Udemy ๊ฐ์๊ฐ ์ด๋๋ง 50ํผ๋ฅผ ๋๊ฒผ๋ค. ์ค๊ฐ์ ์ด๋ ค์ด ๋ถ๋ถ๋ ๋ช ๊ฐ ์์์ง๋ง ์ด์ ์ด๋์ ๋ ํผ์์ ๊ตฌ์ถํ ์ ์์ ๊ฒ ๊ฐ๋ค. ์ฌ์ค ๊ทธ๋ ๊ทธ๋ ๋ฐ๋ก ์ ๋ฆฌํ๊ณ ์ถ์๋๋ฐ ์ด์ฉ๋ค๋ณด๋ ๋ฐ๋ ค์ ์ค๋๋ถํฐ๋ผ๋ ๋ฐ๋ก๋ฐ๋ก ์ ๋ฆฌํ๋ ์๊ฐ์ ๊ฐ์ง๋ ค๊ณ ํ๋ค.
Shop ์ดํ์ ์ฐ์ GridView ๋ก ์ํ์ ๋ณด์ฌ์ฃผ๊ณ ์ํ๋ณ๋ก title ๊ณผ ์ข์์ , ์ฅ๋ฐ๊ตฌ๋๋ด๊ธฐ ๊ฐ ์กด์ฌํ๋ค.

์ฐ์ ์ด๋ ๊ฒ ๋ถ๋ฆฌํ๋ค. models ์๋ ์ํ ์ ๋ํด ์ ์๋์ด ์๋ค.
์๋๊ฐ product.dart ์ด๋ค. ๋ณ์๋ฅผ ์ ์ธํด์ฃผ๊ณ ์์ฑ์๊น์ง ๊ตฌํํด์ฃผ์๋ค.
class Product {
final String id;
final String title;
final String description;
final int price;
final String imageUrl;
Product({
required this.id,
required this.title,
required this.description,
required this.price,
required this.imageUrl,
});
}
๋ค์์ ์ํ๋ค์ UI ์ ๋ณด์ฌ์ฃผ๊ธฐ ์ํ screen ์ด๋ค.
์ด๊ธฐ์ ๋๋ฏธ ๋ฐ์ดํฐ๋ฅผ ์์ฑ์์ผ์คฌ๋ค.
import 'package:flutter/material.dart';
import '../widgets/product_item.dart';
import '../models/product.dart';
class ProductOverViewScreen extends StatelessWidget {
final List<Product> loadedProducts = [
Product(
id: 'p1',
title: 'Red Shirt',
description: 'A red shirt - it is pretty red!',
price: 29,
imageUrl:
'https://cdn.pixabay.com/photo/2016/10/02/22/17/red-t-shirt-1710578_1280.jpg',
),
Product(
id: 'p2',
title: 'Trousers',
description: 'A nice pair of trousers.',
price: 59,
imageUrl:
'https://upload.wikimedia.org/wikipedia/commons/thumb/e/e8/Trousers%2C_dress_%28AM_1960.022-8%29.jpg/512px-Trousers%2C_dress_%28AM_1960.022-8%29.jpg',
),
Product(
id: 'p3',
title: 'Yellow Scarf',
description: 'Warm and cozy - exactly what you need for the winter.',
price: 19,
imageUrl:
'https://live.staticflickr.com/4043/4438260868_cc79b3369d_z.jpg',
),
Product(
id: 'p4',
title: 'A Pan',
description: 'Prepare any meal you want.',
price: 49,
imageUrl:
'https://upload.wikimedia.org/wikipedia/commons/thumb/1/14/Cast-Iron-Pan.jpg/1024px-Cast-Iron-Pan.jpg',
),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("My Shop"),
),
body: GridView.builder(
padding: const EdgeInsets.all(10),
itemCount: loadedProducts.length,
itemBuilder: (ctx, index) => ProductItem(
loadedProducts[index].id,
loadedProducts[index].title,
loadedProducts[index].imageUrl,
),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1.5,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
),
);
}
}
์ดํ Widget ๋ถ๋ถ์ ๋ณด๋ฉด body ์ GridView ๋ฅผ builder ๋ก ์ ์ํด์คฌ๋ค. ๋ค์ํ ๋ฐฉ๋ฒ์ด ์์ง๋ง builder ๊ฐ ๊ฐ์ฅ ํธํ๋ค. builder ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์ itemCount itemBuilder gridDelegate ๊ฐ ํ์ํ๋ค. itemCount ๋ GridView ๋ก ๊ทธ๋ ค๋ผ ์ด ๊ฐ์๋ฅผ ์๋ฏธํ๊ณ , itemBuilder ๋ ๊ทธ๋ ค๋ผ ์์ดํ
์ ์๋ฏธํ๋ค. gridDelegate ๋ GridView ์ ์ต์
์ด๋ค.
builder ์์ ProductItem ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ๊ฒ์ ๋ณผ ์ ์๋๋ฐ ํ๋ชฉ ์ ํด๋นํ๋ ์์ ฏ ์ ๋ถ๋ฆฌํ๊ธฐ ์ํจ์ด๋ค. ๊ทธ๋ผ product_item ์ ์ดํด๋ณด์.
import 'package:flutter/material.dart';
class ProductItem extends StatelessWidget {
final String id;
final String title;
final String imageUrl;
ProductItem(this.id, this.title, this.imageUrl);
@override
Widget build(BuildContext context) {
return GridTile(
child: Image.network(imageUrl, fit: BoxFit.cover),
footer: GridTileBar(
backgroundColor: Colors.black54,
leading: IconButton(
icon: Icon(Icons.favorite),
onPressed: () {},
),
title: Text(
title,
textAlign: TextAlign.center,
),
trailing: IconButton(
icon: Icon(
Icons.shopping_cart,
),
onPressed: () {},
),
),
);
}
}
๋ฃ์ด์ค ๋งค๊ฐ๋ณ์ id , title , imageUrl ์ ์ด์ฉํ์ฌ ํ๋ชฉ ์ ๊ทธ๋ ค๋ธ๋ค. ์ฌ๊ธฐ์ GridTile ๋ก ๊ตฌํํ๋๋ฐ LIstTile ๊ณผ ๋์ผํ๋ค. footer ์ header ๊ฐ ์กด์ฌํ๋ค. ๋ง ๊ทธ๋๋ก footer ๋ฅผ ์ฐ๋ฉด ๋ฐ๋ฅ์ header ๋ฅผ ์ฐ๋ฉด ์ฒ์ฅ์ ๊ตฌํ๋๋ค. ์์๋ฅผ ๋ณด์. ์ฒซ ๋ฒ์งธ ์ฌ์ง์ด header , ๋ ๋ฒ์งธ ์ฌ์ง์ด footer ์ด๋ค.


์ดํ child ์์๋ค๋ก leading title trailing ์ด ์กด์ฌํ๊ณ ์ด๋ค์ ์ด์ฉํ์ฌ ๋ฐฐ์น๋ฅผ ํ ์ ์๋ค. leading ์ front , title ์ ใ
ใ
... trailing ์ back ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ์ฝ๋ค. ๊ทธ๋ ๋ค๋ฉด ํ์ฌ ์ดํ์์๋ 3๊ฐ์ง ๋ฅผ ๋ชจ๋ ์ฌ์ฉํ๋๋ฐ ์ด ์ค์ ํ๋๋ผ๋ ์ฌ๋ผ์ง๋ค๋ฉด? ์ญ์ ์์๋ฅผ ๋ณด์.
์ฒซ ๋ฒ์งธ ์ฌ์ง์ leading ์ ์ ๊ฑฐํ ์ฌ์ง, ๋ ๋ฒ์งธ ์ฌ์ง์ title ์ ์ ๊ฑฐํ ์ฌ์ง, ์ธ ๋ฒ์งธ ์ฌ์ง์ trailing ์ ์ ๊ฑฐํ ์ฌ์ง์ด๋ค.



์ด๋ ๊ฒ ๊ตฌํ์ด ๋๋ค.
GridTileBar ๋ฅผ ๋ณด๋ subTitle ์ด๋ผ๋ ๊ฐ์ด ์กด์ฌํ๋ค. ์ดํด๋ณด์.

์ดํ์ GridTile ๋ก ๊ตฌํํ ์ํ ํ๋จ์ ์ข์์ ์ ์ฅ๋ฐ๊ตฌ๋ ๋ฒํผ์ ์ค์ ๋ก ๋์ํ๋๋ก ๊ตฌํํด๋ดค๋ค. ๊ธฐ์กด์ ๋ฐฉ์๊ณผ ๋ค๋ฅธ ๋ฐฉ์์ผ๋ก ๊ตฌํํ๋๋ฐ Provider ๋ผ๋ ์๋ก์ด ๊ฐ๋
์ ๋์
ํ๋ค. ์ฌ์ค ์ดํดํ๋๋ฐ ์ข ์ด๋ ค์ ๋ค. ์์ง๋ ์ ๋๋ก ์ดํดํ์ง ๋ชปํ๊ณ ์๋ค๊ณ ๋ด๋ ๋ฌด๋ฐฉํ๋ค. ์ผ๋จ ๊ณต์๋ฌธ์๋ฅผ ๋ค๊ณ ์๋ค. ํ์ง๋ง ์ด๋ ค์ด๊ฒ ํ์ค...
https://pub.dev/packages/provider
provider | Flutter Package
A wrapper around InheritedWidget to make them easier to use and more reusable.
pub.dev
์ ๋ฆฌ๋์ด ์๋ ๊ธ์ด ์์ด์ ์ฒจ๋ถํ๋ค.
https://eunjin3786.tistory.com/255
[Flutter] Provider๋ก ์ฑ ์ํ ๊ด๋ฆฌํ๊ธฐ
์๋ ์์งค์ฒ๋ผ ์ฌ๋ฌ ํ๋ฉด์์ ์ํ๋ฅผ ๊ณต์ ํด์ผํ ๋ ์ด๋ป๊ฒ ํด์ผํ๋์ง State management ๋ฌธ์๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค. 1) ์ ์ธ์ ์ผ๋ก ์๊ฐํ๊ธฐ ์ฐ์ ์ ์ธ์ ์ผ๋ก ์๊ฐํ๋ผ๊ณ ํฉ๋๋ค. SwiftUI ์ฒ๋ผ state๊ฐ ๋ฐ
eunjin3786.tistory.com
๊ฐ๋จํ๊ฒ ์ค๋ช
ํ์๋ฉด Flutter ๋ Widget ์ผ๋ก ๊ตฌ์ฑ๋์ด ์๋๋ฐ ์ด๊ฒ ์ดํ์ ์ ์ํ๋ค๋ณด๋ฉด Widget ๊ฐ์ Depth ๊ฐ ๊ธฐํ๊ธ์์ ์ผ๋ก ์ฆ๊ฐํ๊ฒ ๋๋ค. main → class A, class B, class c... ๋ญ ์๊ด ์์์๋ ์์ง๋ง ๋ง์ฝ์ ์๋ก ๋ค๋ฅธ ๋ถ๋ชจ๋ฅผ ๊ฐ๋ (class A-1๊ณผ class C-3 ์ด๋ฐ ๋๋?) ๊ฒฝ์ฐ์ ๋งค๋ฒ build ๋ฅผ ๋ค์ํด์ผํ๋ค. ์ด๋ ๊ฒ ๋งํ๋ฉด ์ดํด๊ฐ ์ด๋ ค์ฐ๋ ์์๋ฅผ ๋ค์ด๋ณด์.

์ํ์ ์ข์์ ์ ์ฅ๋ฐ๊ตฌ๋ ๋ฒํผ์ด ์กด์ฌํ๋๋ฐ ์ข์์ ํ์ด์ง์ ์ฅ๋ฐ๊ตฌ๋ ํ์ด์ง๊ฐ ์๋ค๊ณ ๊ฐ์ ํด๋ณด์. ๋ฒํผ์ด ํด๋ฆญ๋ ๋๋ง๋ค ๊ณ์ build ๊ฐ ๋ฐ์ํ๋ค. Widget ์ Depth ๊ฐ ๊น์ง ์์ผ๋ฉด ์๊ด์ด ์์ง๋ง ์ฐ์์ ์ผ๋ก inherit ๋๋ค๊ณ ๊ฐ์ ํ๋ฉด ๋ฉ๋ชจ๋ฆฌ ๋์ ๊ฐ ๋ฐ์ํ ๊ฒ์ด๋ค. ์ด๋ฅผ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๋ ๊ฒ์ด ๋ฐ๋ก provider ์ด๋ค. ๋ฐ์ดํฐ ์ ์ฝ๊ฒ ์ ๊ทผํ๊ฒ ๋ค๋ ๊ฒ์ด ๋ชฉํ
products.dart ์ ์ด๋ฅผ ์ฌ์ฉํ๋ products_grid ๋ฅผ ์ดํด๋ณด์.
import 'package:flutter/material.dart';
import 'product.dart';
class Products with ChangeNotifier {
List<Product> _items = [
Product(
id: 'p1',
title: 'Red Shirt',
description: 'A red shirt - it is pretty red!',
price: 29,
imageUrl:
'https://cdn.pixabay.com/photo/2016/10/02/22/17/red-t-shirt-1710578_1280.jpg',
),
Product(
id: 'p2',
title: 'Trousers',
description: 'A nice pair of trousers.',
price: 59,
imageUrl:
'https://upload.wikimedia.org/wikipedia/commons/thumb/e/e8/Trousers%2C_dress_%28AM_1960.022-8%29.jpg/512px-Trousers%2C_dress_%28AM_1960.022-8%29.jpg',
),
Product(
id: 'p3',
title: 'Yellow Scarf',
description: 'Warm and cozy - exactly what you need for the winter.',
price: 19,
imageUrl:
'https://live.staticflickr.com/4043/4438260868_cc79b3369d_z.jpg',
),
Product(
id: 'p4',
title: 'A Pan',
description: 'Prepare any meal you want.',
price: 49,
imageUrl:
'https://upload.wikimedia.org/wikipedia/commons/thumb/1/14/Cast-Iron-Pan.jpg/1024px-Cast-Iron-Pan.jpg',
),
];
var showFavoritesOnly = false;
List<Product> get items {
if (showFavoritesOnly) {
return _items.where((productItem) => productItem.isFavorite).toList();
}
return [..._items];
}
Product findById(String id){
return _items.firstWhere((element) => element.id == id);
}
void addProduct() {
notifyListeners();
}
}
import 'package:flutter/material.dart';
import '/providers/products.dart';
import '/widgets/product_item.dart';
import 'package:provider/provider.dart';
class ProductsGrid extends StatelessWidget {
@override
Widget build(BuildContext context) {
final productsData = Provider.of<Products>(context);
final products = productsData.items;
return GridView.builder(
padding: const EdgeInsets.all(10),
itemCount: products.length,
itemBuilder: (ctx, index) => ChangeNotifierProvider(
create: (c) => products[index],
child: ProductItem(),
),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1.5,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
);
}
}
products_grid ๋ฅผ ๋จผ์ ์ดํด๋ณด๋ฉด Provider.of<์๋ฃํ>(context) ๋ก ์ ์ธํ ๊ฒ์ ๋ณผ ์ ์๋ค. ์ด๊ฒ์ด ๋ฐ๋ก Provider ๋ก ๊ด๋ฆฌํ ๋ณ์์ด๋ค. ChangeNotifierProvider ๋ Provider ๋ก ๊ด๋ฆฌํ๋ ์์ดํ
์ ์ํ ๋ณํ๊ฐ ์ผ์ด๋๋ฉด setState ํด์ฃผ๋ ๋ถ๋ถ์ด๋ค.
import 'package:flutter/material.dart';
import '/providers/product.dart';
import 'package:provider/provider.dart';
import '../screens/product_detail_screen.dart';
class ProductItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
final product = Provider.of<Product>(context, listen : false);
return ClipRRect(
borderRadius: BorderRadius.circular(10),
child: GridTile(
child: GestureDetector(
onTap: () {
Navigator.of(context).pushNamed(
ProductDetailScreen.routeName,
arguments: product.id,
);
},
child: Image.network(product.imageUrl, fit: BoxFit.cover),
),
footer: GridTileBar(
backgroundColor: Colors.black54,
leading: Consumer<Product>(
builder: (ctx, product, child) => IconButton(
icon: Icon(
product.isFavorite ? Icons.favorite: Icons.favorite_border,
),
onPressed: () {
product.toggleFavoriteStatus();
},
color: Theme.of(context).accentColor,
),
),
title: Text(
product.title,
textAlign: TextAlign.center,
),
trailing: IconButton(
icon: Icon(
Icons.shopping_cart,
),
onPressed: () {},
color: Theme.of(context).accentColor,
),
),
),
);
}
}
ProductItem ์ ์ดํด๋ณด๋ฉด ์ญ์ Provider.of<Product>(context, listen : false) ๋ก ๊ด๋ฆฌํ๊ณ ์๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ์ดํ์ Consumer ๋ ๋ฑ์ฅํ๋๋ฐ Consumer ๋ถ๋ถ๋ง ์๋ก build ํ๊ฒ๋๋ค.
์ค๋ช
์ด ๋ง์ด ๋ถ์กฑํ๊ธด ํ๋ฐ ๋๋ ์์ง 100% ์ดํดํ๊ณ ์์ง๋ ์์ ๊ฒ ๊ฐ๋ค. ์ค์ ๋ก ์ฌ์ฉํด๋ณด๋ฉด์ ๊ฐ์ ์ก์์ผ๊ฒ ๋ค.
์์ค์ฝ๋๋ ์๋ ๋งํฌ๋ฅผ ํตํด ์ ์ํ๋ฉด ํ์ธํ ์ ์๋ค.
https://github.com/k906506/2021-Summer-Flutter-Study/tree/master/myfourthflutterapp
GitHub - k906506/2021-Summer-Flutter-Study: Studying hybrid app development using Dart and Flutter in summer vacation Mogakko
Studying hybrid app development using Dart and Flutter in summer vacation Mogakko ๐พ - GitHub - k906506/2021-Summer-Flutter-Study: Studying hybrid app development using Dart and Flutter in summer va...
github.com
'โน๏ธ ๋ผ์ดํ > 2021 ์ฌ๋ฆ๋ฐฉํ ๋ชจ๊ฐ์ฝ(๊ฐ์ธ)' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [์ฝ๋ ํ๊ตฌ๋ง 2] 6์ฃผ์ฐจ - Flutter : Shop App 3 (0) | 2021.08.10 |
|---|---|
| [์ฝ๋ ํ๊ตฌ๋ง 2] 5์ฃผ์ฐจ - Flutter : Shop App 2 (0) | 2021.08.10 |
| [์ฝ๋ ํ๊ตฌ๋ง 2] 3์ฃผ์ฐจ - Flutter : Personal Expense App (2) | 2021.07.22 |
| [์ฝ๋ ํ๊ตฌ๋ง 2] 2์ฃผ์ฐจ - Flutter : Personal Expense App (0) | 2021.07.15 |
| [์ฝ๋ ํ๊ตฌ๋ง 2] 1์ฃผ์ฐจ - Flutter : Quiz App (0) | 2021.07.07 |