Flutter开源鸿蒙跨平台训练营 Day 3

OpenHarmony实战:Flutter+Dio打造跨平台猫咪图库应用

在跨平台开发领域,Flutter凭借一次编写多端运行的特性,成为鸿蒙(OpenHarmony/HarmonyOS)跨平台开发的优选框架。本文将结合实战经验,从零开始教大家构建一款基于Flutter+Dio的猫咪图库跨平台应用,实现调用第三方API获取随机猫咪图片、状态管理、多端适配等核心功能,完美支持HarmonyOS和Android双平台运行,同时解决开发过程中遇到的各类常见问题,夯实Flutter跨平台开发的核心技能。

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

  • OpenHarmony实战:Flutter+Dio打造跨平台猫咪图库应用
    • 一、项目核心概述
      • [1.1 功能特性](#1.1 功能特性)
      • [1.2 技术选型](#1.2 技术选型)
    • 二、开发环境配置
      • [2.1 必备工具清单](#2.1 必备工具清单)
      • [2.2 环境验证](#2.2 环境验证)
      • [2.3 项目初始化](#2.3 项目初始化)
    • 三、项目架构设计
      • [3.1 目录结构](#3.1 目录结构)
      • [3.2 架构分层关系](#3.2 架构分层关系)
    • 四、核心代码实现
      • [4.1 配置项目依赖](#4.1 配置项目依赖)
      • [4.2 定义API常量](#4.2 定义API常量)
      • [4.3 封装HTTP工具类](#4.3 封装HTTP工具类)
      • [4.4 定义数据模型](#4.4 定义数据模型)
      • [4.5 实现API服务层](#4.5 实现API服务层)
      • [4.6 状态管理实现](#4.6 状态管理实现)
      • [4.7 封装猫咪卡片组件](#4.7 封装猫咪卡片组件)
      • [4.8 实现猫咪图库页面](#4.8 实现猫咪图库页面)
      • [4.9 实现应用主页面](#4.9 实现应用主页面)
      • [4.10 路由配置](#4.10 路由配置)
      • [4.11 应用入口](#4.11 应用入口)
    • 五、多平台运行演示
      • [5.1 HarmonyOS平台运行](#5.1 HarmonyOS平台运行)
    • 六、常见问题解决方案
      • [6.1 HTTPS证书验证失败](#6.1 HTTPS证书验证失败)
      • [6.2 图片加载缓慢/超时](#6.2 图片加载缓慢/超时)
      • [6.3 Provider状态不更新](#6.3 Provider状态不更新)
      • [6.4 鸿蒙平台打包报错](#6.4 鸿蒙平台打包报错)
      • [6.5 import路径报错](#6.5 import路径报错)
    • 七、项目总结与拓展
      • [7.1 学习收获](#7.1 学习收获)
      • [7.2 后续优化方向](#7.2 后续优化方向)

一、项目核心概述

1.1 功能特性

本应用聚焦核心跨平台开发能力,实现了猫咪图库的基础且实用的功能模块,覆盖网络请求、数据解析、状态管理、UI展示等全流程,具体如下:

功能模块 核心说明
网络请求 基于Dio 5.5.0+1实现HTTP网络请求,调用The Cat API获取猫咪图片数据
图片展示 以卡片形式展示猫咪图片,附带分辨率、图片ID等详细信息,支持刷新/换图
状态管理 通过Provider 6.1.2实现数据响应式更新,管理图片加载、加载状态、错误信息等
多端支持 一次开发,同时适配HarmonyOS和Android平台,实现跨平台运行
异常处理 封装统一的网络错误处理,实现图片加载中/加载失败的UI状态展示

1.2 技术选型

本项目选用成熟、轻量且适配鸿蒙的技术栈,兼顾开发效率和运行性能,核心技术栈及版本如下:

复制代码
技术栈组成:
├── Flutter 3.x      # 跨平台UI框架,支撑多端渲染
├── Dart 3.x         # Flutter官方编程语言,强类型提升代码健壮性
├── Dio 5.5.0+1      # 高性能网络请求库,支持拦截、取消请求、统一错误处理
└── Provider 6.1.2   # 轻量级状态管理框架,适配Flutter组件化开发思想

二、开发环境配置

2.1 必备工具清单

开发前需完成以下工具的安装和配置,确保鸿蒙平台的适配性:

  • Android Studio(最新稳定版):集成Flutter/Dart插件,提供开发和调试环境
  • Flutter SDK(含ohos平台支持):需下载适配鸿蒙的Flutter版本,支持鸿蒙平台编译
  • Dart & Flutter 插件:在Android Studio插件市场安装,保证代码提示、编译运行正常

2.2 环境验证

打开终端执行以下命令,确认环境配置无误且支持鸿蒙平台:

bash 复制代码
# 检查Flutter版本及支持的平台,确认输出中包含ohos
flutter --version

2.3 项目初始化

完成环境验证后,通过终端命令创建Flutter项目并添加鸿蒙平台支持,步骤如下:

bash 复制代码
# 1. 创建基础Flutter项目
flutter create cat_gallery_app
# 2. 进入项目根目录
cd cat_gallery_app
# 3. 为项目添加鸿蒙平台支持
flutter create --platform ohos .

三、项目架构设计

为保证代码的可读性、可维护性和扩展性,本项目采用分层架构设计,遵循"职责单一"原则,将代码按功能模块划分目录,同时明确各层的调用关系。

3.1 目录结构

项目核心代码均在lib/目录下,按功能划分为api、components、contents等子目录,结构清晰,便于后续扩展和维护:

复制代码
lib/
├── api/              # 网络请求服务层,封装API调用方法
├── components/       # 可复用UI组件,如猫咪卡片组件
├── contents/         # 常量配置文件,如API地址、默认参数
├── pages/            # 页面视图,按页面划分子目录
│   ├── Main/         # 应用首页
│   └── Cats/         # 猫咪图库核心页面
├── routes/           # 路由配置,统一管理页面跳转
├── stores/           # 状态管理层,基于Provider实现
├── utils/            # 工具类,如HTTP封装、通用方法
├── viewmodels/       # 数据模型层,解析API返回数据
└── main.dart         # 应用入口文件,初始化全局配置

3.2 架构分层关系

各层之间单向依赖,避免循环引用,保证架构的稳定性,核心调用关系如下:

复制代码
UI Layer (pages) → State Layer (stores) → API Layer (api) → Utils & Models (utils/viewmodels)
  • UI层:负责页面展示和用户交互,监听状态层的数据变化
  • 状态层:管理应用的全局/页面状态,转发UI层的请求至API层,同步数据变化
  • API层:封装具体的API调用逻辑,调用工具类完成网络请求
  • 工具&模型层:提供通用工具方法、解析API返回数据为实体类,为上层提供基础支撑

四、核心代码实现

本项目从依赖配置、工具封装、数据模型、API服务、状态管理、UI开发到路由配置,逐步实现核心功能,所有代码均做了封装和优化,便于复用和修改。

4.1 配置项目依赖

编辑项目根目录下的pubspec.yaml文件,添加Dio、Provider等核心依赖,配置后执行flutter pub get安装依赖:

yaml 复制代码
dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.8  # 系统图标
  dio: ^5.5.0+1            # 网络请求库
  provider: ^6.1.2         # 状态管理库

4.2 定义API常量

创建lib/contents/api_constants.dart,集中管理API基础地址、接口端点和默认参数,避免硬编码,便于后续环境切换和维护:

dart 复制代码
class ApiConstants {
  // API基础地址
  static const String catBaseUrl = 'https://api.thecatapi.com/v1';
  // 猫咪图片接口端点
  static const String catImagesEndpoint = '/images/search';

  // 请求参数默认值
  static const int defaultPageSize = 10;
  static const int defaultTimeout = 15; // 超时时间(秒)
}

4.3 封装HTTP工具类

创建lib/utils/http_util.dart,基于Dio实现HTTP工具类的封装,采用单例模式保证全局唯一实例,同时实现统一的GET请求和错误处理,将Dio异常转换为用户友好的中文提示:

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

class HttpUtil {
  // 单例模式,全局唯一实例
  static final HttpUtil _instance = HttpUtil._internal();
  late Dio _dio;

  factory HttpUtil() => _instance;

  HttpUtil._internal() {
    // 初始化Dio,配置超时时间
    _dio = Dio(BaseOptions(
      connectTimeout: const Duration(seconds: ApiConstants.defaultTimeout),
      receiveTimeout: const Duration(seconds: ApiConstants.defaultTimeout),
    ));
  }

  Dio get dio => _dio;

  // 通用GET请求封装
  Future<dynamic> get(String endpoint, {Map<String, dynamic>? queryParams}) async {
    try {
      final response = await _dio.get(endpoint, queryParameters: queryParams);
      return response.data;
    } on DioException catch (e) {
      throw _handleError(e);
    }
  }

  // 统一错误处理,转换为中文提示
  String _handleError(DioException e) {
    switch (e.type) {
      case DioExceptionType.connectionTimeout:
      case DioExceptionType.receiveTimeout:
        return '网络连接超时,请检查您的网络';
      case DioExceptionType.badResponse:
        return '服务器响应错误: ${e.response?.statusCode}';
      case DioExceptionType.cancel:
        return '请求已取消';
      default:
        return '网络异常: ${e.message}';
    }
  }
}

4.4 定义数据模型

创建lib/viewmodels/cat_model.dart,定义猫咪图片的数据模型类,实现fromJson方法完成JSON数据到实体类的解析,toJson方法实现实体类到JSON的转换,避免直接操作原始JSON数据,提升代码可读性:

dart 复制代码
class CatModel {
  final String id;
  final String url;
  final int width;
  final int height;

  // 构造方法
  CatModel({
    required this.id,
    required this.url,
    required this.width,
    required this.height,
  });

  // 从JSON解析为CatModel实例
  factory CatModel.fromJson(Map<String, dynamic> json) {
    return CatModel(
      id: json['id'] ?? '',
      url: json['url'] ?? '',
      width: json['width'] ?? 0,
      height: json['height'] ?? 0,
    );
  }

  // 从CatModel转换为JSON
  Map<String, dynamic> toJson() => {
        'id': id,
        'url': url,
        'width': width,
        'height': height,
      };
}

4.5 实现API服务层

创建lib/api/cat_api.dart,封装猫咪图片的API调用方法,调用HTTP工具类完成网络请求,将返回的JSON数据解析为CatModel实体类,对外提供简洁的调用接口,支持单张/批量获取猫咪图片、分页加载等功能:

dart 复制代码
import 'package:cat_gallery_app/utils/http_util.dart';
import 'package:cat_gallery_app/viewmodels/cat_model.dart';
import 'package:cat_gallery_app/contents/api_constants.dart';

class CatApi {
  static final HttpUtil _http = HttpUtil();

  /// 获取猫咪图片列表(基础方法,支持多参数)
  static Future<List<CatModel>> getCatImages({
    int limit = 1,
    int? page,
    String? order,
    bool? hasBreeds,
  }) async {
    try {
      final Map<String, dynamic> queryParams = {'limit': limit};
      // 动态拼接请求参数
      if (page != null) queryParams['page'] = page;
      if (order != null) queryParams['order'] = order;
      if (hasBreeds != null) queryParams['has_breeds'] = hasBreeds ? 1 : 0;

      // 调用HTTP工具类发起请求
      final data = await _http.get(
        '${ApiConstants.catBaseUrl}${ApiConstants.catImagesEndpoint}',
        queryParams: queryParams,
      );

      // 数据解析,转换为CatModel列表
      if (data is List) {
        return data.map((json) => CatModel.fromJson(json)).toList();
      } else {
        throw Exception('数据格式异常,非数组类型');
      }
    } catch (e) {
      rethrow;
    }
  }

  /// 获取单张随机猫咪图片(简化接口)
  static Future<CatModel> getSingleCat() async {
    final cats = await getCatImages(limit: 1);
    return cats.first;
  }

  /// 批量获取猫咪图片(简化接口)
  static Future<List<CatModel>> getMultipleCats(int count) async {
    return await getCatImages(limit: count);
  }
}

4.6 状态管理实现

创建lib/stores/cat_store.dart,基于Provider的ChangeNotifier实现状态管理,管理猫咪图片列表、加载状态、错误信息、当前页码等状态,提供数据获取、刷新、清空等方法,通过notifyListeners()实现状态变化的通知,让UI层实时响应数据变化:

dart 复制代码
import 'package:flutter/material.dart';
import 'package:cat_gallery_app/api/cat_api.dart';
import 'package:cat_gallery_app/viewmodels/cat_model.dart';

class CatStore extends ChangeNotifier {
  List<CatModel> _cats = [];
  bool _isLoading = false;
  String? _error;
  int _currentPage = 1;

  // 对外提供只读的状态属性
  List<CatModel> get cats => _cats;
  bool get isLoading => _isLoading;
  String? get error => _error;
  int get currentPage => _currentPage;

  /// 获取猫咪数据,支持加载更多和分页
  Future<void> fetchCats({
    int limit = 10,
    bool hasBreeds = false,
    bool loadMore = false,
  }) async {
    // 加载更多时不重置页码,否则重置为1
    if (!loadMore) {
      _currentPage = 1;
    }
    // 更新加载状态
    _isLoading = true;
    _error = null;
    notifyListeners();

    try {
      // 调用API服务层获取数据
      final newCats = await CatApi.getCatImages(
        limit: limit,
        page: _currentPage,
        hasBreeds: hasBreeds,
      );
      // 加载更多则追加数据,否则替换数据
      if (loadMore) {
        _cats.addAll(newCats);
      } else {
        _cats = newCats;
      }
      // 页码自增
      _currentPage++;
    } catch (e) {
      // 捕获错误,更新错误状态
      _error = e.toString();
    } finally {
      // 结束加载状态
      _isLoading = false;
      notifyListeners();
    }
  }

  /// 刷新数据,按当前数据量重新获取
  Future<void> refresh() async {
    await fetchCats(limit: _cats.length);
  }

  /// 清空所有猫咪数据,重置状态
  void clear() {
    _cats = [];
    _error = null;
    _currentPage = 1;
    notifyListeners();
  }
}

4.7 封装猫咪卡片组件

创建lib/components/cat_card.dart,封装可复用的猫咪卡片UI组件,接收CatModel实例作为参数,实现图片展示、加载中/加载失败状态、图片信息展示等功能,组件化开发提升代码复用性,便于后续统一修改样式:

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

class CatCard extends StatelessWidget {
  final CatModel cat;

  const CatCard({
    super.key,
    required this.cat,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(12),
      ),
      elevation: 2, // 卡片阴影
      child: Column(
        children: [
          // 猫咪图片区域,处理加载中/加载失败
          ClipRRect(
            borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
            child: SizedBox(
              width: double.infinity,
              height: 200,
              child: Image.network(
                cat.url,
                fit: BoxFit.cover,
                // 图片加载中展示加载指示器
                loadingBuilder: (context, child, loadingProgress) {
                  if (loadingProgress == null) return child;
                  return const Center(child: CircularProgressIndicator());
                },
                // 图片加载失败展示占位图标
                errorBuilder: (context, error, stackTrace) {
                  return Container(
                    color: Colors.grey[100],
                    child: const Center(
                      child: Icon(Icons.broken_image, size: 50, color: Colors.grey),
                    ),
                  );
                },
              ),
            ),
          ),
          // 猫咪图片信息区域
          Padding(
            padding: const EdgeInsets.all(12),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  '分辨率: ${cat.width} × ${cat.height}',
                  style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
                ),
                const SizedBox(height: 4),
                Text(
                  '图片ID: ${cat.id}',
                  style: TextStyle(fontSize: 12, color: Colors.grey[600]),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

4.8 实现猫咪图库页面

创建lib/pages/Cats/index.dart,实现猫咪图库核心页面,处理页面初始化、图片加载、刷新/换图、错误处理等逻辑,调用封装的猫咪卡片组件展示图片,实现友好的用户交互:

dart 复制代码
import 'package:flutter/material.dart';
import 'package:cat_gallery_app/api/cat_api.dart';
import 'package:cat_gallery_app/viewmodels/cat_model.dart';
import 'package:cat_gallery_app/components/cat_card.dart';

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

  @override
  State<CatsPage> createState() => _CatsPageState();
}

class _CatsPageState extends State<CatsPage> {
  CatModel? _cat;
  bool _isLoading = false;
  String? _error;

  // 页面初始化时加载第一张猫咪图片
  @override
  void initState() {
    super.initState();
    _loadCatImage();
  }

  // 加载猫咪图片的核心方法
  Future<void> _loadCatImage() async {
    setState(() {
      _isLoading = true;
      _error = null;
    });

    try {
      // 调用API获取单张猫咪图片
      final cat = await CatApi.getSingleCat();
      setState(() {
        _cat = cat;
      });
    } catch (e) {
      // 捕获错误,更新错误状态
      setState(() {
        _error = e.toString();
      });
    } finally {
      // 结束加载状态
      setState(() {
        _isLoading = false;
      });
    }
  }

  // 构建页面内容,根据不同状态展示不同UI
  Widget _buildContent() {
    // 加载中状态
    if (_isLoading) {
      return const Center(child: CircularProgressIndicator());
    }
    // 加载失败状态
    if (_error != null) {
      return Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Icon(Icons.error_outline, size: 64, color: Colors.red),
            const SizedBox(height: 16),
            Text('加载失败: $_error'),
            const SizedBox(height: 16),
            ElevatedButton(onPressed: _loadCatImage, child: const Text('重试')),
          ],
        ),
      );
    }
    // 无数据状态
    if (_cat == null) {
      return const Center(child: Text('暂无数据'));
    }
    // 数据加载成功,展示猫咪卡片和操作按钮
    return SingleChildScrollView(
      child: Center(
        child: Padding(
          padding: const EdgeInsets.all(20),
          child: Column(
            children: [
              CatCard(cat: _cat!),
              const SizedBox(height: 20),
              ElevatedButton.icon(
                onPressed: _loadCatImage,
                icon: const Icon(Icons.cached),
                label: const Text('换一张'),
              ),
              const SizedBox(height: 20),
              // 图片详细信息展示
              Container(
                padding: const EdgeInsets.all(16),
                decoration: BoxDecoration(
                  color: Colors.grey[50],
                  borderRadius: BorderRadius.circular(12),
                ),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text('详细信息', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
                    const SizedBox(height: 8),
                    Text('ID: ${_cat!.id}'),
                    Text('宽度: ${_cat!.width}px'),
                    Text('高度: ${_cat!.height}px'),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('猫咪图库'),
        // 右上角刷新按钮
        actions: [IconButton(icon: const Icon(Icons.refresh), onPressed: _loadCatImage)],
      ),
      body: _buildContent(),
    );
  }
}

4.9 实现应用主页面

创建lib/pages/Main/index.dart,实现应用的首页,作为入口页面,提供跳转到猫咪图库页面的按钮,样式简洁,引导用户操作:

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('猫咪图库应用'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              '欢迎使用猫咪图库',
              style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 40),
            SizedBox(
              width: 200,
              child: ElevatedButton.icon(
                onPressed: () {
                  // 跳转到猫咪图库页面
                  Navigator.pushNamed(context, '/cats');
                },
                icon: const Icon(Icons.pets),
                label: const Text('开始浏览'),
                style: ElevatedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 16)),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

4.10 路由配置

创建lib/routes/index.dart,统一管理应用的路由,定义路由名称和对应的页面,提供路由初始化方法,简化页面跳转逻辑,便于后续添加新页面:

dart 复制代码
import 'package:flutter/material.dart';
import 'package:cat_gallery_app/pages/Main/index.dart';
import 'package:cat_gallery_app/pages/Cats/index.dart';

// 获取根Widget,初始化MaterialApp
Widget getRootWidget() {
  return MaterialApp(
    title: '猫咪图库',
    initialRoute: '/', // 默认入口页面
    routes: getRootRoutes(), // 路由映射
  );
}

// 定义路由映射关系
Map<String, Widget Function(BuildContext)> getRootRoutes() {
  return {
    '/': (context) => const MainPage(), // 首页
    '/cats': (context) => const CatsPage(), // 猫咪图库页
  };
}

4.11 应用入口

修改项目根目录下的lib/main.dart,作为应用的全局入口,初始化Provider状态管理,将CatStore注入到全局环境中,让所有页面都能访问状态,同时加载路由配置:

dart 复制代码
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:cat_gallery_app/routes/index.dart';
import 'package:cat_gallery_app/stores/cat_store.dart';

void main() {
  runApp(
    // 多Provider配置,便于后续添加更多状态管理类
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => CatStore()),
      ],
      child: getRootWidget(),
    ),
  );
}

五、多平台运行演示

本项目实现了HarmonyOS和Android双平台适配,以下为鸿蒙平台的核心运行步骤,Android平台可直接在Android Studio中选择安卓模拟器/真机运行,步骤类似。

5.1 HarmonyOS平台运行

  1. 启动DevEco Studio,确保鸿蒙模拟器已创建并启动;
  2. 回到Android Studio,选择对应的鸿蒙模拟器设备;
  3. 点击运行按钮,Flutter会自动编译鸿蒙版本的应用并安装到模拟器;
  4. 启动应用后,先进入首页,点击「开始浏览」即可进入猫咪图库页面,实现图片加载、换图、刷新等功能。

运行效果:猫咪图片以卡片形式展示,加载中展示圆形加载指示器,加载失败展示错误提示和重试按钮,点击「换一张」可快速获取新的随机猫咪图片,完美适配鸿蒙设备的屏幕尺寸。

六、常见问题解决方案

在Flutter鸿蒙跨平台开发过程中,常会遇到网络请求、图片加载、状态管理、打包等问题,本文整理了最常见的问题及针对性的解决方案,帮助开发者快速排障。

6.1 HTTPS证书验证失败

问题现象 :运行时抛出Dio错误HandshakeException: Handshake error in client,因鸿蒙平台对HTTPS证书验证严格导致。
解决方案 :在HTTP工具类的初始化方法中添加证书忽略逻辑(仅适用于开发调试环境,生产环境需配置合法证书):

dart 复制代码
HttpUtil._internal() {
  _dio = Dio(BaseOptions(
    connectTimeout: const Duration(seconds: 15),
    receiveTimeout: const Duration(seconds: 15),
  ));
  // 添加证书忽略逻辑
  (_dio.httpClientAdapter as DefaultHttpClientAdapter)
      .onHttpClientCreate = (client) {
    client.badCertificateCallback = (cert, host, port) => true;
    return client;
  };
}

6.2 图片加载缓慢/超时

问题现象 :猫咪图片加载耗时久,甚至触发超时错误,因网络或API接口响应慢导致。
优化方案

  1. 增加Dio的超时时间,将默认15秒改为30秒;
  2. 引入图片缓存库cached_network_image,缓存已加载的图片,避免重复请求:
yaml 复制代码
# 第一步:在pubspec.yaml添加依赖
dependencies:
  cached_network_image: ^3.3.0
dart 复制代码
# 第二步:替换Image.network为CachedNetworkImage
CachedNetworkImage(
  imageUrl: cat.url,
  fit: BoxFit.cover,
  placeholder: (context, url) => const CircularProgressIndicator(),
  errorWidget: (context, url, error) => const Icon(Icons.broken_image),
)

6.3 Provider状态不更新

问题现象 :修改状态后,UI层未同步更新,页面无变化。
检查要点

  1. 确保状态修改后调用了notifyListeners()方法,通知UI层刷新;
  2. UI层需通过Consumercontext.watchcontext.select监听状态,而非直接获取状态值:
dart 复制代码
// 正确用法:使用Consumer包裹需要刷新的UI
Consumer<CatStore>(
  builder: (context, catStore, child) {
    return ListView.builder(
      itemCount: catStore.cats.length,
      itemBuilder: (context, index) => CatCard(cat: catStore.cats[index]),
    );
  },
)

6.4 鸿蒙平台打包报错

问题现象 :执行鸿蒙打包命令时抛出Execution failed for task ':ohos:assembleDebug'错误。
解决步骤

  1. 确认Flutter SDK已包含鸿蒙平台支持,重新执行flutter create --platform ohos .
  2. 检查DevEco Studio的鸿蒙SDK配置是否正确,确保与Flutter的鸿蒙版本兼容;
  3. 清理项目缓存,执行flutter clean后重新打包。

6.5 import路径报错

问题现象 :导入自定义类时提示"找不到文件",因路径错误或项目命名问题导致。
解决步骤

  1. 确保导入路径与目录结构一致,使用相对路径;
  2. 检查项目名称是否包含特殊字符,建议使用纯小写+下划线的命名规则;
  3. 执行flutter pub get后重启Android Studio,刷新代码索引。

七、项目总结与拓展

7.1 学习收获

本项目从0到1实现了Flutter跨平台猫咪图库应用,覆盖了Flutter开发的核心知识点:

  1. 掌握Dio库的封装和使用,实现统一的网络请求和错误处理;
  2. 理解Provider状态管理的核心思想,实现数据的响应式更新;
  3. 掌握Flutter组件化开发,封装可复用的UI组件;
  4. 实现Flutter对HarmonyOS和Android的双平台适配,了解鸿蒙跨平台开发的核心流程;
  5. 学会分析和解决Flutter开发中的常见问题,提升排障能力。

7.2 后续优化方向

本项目为基础实战项目,可在此基础上进行功能拓展和性能优化,提升应用的体验和实用性:

  1. 功能拓展:添加图片收藏、图片下载、分类筛选、下拉刷新/上拉加载更多等功能;
  2. 性能优化:实现图片懒加载、列表分页加载、优化状态管理减少不必要的UI刷新;
  3. UI优化:适配深色模式、添加动画效果、优化页面布局,提升视觉体验;
  4. 生产环境适配:配置HTTPS合法证书、添加日志统计、实现异常上报;
  5. 多端拓展:适配iOS平台,实现真正的跨三端运行。

Flutter作为跨平台开发的主流框架,与鸿蒙的适配性不断提升,本项目的开发思路和代码封装方式可迁移到更多Flutter鸿蒙跨平台项目中,希望通过本实战教程,能帮助开发者快速上手Flutter鸿蒙开发,夯实跨平台开发的核心能力。


✨ 坚持用 清晰的图解 +易懂的硬件架构 + 硬件解析, 让每个知识点都 简单明了 !

🚀 个人主页一只大侠的侠 · CSDN

💬 座右铭 : "所谓成功就是以自己的方式度过一生。"

相关推荐
HelloGitHub3 小时前
《HelloGitHub》第 119 期
开源·github
冬奇Lab15 小时前
一天一个开源项目(第35篇):GitHub Store - 跨平台的 GitHub Releases 应用商店
开源·github·资讯
Zsnoin能20 小时前
Flutter仿ios液态玻璃效果
flutter
傅里叶1 天前
iOS相机权限获取
flutter·ios
Haha_bj1 天前
Flutter—— 本地存储(shared_preferences)
flutter
心之语歌1 天前
Flutter 存储权限:适配主流系统
flutter
Bigger1 天前
为什么你的 Git 提交需要签名?—— Git Commit Signing 完全指南
git·开源·github
恋猫de小郭1 天前
Android 官方正式官宣 AI 支持 AppFunctions ,Android 官方 MCP 和系统级 OpenClaw 雏形
android·前端·flutter
在人间耕耘2 天前
HarmonyOS Vision Kit 视觉AI实战:把官方 Demo 改造成一套能长期复用的组件库
人工智能·深度学习·harmonyos
MakeZero2 天前
Flutter那些事-布局篇
flutter