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
์ ๋ฆฌ๋์ด ์๋ ๊ธ์ด ์์ด์ ์ฒจ๋ถํ๋ค.
https://eunjin3786.tistory.com/255
๊ฐ๋จํ๊ฒ ์ค๋ช
ํ์๋ฉด 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
'โน๏ธ ๋ผ์ดํ > 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 |