在这里插入图片描述

✨“俺はモンキー・D・ルフィ。海贼王になる男だ!”

在这里插入图片描述


在这里插入图片描述

项目背景

在电商类应用开发中,首页的分类导航是一个核心功能模块。用户通过横向滚动的分类列表快速找到目标商品类型。本文将带领大家从零开始,使用Flutter框架结合AI编程助手,完成一个完整的商城分类导航功能开发。

我们将实现的功能包括:

  • 动态加载服务器端分类数据
  • 分类图标与文字的展示
  • 横向滚动交互效果
  • 圆角卡片式UI设计

实现步骤

第一步:配置API基础信息

首先,我们需要在项目中定义API相关常量。创建配置文件可以统一管理接口地址,便于后续维护。

修改文件:lib/constants/config.dart

/// API配置中心
class ApiConfig {
  /// 服务器基础地址
  static const String baseUrl = "https://meikou-api.itheima.net/";

  /// 网络请求超时时长(秒)
  static const int timeoutDuration = 10;

  /// 业务接口成功状态码
  static const String successCode = "1";
}

/// 接口路径定义
class ApiPath {
  /// 首页轮播图数据接口
  static const String bannerList = "/home/banner";

  /// 首页分类列表接口
  static const String categoryList = "/home/category/head";
}

第二步:构建数据模型

分类数据采用树形结构,每个分类包含多个子分类。我们需要创建对应的数据模型类,并实现JSON序列化功能。

接口数据结构预览:

创建文件:lib/models/category_model.dart

/// 分类数据模型
class CategoryModel {
  /// 分类ID
  final String id;

  /// 分类名称
  final String name;

  /// 分类图标URL
  final String? iconUrl;

  /// 子分类列表
  final List<CategoryModel>? subCategories;

  CategoryModel({
    required this.id,
    required this.name,
    this.iconUrl,
    this.subCategories,
  });

  /// 从JSON数据创建实例(工厂构造函数)
  factory CategoryModel.fromJson(Map<String, dynamic> json) {
    return CategoryModel(
      id: json["id"]?.toString() ?? "",
      name: json["name"]?.toString() ?? "",
      iconUrl: json["picture"]?.toString(),
      subCategories: json["children"] != null
          ? (json["children"] as List)
              .map((item) => CategoryModel.fromJson(item as Map<String, dynamic>))
              .toList()
          : null,
    );
  }
}

第三步:封装网络请求层

为了更好地管理API调用,我们需要创建一个专门的API服务层。

创建文件:lib/services/home_service.dart

import 'package:dio/dio.dart';
import '../constants/config.dart';
import '../models/category_model.dart';

/// 首页数据服务
class HomeService {
  static final Dio _dio = Dio(
    BaseOptions(
      baseUrl: ApiConfig.baseUrl,
      connectTimeout: Duration(seconds: ApiConfig.timeoutDuration),
      receiveTimeout: Duration(seconds: ApiConfig.timeoutDuration),
    ),
  );

  /// 获取分类列表数据
  static Future<List<CategoryModel>> fetchCategories() async {
    try {
      final response = await _dio.get(ApiPath.categoryList);

      // 判断业务状态码
      if (response.data['code'] == ApiConfig.successCode) {
        final List<dynamic> dataList = response.data['result'];
        return dataList
            .map((item) => CategoryModel.fromJson(item as Map<String, dynamic>))
            .toList();
      } else {
        throw Exception('业务错误: ${response.data['message']}');
      }
    } catch (e) {
      throw Exception('网络请求失败: $e');
    }
  }
}

第四步:实现分类列表组件

接下来我们创建UI组件,以横向滚动的方式展示分类列表。

创建文件:lib/widgets/category_bar.dart

先创建一个基础版本,验证布局结构:

import 'package:flutter/material.dart';
import '../models/category_model.dart';

/// 首页分类导航栏组件
class CategoryBar extends StatefulWidget {
  /// 分类数据列表
  final List<CategoryModel> categories;

  const CategoryBar({
    super.key,
    required this.categories,
  });

  
  State<CategoryBar> createState() => _CategoryBarState();
}

class _CategoryBarState extends State<CategoryBar> {
  
  Widget build(BuildContext context) {
    // 使用SizedBox固定高度,ListView不能直接设置高度
    return SizedBox(
      height: 100,
      child: ListView.builder(
        scrollDirection: Axis.horizontal, // 横向滚动
        itemCount: widget.categories.length,
        itemBuilder: (context, index) {
          final category = widget.categories[index];
          return _buildCategoryItem(category);
        },
      ),
    );
  }

