Flutter for OpenHarmony二手物品置换App实战 - 项目总结

我们完成了"闲置换"二手物品置换App的开发。今天来做一个项目总结,回顾学到的知识点,并展望后续的优化方向。

项目回顾

我们从零开始,搭建了一个完整的二手交易App。首页模块包含搜索栏、活动Banner、分类导航和商品瀑布流列表;搜索模块实现了搜索历史和热门搜索;分类模块支持筛选和排序;商品详情展示图片画廊、价格和卖家信息;发布模块处理图片选择和表单验证;消息模块包含会话列表和聊天对话;个人中心展示用户信息和功能菜单;设置模块管理通知和缓存。

技术栈总结

我们使用Flutter框架进行跨平台UI开发,一套代码运行在多个平台。GetX负责状态管理、路由管理和依赖注入,API简洁易用。flutter_screenutil处理屏幕适配,让UI在不同设备上保持一致。convex_bottom_bar实现凸起底部导航栏,突出发布按钮。这套技术栈组合起来,开发效率很高,代码也比较好维护。

项目结构

复制代码
lib/
├── main.dart                 # 应用入口
├── pages/
│   ├── splash_page.dart      # 启动页
│   ├── main_page.dart        # 主框架
│   ├── home/
│   │   └── home_page.dart    # 首页
│   ├── search/
│   │   └── search_page.dart  # 搜索页
│   ├── category/
│   │   └── category_page.dart # 分类页
│   ├── product/
│   │   └── product_detail_page.dart # 商品详情
│   ├── publish/
│   │   └── publish_page.dart # 发布页
│   ├── message/
│   │   ├── message_page.dart # 消息列表
│   │   └── chat_page.dart    # 聊天页
│   └── profile/
│       ├── profile_page.dart # 个人中心
│       ├── settings_page.dart # 设置页
│       ├── my_products_page.dart # 我的发布
│       └── favorites_page.dart # 我的收藏
├── widgets/                  # 自定义通用组件
├── models/                   # 数据模型
├── services/                 # 网络/本地存储服务
└── controllers/              # GetX状态管理控制器

以上是优化后的项目目录结构,在原有基础上拆分出widgets、models、services、controllers目录,让代码职责更清晰,符合"单一职责原则"。

核心知识点

1. 应用入口main.dart

dart 复制代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'pages/splash_page.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return ScreenUtilInit(
      designSize: const Size(375, 812), // 设计稿尺寸
      minTextAdapt: true,
      splitScreenMode: true,
      builder: (context, child) {
        return GetMaterialApp(
          title: '闲置换',
          theme: ThemeData(primarySwatch: Colors.blue),
          home: const SplashPage(),
          debugShowCheckedModeBanner: false,
        );
      },
    );
  }
}

这段代码是App的入口,核心做了三件事:初始化屏幕适配工具flutter_screenutil(指定设计稿尺寸,保证多设备UI一致)、初始化GetX的GetMaterialApp(替代原生MaterialApp,支持GetX路由/状态管理)、设置启动页为SplashPage。其中ScreenUtilInit是适配的关键,minTextAdapt开启文字适配,splitScreenMode支持分屏适配。

2. 首页商品瀑布流

dart 复制代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../controllers/home_controller.dart';
import '../../widgets/product_item_widget.dart';

class HomePage extends GetView<HomeController> {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('首页')),
      body: Obx(() {
        if (controller.productList.isEmpty) {
          return const Center(child: CircularProgressIndicator());
        }
        // 瀑布流列表
        return GridView.builder(
          padding: EdgeInsets.all(10.w),
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 2, // 列数
            crossAxisSpacing: 10.w, // 列间距
            mainAxisSpacing: 10.h, // 行间距
            childAspectRatio: 0.75, // 宽高比
          ),
          itemCount: controller.productList.length,
          itemBuilder: (context, index) {
            // 复用商品卡片组件
            return ProductItemWidget(
              product: controller.productList[index],
              onTap: () => Get.toNamed('/productDetail', arguments: controller.productList[index]),
            );
          },
        );
      }),
    );
  }
}

这里使用GridView.builder实现商品瀑布流,核心优势是"懒加载"------只渲染当前可视区域的商品卡片,避免一次性加载所有数据导致内存占用过高。Obx是GetX的响应式组件,当controller.productList数据变化时,会自动刷新UI,无需手动调用setState。

3. GetX控制器

dart 复制代码
import 'package:get/get.dart';
import '../../models/product_model.dart';
import '../../services/api_service.dart';

class HomeController extends GetxController {
  // 响应式商品列表(.obs标记为响应式)
  final RxList<ProductModel> productList = <ProductModel>[].obs;

  @override
  void onInit() {
    super.onInit();
    // 初始化加载数据
    loadProductList();
  }

