Flutter 打车出行应用在 OpenHarmony 上的跨平台实践

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

作者:maaath

一、引言

随着 OpenHarmony 生态的快速发展,跨平台开发框架在鸿蒙设备上的适配成为开发者关注的焦点。Flutter 作为业界领先的跨平台 UI 框架,凭借其高性能渲染引擎和丰富的组件生态,已经成为 OpenHarmony 应用开发的重要选择之一。

本文将基于笔者在实际项目中开发的打车出行应用,详细介绍如何使用 Flutter 在 OpenHarmony 设备上构建一个功能完整的打车应用。该应用涵盖了附近车辆搜索、价格预估比较、预约叫车、行程记录管理、司机评价、紧急求助等核心功能,完整展示了 Flutter 在 OpenHarmony 平台上的开发实践。

本文所有代码均已在 OpenHarmony 设备上验证通过,项目源码托管于 AtomGit:https://atomgit.com

二、项目架构设计

打车出行应用采用经典的分层架构设计,整体分为数据模型层、服务层和 UI 层三个层次:

  • 数据模型层(models):定义车辆类型、司机信息、行程订单、发票等核心数据实体
  • 服务层(services):提供模拟数据生成、业务逻辑处理、状态管理等能力
  • UI 层(pages):基于 Flutter Material Design 构建用户界面,包含打车首页、价格比较、叫车流程、行程记录、紧急求助等页面

这种分层设计使得代码职责清晰、易于维护,同时也方便后续接入真实后端服务。

三、数据模型设计

首先定义打车应用的核心数据模型。我们使用 Dart 的枚举和类来抽象车辆类型、行程状态、司机信息等业务实体。

enum VehicleType {
  economy,   // 经济型
  comfort,   // 舒适型
  business,  // 商务型
  luxury,    // 豪华型
  carpool,   // 拼车
}

enum RideStatus {
  searching,   // 寻找车辆
  matched,     // 司机已接单
  arrived,     // 司机已到达
  inProgress,  // 行程中
  completed,   // 已完成
  cancelled,   // 已取消
}

车辆类型信息使用 VehicleTypeInfo 类封装,包含基础定价策略:

class VehicleTypeInfo {
  final VehicleType type;
  final String name;
  final double basePrice;
  final double pricePerKm;
  final double pricePerMinute;

  const VehicleTypeInfo({
    required this.type,
    required this.name,
    required this.basePrice,
    required this.pricePerKm,
    required this.pricePerMinute,
  });

  static const List<VehicleTypeInfo> allTypes = [
    VehicleTypeInfo(
      type: VehicleType.economy, name: '经济型',
      basePrice: 8.0, pricePerKm: 1.6, pricePerMinute: 0.3,
    ),
    VehicleTypeInfo(
      type: VehicleType.comfort, name: '舒适型',
      basePrice: 12.0, pricePerKm: 2.2, pricePerMinute: 0.4,
    ),
    VehicleTypeInfo(
      type: VehicleType.business, name: '商务型',
      basePrice: 18.0, pricePerKm: 3.0, pricePerMinute: 0.5,
    ),
    VehicleTypeInfo(
      type: VehicleType.luxury, name: '豪华型',
      basePrice: 28.0, pricePerKm: 4.5, pricePerMinute: 0.8,
    ),
    VehicleTypeInfo(
      type: VehicleType.carpool, name: '拼车',
      basePrice: 5.0, pricePerKm: 1.0, pricePerMinute: 0.2,
    ),
  ];
}

行程订单模型 RideOrder 记录了完整的行程信息,包括上下车地点、车辆类型、司机信息、费用明细等。通过 copyWith 方法实现不可变对象的状态更新,这是 Flutter 中推荐的做法。

四、服务层实现

服务层采用单例模式,提供模拟数据生成和业务逻辑处理能力。以下是附近车辆搜索的核心实现:

class RideService {
  static final RideService _instance = RideService._();
  factory RideService() => _instance;
  RideService._() {
    _initMockDrivers();
    _initMockLocations();
  }

  List<Driver> getNearbyDrivers(
    double latitude, double longitude, {
    VehicleType? vehicleType,
    double radiusKm = 3.0,
  }) {
    return _drivers.where((d) {
      if (!d.isAvailable) return false;
      if (vehicleType != null && d.vehicleType != vehicleType) return false;
      final distance = _calculateDistance(
        latitude, longitude, d.latitude, d.longitude,
      );
      return distance <= radiusKm;
    }).toList();
  }

