Flutter for OpenHarmony《淘淘购物》主页点击跳转功能详解:从 UI 响应到页面导航的完整实现

Flutter for OpenHarmony《淘淘购物》主页点击跳转功能详解:从 UI 响应到页面导航的完整实现

在移动应用开发中,点击跳转 是最基础也最重要的交互模式之一。它连接了应用的不同页面,构成了用户的核心使用路径。本文将基于您提供的完整

Flutter 代码 (import+pa.txt),深入剖析《淘淘购物》App 主页(HomeScreen)

中所有可点击元素的跳转逻辑,揭示其背后精妙的设计与实现。

我们将逐一解析以下关键跳转点:

  1. 顶部搜索图标/搜索框 -> 搜索页面
  2. 轮播图 (Banner) -> 分类详情页
  3. 快捷入口 (QuickEntry) -> 分类详情页
  4. 推荐商品卡片 (ProductCard) -> 商品详情页

通过本文,您将掌握 Flutter 中 NavigatorGestureDetectorMaterialPageRoute 等核心概念,并理解如何构建一个流畅、响应迅速的电商应用导航体系。


完整效果展示

这里点击就可以跳转到相对应的页面


完整代码展示

dart 复制代码
import 'package:flutter/material.dart';

void main() {
  runApp(const ShoppingApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '淘淘购物',
      theme: ThemeData(
        primaryColor: Colors.orange,
        scaffoldBackgroundColor: Colors.grey[100],
        useMaterial3: true,
      ),
      home: const MainScreen(),
      debugShowCheckedModeBanner: false,
    );
  }
}

// 搜索结果页面
class SearchResultScreen extends StatelessWidget {
  final String query;

  const SearchResultScreen({super.key, required this.query});

  @override
  Widget build(BuildContext context) {
    // 过滤商品
    final filteredProducts = _allProducts.where((product) {
      return product.name.toLowerCase().contains(query.toLowerCase());
    }).toList();

    return Scaffold(
      appBar: AppBar(
        title: Text('搜索: $query'),
        backgroundColor: Colors.orange,
      ),
      body: filteredProducts.isEmpty
          ? const Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Icon(Icons.search_off, size: 80, color: Colors.grey),
                  SizedBox(height: 20),
                  Text(
                    '未找到相关商品',
                    style: TextStyle(fontSize: 18, color: Colors.grey),
                  ),
                ],
              ),
            )
          : GridView.builder(
              padding: const EdgeInsets.all(10),
              gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                childAspectRatio: 0.75,
                crossAxisSpacing: 10,
                mainAxisSpacing: 10,
              ),
              itemCount: filteredProducts.length,
              itemBuilder: (context, index) {
                return ProductCard(
                  product: filteredProducts[index],
                  onTap: () {
                    Navigator.push(
                      context,
                      MaterialPageRoute(
                        builder: (context) => ProductDetailScreen(
                          product: filteredProducts[index],
                        ),
                      ),
                    );
                  },
                );
              },
            ),
    );
  }
}

class MainScreen extends StatefulWidget {
  const MainScreen({super.key});

  @override
  State<MainScreen> createState() => _MainScreenState();
}

class _MainScreenState extends State<MainScreen> {
  int _currentIndex = 0;
  final List<Widget> _screens = [
    const HomeScreen(),
    const CategoryScreen(),
    const CartScreen(),
    const ProfileScreen(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: _screens[_currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        onTap: (index) {
          setState(() {
            _currentIndex = index;
          });
        },
        type: BottomNavigationBarType.fixed,
        selectedItemColor: Colors.orange,
        unselectedItemColor: Colors.grey,
        items: const [
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: '首页',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.category),
            label: '分类',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.shopping_cart),
            label: '购物车',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.person),
            label: '我的',
          ),
        ],
      ),
    );
  }
}

// 首页
class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('淘淘购物'),
        backgroundColor: Colors.orange,
        elevation: 0,
        actions: [
          IconButton(
            icon: const Icon(Icons.search),
            onPressed: () {
              _showSearchDialog(context);
            },
          ),
          IconButton(
            icon: const Icon(Icons.message),
            onPressed: () {},
          ),
        ],
      ),
      body: ListView(
        children: [
          // 搜索框
          GestureDetector(
            onTap: () => _showSearchDialog(context),
            child: Container(
              padding: const EdgeInsets.all(10),
              color: Colors.orange,
              child: Container(
                padding:
                    const EdgeInsets.symmetric(horizontal: 15, vertical: 12),
                decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(20),
                ),
                child: Row(
                  children: const [
                    Icon(Icons.search, color: Colors.grey),
                    SizedBox(width: 10),
                    Text(
                      '搜索宝贝',
                      style: TextStyle(color: Colors.grey, fontSize: 16),
                    ),
                  ],
                ),
              ),
            ),
          ),

          // 轮播图区域
          _BannerCarousel(),

          // 快捷入口
          Container(
            padding: const EdgeInsets.all(15),
            color: Colors.white,
            child: GridView.count(
              crossAxisCount: 5,
              shrinkWrap: true,
              physics: const NeverScrollableScrollPhysics(),
              children: [
                QuickEntry(Icons.shopping_bag, '天猫', 0),
                QuickEntry(Icons.card_giftcard, '聚划算', 1),
                QuickEntry(Icons.local_offer, '优惠券', 2),
                QuickEntry(Icons.phone_android, '数码', 1),
                QuickEntry(Icons.style, '服饰', 5),
                QuickEntry(Icons.home, '家居', 4),
                QuickEntry(Icons.restaurant, '美食', 9),
                QuickEntry(Icons.flight, '旅行', 2),
                QuickEntry(Icons.sports_basketball, '运动', 7),
                QuickEntry(Icons.book, '图书', 0),
              ],
            ),
          ),

          const SizedBox(height: 10),

          // 推荐商品
          Container(
            color: Colors.white,
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                const Padding(
                  padding: EdgeInsets.all(15),
                  child: Text(
                    '为你推荐',
                    style: TextStyle(
                      fontSize: 18,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ),
                GridView.builder(
                  shrinkWrap: true,
                  physics: const NeverScrollableScrollPhysics(),
                  padding: const EdgeInsets.all(10),
                  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                    crossAxisCount: 2,
                    childAspectRatio: 0.75,
                    crossAxisSpacing: 10,
                    mainAxisSpacing: 10,
                  ),
                  itemCount: _recommendedProducts.length,
                  itemBuilder: (context, index) {
                    return ProductCard(
                      product: _recommendedProducts[index],
                      onTap: () {
                        Navigator.push(
                          context,
                          MaterialPageRoute(
                            builder: (context) => ProductDetailScreen(
                              product: _recommendedProducts[index],
                            ),
                          ),
                        );
                      },
                    );
                  },
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  // 显示搜索对话框
  void _showSearchDialog(BuildContext context) {
    final TextEditingController searchController = TextEditingController();

    showSearch(
      context: context,
      delegate: ProductSearchDelegate(),
    );
  }
}

// 搜索委托
class ProductSearchDelegate extends SearchDelegate<String> {
  @override
  List<Widget> buildActions(BuildContext context) {
    return [
      IconButton(
        icon: const Icon(Icons.clear),
        onPressed: () {
          query = '';
        },
      ),
    ];
  }

  @override
  Widget buildLeading(BuildContext context) {
    return IconButton(
      icon: const Icon(Icons.arrow_back),
      onPressed: () {
        close(context, '');
      },
    );
  }

  @override
  Widget buildResults(BuildContext context) {
    final filteredProducts = _allProducts.where((product) {
      return product.name.toLowerCase().contains(query.toLowerCase());
    }).toList();

    return filteredProducts.isEmpty
        ? const Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Icon(Icons.search_off, size: 80, color: Colors.grey),
                SizedBox(height: 20),
                Text(
                  '未找到相关商品',
                  style: TextStyle(fontSize: 18, color: Colors.grey),
                ),
              ],
            ),
          )
        : GridView.builder(
            padding: const EdgeInsets.all(10),
            gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 2,
              childAspectRatio: 0.75,
              crossAxisSpacing: 10,
              mainAxisSpacing: 10,
            ),
            itemCount: filteredProducts.length,
            itemBuilder: (context, index) {
              return ProductCard(
                product: filteredProducts[index],
                onTap: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => ProductDetailScreen(
                        product: filteredProducts[index],
                      ),
                    ),
                  );
                },
              );
            },
          );
  }

  @override
  Widget buildSuggestions(BuildContext context) {
    final suggestionList = query.isEmpty
        ? _allProducts.take(6).toList()
        : _allProducts.where((product) {
            return product.name.toLowerCase().contains(query.toLowerCase());
          }).toList();

    return ListView.builder(
      itemCount: suggestionList.length,
      itemBuilder: (context, index) {
        final product = suggestionList[index];
        return ListTile(
          leading: ClipRRect(
            borderRadius: BorderRadius.circular(5),
            child: Image.network(
              product.imageUrl,
              width: 50,
              height: 50,
              fit: BoxFit.cover,
              errorBuilder: (context, error, stackTrace) {
                return Container(
                  width: 50,
                  height: 50,
                  decoration: BoxDecoration(
                    color: Colors.orange[100],
                    borderRadius: BorderRadius.circular(5),
                  ),
                  child: const Icon(Icons.image, color: Colors.orange),
                );
              },
            ),
          ),
          title: Text(product.name),
          subtitle: Text('¥${product.price.toStringAsFixed(2)}'),
          trailing: const Icon(Icons.arrow_forward_ios, size: 16),
          onTap: () {
            query = product.name;
            showResults(context);
          },
        );
      },
    );
  }
}

// 快捷入口组件
class QuickEntry extends StatefulWidget {
  final IconData icon;
  final String label;
  final int categoryIndex;

  const QuickEntry(this.icon, this.label, this.categoryIndex, {super.key});

  @override
  State<QuickEntry> createState() => _QuickEntryState();
}

