作为一名有多年Flutter开发经验的工程师,我最近完成了一个面向拼豆爱好者的实体店查询应用开发项目。这个项目不仅支持Android和iOS平台,还特别针对华为鸿蒙系统进行了深度适配。下面我将从技术选型到具体实现,详细分享这个项目的开发过程。
拼豆手工艺近年来在国内迅速流行,成为许多手工爱好者和亲子活动的新宠。但在实际使用中,爱好者们普遍反映存在几个痛点:
我们的应用正是为了解决这些问题而生。通过整合全国300+城市的拼豆店铺信息,结合智能推荐算法和精准导航功能,为拼豆爱好者提供一站式解决方案。
经过仔细评估,我们选择了Flutter作为主要开发框架,主要基于以下几点考虑:
针对鸿蒙系统的适配,我们使用了华为提供的Flutter鸿蒙适配层,确保应用在鸿蒙设备上也能完美运行。
我们采用经典的MVVM架构,将项目分为以下几个层次:
code复制┌───────────────────────┐
│ Presentation │ // UI界面层
├───────────────────────┤
│ Business │ // 业务逻辑层
├───────────────────────┤
│ Data │ // 数据访问层
└───────────────────────┘
这种分层架构使得各模块职责清晰,便于团队协作和维护。同时,我们使用了Provider进行状态管理,在保证性能的同时简化了代码结构。
数据模型是整个应用的基础,我们设计了完善的店铺数据模型:
dart复制class BeadStore {
final String id; // 店铺唯一ID
final String name; // 店铺名称
final String address; // 详细地址
final GeoPoint location; // 经纬度坐标
final List<String> brands; // 经营的品牌
final List<BeadProduct> products; // 商品列表
final StoreRating rating; // 评分信息
// 动态计算属性
double distanceFromUser; // 距离用户的距离
bool isFavorite; // 是否收藏
}
这个模型不仅包含店铺的基本信息,还包含了与业务逻辑相关的动态属性。通过Hive进行本地存储,我们实现了高效的离线数据访问。
地图功能是应用的核心之一,我们集成了高德地图SDK来实现精准定位和导航:
dart复制// 初始化地图控制器
final AMapController mapController;
// 添加店铺标记
void _addStoreMarkers() {
stores.forEach((store) {
mapController.addMarker(MarkerOptions(
position: LatLng(store.location.lat, store.location.lng),
icon: BitmapDescriptor.defaultMarkerWithHue(
store.isFavorite ? BitmapDescriptor.hueRose : BitmapDescriptor.hueBlue
),
infoWindow: InfoWindow(
title: store.name,
snippet: '距离${store.distanceFromUser.toStringAsFixed(1)}公里',
),
));
});
}
// 处理定位权限
Future<void> _checkLocationPermission() async {
final status = await Permission.location.request();
if (status.isGranted) {
final position = await Geolocator.getCurrentPosition();
mapController.animateCamera(CameraUpdate.newLatLngZoom(
LatLng(position.latitude, position.longitude),
14.0,
));
}
}
在实际开发中,我们发现鸿蒙系统对定位权限的管理有些特殊,需要额外处理:
注意:在鸿蒙系统上,除了常规的权限申请外,还需要在config.json中声明ohos.permission.LOCATION权限,否则定位功能将无法正常工作。
为了提高用户体验,我们实现了一个基于用户行为的推荐系统:
dart复制class RecommendationEngine {
final UserRepository userRepo;
final StoreRepository storeRepo;
Future<List<BeadStore>> getRecommendedStores(String userId) async {
// 获取用户历史行为
final userHistory = await userRepo.getUserHistory(userId);
// 分析用户偏好
final preferredBrands = _analyzeBrandPreference(userHistory);
final preferredLocation = _analyzeLocationPreference(userHistory);
// 获取候选店铺
var candidates = await storeRepo.getStoresByLocation(preferredLocation);
// 根据偏好排序
candidates.sort((a, b) {
final aScore = _calculateStoreScore(a, preferredBrands);
final bScore = _calculateStoreScore(b, preferredBrands);
return bScore.compareTo(aScore);
});
return candidates.take(10).toList();
}
double _calculateStoreScore(BeadStore store, Set<String> preferredBrands) {
double score = store.rating.overall * 2;
// 品牌匹配加分
if (store.brands.any(preferredBrands.contains)) {
score += 1.5;
}
// 距离加权
if (store.distanceFromUser < 3.0) {
score += (3.0 - store.distanceFromUser) * 0.5;
}
return score;
}
}
这个算法会综合考虑店铺评分、用户偏好的品牌以及距离因素,为用户推荐最可能感兴趣的店铺。
在鸿蒙系统上,有些功能需要通过OHOS的特定API实现。我们封装了一个平台接口来处理这些差异:
dart复制abstract class PlatformService {
Future<String> getDeviceInfo();
Future<bool> checkHarmonyFeature(String feature);
}
// 鸿蒙实现
class HarmonyPlatformService implements PlatformService {
@override
Future<String> getDeviceInfo() async {
try {
final result = await MethodChannel('com.example/harmony')
.invokeMethod('getDeviceInfo');
return result as String;
} on PlatformException catch (e) {
debugPrint("Failed to get device info: ${e.message}");
return 'Unknown Harmony Device';
}
}
}
鸿蒙系统的显示特性与Android有些差异,我们在UI适配时特别注意了以下几点:
我们通过平台检测来实现差异化渲染:
dart复制Widget build(BuildContext context) {
return FutureBuilder<bool>(
future: PlatformService().isHarmonyOS(),
builder: (context, snapshot) {
final isHarmony = snapshot.data ?? false;
return Text(
'拼豆店铺查询',
style: TextStyle(
fontSize: isHarmony ? 18.0 : 16.0,
height: isHarmony ? 1.3 : 1.2,
),
);
},
);
}
店铺和商品图片的加载是性能瓶颈之一,我们采用了多级缓存策略:
dart复制CachedNetworkImage(
imageUrl: store.primaryImage,
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
fadeInDuration: Duration(milliseconds: 300),
memCacheWidth: 400,
maxWidthDiskCache: 800,
);
为了提高用户体验,我们实现了智能预加载机制:
dart复制class PreloadManager {
final StoreProvider storeProvider;
final LocationService locationService;
void startPreloading() {
locationService.onLocationChanged.listen((position) {
// 预加载周围3公里内的店铺
storeProvider.preloadNearbyStores(
position.latitude,
position.longitude,
radius: 3.0,
);
});
// 监听用户行为模式
_analyzeUserBehavior();
}
void _analyzeUserBehavior() {
// 实现行为分析逻辑...
}
}
在开发过程中,我们遇到了几个典型的兼容性问题:
解决方案是建立完善的平台检测机制和抽象层:
dart复制enum PlatformType {
android,
ios,
harmony,
others,
}
Future<PlatformType> detectPlatform() async {
if (Platform.isAndroid) {
try {
final isHarmony = await MethodChannel('platform')
.invokeMethod('isHarmonyOS');
return isHarmony ? PlatformType.harmony : PlatformType.android;
} catch (e) {
return PlatformType.android;
}
}
return Platform.isIOS ? PlatformType.ios : PlatformType.others;
}
在复杂的跨平台应用中,状态管理尤为重要。我们总结了几条经验:
dart复制// 使用Provider管理店铺详情状态
class StoreDetailProvider with ChangeNotifier {
BeadStore? _store;
bool _isLoading = false;
String? _error;
Future<void> loadStore(String storeId) async {
_isLoading = true;
notifyListeners();
try {
_store = await storeRepository.getStoreById(storeId);
_error = null;
} catch (e) {
_error = e.toString();
} finally {
_isLoading = false;
notifyListeners();
}
}
}
目前,这个拼豆店铺查询应用已经在各大应用商店上线,获得了不错的用户反馈:
未来我们计划进一步优化:
这个项目的成功实践证明了Flutter在跨平台开发中的强大能力,即使是针对鸿蒙这样的新兴系统,也能通过适当的适配工作获得出色的用户体验。