  double _calculateDistance(
    double lat1, double lng1, double lat2, double lng2,
  ) {
    const r = 6371.0;
    final dLat = _toRadians(lat2 - lat1);
    final dLng = _toRadians(lng2 - lng1);
    final a = sin(dLat / 2) * sin(dLat / 2) +
        cos(_toRadians(lat1)) * cos(_toRadians(lat2)) *
        sin(dLng / 2) * sin(dLng / 2);
    final c = 2 * atan2(sqrt(a), sqrt(1 - a));
    return double.parse((r * c).toStringAsFixed(2));
  }
}

价格预估功能根据里程和时长计算各车型的预估价格,并支持优惠券抵扣:

List<PriceEstimate> getPriceEstimates(
  double distanceKm, int durationMinutes,
) {
  return VehicleTypeInfo.allTypes.map((info) {
    final price = info.basePrice +
        info.pricePerKm * distanceKm +
        info.pricePerMinute * durationMinutes;
    return PriceEstimate(
      vehicleType: info.type,
      estimatedPrice: double.parse(price.toStringAsFixed(2)),
      estimatedDistance: distanceKm,
      estimatedDuration: durationMinutes,
    );
  }).toList();
}

五、UI 层实现

5.1 打车首页与附近车辆搜索

打车首页是用户进入应用的第一界面,包含上车地点选择、目的地输入、附近车辆展示和快捷操作入口。我们使用 Timer.periodic 实现每 5 秒自动刷新附近车辆列表,模拟实时定位效果。

class _RideHomePageState extends State<RideHomePage> {
  final _rideService = RideService();
  final _pickupController = TextEditingController();
  final _dropoffController = TextEditingController();

  RideLocation? _pickupLocation;
  RideLocation? _dropoffLocation;
  List<Driver> _nearbyDrivers = [];
  Timer? _refreshTimer;

  
  void initState() {
    super.initState();
    _pickupLocation = _rideService.savedLocations.first;
    _pickupController.text = _pickupLocation!.name;
    _refreshNearbyDrivers();
    _refreshTimer = Timer.periodic(
      const Duration(seconds: 5), (_) => _refreshNearbyDrivers(),
    );
  }

  void _refreshNearbyDrivers() {
    if (_pickupLocation == null) return;
    setState(() {
      _nearbyDrivers = _rideService.getNearbyDrivers(
        _pickupLocation!.latitude,
        _pickupLocation!.longitude,
      );
    });
  }
}

地点选择采用底部弹出面板(BottomSheet)的设计,展示常用地点和热门目的地,用户点击即可快速填入。

5.2 价格预估比较

价格预估页面展示五种车型的价格对比,按价格从低到高排序,并标注"最实惠"标签。用户可以选择优惠券进行抵扣,实时查看折后价格。

Widget _buildPriceList(ThemeData theme) {
  final sortedEstimates = List<PriceEstimate>.from(_estimates)
    ..sort((a, b) => a.estimatedPrice.compareTo(b.estimatedPrice));

  return ListView.builder(
    padding: const EdgeInsets.all(16),
    itemCount: sortedEstimates.length,
    itemBuilder: (context, index) {
      final estimate = sortedEstimates[index];
      final typeInfo = VehicleTypeInfo.getByType(estimate.vehicleType);
      final discountedPrice = _getDiscountedPrice(estimate.estimatedPrice);

      return Container(
        margin: const EdgeInsets.only(bottom: 12),
        decoration: BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.circular(16),
          border: Border.all(
            color: index == 0 ? Colors.green : Colors.grey.shade200,
            width: index == 0 ? 2 : 1,
          ),
        ),
        child: ListTile(
          leading: Text(typeInfo.icon, style: const TextStyle(fontSize: 28)),
          title: Text(typeInfo.name),
          subtitle: Text(typeInfo.description),
          trailing: Column(
            children: [
              Text(${discountedPrice.toStringAsFixed(2)}',
                style: const TextStyle(
                  fontSize: 20, fontWeight: FontWeight.bold,
                ),
              ),
            ],
          ),
          onTap: () => _confirmRide(estimate.vehicleType, estimate.estimatedPrice),
        ),
      );
    },
  );
}