  // 加载商品列表数据
  Future<void> loadProductList() async {
    try {
      final result = await ApiService.getProductList();
      productList.assignAll(result); // 赋值并触发UI刷新
    } catch (e) {
      Get.snackbar('提示', '数据加载失败:$e'); // 全局提示
    }
  }

  // 下拉刷新
  Future<void> onRefresh() async {
    productList.clear();
    await loadProductList();
  }
}

这是首页的GetX控制器,核心是响应式数据管理:用.obs标记productList为响应式,onInit生命周期方法中加载数据(对应页面初始化时执行),loadProductList中调用网络服务获取数据,通过assignAll更新列表并触发UI刷新。GetX控制器的生命周期(onInit/onReady/onClose)能精准管理数据加载/释放,避免内存泄漏。

4. 商品详情页参数接收

dart 复制代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../models/product_model.dart';

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

  @override
  Widget build(BuildContext context) {
    // 获取路由传递的商品参数
    final ProductModel product = Get.arguments;
    
    return Scaffold(
      appBar: AppBar(title: Text(product.name)),
      body: SingleChildScrollView(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // 商品图片画廊
            SizedBox(
              height: 300.h,
              child: PageView.builder(
                itemCount: product.images.length,
                itemBuilder: (context, index) {
                  return Image.network(
                    product.images[index],
                    fit: BoxFit.cover,
                  );
                },
              ),
            ),
            // 商品价格
            Padding(
              padding: EdgeInsets.all(10.w),
              child: Text(
                '¥${product.price}',
                style: TextStyle(fontSize: 20.sp, color: Colors.red),
              ),
            ),
            // 商品描述
            Padding(
              padding: EdgeInsets.all(10.w),
              child: Text(
                product.description,
                style: TextStyle(fontSize: 14.sp, color: Colors.grey[800]),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

GetX路由传参非常简洁,通过Get.arguments即可获取上一页传递的参数(无需像原生路由那样手动解析)。这里用PageView.builder实现商品图片画廊,SingleChildScrollView包裹Column解决内容超出屏幕的滚动问题,结合screenutil的尺寸单位(w/h/sp)保证不同设备显示比例一致。

5. 发布页表单验证

dart 复制代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../controllers/publish_controller.dart';

class PublishPage extends GetView<PublishController> {
  const PublishPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('发布商品')),
      body: Form(
        key: controller.formKey,
        child: ListView(
          padding: EdgeInsets.all(15.w),
          children: [
            // 商品名称输入框
            TextFormField(
              controller: controller.nameController,
              decoration: InputDecoration(
                hintText: '请输入商品名称',
                hintStyle: TextStyle(fontSize: 14.sp),
                border: OutlineInputBorder(borderRadius: BorderRadius.circular(8.r)),
              ),
              validator: (value) {
                if (value == null || value.isEmpty) {
                  return '商品名称不能为空';
                }
                return null;
              },
            ),
            SizedBox(height: 15.h),
            // 商品价格输入框
            TextFormField(
              controller: controller.priceController,
              keyboardType: TextInputType.number,
              decoration: InputDecoration(
                hintText: '请输入商品价格',
                border: OutlineInputBorder(borderRadius: BorderRadius.circular(8.r)),
              ),
              validator: (value) {
                if (value == null || value.isEmpty) {
                  return '商品价格不能为空';
                }
                if (double.tryParse(value) == null) {
                  return '请输入有效的价格';
                }
                return null;
              },
            ),
            SizedBox(height: 20.h),
            // 提交按钮
            ElevatedButton(
              onPressed: controller.submitForm,
              style: ElevatedButton.styleFrom(
                fixedSize: Size(double.infinity, 45.h),
                borderRadius: BorderRadius.circular(8.r),
              ),
              child: Text('发布', style: TextStyle(fontSize: 16.sp)),
            ),
          ],
        ),
      ),
    );
  }
}

这段代码实现发布页的表单验证,核心是结合Flutter原生Form组件和GetX控制器:Form的key绑定控制器的formKey,TextFormField的validator实现字段校验(非空、价格格式),提交按钮调用controller.submitForm方法。通过表单验证能避免无效数据提交,提升用户体验,而将表单逻辑抽离到控制器中,也符合"视图与逻辑分离"的设计思想。

6. 发布控制器

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

class PublishController extends GetxController {
  final GlobalKey<FormState> formKey = GlobalKey<FormState>();
  final TextEditingController nameController = TextEditingController();
  final TextEditingController priceController = TextEditingController();

  // 提交表单
  void submitForm() {
    if (formKey.currentState!.validate()) {
      // 表单验证通过,提交数据
      _publishProduct();
    }
  }

  // 发布商品接口调用
  Future<void> _publishProduct() async {
    try {
      await ApiService.publishProduct(
        name: nameController.text,
        price: double.parse(priceController.text),
      );
      Get.snackbar('成功', '商品发布成功');
      Get.back(); // 返回上一页
    } catch (e) {
      Get.snackbar('失败', '发布失败:$e');
    }
  }

  @override
  void onClose() {
    // 释放控制器,避免内存泄漏
    nameController.dispose();
    priceController.dispose();
    super.onClose();
  }
}

这是发布页的控制器,核心处理表单提交逻辑:submitForm方法先校验表单,通过后调用_publishProduct方法提交数据;onClose生命周期释放TextEditingController,避免内存泄漏。其中ApiService.publishProduct是封装的网络请求方法,调用成功后通过Get.snackbar给出全局提示,并返回上一页,失败则提示错误信息,错误处理更友好。

后续优化方向

这个项目还有很多可以优化的地方。以下是结合代码层面的优化方向:

1. 用户认证

dart 复制代码
// 示例:手机号登录接口封装
Future<void> loginWithPhone(String phone, String code) async {
  final response = await dio.post('/api/login/phone', data: {
    'phone': phone,
    'code': code,
  });
  if (response.statusCode == 200) {
    // 存储token到本地
    await GetStorage().write('token', response.data['token']);
  }
}

用户认证是App的基础功能,通过手机号+验证码登录是最常用的方式。上述代码封装了登录接口,登录成功后将token存储到本地(使用GetStorage),后续接口请求时携带token完成身份验证,保证接口访问的安全性。

2. 实时消息

dart 复制代码
// 示例:WebSocket初始化
import 'package:web_socket_channel/io.dart';

class ChatController extends GetxController {
  final RxList<String> messageList = <String>[].obs;
  late IOWebSocketChannel channel;

  @override
  void onInit() {
    super.onInit();
    // 连接WebSocket
    channel = IOWebSocketChannel.connect('ws://your-server-url/ws/chat');
    // 监听消息
    channel.stream.listen((message) {
      messageList.add(message);
    });
  }

  // 发送消息
  void sendMessage(String msg) {
    channel.sink.add(msg);
  }

  @override
  void onClose() {
    channel.sink.close(); // 关闭连接
    super.onClose();
  }
}

通过WebSocket可以实现实时聊天功能,上述代码在ChatController中初始化WebSocket连接,监听服务端消息并更新响应式列表,发送消息时调用sink.add方法。onClose生命周期关闭连接,避免资源浪费,这也是实时消息功能的核心代码逻辑。

开发建议

写代码要遵循Dart规范,用lint检查代码质量。重复的UI要封装成组件(如上述的ProductItemWidget),提高复用性。状态管理要合理使用,避免状态混乱导致bug难查。错误处理要做好,给用户友好的提示而不是直接崩溃。性能问题要注意,避免卡顿和内存泄漏影响体验。最重要的是关注用户体验,让App用起来舒服自然。

结语

通过这个项目的学习,相信你已经掌握了Flutter开发的核心技能。Flutter是一个强大的跨平台框架,能够高效地开发出精美的App。希望你能继续深入学习,开发出更多优秀的应用。感谢你的阅读,祝你在Flutter开发的道路上越走越远!


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

相关推荐
●VON3 小时前
Flutter for OpenHarmony:基于可选描述字段与上下文感知渲染的 TodoList 任务详情子系统实现
学习·flutter·架构·交互·von
雨季6663 小时前
构建 OpenHarmony 简易单位换算器:用基础运算实现可靠转换
flutter·ui·自动化·dart
一起养小猫4 小时前
Flutter for OpenHarmony 实战:贪吃蛇游戏核心架构设计
flutter·游戏
无穷小亮4 小时前
Flutter框架跨平台鸿蒙开发——育儿知识APP的开发流程
flutter·华为·harmonyos·鸿蒙
嘴贱欠吻!4 小时前
Flutter鸿蒙开发指南(四):主页Tab栏实现
flutter
雨季6666 小时前
构建 OpenHarmony 跨设备任务协同中心:Flutter 实现多端任务流转与状态同步
flutter
●VON6 小时前
Flutter for OpenHarmony:基于可空截止日期与时间语义可视化的 TodoList 时间管理子系统实现
安全·flutter·交互·openharmony·跨平台开发
晚霞的不甘6 小时前
Flutter for OpenHarmony 引力弹球游戏开发全解析:从零构建一个交互式物理小游戏
前端·flutter·云原生·前端框架·游戏引擎·harmonyos·骨骼绑定
雨季6667 小时前
构建 OpenHarmony 智能场景自动化配置面板:Flutter 实现可视化规则编排
运维·flutter·自动化