المتجر: تفاصيل المنتج
جزء من قسم المشاريع في رحلة 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 عبر الإنترنت