5.3 叫车流程模拟

叫车页面完整模拟了从"寻找车辆"到"行程结束"的完整流程,通过状态机驱动 UI 变化:

// 状态流转:searching -> matched -> arrived -> inProgress -> completed
_statusTimer = Timer(const Duration(seconds: 3), () {
  final driver = _rideService.matchDriver(order);
  if (driver != null && mounted) {
    setState(() {
      _matchedDriver = driver;
      _status = RideStatus.matched;
      _rideService.updateOrderStatus(order.id, RideStatus.matched, driver: driver);
    });
    // 后续状态依次推进...
  }
});

每个状态对应不同的 UI 展示:搜索中显示加载动画和等待时间,已接单展示司机信息和车辆详情,行程中显示安全提示,行程结束后提供评价和开票入口。

5.4 紧急求助功能

紧急求助页面是打车应用安全体系的重要组成部分,提供 SOS 一键报警、行程分享、录音取证、快速拍照等安全功能:

class RideEmergencyPage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('紧急求助'),
        backgroundColor: Colors.red,
        foregroundColor: Colors.white,
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [
            _buildEmergencyBanner(theme),
            _buildSOSButton(context, theme),
            _buildEmergencyContacts(context, contacts, theme),
            _buildSafetyTips(theme),
          ],
        ),
      ),
    );
  }
}

SOS 按钮采用醒目的红色圆形设计,点击后弹出确认对话框,确认后模拟拨打 110 并通知紧急联系人。同时提供行程分享、录音取证等辅助安全功能,全方位保障用户出行安全。

5.5 行程记录与评价发票

行程记录页面按状态分类展示所有订单,支持查看详情。在行程详情页中,用户可以对司机进行星级评分和标签评价,也可以申请开具电子发票(支持个人和企业两种类型)。

六、在 OpenHarmony 上的运行效果

以下是在 OpenHarmony 设备上运行该打车应用的截图展示:

截图一:打车首页
展示上车地点选择、目的地输入、附近车辆分布和快捷操作入口。顶部显示"打车出行"标题,右侧提供订单查看和紧急求助入口。
在这里插入图片描述

截图二:价格预估比较
展示五种车型的价格对比列表,每种车型显示图标、名称、描述和预估价格,最实惠的车型高亮显示。
在这里插入图片描述

截图三:叫车流程
展示司机接单后的信息展示界面,包含司机头像、姓名、评分、车牌号、车辆型号和颜色等信息。
在这里插入图片描述

截图四:紧急求助
展示 SOS 一键报警按钮、紧急联系人列表和安全提示信息,红色主题设计醒目突出。
在这里插入图片描述

截图五:行程记录
展示按状态分类的行程列表,每条记录包含路线信息、费用和状态标签。

在这里插入图片描述

七、项目源码

本文完整项目源码已托管至 AtomGit,欢迎访问获取:

https://atomgit.com

项目结构如下:

lib/
├── models/
│   └── ride_model.dart          # 打车数据模型
├── services/
│   └── ride_service.dart        # 打车服务层
└── pages/
    └── ride/
        ├── ride_home_page.dart           # 打车首页
        ├── ride_price_compare_page.dart  # 价格预估
        ├── ride_booking_page.dart        # 叫车流程
        ├── ride_order_list_page.dart     # 行程记录
        ├── ride_order_detail_page.dart   # 行程详情
        └── ride_emergency_page.dart      # 紧急求助

八、总结

本文详细介绍了如何使用 Flutter 在 OpenHarmony 平台上构建一个功能完整的打车出行应用。通过分层架构设计、数据模型抽象、服务层封装和 Material Design UI 构建,我们实现了附近车辆搜索、价格预估比较、预约叫车、行程记录管理、司机评价、紧急求助等核心功能。

Flutter 优秀的跨平台能力和 OpenHarmony 生态的快速发展,为开发者提供了广阔的应用开发空间。希望本文能为正在探索 Flutter for OpenHarmony 开发的读者提供有价值的参考和指导。

欢迎加入开源鸿蒙跨平台社区,共同推动 OpenHarmony 生态繁荣发展:
https://openharmonycrossplatform.csdn.net

Logo

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

更多推荐