class _QuickEntryState extends State<QuickEntry>
    with SingleTickerProviderStateMixin {
  late AnimationController _animationController;
  late Animation<double> _scaleAnimation;
  bool _isPressed = false;

  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
      duration: const Duration(milliseconds: 100),
      vsync: this,
    );
    _scaleAnimation = Tween<double>(begin: 1.0, end: 0.9).animate(
      CurvedAnimation(parent: _animationController, curve: Curves.easeInOut),
    );
  }

  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTapDown: (_) {
        setState(() {
          _isPressed = true;
        });
        _animationController.forward();
      },
      onTapUp: (_) {
        setState(() {
          _isPressed = false;
        });
        _animationController.reverse();
        // 跳转到分类页面
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => CategoryDetailScreen(
              initialIndex: widget.categoryIndex,
            ),
          ),
        );
      },
      onTapCancel: () {
        setState(() {
          _isPressed = false;
        });
        _animationController.reverse();
      },
      child: ScaleTransition(
        scale: _scaleAnimation,
        child: AnimatedContainer(
          duration: const Duration(milliseconds: 200),
          decoration: BoxDecoration(
            color: _isPressed ? Colors.orange[50] : Colors.transparent,
            borderRadius: BorderRadius.circular(10),
          ),
          padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 3),
          child: Column(
            children: [
              AnimatedContainer(
                duration: const Duration(milliseconds: 200),
                padding: const EdgeInsets.all(10),
                decoration: BoxDecoration(
                  color: _isPressed ? Colors.orange[100] : Colors.transparent,
                  borderRadius: BorderRadius.circular(15),
                ),
                child: Icon(
                  widget.icon,
                  color: Colors.orange,
                  size: 30,
                ),
              ),
              const SizedBox(height: 5),
              Text(
                widget.label,
                style: TextStyle(
                  fontSize: 12,
                  color: _isPressed ? Colors.orange : Colors.black87,
                  fontWeight: _isPressed ? FontWeight.bold : FontWeight.normal,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

// 分类页
class CategoryScreen extends StatefulWidget {
  const CategoryScreen({super.key});

  @override
  State<CategoryScreen> createState() => _CategoryScreenState();
}

class _CategoryScreenState extends State<CategoryScreen> {
  int _selectedCategoryIndex = 0;

  // 定义子分类对应的图片URL
  final Map<String, String> categoryImages = {
    // 手机数码
    '手机': 'https://images.unsplash.com/photo-1511707171634-5f897ff02aa9?w=200',
    '平板': 'https://images.unsplash.com/photo-1544244015-0df4b3ffc6b0?w=200',
    '耳机': 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=200',
    '充电宝': 'https://images.unsplash.com/photo-1609091839311-d5365f9ff1c5?w=200',
    '数据线': 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=200',
    '保护壳': 'https://images.unsplash.com/photo-1601784551446-20c9e07cdbdb?w=200',
    '智能手表':
        'https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=200',
    '蓝牙音箱':
        'https://images.unsplash.com/photo-1589492477829-5e65395b66cc?w=200',
    '路由器': 'https://images.unsplash.com/photo-1544197150-b99a580bb7a8?w=200',
    '数码相机':
        'https://images.unsplash.com/photo-1526170375885-4d8ecf77b99f?w=200',
    '游戏手柄':
        'https://images.unsplash.com/photo-1592840496694-26d035b52b48?w=200',
    '对讲机': 'https://images.unsplash.com/photo-1496181133206-80ce9b88a853?w=200',
    '行车记录仪':
        'https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=200',
    '电子烟': 'https://images.unsplash.com/photo-1511707171634-5f897ff02aa9?w=200',
    '智能门锁':
        'https://images.unsplash.com/photo-1589492477829-5e65395b66cc?w=200',
    '无人机': 'https://images.unsplash.com/photo-1526170375885-4d8ecf77b99f?w=200',
    'VR眼镜':
        'https://images.unsplash.com/photo-1592840496694-26d035b52b48?w=200',
    // 家用电器
    '冰箱': 'https://images.unsplash.com/photo-1584568694244-14fbdf83bd30?w=200',
    '洗衣机': 'https://images.unsplash.com/photo-1626806819282-2c1dc01a5e0c?w=200',
    '空调': 'https://images.unsplash.com/photo-1578632292335-df3abbb0d586?w=200',
    '电视': 'https://images.unsplash.com/photo-1593359677879-a4bb92f829d1?w=200',
    '微波炉': 'https://images.unsplash.com/photo-1586953208448-b95a79798f07?w=200',
    '电饭煲': 'https://images.unsplash.com/photo-1584990347449-a2d4e2b40c95?w=200',
    '热水器': 'https://images.unsplash.com/photo-1584622650111-993a426fbf0a?w=200',
    '烤箱': 'https://images.unsplash.com/photo-1558346490-a72e53ae2d4f?w=200',
    '吸尘器': 'https://images.unsplash.com/photo-1558317374-067fb5f30001?w=200',
    '豆浆机': 'https://images.unsplash.com/photo-1514432324607-a09d9b4aefdd?w=200',
    '电压力锅':
        'https://images.unsplash.com/photo-1584990347449-a2d4e2b40c95?w=200',
    '扫地机器人': 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=200',
    '净水器': 'https://images.unsplash.com/photo-1548839140-29a749e1cf4d?w=200',
    '空气净化器': 'https://images.unsplash.com/photo-1558346490-a72e53ae2d4f?w=200',
    // 电脑办公
    '笔记本': 'https://images.unsplash.com/photo-1496181133206-80ce9b88a853?w=200',
    '台式机': 'https://images.unsplash.com/photo-1587202372775-e229f172b9d7?w=200',
    '键盘': 'https://images.unsplash.com/photo-1587829741301-dc798b91a603?w=200',
    '鼠标': 'https://images.unsplash.com/photo-1527864550417-7fd91fc51a46?w=200',
    '显示器': 'https://images.unsplash.com/photo-1527443224154-c4a3942d3acf?w=200',
    '音响': 'https://images.unsplash.com/photo-1589492477829-5e65395b66cc?w=200',
    '打印机': 'https://images.unsplash.com/photo-1565437837960-0e65c5b4382c?w=200',
    'U盘': 'https://images.unsplash.com/photo-1511707171634-5f897ff02aa9?w=200',
    '移动硬盘': 'https://images.unsplash.com/photo-1563770095-39d468f95c34?w=200',
    '交换机': 'https://images.unsplash.com/photo-1544197150-b99a580bb7a8?w=200',
    '投影仪': 'https://images.unsplash.com/photo-1478720568477-152d9b164e26?w=200',
    '扫描仪': 'https://images.unsplash.com/photo-1558346490-a72e53ae2d4f?w=200',
    '摄像头': 'https://images.unsplash.com/photo-1585659722983-3a675dabf23d?w=200',
    // 家居家装
    '沙发': 'https://images.unsplash.com/photo-1555041469-a586c61ea9bc?w=200',
    '床': 'https://images.unsplash.com/photo-1505693416388-ac5ce068fe85?w=200',
    '衣柜': 'https://images.unsplash.com/photo-1558997519-83ea9252edf8?w=200',
    '餐桌': 'https://images.unsplash.com/photo-1617806118233-18e1de247200?w=200',
    '椅子': 'https://images.unsplash.com/photo-1592078615290-033ee584e267?w=200',
    '书桌': 'https://images.unsplash.com/photo-1518455027359-f3f8164ba6bd?w=200',
    '茶几': 'https://images.unsplash.com/photo-1533090481720-856c6e3c1fdc?w=200',
    '电视柜': 'https://images.unsplash.com/photo-1558997519-83ea9252edf8?w=200',
    '鞋柜': 'https://images.unsplash.com/photo-1584990347449-a2d4e2b40c95?w=200',
    '床垫': 'https://images.unsplash.com/photo-1505693416388-ac5ce068fe85?w=200',
    '窗帘': 'https://images.unsplash.com/photo-1617806118233-18e1de247200?w=200',
    '地毯': 'https://images.unsplash.com/photo-1555041469-a586c61ea9bc?w=200',
    '灯具': 'https://images.unsplash.com/photo-1524484485831-a92ffc0de03f?w=200',
    '装饰画': 'https://images.unsplash.com/photo-1579783902614-a3fb3927b6a5?w=200',
    // 服装服饰
    '男装': 'https://images.unsplash.com/photo-1594938298603-c8148c4dae35?w=200',
    '女装': 'https://images.unsplash.com/photo-1515886657613-9f3515b0c78f?w=200',
    '童装': 'https://images.unsplash.com/photo-1503919545889-aef636e10ad4?w=200',
    '内衣': 'https://images.unsplash.com/photo-1582552938357-32b906df40cb?w=200',
    '运动服': 'https://images.unsplash.com/photo-1515886657613-9f3515b0c78f?w=200',
    '牛仔裤': 'https://images.unsplash.com/photo-1542272454315-4c01d7abdf4a?w=200',
    'T恤': 'https://images.unsplash.com/photo-1521572163474-6864f9cf17ab?w=200',
    '衬衫': 'https://images.unsplash.com/photo-1596755094514-f87e34085b2c?w=200',
    '连衣裙': 'https://images.unsplash.com/photo-1515886657613-9f3515b0c78f?w=200',
    '羽绒服': 'https://images.unsplash.com/photo-1544022613-e87ca75a784a?w=200',
    '夹克': 'https://images.unsplash.com/photo-1551028719-00167b16eac5?w=200',
    '卫衣': 'https://images.unsplash.com/photo-1556821840-3a63f95609a7?w=200',
    '毛衣': 'https://images.unsplash.com/photo-1434389677669-e08b4cac3105?w=200',
    '西装': 'https://images.unsplash.com/photo-1593032465175-481ac7f401a0?w=200',
    // 鞋靴箱包
    '运动鞋': 'https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=200',
    '皮鞋': 'https://images.unsplash.com/photo-1614252369475-531eba835eb1?w=200',
    '靴子': 'https://images.unsplash.com/photo-1608256246200-53e635b5b65f?w=200',
    '凉鞋': 'https://images.unsplash.com/photo-1543163521-1bf539c55dd2?w=200',
    '拖鞋': 'https://images.unsplash.com/photo-1617806118233-18e1de247200?w=200',
    '休闲鞋': 'https://images.unsplash.com/photo-1549298916-b41d501d3772?w=200',
    '高跟鞋': 'https://images.unsplash.com/photo-1543163521-1bf539c55dd2?w=200',
    '帆布鞋': 'https://images.unsplash.com/photo-1460353581641-37baddab0fa2?w=200',
    '登山鞋': 'https://images.unsplash.com/photo-1606107557195-0e29a4b5b4aa?w=200',
    '篮球鞋': 'https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=200',
    '足球鞋': 'https://images.unsplash.com/photo-1489987707025-afc232f7ea0f?w=200',
    '跑鞋': 'https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=200',
    '板鞋': 'https://images.unsplash.com/photo-1460353581641-37baddab0fa2?w=200',
    '马丁靴': 'https://images.unsplash.com/photo-1608256246200-53e635b5b65f?w=200',
    // 运动户外
    '篮球': 'https://images.unsplash.com/photo-1535248344320-4a1b93f8e85f?w=200',
    '足球': 'https://images.unsplash.com/photo-1519861531473-9200262188bf?w=200',
    '羽毛球': 'https://images.unsplash.com/photo-1622279457486-62dcc4a431d6?w=200',
    '网球': 'https://images.unsplash.com/photo-1622279457486-62dcc4a431d6?w=200',
    '乒乓球': 'https://images.unsplash.com/photo-1535248344320-4a1b93f8e85f?w=200',
    '游泳': 'https://images.unsplash.com/photo-1530549387789-4c1017266635?w=200',
    '健身': 'https://images.unsplash.com/photo-1517836357463-d25dfeac3438?w=200',
    '瑜伽': 'https://images.unsplash.com/photo-1544367563-12123d896889?w=200',
    '跑步': 'https://images.unsplash.com/photo-1552674605-db6ffd4facb5?w=200',
    '骑行': 'https://images.unsplash.com/photo-1485965120184-e224f7a1db69?w=200',
    '登山': 'https://images.unsplash.com/photo-1551632811-561732d1e306?w=200',
    '滑雪': 'https://images.unsplash.com/photo-1551698618-1dfe5d97d256?w=200',
    '钓鱼': 'https://images.unsplash.com/photo-1544161515-4ab6ce6db874?w=200',
    '滑板': 'https://images.unsplash.com/photo-1460353581641-37baddab0fa2?w=200',
    // 美妆个护
    '护肤': 'https://images.unsplash.com/photo-1556228720-195a672e8a03?w=200',
    '彩妆': 'https://images.unsplash.com/photo-1596462502278-27bfdc403348?w=200',
    '香水': 'https://images.unsplash.com/photo-1541643600914-78b084683601?w=200',
    '面膜': 'https://images.unsplash.com/photo-1596755389377-c760f65e806e?w=200',
    '洗护': 'https://images.unsplash.com/photo-1598440947619-2c35fc9aa908?w=200',
    '美发': 'https://images.unsplash.com/photo-1522337360788-8b13dee7a37e?w=200',
    '美甲': 'https://images.unsplash.com/photo-1604654894610-df63bc536371?w=200',
    '美容仪': 'https://images.unsplash.com/photo-1596755094514-f87e34085b2c?w=200',
    '口腔护理':
        'https://images.unsplash.com/photo-1606811841689-23dfddce3e95?w=200',
    '身体护理': 'https://images.unsplash.com/photo-1556228720-195a672e8a03?w=200',
    '眼部护理':
        'https://images.unsplash.com/photo-1576426863848-c21f53c60b19?w=200',
    '防晒': 'https://images.unsplash.com/photo-1556228720-195a672e8a03?w=200',
    '脱毛': 'https://images.unsplash.com/photo-1598440947619-2c35fc9aa908?w=200',
    '足部护理':
        'https://images.unsplash.com/photo-1516975080664-ed2fc6a32937?w=200',
    // 食品生鲜
    '水果': 'https://images.unsplash.com/photo-1619566636858-adf3ef46400b?w=200',
    '蔬菜': 'https://images.unsplash.com/photo-1540420773420-3366772f4999?w=200',
    '肉类': 'https://images.unsplash.com/photo-1544025162-d76694265947?w=200',
    '海鲜': 'https://images.unsplash.com/photo-1565680018434-b513d5e5fd47?w=200',
    '零食': 'https://images.unsplash.com/photo-1563729784474-d77dbb933a9e?w=200',
    '饮料': 'https://images.unsplash.com/photo-1544145945-f90425340c7e?w=200',
    '粮油': 'https://images.unsplash.com/photo-1586201375761-83865001e31c?w=200',
    '调味品': 'https://images.unsplash.com/photo-1596040033229-a9821ebd058d?w=200',
    '速食': 'https://images.unsplash.com/photo-1568901346375-23c9450c58cd?w=200',
    '酒水': 'https://images.unsplash.com/photo-1569529465841-dfecdab7503b?w=200',
    '茶叶': 'https://images.unsplash.com/photo-1556679343-c7306c1976bc?w=200',
    '进口食品':
        'https://images.unsplash.com/photo-1563729784474-d77dbb933a9e?w=200',
    '地方特产':
        'https://images.unsplash.com/photo-1604908176997-125f25cc6f3d?w=200',
    '生鲜': 'https://images.unsplash.com/photo-1565680018434-b513d5e5fd47?w=200',
  };

  // 定义每个主分类对应的子分类
  final Map<int, List<String>> categorySubCategories = {
    0: [
      '手机',
      '平板',
      '耳机',
      '充电宝',
      '数据线',
      '保护壳',
      '智能手表',
      '蓝牙音箱',
      '路由器',
      '数码相机',
      '游戏手柄',
      '智能门锁',
      '无人机',
      'VR眼镜'
    ],
    1: [
      '手机',
      '平板',
      '耳机',
      '充电宝',
      '数据线',
      '保护壳',
      '智能手表',
      '蓝牙音箱',
      '路由器',
      '数码相机',
      '游戏手柄',
      '对讲机',
      '行车记录仪',
      '电子烟'
    ],
    2: [
      '冰箱',
      '洗衣机',
      '空调',
      '电视',
      '微波炉',
      '电饭煲',
      '热水器',
      '烤箱',
      '吸尘器',
      '豆浆机',
      '电压力锅',
      '扫地机器人',
      '净水器',
      '空气净化器'
    ],
    3: [
      '笔记本',
      '台式机',
      '键盘',
      '鼠标',
      '显示器',
      '音响',
      '打印机',
      'U盘',
      '移动硬盘',
      '路由器',
      '交换机',
      '投影仪',
      '扫描仪',
      '摄像头'
    ],
    4: [
      '沙发',
      '床',
      '衣柜',
      '餐桌',
      '椅子',
      '书桌',
      '茶几',
      '电视柜',
      '鞋柜',
      '床垫',
      '窗帘',
      '地毯',
      '灯具',
      '装饰画'
    ],
    5: [
      '男装',
      '女装',
      '童装',
      '内衣',
      '运动服',
      '牛仔裤',
      'T恤',
      '衬衫',
      '连衣裙',
      '羽绒服',
      '夹克',
      '卫衣',
      '毛衣',
      '西装'
    ],
    6: [
      '运动鞋',
      '皮鞋',
      '靴子',
      '凉鞋',
      '拖鞋',
      '休闲鞋',
      '高跟鞋',
      '帆布鞋',
      '登山鞋',
      '篮球鞋',
      '足球鞋',
      '跑鞋',
      '板鞋',
      '马丁靴'
    ],
    7: [
      '篮球',
      '足球',
      '羽毛球',
      '网球',
      '乒乓球',
      '游泳',
      '健身',
      '瑜伽',
      '跑步',
      '骑行',
      '登山',
      '滑雪',
      '钓鱼',
      '滑板'
    ],
    8: [
      '护肤',
      '彩妆',
      '香水',
      '面膜',
      '洗护',
      '美发',
      '美甲',
      '美容仪',
      '口腔护理',
      '身体护理',
      '眼部护理',
      '防晒',
      '脱毛',
      '足部护理'
    ],
    9: [
      '水果',
      '蔬菜',
      '肉类',
      '海鲜',
      '零食',
      '饮料',
      '粮油',
      '调味品',
      '速食',
      '酒水',
      '茶叶',
      '进口食品',
      '地方特产',
      '生鲜'
    ],
  };

  List<String> get _currentSubCategories {
    return categorySubCategories[_selectedCategoryIndex] ?? _subCategories;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('商品分类'),
        backgroundColor: Colors.orange,
      ),
      body: Row(
        children: [
          // 左侧分类列表
          Container(
            width: 100,
            color: Colors.grey[200],
            child: ListView.builder(
              itemCount: _categories.length,
              itemBuilder: (context, index) {
                final isSelected = index == _selectedCategoryIndex;
                return AnimatedContainer(
                  duration: const Duration(milliseconds: 200),
                  padding: const EdgeInsets.symmetric(vertical: 20),
                  decoration: BoxDecoration(
                    color: isSelected ? Colors.white : Colors.transparent,
                    border: isSelected
                        ? const Border(
                            left: BorderSide(color: Colors.orange, width: 3))
                        : null,
                  ),
                  child: InkWell(
                    onTap: () {
                      setState(() {
                        _selectedCategoryIndex = index;
                      });
                      ScaffoldMessenger.of(context).showSnackBar(
                        SnackBar(
                          content: Text('已选择 ${_categories[index]}'),
                          duration: const Duration(milliseconds: 800),
                          behavior: SnackBarBehavior.floating,
                        ),
                      );
                    },
                    splashColor: Colors.orange.withOpacity(0.1),
                    highlightColor: Colors.orange.withOpacity(0.05),
                    child: Center(
                      child: Text(
                        _categories[index],
                        style: TextStyle(
                          color: isSelected ? Colors.orange : Colors.black87,
                          fontWeight:
                              isSelected ? FontWeight.bold : FontWeight.normal,
                        ),
                      ),
                    ),
                  ),
                );
              },
            ),
          ),
          // 右侧商品列表
          Expanded(
            child: GridView.builder(
              padding: const EdgeInsets.all(10),
              gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 3,
                childAspectRatio: 0.8,
                crossAxisSpacing: 10,
                mainAxisSpacing: 10,
              ),
              itemCount: _currentSubCategories.length,
              itemBuilder: (context, index) {
                return SubCategoryCard(
                  category: _currentSubCategories[index],
                  imageUrl: categoryImages[_currentSubCategories[index]] ??
                      'https://images.unsplash.com/photo-1511707171634-5f897ff02aa9?w=200',
                  onTap: () {
                    ScaffoldMessenger.of(context).showSnackBar(
                      SnackBar(
                        content: Text('查看 ${_currentSubCategories[index]}'),
                        duration: const Duration(milliseconds: 800),
                        behavior: SnackBarBehavior.floating,
                      ),
                    );
                  },
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

// 子分类卡片
class SubCategoryCard extends StatefulWidget {
  final String category;
  final String imageUrl;
  final VoidCallback onTap;

  const SubCategoryCard({
    super.key,
    required this.category,
    required this.imageUrl,
    required this.onTap,
  });

  @override
  State<SubCategoryCard> createState() => _SubCategoryCardState();
}

class _SubCategoryCardState extends State<SubCategoryCard>
    with SingleTickerProviderStateMixin {
  late AnimationController _animationController;
  late Animation<double> _scaleAnimation;
  bool _isPressed = false;

  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
      duration: const Duration(milliseconds: 100),
      vsync: this,
    );
    _scaleAnimation = Tween<double>(begin: 1.0, end: 0.92).animate(
      CurvedAnimation(parent: _animationController, curve: Curves.easeInOut),
    );
  }

  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTapDown: (_) {
        setState(() {
          _isPressed = true;
        });
        _animationController.forward();
      },
      onTapUp: (_) {
        setState(() {
          _isPressed = false;
        });
        _animationController.reverse();
        widget.onTap();
      },
      onTapCancel: () {
        setState(() {
          _isPressed = false;
        });
        _animationController.reverse();
      },
      child: ScaleTransition(
        scale: _scaleAnimation,
        child: AnimatedContainer(
          duration: const Duration(milliseconds: 200),
          padding: const EdgeInsets.all(8),
          decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.circular(10),
            border: Border.all(
              color: _isPressed ? Colors.orange : Colors.transparent,
              width: 2,
            ),
            boxShadow: _isPressed
                ? [
                    BoxShadow(
                      color: Colors.orange.withOpacity(0.3),
                      blurRadius: 8,
                      offset: const Offset(0, 2),
                    ),
                  ]
                : [
                    BoxShadow(
                      color: Colors.grey.withOpacity(0.1),
                      blurRadius: 3,
                      offset: const Offset(0, 1),
                    ),
                  ],
          ),
          child: Column(
            children: [
              Container(
                width: 50,
                height: 50,
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(10),
                  border: _isPressed
                      ? Border.all(color: Colors.orange, width: 2)
                      : null,
                ),
                child: ClipRRect(
                  borderRadius: BorderRadius.circular(10),
                  child: Image.network(
                    widget.imageUrl,
                    fit: BoxFit.cover,
                    errorBuilder: (context, error, stackTrace) {
                      return Container(
                        width: 50,
                        height: 50,
                        decoration: BoxDecoration(
                          gradient: LinearGradient(
                            colors: _isPressed
                                ? [Colors.orange, Colors.orange[700]!]
                                : [Colors.orange[100]!, Colors.orange[200]!],
                            begin: Alignment.topLeft,
                            end: Alignment.bottomRight,
                          ),
                          borderRadius: BorderRadius.circular(10),
                        ),
                        child: Icon(
                          Icons.image,
                          color: _isPressed ? Colors.white : Colors.orange,
                          size: _isPressed ? 28 : 24,
                        ),
                      );
                    },
                    loadingBuilder: (context, child, loadingProgress) {
                      if (loadingProgress == null) return child;
                      return Container(
                        width: 50,
                        height: 50,
                        decoration: BoxDecoration(
                          color: Colors.orange[100],
                          borderRadius: BorderRadius.circular(10),
                        ),
                        child: const CircularProgressIndicator(
                          strokeWidth: 2,
                          color: Colors.orange,
                        ),
                      );
                    },
                  ),
                ),
              ),
              const SizedBox(height: 8),
              Text(
                widget.category,
                style: TextStyle(
                  fontSize: 12,
                  color: _isPressed ? Colors.orange : Colors.black87,
                  fontWeight: _isPressed ? FontWeight.bold : FontWeight.normal,
                ),
                textAlign: TextAlign.center,
                maxLines: 2,
                overflow: TextOverflow.ellipsis,
              ),
            ],
          ),
        ),
      ),
    );
  }
}

// 购物车页
class CartScreen extends StatefulWidget {
  const CartScreen({super.key});

  @override
  State<CartScreen> createState() => _CartScreenState();
}

class _CartScreenState extends State<CartScreen> {
  double _totalPrice = 0.0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('购物车'),
        backgroundColor: Colors.orange,
      ),
      body: Column(
        children: [
          Expanded(
            child: ListView.builder(
              itemCount: _cartItems.length,
              itemBuilder: (context, index) {
                return CartItemCard(
                  item: _cartItems[index],
                  onQuantityChanged: (quantity) {
                    setState(() {
                      _totalPrice = _calculateTotal();
                    });
                  },
                );
              },
            ),
          ),
          // 底部结算栏
          Container(
            padding: const EdgeInsets.all(15),
            decoration: BoxDecoration(
              color: Colors.white,
              boxShadow: [
                BoxShadow(
                  color: Colors.grey.withOpacity(0.3),
                  blurRadius: 5,
                  offset: const Offset(0, -2),
                ),
              ],
            ),
            child: Row(
              children: [
                const Text(
                  '合计: ',
                  style: TextStyle(fontSize: 16),
                ),
                Text(
                  '¥$_totalPrice',
                  style: const TextStyle(
                    fontSize: 24,
                    color: Colors.orange,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                const Spacer(),
                ElevatedButton(
                  onPressed: () {},
                  style: ElevatedButton.styleFrom(
                    backgroundColor: Colors.orange,
                    padding: const EdgeInsets.symmetric(
                      horizontal: 40,
                      vertical: 12,
                    ),
                  ),
                  child: const Text('结算', style: TextStyle(fontSize: 16)),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  double _calculateTotal() {
    return _cartItems.fold(
        0.0, (sum, item) => sum + item.price * item.quantity);
  }
}

// 我的页面
class ProfileScreen extends StatelessWidget {
  const ProfileScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('我的淘淘'),
        backgroundColor: Colors.orange,
      ),
      body: ListView(
        children: [
          // 用户信息
          Container(
            padding: const EdgeInsets.all(20),
            color: Colors.orange,
            child: Column(
              children: [
                Container(
                  width: 80,
                  height: 80,
                  decoration: const BoxDecoration(
                    color: Colors.white,
                    shape: BoxShape.circle,
                  ),
                  child:
                      const Icon(Icons.person, size: 50, color: Colors.orange),
                ),
                const SizedBox(height: 10),
                const Text(
                  '用户昵称',
                  style: TextStyle(
                    color: Colors.white,
                    fontSize: 20,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                const SizedBox(height: 5),
                Container(
                  padding:
                      const EdgeInsets.symmetric(horizontal: 20, vertical: 5),
                  decoration: BoxDecoration(
                    border: Border.all(color: Colors.white),
                    borderRadius: BorderRadius.circular(15),
                  ),
                  child: const Text(
                    '登录/注册',
                    style: TextStyle(color: Colors.white),
                  ),
                ),
              ],
            ),
          ),

          const SizedBox(height: 10),

          // 订单状态
          Container(
            padding: const EdgeInsets.all(15),
            color: Colors.white,
            child: Column(
              children: [
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    const Text(
                      '我的订单',
                      style: TextStyle(
                        fontSize: 16,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    Row(
                      children: const [
                        Text('全部订单', style: TextStyle(color: Colors.grey)),
                        Icon(Icons.chevron_right, color: Colors.grey),
                      ],
                    ),
                  ],
                ),
                const SizedBox(height: 15),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceAround,
                  children: const [
                    OrderStatusIcon(Icons.payment, '待付款'),
                    OrderStatusIcon(Icons.inventory, '待发货'),
                    OrderStatusIcon(Icons.local_shipping, '待收货'),
                    OrderStatusIcon(Icons.star, '待评价'),
                    OrderStatusIcon(Icons.replay, '退款/售后'),
                  ],
                ),
              ],
            ),
          ),

          const SizedBox(height: 10),

          // 功能列表
          Container(
            color: Colors.white,
            child: Column(
              children: const [
                ListTile(
                  leading: Icon(Icons.favorite_border, color: Colors.orange),
                  title: Text('我的收藏'),
                  trailing: Icon(Icons.chevron_right),
                ),
                Divider(height: 1),
                ListTile(
                  leading: Icon(Icons.location_on, color: Colors.orange),
                  title: Text('收货地址'),
                  trailing: Icon(Icons.chevron_right),
                ),
                Divider(height: 1),
                ListTile(
                  leading: Icon(Icons.help_outline, color: Colors.orange),
                  title: Text('联系客服'),
                  trailing: Icon(Icons.chevron_right),
                ),
                Divider(height: 1),
                ListTile(
                  leading: Icon(Icons.settings, color: Colors.orange),
                  title: Text('设置'),
                  trailing: Icon(Icons.chevron_right),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

// 订单状态图标
class OrderStatusIcon extends StatelessWidget {
  final IconData icon;
  final String label;

  const OrderStatusIcon(this.icon, this.label, {super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Icon(icon, color: Colors.orange, size: 28),
        const SizedBox(height: 5),
        Text(label, style: const TextStyle(fontSize: 12)),
      ],
    );
  }
}

// 商品卡片
class ProductCard extends StatefulWidget {
  final Product product;
  final VoidCallback? onTap;

  const ProductCard({super.key, required this.product, this.onTap});

  @override
  State<ProductCard> createState() => _ProductCardState();
}

class _ProductCardState extends State<ProductCard>
    with SingleTickerProviderStateMixin {
  late AnimationController _animationController;
  late Animation<double> _scaleAnimation;
  bool _isPressed = false;

  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
      duration: const Duration(milliseconds: 100),
      vsync: this,
    );
    _scaleAnimation = Tween<double>(begin: 1.0, end: 0.97).animate(
      CurvedAnimation(parent: _animationController, curve: Curves.easeInOut),
    );
  }

  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTapDown: (_) {
        setState(() {
          _isPressed = true;
        });
        _animationController.forward();
      },
      onTapUp: (_) {
        setState(() {
          _isPressed = false;
        });
        _animationController.reverse();
        if (widget.onTap != null) {
          widget.onTap!();
        }
      },
      onTapCancel: () {
        setState(() {
          _isPressed = false;
        });
        _animationController.reverse();
      },
      child: ScaleTransition(
        scale: _scaleAnimation,
        child: AnimatedContainer(
          duration: const Duration(milliseconds: 200),
          decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.circular(10),
            border: Border.all(
              color: _isPressed ? Colors.orange : Colors.transparent,
              width: 2,
            ),
            boxShadow: _isPressed
                ? [
                    BoxShadow(
                      color: Colors.orange.withOpacity(0.3),
                      blurRadius: 8,
                      offset: const Offset(0, 2),
                    ),
                  ]
                : [
                    BoxShadow(
                      color: Colors.grey.withOpacity(0.2),
                      blurRadius: 5,
                      offset: const Offset(0, 2),
                    ),
                  ],
          ),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Expanded(
                child: ClipRRect(
                  borderRadius:
                      const BorderRadius.vertical(top: Radius.circular(8)),
                  child: Image.network(
                    widget.product.imageUrl,
                    fit: BoxFit.cover,
                    width: double.infinity,
                    height: double.infinity,
                    errorBuilder: (context, error, stackTrace) {
                      return Container(
                        color: Colors.orange[100],
                        child: const Center(
                          child:
                              Icon(Icons.image, color: Colors.orange, size: 50),
                        ),
                      );
                    },
                    loadingBuilder: (context, child, loadingProgress) {
                      if (loadingProgress == null) return child;
                      return Container(
                        color: Colors.orange[100],
                        child: const Center(
                          child:
                              CircularProgressIndicator(color: Colors.orange),
                        ),
                      );
                    },
                  ),
                ),
              ),
              Padding(
                padding: const EdgeInsets.all(8),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      widget.product.name,
                      style: const TextStyle(fontSize: 14),
                      maxLines: 2,
                      overflow: TextOverflow.ellipsis,
                    ),
                    const SizedBox(height: 5),
                    Row(
                      children: [
                        Text(
                          '¥${widget.product.price.toStringAsFixed(2)}',
                          style: const TextStyle(
                            color: Colors.orange,
                            fontSize: 16,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                        const SizedBox(width: 5),
                        Text(
                          '${widget.product.sales}人付款',
                          style:
                              const TextStyle(fontSize: 12, color: Colors.grey),
                        ),
                      ],
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

// 购物车商品卡片
class CartItemCard extends StatefulWidget {
  final CartItem item;
  final Function(int) onQuantityChanged;

  const CartItemCard({
    super.key,
    required this.item,
    required this.onQuantityChanged,
  });

  @override
  State<CartItemCard> createState() => _CartItemCardState();
}

class _CartItemCardState extends State<CartItemCard> {
  bool _isChecked = true;

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.all(10),
      padding: const EdgeInsets.all(10),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(10),
        boxShadow: [
          BoxShadow(
            color: Colors.grey.withOpacity(0.2),
            blurRadius: 5,
            offset: const Offset(0, 2),
          ),
        ],
      ),
      child: Row(
        children: [
          Checkbox(
            value: _isChecked,
            onChanged: (value) {
              setState(() {
                _isChecked = value ?? false;
              });
            },
          ),
          ClipRRect(
            borderRadius: BorderRadius.circular(5),
            child: Image.network(
              widget.item.imageUrl,
              width: 80,
              height: 80,
              fit: BoxFit.cover,
              errorBuilder: (context, error, stackTrace) {
                return Container(
                  width: 80,
                  height: 80,
                  decoration: BoxDecoration(
                    color: Colors.orange[100],
                    borderRadius: BorderRadius.circular(5),
                  ),
                  child: const Icon(Icons.image, color: Colors.orange),
                );
              },
              loadingBuilder: (context, child, loadingProgress) {
                if (loadingProgress == null) return child;
                return Container(
                  width: 80,
                  height: 80,
                  decoration: BoxDecoration(
                    color: Colors.orange[100],
                    borderRadius: BorderRadius.circular(5),
                  ),
                  child: const CircularProgressIndicator(strokeWidth: 2),
                );
              },
            ),
          ),
          const SizedBox(width: 10),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  widget.item.name,
                  style: const TextStyle(fontSize: 14),
                  maxLines: 2,
                  overflow: TextOverflow.ellipsis,
                ),
                const SizedBox(height: 10),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Text(
                      '¥${widget.item.price.toStringAsFixed(2)}',
                      style: const TextStyle(
                        color: Colors.orange,
                        fontSize: 16,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    Row(
                      children: [
                        IconButton(
                          icon: const Icon(Icons.remove, size: 18),
                          onPressed: () {
                            if (widget.item.quantity > 1) {
                              widget.item.quantity--;
                              widget.onQuantityChanged(widget.item.quantity);
                            }
                          },
                        ),
                        Text('${widget.item.quantity}'),
                        IconButton(
                          icon: const Icon(Icons.add, size: 18),
                          onPressed: () {
                            widget.item.quantity++;
                            widget.onQuantityChanged(widget.item.quantity);
                          },
                        ),
                      ],
                    ),
                  ],
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

// 数据模型
class Product {
  final String name;
  final double price;
  final int sales;
  final String imageUrl;

  Product({
    required this.name,
    required this.price,
    required this.sales,
    required this.imageUrl,
  });
}

class CartItem {
  final String name;
  final double price;
  final String imageUrl;
  int quantity;

  CartItem({
    required this.name,
    required this.price,
    required this.imageUrl,
    this.quantity = 1,
  });
}

// 所有商品数据(用于搜索)
final List<Product> _allProducts = [
  Product(
      name: '新款智能手机',
      price: 2999.00,
      sales: 1000,
      imageUrl:
          'https://images.unsplash.com/photo-1511707171634-5f897ff02aa9?w=400'),
  Product(
      name: '无线蓝牙耳机',
      price: 199.00,
      sales: 5000,
      imageUrl:
          'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=400'),
  Product(
      name: '智能手表',
      price: 899.00,
      sales: 2000,
      imageUrl:
          'https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=400'),
  Product(
      name: '运动鞋',
      price: 399.00,
      sales: 3000,
      imageUrl:
          'https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=400'),
  Product(
      name: '男士休闲裤',
      price: 159.00,
      sales: 1500,
      imageUrl:
          'https://images.unsplash.com/photo-1624378439575-d8705ad7ae80?w=400'),
  Product(
      name: '女士连衣裙',
      price: 299.00,
      sales: 2500,
      imageUrl:
          'https://images.unsplash.com/photo-1515886657613-9f3515b0c78f?w=400'),
  Product(
      name: '笔记本电脑',
      price: 5999.00,
      sales: 800,
      imageUrl:
          'https://images.unsplash.com/photo-1496181133206-80ce9b88a853?w=400'),
  Product(
      name: '平板电脑',
      price: 3299.00,
      sales: 1200,
      imageUrl:
          'https://images.unsplash.com/photo-1544244015-0df4b3ffc6b0?w=400'),
  Product(
      name: '数码相机',
      price: 4599.00,
      sales: 500,
      imageUrl:
          'https://images.unsplash.com/photo-1526170375885-4d8ecf77b99f?w=400'),
  Product(
      name: '游戏手柄',
      price: 299.00,
      sales: 1800,
      imageUrl:
          'https://images.unsplash.com/photo-1592840496694-26d035b52b48?w=400'),
  Product(
      name: '智能音箱',
      price: 499.00,
      sales: 2200,
      imageUrl:
          'https://images.unsplash.com/photo-1589492477829-5e65395b66cc?w=400'),
  Product(
      name: '无线鼠标',
      price: 99.00,
      sales: 4000,
      imageUrl:
          'https://images.unsplash.com/photo-1527864550417-7fd91fc51a46?w=400'),
  Product(
      name: '机械键盘',
      price: 399.00,
      sales: 1500,
      imageUrl:
          'https://images.unsplash.com/photo-1587829741301-dc798b91a603?w=400'),
  Product(
      name: '显示器',
      price: 1299.00,
      sales: 900,
      imageUrl:
          'https://images.unsplash.com/photo-1527443224154-c4a3942d3acf?w=400'),
  Product(
      name: '路由器',
      price: 199.00,
      sales: 2500,
      imageUrl:
          'https://images.unsplash.com/photo-1544197150-b99a580bb7a8?w=400'),
  Product(
      name: '充电宝',
      price: 129.00,
      sales: 6000,
      imageUrl:
          'https://images.unsplash.com/photo-1609091839311-d5365f9ff1c5?w=400'),
  Product(
      name: '数据线',
      price: 29.00,
      sales: 8000,
      imageUrl:
          'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400'),
  Product(
      name: '手机壳',
      price: 39.00,
      sales: 7000,
      imageUrl:
          'https://images.unsplash.com/photo-1601784551446-20c9e07cdbdb?w=400'),
  Product(
      name: '钢化膜',
      price: 19.00,
      sales: 9000,
      imageUrl:
          'https://images.unsplash.com/photo-1598327105666-5b89351aff4e?w=400'),
  Product(
      name: '耳机支架',
      price: 49.00,
      sales: 3000,
      imageUrl:
          'https://images.unsplash.com/photo-1613040809024-b4ef7ba99bc3?w=400'),
];

// 推荐商品
final List<Product> _recommendedProducts = _allProducts.take(6).toList();

final List<String> _categories = [
  '热门推荐',
  '手机数码',
  '家用电器',
  '电脑办公',
  '家居家装',
  '服装服饰',
  '鞋靴箱包',
  '运动户外',
  '美妆个护',
  '食品生鲜',
];

final List<String> _subCategories = [
  '手机',
  '平板',
  '耳机',
  '充电宝',
  '数据线',
  '保护壳',
  '笔记本',
  '键盘',
  '鼠标',
  '显示器',
  '音响',
  '耳机',
  '冰箱',
  '洗衣机',
  '空调',
  '电视',
  '微波炉',
  '电饭煲',
];

final List<CartItem> _cartItems = [
  CartItem(
      name: '新款智能手机',
      price: 2999.00,
      imageUrl:
          'https://images.unsplash.com/photo-1511707171634-5f897ff02aa9?w=400',
      quantity: 1),
  CartItem(
      name: '无线蓝牙耳机',
      price: 199.00,
      imageUrl:
          'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=400',
      quantity: 2),
  CartItem(
      name: '智能手表',
      price: 899.00,
      imageUrl:
          'https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=400',
      quantity: 1),
];

// 全局分类数据 - 供 CategoryDetailScreen 使用
final Map<String, String> categoryImages = {
  // 手机数码
  '手机': 'https://images.unsplash.com/photo-1511707171634-5f897ff02aa9?w=200',
  '平板': 'https://images.unsplash.com/photo-1544244015-0df4b3ffc6b0?w=200',
  '耳机': 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=200',
  '充电宝': 'https://images.unsplash.com/photo-1609091839311-d5365f9ff1c5?w=200',
  '数据线': 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=200',
  '保护壳': 'https://images.unsplash.com/photo-1601784551446-20c9e07cdbdb?w=200',
  '智能手表': 'https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=200',
  '蓝牙音箱': 'https://images.unsplash.com/photo-1589492477829-5e65395b66cc?w=200',
  '路由器': 'https://images.unsplash.com/photo-1544197150-b99a580bb7a8?w=200',
  '数码相机': 'https://images.unsplash.com/photo-1526170375885-4d8ecf77b99f?w=200',
  '游戏手柄': 'https://images.unsplash.com/photo-1592840496694-26d035b52b48?w=200',
  '对讲机': 'https://images.unsplash.com/photo-1496181133206-80ce9b88a853?w=200',
  '行车记录仪': 'https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=200',
  '电子烟': 'https://images.unsplash.com/photo-1511707171634-5f897ff02aa9?w=200',
  '智能门锁': 'https://images.unsplash.com/photo-1589492477829-5e65395b66cc?w=200',
  '无人机': 'https://images.unsplash.com/photo-1526170375885-4d8ecf77b99f?w=200',
  'VR眼镜': 'https://images.unsplash.com/photo-1592840496694-26d035b52b48?w=200',
  // 家用电器
  '冰箱': 'https://images.unsplash.com/photo-1584568694244-14fbdf83bd30?w=200',
  '洗衣机': 'https://images.unsplash.com/photo-1626806819282-2c1dc01a5e0c?w=200',
  '空调': 'https://images.unsplash.com/photo-1578632292335-df3abbb0d586?w=200',
  '电视': 'https://images.unsplash.com/photo-1593359677879-a4bb92f829d1?w=200',
  '微波炉': 'https://images.unsplash.com/photo-1586953208448-b95a79798f07?w=200',
  '电饭煲': 'https://images.unsplash.com/photo-1584990347449-a2d4e2b40c95?w=200',
  '热水器': 'https://images.unsplash.com/photo-1584622650111-993a426fbf0a?w=200',
  '烤箱': 'https://images.unsplash.com/photo-1558346490-a72e53ae2d4f?w=200',
  '吸尘器': 'https://images.unsplash.com/photo-1558317374-067fb5f30001?w=200',
  '豆浆机': 'https://images.unsplash.com/photo-1514432324607-a09d9b4aefdd?w=200',
  '电压力锅': 'https://images.unsplash.com/photo-1584990347449-a2d4e2b40c95?w=200',
  '扫地机器人': 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=200',
  '净水器': 'https://images.unsplash.com/photo-1548839140-29a749e1cf4d?w=200',
  '空气净化器': 'https://images.unsplash.com/photo-1558346490-a72e53ae2d4f?w=200',
  // 电脑办公
  '笔记本': 'https://images.unsplash.com/photo-1496181133206-80ce9b88a853?w=200',
  '台式机': 'https://images.unsplash.com/photo-1587202372775-e229f172b9d7?w=200',
  '键盘': 'https://images.unsplash.com/photo-1587829741301-dc798b91a603?w=200',
  '鼠标': 'https://images.unsplash.com/photo-1527864550417-7fd91fc51a46?w=200',
  '显示器': 'https://images.unsplash.com/photo-1527443224154-c4a3942d3acf?w=200',
  '音响': 'https://images.unsplash.com/photo-1589492477829-5e65395b66cc?w=200',
  '打印机': 'https://images.unsplash.com/photo-1565437837960-0e65c5b4382c?w=200',
  'U盘': 'https://images.unsplash.com/photo-1511707171634-5f897ff02aa9?w=200',
  '移动硬盘': 'https://images.unsplash.com/photo-1563729784474-d77dbb933a9e?w=200',
  '交换机': 'https://images.unsplash.com/photo-1544197150-b99a580bb7a8?w=200',
  '投影仪': 'https://images.unsplash.com/photo-1478720568477-152d9b164e26?w=200',
  '扫描仪': 'https://images.unsplash.com/photo-1558346490-a72e53ae2d4f?w=200',
  '摄像头': 'https://images.unsplash.com/photo-1585659722983-3a675dabf23d?w=200',
  // 家居家装
  '沙发': 'https://images.unsplash.com/photo-1555041469-a586c61ea9bc?w=200',
  '床': 'https://images.unsplash.com/photo-1505693416388-ac5ce068fe85?w=200',
  '衣柜': 'https://images.unsplash.com/photo-1558997519-83ea9252edf8?w=200',
  '餐桌': 'https://images.unsplash.com/photo-1617806118233-18e1de247200?w=200',
  '椅子': 'https://images.unsplash.com/photo-1592078615290-033ee584e267?w=200',
  '书桌': 'https://images.unsplash.com/photo-1518455027359-f3f8164ba6bd?w=200',
  '茶几': 'https://images.unsplash.com/photo-1533090481720-856c6e3c1fdc?w=200',
  '电视柜': 'https://images.unsplash.com/photo-1558997519-83ea9252edf8?w=200',
  '鞋柜': 'https://images.unsplash.com/photo-1584990347449-a2d4e2b40c95?w=200',
  '床垫': 'https://images.unsplash.com/photo-1505693416388-ac5ce068fe85?w=200',
  '窗帘': 'https://images.unsplash.com/photo-1617806118233-18e1de247200?w=200',
  '地毯': 'https://images.unsplash.com/photo-1555041469-a586c61ea9bc?w=200',
  '灯具': 'https://images.unsplash.com/photo-1524484485831-a92ffc0de03f?w=200',
  '装饰画': 'https://images.unsplash.com/photo-1579783902614-a3fb3927b6a5?w=200',
  // 服装服饰
  '男装': 'https://images.unsplash.com/photo-1594938298603-c8148c4dae35?w=200',
  '女装': 'https://images.unsplash.com/photo-1515886657613-9f3515b0c78f?w=200',
  '童装': 'https://images.unsplash.com/photo-1503919545889-aef636e10ad4?w=200',
  '内衣': 'https://images.unsplash.com/photo-1582552938357-32b906df40cb?w=200',
  '运动服': 'https://images.unsplash.com/photo-1515886657613-9f3515b0c78f?w=200',
  '牛仔裤': 'https://images.unsplash.com/photo-1542272454315-4c01d7abdf4a?w=200',
  'T恤': 'https://images.unsplash.com/photo-1521572163474-6864f9cf17ab?w=200',
  '衬衫': 'https://images.unsplash.com/photo-1596755094514-f87e34085b2c?w=200',
  '连衣裙': 'https://images.unsplash.com/photo-1515886657613-9f3515b0c78f?w=200',
  '羽绒服': 'https://images.unsplash.com/photo-1544022613-e87ca75a784a?w=200',
  '夹克': 'https://images.unsplash.com/photo-1551028719-00167b16eac5?w=200',
  '卫衣': 'https://images.unsplash.com/photo-1556821840-3a63f95609a7?w=200',
  '毛衣': 'https://images.unsplash.com/photo-1434389677669-e08b4cac3105?w=200',
  '西装': 'https://images.unsplash.com/photo-1593032465175-481ac7f401a0?w=200',
  // 鞋靴箱包
  '运动鞋': 'https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=200',
  '皮鞋': 'https://images.unsplash.com/photo-1614252369475-5e65395b5b65f?w=200',
  '靴子': 'https://images.unsplash.com/photo-1608256246200-53e635b5b65f?w=200',
  '凉鞋': 'https://images.unsplash.com/photo-1543163521-1bf539c55dd2?w=200',
  '拖鞋': 'https://images.unsplash.com/photo-1617806118233-18e1de247200?w=200',
  '休闲鞋': 'https://images.unsplash.com/photo-1549298916-b41d501d3772?w=200',
  '高跟鞋': 'https://images.unsplash.com/photo-1543163521-1bf539c55dd2?w=200',
  '帆布鞋': 'https://images.unsplash.com/photo-1460353581641-37baddab0fa2?w=200',
  '登山鞋': 'https://images.unsplash.com/photo-1606107557195-0e29a4b5b4aa?w=200',
  '篮球鞋': 'https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=200',
  '足球鞋': 'https://images.unsplash.com/photo-1489987707025-afc232f7ea0f?w=200',
  '跑鞋': 'https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=200',
  '板鞋': 'https://images.unsplash.com/photo-1460353581641-37baddab0fa2?w=200',
  '马丁靴': 'https://images.unsplash.com/photo-1608256246200-53e635b5b65f?w=200',
  // 运动户外
  '篮球': 'https://images.unsplash.com/photo-1535248344320-4a1b93f8e85f?w=200',
  '足球': 'https://images.unsplash.com/photo-1519861531473-9200262188bf?w=200',
  '羽毛球': 'https://images.unsplash.com/photo-1622279457486-62dcc4a431d6?w=200',
  '网球': 'https://images.unsplash.com/photo-1622279457486-62dcc4a431d6?w=200',
  '乒乓球': 'https://images.unsplash.com/photo-1535248344320-4a1b93f8e85f?w=200',
  '游泳': 'https://images.unsplash.com/photo-1530549387789-4c1017266635?w=200',
  '健身': 'https://images.unsplash.com/photo-1517836357463-d25dfeac3438?w=200',
  '瑜伽': 'https://images.unsplash.com/photo-1544367563-12123d896889?w=200',
  '跑步': 'https://images.unsplash.com/photo-1552674605-db6ffd4facb5?w=200',
  '骑行': 'https://images.unsplash.com/photo-1485965120184-e224f7a1db69?w=200',
  '登山': 'https://images.unsplash.com/photo-1551632811-561732d1e306?w=200',
  '滑雪': 'https://images.unsplash.com/photo-1551698618-1dfe5d97d256?w=200',
  '钓鱼': 'https://images.unsplash.com/photo-1544161515-4ab6ce6db874?w=200',
  '滑板': 'https://images.unsplash.com/photo-1460353581641-37baddab0fa2?w=200',
  // 美妆个护
  '护肤': 'https://images.unsplash.com/photo-1556228720-195a672e8a03?w=200',
  '彩妆': 'https://images.unsplash.com/photo-1596462502278-27bfdc403348?w=200',
  '香水': 'https://images.unsplash.com/photo-1541643600914-78b084683601?w=200',
  '面膜': 'https://images.unsplash.com/photo-1596755389377-c760f65e806e?w=200',
  '洗护': 'https://images.unsplash.com/photo-1598440947619-2c35fc9aa908?w=200',
  '美发': 'https://images.unsplash.com/photo-1522337360788-8b13dee7a37e?w=200',
  '美甲': 'https://images.unsplash.com/photo-1604654894610-df63bc536371?w=200',
  '美容仪': 'https://images.unsplash.com/photo-1596755094514-f87e34085b2c?w=200',
  '口腔护理': 'https://images.unsplash.com/photo-1606811841689-23dfddce3e95?w=200',
  '身体护理': 'https://images.unsplash.com/photo-1556228720-195a672e8a03?w=200',
  '眼部护理': 'https://images.unsplash.com/photo-1576426863848-c21f53c60b19?w=200',
  '防晒': 'https://images.unsplash.com/photo-1556228720-195a672e8a03?w=200',
  '脱毛': 'https://images.unsplash.com/photo-1598440947619-2c35fc9aa908?w=200',
  '足部护理': 'https://images.unsplash.com/photo-1516975080664-ed2fc6a32937?w=200',
  // 食品生鲜
  '水果': 'https://images.unsplash.com/photo-1619566636858-adf3ef46400b?w=200',
  '蔬菜': 'https://images.unsplash.com/photo-1540420773420-3366772f4999?w=200',
  '肉类': 'https://images.unsplash.com/photo-1544025162-d76694265947?w=200',
  '海鲜': 'https://images.unsplash.com/photo-1565680018434-b513d5e5fd47?w=200',
  '零食': 'https://images.unsplash.com/photo-1563729784474-d77dbb933a9e?w=200',
  '饮料': 'https://images.unsplash.com/photo-1544145945-f90425340c7e?w=200',
  '粮油': 'https://images.unsplash.com/photo-1586201375761-83865001e31c?w=200',
  '调味品': 'https://images.unsplash.com/photo-1596040033229-a9821ebd058d?w=200',
  '速食': 'https://images.unsplash.com/photo-1568901346375-23c9450c58cd?w=200',
  '酒水': 'https://images.unsplash.com/photo-1569529465841-dfecdab7503b?w=200',
  '茶叶': 'https://images.unsplash.com/photo-1556679343-c7306c1976bc?w=200',
  '进口食品': 'https://images.unsplash.com/photo-1563729784474-d77dbb933a9e?w=200',
  '地方特产': 'https://images.unsplash.com/photo-1604908176997-125f25cc6f3d?w=200',
  '生鲜': 'https://images.unsplash.com/photo-1565680018434-b513d5e5fd47?w=200',
};

final Map<int, List<String>> categorySubCategories = {
  0: [
    '手机',
    '平板',
    '耳机',
    '充电宝',
    '数据线',
    '保护壳',
    '智能手表',
    '蓝牙音箱',
    '路由器',
    '数码相机',
    '游戏手柄',
    '智能门锁',
    '无人机',
    'VR眼镜'
  ],
  1: [
    '手机',
    '平板',
    '耳机',
    '充电宝',
    '数据线',
    '保护壳',
    '智能手表',
    '蓝牙音箱',
    '路由器',
    '数码相机',
    '游戏手柄',
    '对讲机',
    '行车记录仪',
    '电子烟'
  ],
  2: [
    '冰箱',
    '洗衣机',
    '空调',
    '电视',
    '微波炉',
    '电饭煲',
    '热水器',
    '烤箱',
    '吸尘器',
    '豆浆机',
    '电压力锅',
    '扫地机器人',
    '净水器',
    '空气净化器'
  ],
  3: [
    '笔记本',
    '台式机',
    '键盘',
    '鼠标',
    '显示器',
    '音响',
    '打印机',
    'U盘',
    '移动硬盘',
    '路由器',
    '交换机',
    '投影仪',
    '扫描仪',
    '摄像头'
  ],
  4: [
    '沙发',
    '床',
    '衣柜',
    '餐桌',
    '椅子',
    '书桌',
    '茶几',
    '电视柜',
    '鞋柜',
    '床垫',
    '窗帘',
    '地毯',
    '灯具',
    '装饰画'
  ],
  5: [
    '男装',
    '女装',
    '童装',
    '内衣',
    '运动服',
    '牛仔裤',
    'T恤',
    '衬衫',
    '连衣裙',
    '羽绒服',
    '夹克',
    '卫衣',
    '毛衣',
    '西装'
  ],
  6: [
    '运动鞋',
    '皮鞋',
    '靴子',
    '凉鞋',
    '拖鞋',
    '休闲鞋',
    '高跟鞋',
    '帆布鞋',
    '登山鞋',
    '篮球鞋',
    '足球鞋',
    '跑鞋',
    '板鞋',
    '马丁靴'
  ],
  7: [
    '篮球',
    '足球',
    '羽毛球',
    '网球',
    '乒乓球',
    '游泳',
    '健身',
    '瑜伽',
    '跑步',
    '骑行',
    '登山',
    '滑雪',
    '钓鱼',
    '滑板'
  ],
  8: [
    '护肤',
    '彩妆',
    '香水',
    '面膜',
    '洗护',
    '美发',
    '美甲',
    '美容仪',
    '口腔护理',
    '身体护理',
    '眼部护理',
    '防晒',
    '脱毛',
    '足部护理'
  ],
  9: [
    '水果',
    '蔬菜',
    '肉类',
    '海鲜',
    '零食',
    '饮料',
    '粮油',
    '调味品',
    '速食',
    '酒水',
    '茶叶',
    '进口食品',
    '地方特产',
    '生鲜'
  ],
};

// 轮播图组件
class _BannerCarousel extends StatefulWidget {
  const _BannerCarousel({super.key});

  @override
  State<_BannerCarousel> createState() => _BannerCarouselState();
}

class _BannerCarouselState extends State<_BannerCarousel> {
  final PageController _pageController = PageController();
  int _currentIndex = 0;
  final List<Map<String, String>> _banners = [
    {
      'title': '春季大促',
      'subtitle': '全场5折起',
      'image':
          'https://images.unsplash.com/photo-1607082348824-0a96f2a4b9da?w=800',
      'category': '0',
    },
    {
      'title': '数码狂欢',
      'subtitle': '数码好物推荐',
      'image':
          'https://images.unsplash.com/photo-1511707171634-5f897ff02aa9?w=800',
      'category': '1',
    },
    {
      'title': '家居生活',
      'subtitle': '品质生活从这里开始',
      'image':
          'https://images.unsplash.com/photo-1556228720-195a672e8a03?w=800',
      'category': '4',
    },
  ];

  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 150,
      margin: const EdgeInsets.all(10),
      child: Stack(
        children: [
          PageView.builder(
            controller: _pageController,
            itemCount: _banners.length,
            onPageChanged: (index) {
              setState(() {
                _currentIndex = index;
              });
            },
            itemBuilder: (context, index) {
              final banner = _banners[index];
              return GestureDetector(
                onTap: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => CategoryDetailScreen(
                        initialIndex: int.parse(banner['category']!),
                      ),
                    ),
                  );
                },
                child: ClipRRect(
                  borderRadius: BorderRadius.circular(10),
                  child: Stack(
                    fit: StackFit.expand,
                    children: [
                      Image.network(
                        banner['image']!,
                        fit: BoxFit.cover,
                        errorBuilder: (context, error, stackTrace) {
                          return Container(
                            decoration: BoxDecoration(
                              gradient: LinearGradient(
                                colors: [
                                  Colors.orange[300]!,
                                  Colors.orange[500]!
                                ],
                                begin: Alignment.topLeft,
                                end: Alignment.bottomRight,
                              ),
                            ),
                          );
                        },
                      ),
                      Container(
                        decoration: BoxDecoration(
                          gradient: LinearGradient(
                            colors: [
                              Colors.transparent,
                              Colors.black.withOpacity(0.3)
                            ],
                            begin: Alignment.topCenter,
                            end: Alignment.bottomCenter,
                          ),
                        ),
                      ),
                      Positioned(
                        left: 20,
                        bottom: 20,
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text(
                              banner['title']!,
                              style: const TextStyle(
                                color: Colors.white,
                                fontSize: 24,
                                fontWeight: FontWeight.bold,
                              ),
                            ),
                            const SizedBox(height: 5),
                            Text(
                              banner['subtitle']!,
                              style: const TextStyle(
                                color: Colors.white,
                                fontSize: 14,
                              ),
                            ),
                          ],
                        ),
                      ),
                    ],
                  ),
                ),
              );
            },
          ),
          Positioned(
            bottom: 10,
            right: 10,
            child: Row(
              children: List.generate(
                _banners.length,
                (index) => Container(
                  margin: const EdgeInsets.symmetric(horizontal: 3),
                  width: _currentIndex == index ? 20 : 6,
                  height: 6,
                  decoration: BoxDecoration(
                    color: _currentIndex == index
                        ? Colors.white
                        : Colors.white.withOpacity(0.5),
                    borderRadius: BorderRadius.circular(3),
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

// 分类详情页面
class CategoryDetailScreen extends StatefulWidget {
  final int initialIndex;

  const CategoryDetailScreen({super.key, required this.initialIndex});

  @override
  State<CategoryDetailScreen> createState() => _CategoryDetailScreenState();
}

class _CategoryDetailScreenState extends State<CategoryDetailScreen> {
  int _selectedCategoryIndex = 0;

  @override
  void initState() {
    super.initState();
    _selectedCategoryIndex = widget.initialIndex;
  }

  List<String> get _currentSubCategories {
    return categorySubCategories[_selectedCategoryIndex] ?? _subCategories;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(_categories[_selectedCategoryIndex]),
        backgroundColor: Colors.orange,
      ),
      body: Row(
        children: [
          Container(
            width: 100,
            color: Colors.grey[200],
            child: ListView.builder(
              itemCount: _categories.length,
              itemBuilder: (context, index) {
                final isSelected = index == _selectedCategoryIndex;
                return AnimatedContainer(
                  duration: const Duration(milliseconds: 200),
                  padding: const EdgeInsets.symmetric(vertical: 20),
                  decoration: BoxDecoration(
                    color: isSelected ? Colors.white : Colors.transparent,
                    border: isSelected
                        ? const Border(
                            left: BorderSide(color: Colors.orange, width: 3))
                        : null,
                  ),
                  child: InkWell(
                    onTap: () {
                      setState(() {
                        _selectedCategoryIndex = index;
                      });
                    },
                    splashColor: Colors.orange.withOpacity(0.1),
                    highlightColor: Colors.orange.withOpacity(0.05),
                    child: Center(
                      child: Text(
                        _categories[index],
                        style: TextStyle(
                          color: isSelected ? Colors.orange : Colors.black87,
                          fontWeight:
                              isSelected ? FontWeight.bold : FontWeight.normal,
                        ),
                      ),
                    ),
                  ),
                );
              },
            ),
          ),
          Expanded(
            child: GridView.builder(
              padding: const EdgeInsets.all(10),
              gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 3,
                childAspectRatio: 0.8,
                crossAxisSpacing: 10,
                mainAxisSpacing: 10,
              ),
              itemCount: _currentSubCategories.length,
              itemBuilder: (context, index) {
                return SubCategoryCard(
                  category: _currentSubCategories[index],
                  imageUrl: categoryImages[_currentSubCategories[index]] ??
                      'https://images.unsplash.com/photo-1511707171634-5f897ff02aa9?w=200',
                  onTap: () {
                    ScaffoldMessenger.of(context).showSnackBar(
                      SnackBar(
                        content: Text('查看 ${_currentSubCategories[index]}'),
                        duration: const Duration(milliseconds: 800),
                        behavior: SnackBarBehavior.floating,
                      ),
                    );
                  },
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

// 商品详情页面
class ProductDetailScreen extends StatefulWidget {
  final Product product;

  const ProductDetailScreen({super.key, required this.product});

  @override
  State<ProductDetailScreen> createState() => _ProductDetailScreenState();
}

class _ProductDetailScreenState extends State<ProductDetailScreen> {
  int _quantity = 1;
  bool _isFavorite = false;
  int _selectedSpec = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('商品详情'),
        backgroundColor: Colors.orange,
        actions: [
          IconButton(
            icon: Icon(
              _isFavorite ? Icons.favorite : Icons.favorite_border,
              color: Colors.white,
            ),
            onPressed: () {
              setState(() {
                _isFavorite = !_isFavorite;
              });
              ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(
                  content: Text(_isFavorite ? '已收藏' : '已取消收藏'),
                  duration: const Duration(milliseconds: 800),
                  behavior: SnackBarBehavior.floating,
                ),
              );
            },
          ),
          IconButton(
            icon: const Icon(Icons.share),
            onPressed: () {
              ScaffoldMessenger.of(context).showSnackBar(
                const SnackBar(
                  content: Text('分享功能开发中'),
                  duration: Duration(milliseconds: 800),
                  behavior: SnackBarBehavior.floating,
                ),
              );
            },
          ),
        ],
      ),
      body: SingleChildScrollView(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Container(
              width: double.infinity,
              height: 400,
              decoration: BoxDecoration(
                color: Colors.grey[100],
              ),
              child: Image.network(
                widget.product.imageUrl,
                fit: BoxFit.contain,
                errorBuilder: (context, error, stackTrace) {
                  return Container(
                    decoration: BoxDecoration(
                      gradient: LinearGradient(
                        colors: [Colors.orange[100]!, Colors.orange[200]!],
                        begin: Alignment.topLeft,
                        end: Alignment.bottomRight,
                      ),
                    ),
                    child: const Center(
                      child: Icon(Icons.image, size: 80, color: Colors.orange),
                    ),
                  );
                },
              ),
            ),
            Padding(
              padding: const EdgeInsets.all(16),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    '¥${widget.product.price.toStringAsFixed(2)}',
                    style: const TextStyle(
                      fontSize: 28,
                      color: Colors.orange,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(height: 10),
                  Text(
                    widget.product.name,
                    style: const TextStyle(
                      fontSize: 18,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(height: 8),
                  Text(
                    '${widget.product.sales}人付款',
                    style: TextStyle(
                      fontSize: 14,
                      color: Colors.grey[600],
                    ),
                  ),
                  const SizedBox(height: 20),
                  const Text(
                    '规格',
                    style: TextStyle(
                      fontSize: 16,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(height: 10),
                  Wrap(
                    spacing: 10,
                    children: List.generate(
                      3,
                      (index) => _SpecChip(
                        label: '规格${index + 1}',
                        isSelected: _selectedSpec == index,
                        onTap: () {
                          setState(() {
                            _selectedSpec = index;
                          });
                        },
                      ),
                    ),
                  ),
                  const SizedBox(height: 20),
                  Row(
                    children: [
                      const Text('数量:'),
                      IconButton(
                        icon: const Icon(Icons.remove),
                        onPressed: () {
                          if (_quantity > 1) {
                            setState(() {
                              _quantity--;
                            });
                          }
                        },
                      ),
                      Container(
                        padding: const EdgeInsets.symmetric(horizontal: 20),
                        child: Text(
                          '$_quantity',
                          style: const TextStyle(fontSize: 16),
                        ),
                      ),
                      IconButton(
                        icon: const Icon(Icons.add),
                        onPressed: () {
                          setState(() {
                            _quantity++;
                          });
                        },
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
      bottomNavigationBar: Container(
        padding: const EdgeInsets.all(16),
        decoration: BoxDecoration(
          color: Colors.white,
          boxShadow: [
            BoxShadow(
              color: Colors.grey.withOpacity(0.3),
              blurRadius: 5,
              offset: const Offset(0, -2),
            ),
          ],
        ),
        child: Row(
          children: [
            Expanded(
              child: ElevatedButton.icon(
                onPressed: () {
                  ScaffoldMessenger.of(context).showSnackBar(
                    const SnackBar(
                      content: Text('已加入购物车'),
                      duration: Duration(milliseconds: 800),
                      behavior: SnackBarBehavior.floating,
                    ),
                  );
                },
                style: ElevatedButton.styleFrom(
                  backgroundColor: Colors.orange,
                  padding: const EdgeInsets.symmetric(vertical: 15),
                ),
                icon: const Icon(Icons.shopping_cart),
                label: const Text('加入购物车'),
              ),
            ),
            const SizedBox(width: 10),
            Expanded(
              child: ElevatedButton(
                onPressed: () {
                  ScaffoldMessenger.of(context).showSnackBar(
                    const SnackBar(
                      content: Text('立即购买'),
                      duration: Duration(milliseconds: 800),
                      behavior: SnackBarBehavior.floating,
                    ),
                  );
                },
                style: ElevatedButton.styleFrom(
                  backgroundColor: Colors.red,
                  padding: const EdgeInsets.symmetric(vertical: 15),
                ),
                child: const Text('立即购买'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

// 规格选择芯片
class _SpecChip extends StatelessWidget {
  final String label;
  final bool isSelected;
  final VoidCallback onTap;

  const _SpecChip({
    required this.label,
    required this.isSelected,
    required this.onTap,
  });

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: AnimatedContainer(
        duration: const Duration(milliseconds: 200),
        padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
        decoration: BoxDecoration(
          color: isSelected ? Colors.orange : Colors.grey[200],
          borderRadius: BorderRadius.circular(20),
          border: Border.all(
            color: isSelected ? Colors.orange : Colors.transparent,
            width: 2,
          ),
        ),
        child: Text(
          label,
          style: TextStyle(
            color: isSelected ? Colors.white : Colors.black87,
            fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
          ),
        ),
      ),
    );
  }
}

一、导航基石:Navigator 与路由

在 Flutter 中,页面跳转由 Navigator 组件管理。它维护着一个"路由栈"(Route Stack),新页面被推入(push)栈顶,旧页面被弹出(pop)。

dart 复制代码
// 标准的页面跳转语法
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => TargetPage(), // 目标页面的构造函数
  ),
);

例如这样

  • context : 当前 Widget 的上下文,用于定位 Navigator
  • MaterialPageRoute: 提供符合 Material Design 规范的页面切换动画(如从右向左滑入)。

《淘淘购物》App 正是基于此模式,实现了所有页面间的无缝衔接。


二、主页跳转点深度解析

2.1 搜索功能:双入口,一目标

主页提供了 两个 触发搜索的方式,均指向同一个搜索委托(SearchDelegate)。

跳转点 1:AppBar 右侧的搜索图标
dart 复制代码
// HomeScreen 的 AppBar.actions
IconButton(
  icon: const Icon(Icons.search),
  onPressed: () {
    _showSearchDialog(context); // 调用自定义方法
  },
)
跳转点 2:中部的搜索框
dart 复制代码
// HomeScreen 的 body 中
GestureDetector(
  onTap: () => _showSearchDialog(context), // 同样调用该方法
  child: Container(...), // 搜索框UI
)
跳转逻辑:_showSearchDialog 方法
dart 复制代码
void _showSearchDialog(BuildContext context) {
  showSearch(
    context: context,
    delegate: ProductSearchDelegate(), // 关键:传入搜索委托
  );
}
  • showSearch : Flutter 内置的、专为搜索设计的 API。它会自动创建一个全屏的搜索界面,并将控制权交给 delegate
  • ProductSearchDelegate : 一个继承自 SearchDelegate 的自定义类,负责处理搜索框内的所有逻辑(输入、建议、结果展示)。这不是一个普通的
    StatefulWidget,而是一个特殊的路由委托。

📌 总结 :无论点击哪里,最终都通过 showSearch 进入由 ProductSearchDelegate 管理的搜索流程,保证了体验的一致性。


2.2 轮播图 (Banner Carousel):营销位的精准引流

轮播图是电商 App 的黄金广告位,其点击应能直达相关商品分类。

跳转点:_BannerCarousel 组件
dart 复制代码
// 在 PageView.builder 的 itemBuilder 中
GestureDetector(
  onTap: () {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => CategoryDetailScreen(
          initialIndex: int.parse(banner['category']!), // 传递初始索引
        ),
      ),
    );
  },
  child: ... // 轮播图图片和文字
)
关键数据:_banners 列表
dart 复制代码
final List<Map<String, String>> _banners = [
  {'title': '春季大促', 'category': '0'}, // category '0' 对应 _categories[0] -> '热门推荐'
  {'title': '数码狂欢', 'category': '1'}, // category '1' 对应 _categories[1] -> '手机数码'
  {'title': '家居生活', 'category': '4'}, // category '4' 对应 _categories[4] -> '家居家装'
];
  • category 字段 :存储了目标主分类在 _categories 列表中的 索引值
  • CategoryDetailScreen :一个专门用于展示分类详情的页面,接收 initialIndex 作为初始化参数。

🎯 商业价值 :运营人员只需修改 _banners 数据,即可动态调整轮播图的跳转目标,无需改动任何业务逻辑代码,实现了内容与逻辑的解耦。


2.3 快捷入口 (QuickEntry):高频频道的快速直达

首页下方的 10 个快捷入口(天猫、聚划算等)是用户访问核心频道的捷径。

跳转点:QuickEntry 组件
dart 复制代码
// QuickEntry 的 build 方法中
GestureDetector(
  onTapUp: (_) {
    // ... 动画逻辑
    // 跳转到分类页面
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => CategoryDetailScreen(
          initialIndex: widget.categoryIndex, // 传递预设的分类索引
        ),
      ),
    );
  },
  child: ... // 图标和文字
)
关键属性:categoryIndex

HomeScreen 中创建 QuickEntry 时,为其指定了目标分类的索引:

dart 复制代码
// HomeScreen 的 GridView.children
QuickEntry(Icons.shopping_bag, '天猫', 0),   // 0 -> 热门推荐
QuickEntry(Icons.card_giftcard, '聚划算', 1), // 1 -> 手机数码
QuickEntry(Icons.local_offer, '优惠券', 2),   // 2 -> 家用电器
QuickEntry(Icons.phone_android, '数码', 1),   // 1 -> 手机数码
// ... 其他入口
  • QuickEntry 构造函数const QuickEntry(this.icon, this.label, this.categoryIndex, {super.key});
  • 复用性 :同一个 QuickEntry 组件,通过传入不同的 categoryIndex,可以跳转到任意分类页。

🔁 统一处理 :和轮播图一样,快捷入口也跳转到了 CategoryDetailScreen,这体现了架构设计的统一性,减少了重复代码。


2.4 推荐商品卡片 (ProductCard):从发现到详情

"为你推荐"区域的商品卡片,是促成用户下单的关键一步。

跳转点:ProductCard 组件
dart 复制代码
// ProductCard 的 build 方法中
GestureDetector(
  onTapUp: (_) {
    // ... 动画逻辑
    if (widget.onTap != null) {
      widget.onTap!(); // 执行外部传入的回调
    }
  },
  child: ... // 商品图片、名称、价格
)
跳转逻辑:由父组件 (HomeScreen) 定义

ProductCard 本身不关心跳转到哪里,它只负责在被点击时执行一个回调 onTap。具体的跳转逻辑由使用它的页面来决定。

HomeScreen 中:

dart 复制代码
// GridView.builder 的 itemBuilder
itemBuilder: (context, index) {
  return ProductCard(
    product: _recommendedProducts[index],
    onTap: () {
      // 这里定义了具体的跳转行为
      Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => ProductDetailScreen(
            product: _recommendedProducts[index], // 传递完整的商品对象
          ),
        ),
      );
    },
  );
}
  • 高内聚低耦合ProductCard 专注于展示商品和处理点击事件,而 HomeScreen 专注于业务逻辑(即点击后要做什么)。这种设计使得 ProductCard 可以在搜索结果页、购物车页等任何地方复用,只需传入不同的 onTap 回调即可。

🧩 设计哲学:这是 Flutter 组合式(Compositional)思想的完美体现。复杂的功能由多个简单、专注的小组件组合而成。


三、目标页面概览

所有跳转最终都导向了以下几个核心页面:

源跳转点 目标页面 传递的数据 用途
搜索图标/框 ProductSearchDelegate 无 (内部处理查询) 全局商品搜索
轮播图 / 快捷入口 CategoryDetailScreen initialIndex (int) 展示指定主分类下的所有子分类
推荐商品卡片 ProductDetailScreen product (Product 对象) 展示单个商品的详细信息、规格、购买选项

这些页面共同构成了《淘淘购物》App 的核心用户体验闭环:发现 -> 浏览 -> 了解 -> (加入购物车/购买)


四、总结:构建流畅用户体验的关键要素

通过对主页跳转功能的全面分析,我们可以提炼出构建优秀 Flutter 应用导航的几个关键实践:

  1. 一致性 :相似的交互(如点击卡片)采用相同的视觉反馈(按压动效)和导航模式(Navigator.push)。
  2. 解耦与复用ProductCardQuickEntry 等组件通过回调 (onTap) 实现逻辑与UI分离,极大提升了代码的可维护性和复用性。
  3. 数据驱动 :跳转的目标和行为由数据(如 categoryIndex, product 对象)决定,而非硬编码,使应用更灵活、易于扩展。
  4. 用户体验优先:从双入口搜索到拟物化按压反馈,每一个细节都旨在降低用户认知负荷,提供流畅、愉悦的操作感。

🌐 加入社区

欢迎加入 开源鸿蒙跨平台开发者社区 ,获取最新资源与技术支持:

👉 开源鸿蒙跨平台开发者社区


技术因分享而进步,生态因共建而繁荣

------ 晚霞的不甘 · 与您共赴鸿蒙跨平台开发之旅

相关推荐
代码匠心1 天前
AI 自动编程:一句话设计高颜值博客
前端·ai·ai编程·claude
_AaronWong1 天前
Electron 实现仿豆包划词取词功能:从 AI 生成到落地踩坑记
前端·javascript·vue.js
cxxcode1 天前
I/O 多路复用:从浏览器到 Linux 内核
前端
用户5433081441941 天前
AI 时代,前端逆向的门槛已经低到离谱 — 以 Upwork 为例
前端
JarvanMo1 天前
Flutter 版本的 material_ui 已经上架 pub.dev 啦!快来抢先体验吧。
前端
恋猫de小郭1 天前
AI 可以让 WIFI 实现监控室内人体位置和姿态,无需摄像头?
前端·人工智能·ai编程
哀木1 天前
给自己整一个 claude code,解锁编程新姿势
前端
程序员鱼皮1 天前
GitHub 关注突破 2w,我总结了 10 个涨星涨粉技巧!
前端·后端·github
UrbanJazzerati1 天前
Vue3 父子组件通信完全指南
前端·面试
MakeZero1 天前
Flutter那些事-展示型组件篇
flutter