Open5

SliverListとは?

muranakarmuranakar

以下サンプルコード

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Grid & List Sample',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        useMaterial3: true,
      ),
      home: const HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('ショッピングアプリサンプル'),
      ),
      body: CustomScrollView(
        slivers: [
          // カテゴリーグリッドセクション
          SliverToBoxAdapter(
            child: Padding(
              padding: const EdgeInsets.all(16.0),
              child: Text(
                'カテゴリー',
                style: Theme.of(context).textTheme.headlineSmall,
              ),
            ),
          ),
          SliverPadding(
            padding: const EdgeInsets.symmetric(horizontal: 14.0),
            sliver: SliverGrid(
              gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 4,
                mainAxisSpacing: 16.0,
                crossAxisSpacing: 16.0,
                childAspectRatio: 1.0,
              ),
              delegate: SliverChildBuilderDelegate(
                (context, index) {
                  final categories = [
                    {'icon': Icons.smartphone, 'name': 'スマホ'},
                    {'icon': Icons.laptop, 'name': 'PC'},
                    {'icon': Icons.headphones, 'name': '音響機器'},
                    {'icon': Icons.camera_alt, 'name': 'カメラ'},
                    {'icon': Icons.watch, 'name': '時計'},
                    {'icon': Icons.sports_esports, 'name': 'ゲーム'},
                    {'icon': Icons.tv, 'name': 'TV'},
                    {'icon': Icons.kitchen, 'name': '家電'},
                  ];
                  
                  return  CategoryItem(
                    icon: categories[index]['icon'] as IconData,
                    name: categories[index]['name'] as String,
                  );
                },
                childCount: 8,
              ),
            ),
          ),

          // おすすめ商品リストセクション
          SliverToBoxAdapter(
            child: Padding(
              padding: const EdgeInsets.all(16.0),
              child: Text(
                'おすすめ商品',
                style: Theme.of(context).textTheme.headlineSmall,
              ),
            ),
          ),
          SliverList(
            delegate: SliverChildBuilderDelegate(
              (context, index) {
                return ProductListItem(
                  title: '商品名 ${index + 1}',
                  price: ${(index + 1) * 1000}',
                  imageUrl: 'https://via.placeholder.com/150',
                );
              },
              childCount: 10,
            ),
          ),
        ],
      ),
    );
  }
}

// カテゴリーアイテムウィジェット
class CategoryItem extends StatelessWidget {
  final IconData icon;
  final String name;

  const CategoryItem({
    super.key,
    required this.icon,
    required this.name,
  });

  
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Container(
          padding: const EdgeInsets.all(12),
          decoration: BoxDecoration(
            color: Colors.blue.shade50,
            borderRadius: BorderRadius.circular(12),
          ),
          child: Icon(icon, size: 32, color: Colors.blue),
        ),
        const SizedBox(height: 8),
        Text(
          name,
          style: const TextStyle(fontSize: 12),
          textAlign: TextAlign.center,
        ),
      ],
    );
  }
}

// 商品リストアイテムウィジェット
class ProductListItem extends StatelessWidget {
  final String title;
  final String price;
  final String imageUrl;

  const ProductListItem({
    super.key,
    required this.title,
    required this.price,
    required this.imageUrl,
  });

  
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
      child: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Row(
          children: [
            Container(
              width: 80,
              height: 80,
              decoration: BoxDecoration(
                color: Colors.grey.shade200,
                borderRadius: BorderRadius.circular(8),
              ),
              child: const Icon(Icons.image, size: 40, color: Colors.grey),
            ),
            const SizedBox(width: 16),
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    title,
                    style: const TextStyle(
                      fontSize: 16,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(height: 8),
                  Text(
                    price,
                    style: TextStyle(
                      fontSize: 14,
                      color: Colors.green.shade700,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ],
              ),
            ),
            IconButton(
              icon: const Icon(Icons.favorite_border),
              onPressed: () {},
            ),
          ],
        ),
      ),
    );
  }
}