חנות: פרטי המוצר
חלק מהיחידה פרויקטים במסלול ה-Flutter של Coddy. שיעור 21 מתוך 36.
לוחצים על מוצר בחנות ומגיעים לדף שלו: תמונה גדולה, המחיר וכמה מילים עליו. זהו מסך שני, ProductScreen, שמקבל את המוצר דרך הבנאי שלו.
כדי לאפשר לחיצה על כרטיס שלם, מציבים InkWell בתוך Card, סביב התוכן שלו. הוא מפעיל את onTap ומצייר את אפקט הגלים שרואים באפליקציות Material; Clip.antiAlias של הכרטיס משאיר את אפקט הגלים בתוך הפינות המעוגלות.
Card(
clipBehavior: Clip.antiAlias,
child: InkWell(
onTap: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) => ProductScreen(product: product),
));
},
child: Column(...),
),
)דף המוצר הוא ListView, כך שאפשר לגלול בתיאור ארוך. AppBar שלו מציג את שם המוצר ואת החץ לחזרה, ש-Flutter מוסיף עבורך במסך שנפתח באמצעות דחיפה.
כדי להציג מחיר גדול, מתחילים מ-headlineSmall של ערכת העיצוב ומשנים רק את מה שצריך באמצעות copyWith: textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold). הסימן ?. מופיע כי סגנון בערכת העיצוב יכול להיות null.
אתגר
בינוניהקצה לכל מוצר דף:
- כתוב
ProductScreenשמקבלrequiredproduct, כשהשם מופיע ב־AppBarשלו - הגוף שלו הוא
ListViewעם ריווח פנימי של 24 פיקסלים: אריח בגובה 240 פיקסלים בצבעproduct.color.shade50עם פינות מעוגלות ברדיוס 24 והסמל בגודל 120, אחריו רווח של 24 פיקסלים, המחיר (בגופן מודגשheadlineSmallובצבע הראשי), רווח של 12 פיקסלים והתיאור בגופןbodyLarge - עטוף את ה־
Columnשל כל כרטיס ב־InkWellשמעביר לדף המוצר
נסו בעצמכם
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
void main() {
runApp(const MyApp());
}
final money = NumberFormat.currency(locale: 'en_US', symbol: '\$');
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.orange),
),
home: const ShopScreen(),
);
}
}
class Product {
const Product({
required this.name,
required this.price,
required this.icon,
required this.color,
required this.description,
});
final String name;
final double price;
final IconData icon;
final MaterialColor color;
final String description;
}
const List<Product> products = [
Product(
name: 'Headphones',
price: 59,
icon: Icons.headphones,
color: Colors.indigo,
description: 'Wireless headphones with 30 hours of battery.',
),
Product(
name: 'Watch',
price: 129,
icon: Icons.watch,
color: Colors.teal,
description: 'A smart watch that counts your steps.',
),
Product(
name: 'Backpack',
price: 45.5,
icon: Icons.backpack,
color: Colors.orange,
description: 'Room for a laptop, a lunch and a jacket.',
),
Product(
name: 'Mug',
price: 12,
icon: Icons.coffee,
color: Colors.brown,
description: 'Holds a big coffee and keeps it warm.',
),
Product(
name: 'Camera',
price: 1299,
icon: Icons.photo_camera,
color: Colors.blueGrey,
description: 'A mirrorless camera for sharp photos.',
),
Product(
name: 'Sneakers',
price: 89.99,
icon: Icons.directions_run,
color: Colors.red,
description: 'Light running shoes for every day.',
),
];
class ShopScreen extends StatelessWidget {
const ShopScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Coddy Shop')),
body: GridView.count(
crossAxisCount: 2,
padding: const EdgeInsets.all(16),
mainAxisSpacing: 12,
crossAxisSpacing: 12,
childAspectRatio: 0.8,
children: [
for (Product product in products) ProductCard(product: product),
],
),
);
}
}
class ProductCard extends StatelessWidget {
const ProductCard({super.key, required this.product});
final Product product;
@override
Widget build(BuildContext context) {
return Card(
clipBehavior: Clip.antiAlias,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Container(
width: double.infinity,
alignment: Alignment.center,
color: product.color.shade50,
child: Icon(product.icon, size: 56, color: product.color),
),
),
Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.name,
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 4),
Text(
money.format(product.price),
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary,
),
),
],
),
),
],
),
);
}
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה פרויקטים
תרגלו בעצמכם: קומפיילר Flutter אונליין