  Widget _buildCategoryItem(CategoryModel category) {
    return Container(
      width: 80,
      margin: const EdgeInsets.symmetric(horizontal: 8),
      decoration: BoxDecoration(
        color: Colors.blue,
        borderRadius: BorderRadius.circular(12),
      ),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Image.network(
            category.iconUrl ?? '',
            width: 40,
            height: 40,
            errorBuilder: (context, error, stackTrace) {
              return const Icon(Icons.category, size: 40);
            },
          ),
          const SizedBox(height: 4),
          Text(
            category.name,
            style: const TextStyle(color: Colors.white),
            maxLines: 1,
            overflow: TextOverflow.ellipsis,
          ),
        ],
      ),
    );
  }
}

第五步:集成到首页

在首页中调用分类API并展示数据。

修改文件:lib/pages/home_page.dart

import 'package:flutter/material.dart';
import '../services/home_service.dart';
import '../models/category_model.dart';
import '../widgets/category_bar.dart';

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

  
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  /// 分类数据列表
  List<CategoryModel> _categories = [];

  /// 加载状态
  bool _isLoading = false;

  
  void initState() {
    super.initState();
    _loadCategoryData();
  }

  /// 加载分类数据
  Future<void> _loadCategoryData() async {
    setState(() => _isLoading = true);

    try {
      final data = await HomeService.fetchCategories();
      setState(() {
        _categories = data;
        _isLoading = false;
      });
    } catch (e) {
      setState(() => _isLoading = false);
      // 实际项目中应该用toast提示
      debugPrint('加载失败: $e');
    }
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('商城首页')),
      body: _isLoading
          ? const Center(child: CircularProgressIndicator())
          : CustomScrollView(
              slivers: [
                // 分类导航栏
                SliverToBoxAdapter(
                  child: Padding(
                    padding: const EdgeInsets.symmetric(vertical: 10),
                    child: CategoryBar(categories: _categories),
                  ),
                ),
                // 其他模块...
              ],
            ),
    );
  }
}

第六步:优化UI设计

现在我们来美化分类卡片,采用浅灰色圆角背景的设计风格。

优化后的 lib/widgets/category_bar.dart

import 'package:flutter/material.dart';
import '../models/category_model.dart';

/// 首页分类导航栏组件
class CategoryBar extends StatefulWidget {
  final List<CategoryModel> categories;

  const CategoryBar({
    super.key,
    required this.categories,
  });

  
  State<CategoryBar> createState() => _CategoryBarState();
}

class _CategoryBarState extends State<CategoryBar> {
  
  Widget build(BuildContext context) {
    return SizedBox(
      height: 100,
      child: ListView.builder(
        scrollDirection: Axis.horizontal,
        padding: const EdgeInsets.symmetric(horizontal: 12),
        itemCount: widget.categories.length,
        itemBuilder: (context, index) {
          final category = widget.categories[index];
          return _buildCategoryItem(category);
        },
      ),
    );
  }

  Widget _buildCategoryItem(CategoryModel category) {
    return Container(
      width: 70,
      margin: const EdgeInsets.only(right: 12),
      decoration: BoxDecoration(
        color: const Color(0xFFE7E8EA), // 浅灰色背景
        borderRadius: BorderRadius.circular(35), // 圆角效果
      ),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          // 分类图标
          Image.network(
            category.iconUrl ?? '',
            width: 40,
            height: 40,
            fit: BoxFit.cover,
            errorBuilder: (context, error, stackTrace) {
              return const Icon(
                Icons.image_outlined,
                size: 40,
                color: Colors.grey,
              );
            },
          ),
          const SizedBox(height: 6),

          // 分类名称
          Text(
            category.name,
            style: const TextStyle(
              fontSize: 12,
              color: Colors.black87,
            ),
            maxLines: 1,
            overflow: TextOverflow.ellipsis,
            textAlign: TextAlign.center,
          ),
        ],
      ),
    );
  }
}

在这里插入图片描述

核心要点总结

1. 数据模型设计

  • 使用factory构造函数实现JSON解析
  • 处理可空字段和嵌套数组
  • 使用??操作符提供默认值

2. 网络请求封装

  • 统一管理baseUrl和超时配置
  • 业务状态码与HTTP状态码分离处理
  • 使用try-catch捕获异常

3. UI组件技巧

  • ListView横向滚动需配合SizedBox设置高度
  • Decoration实现圆角和背景色
  • Image.network配合errorBuilder处理加载失败

代码提交

完成开发后,建议使用Git进行版本管理:

git add .
git commit -m "feat: 实现首页分类导航功能"

相关推荐阅读:

欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