Flutter 框架跨平台鸿蒙开发 - 手账胶带收藏本应用开发教程
solid, // 纯色floral, // 花卉geometric, // 几何character, // 卡通人物text, // 文字vintage, // 复古seasonal, // 季节性food, // 食物animal, // 动物landscape, // 风景Flutter应用架构设计复杂数据模型的设计和管理多页面导航和状态管理搜索和筛选功能的实现数据统计和可视化动画效果的应用
Flutter手账胶带收藏本应用开发教程
项目概述
本教程将带你开发一个功能完整的Flutter手账胶带收藏本应用。这款应用专为手账爱好者设计,提供胶带收藏管理、使用记录追踪、愿望清单管理和统计分析等功能,帮助用户更好地管理和享受手账创作生活。
运行效果图



应用特色
- 胶带收藏管理:记录胶带的详细信息,包括品牌、系列、图案、规格等
- 库存状态监控:实时显示剩余量,智能提醒库存不足和即将用完
- 使用记录追踪:记录每次使用的长度、项目、用途和满意度
- 愿望清单管理:管理想要购买的胶带,设置优先级和预算
- 智能筛选搜索:支持按图案、宽度、品牌、收藏状态等多维度筛选
- 统计分析功能:提供收藏统计、图案分布、品牌分析等数据洞察
- 个性化标签:自定义标签系统,便于分类管理
技术栈
- 框架:Flutter 3.x
- 语言:Dart
- UI组件:Material Design 3
- 状态管理:StatefulWidget
- 动画:AnimationController + FadeTransition
- 数据存储:内存存储(可扩展为本地数据库)
- 导航:NavigationBar
项目结构设计
核心数据模型
1. 胶带信息模型(TapeItem)
class TapeItem {
final String id; // 唯一标识
final String name; // 胶带名称
final String brand; // 品牌
final String series; // 系列
final String pattern; // 图案类型
final double width; // 宽度(mm)
final double length; // 长度(m)
final double remainingLength; // 剩余长度
final String color; // 主色调
final DateTime purchaseDate; // 购买日期
final double price; // 价格
final String purchasePlace; // 购买地点
final String notes; // 备注
final List<String> tags; // 标签
final String imageUrl; // 图片URL
bool isFavorite; // 是否收藏
int usageCount; // 使用次数
DateTime? lastUsedDate; // 最后使用日期
String condition; // 状态:全新、轻微使用、半用、快用完
}
2. 使用记录模型(UsageRecord)
class UsageRecord {
final String id; // 唯一标识
final String tapeId; // 关联的胶带ID
final DateTime usageDate; // 使用日期
final double lengthUsed; // 使用长度
final String project; // 使用项目
final String purpose; // 用途
final String notes; // 备注
final int satisfaction; // 满意度 1-5
}
3. 愿望清单模型(WishlistItem)
class WishlistItem {
final String id; // 唯一标识
final String name; // 胶带名称
final String brand; // 品牌
final String series; // 系列
final double estimatedPrice; // 预估价格
final String reason; // 想要的原因
final int priority; // 优先级 1-5
final DateTime addedDate; // 添加日期
final String imageUrl; // 图片URL
bool isPurchased; // 是否已购买
}
枚举定义
胶带图案类型枚举
enum PatternType {
solid, // 纯色
floral, // 花卉
geometric, // 几何
character, // 卡通人物
text, // 文字
vintage, // 复古
seasonal, // 季节性
food, // 食物
animal, // 动物
landscape, // 风景
}
胶带宽度枚举
enum TapeWidth {
narrow, // 窄胶带 (5-10mm)
medium, // 中等 (15-20mm)
wide, // 宽胶带 (25-30mm)
extraWide, // 超宽 (40mm+)
}
页面架构
应用采用底部导航栏设计,包含四个主要页面:
- 收藏页面:展示所有胶带收藏,支持搜索和筛选
- 使用记录页面:记录和查看胶带使用历史
- 愿望清单页面:管理想要购买的胶带
- 统计页面:展示收藏统计和数据分析
详细实现步骤
第一步:项目初始化
创建新的Flutter项目:
flutter create tape_collection_app
cd tape_collection_app
第二步:主应用结构
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: '手账胶带收藏本',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.pink),
useMaterial3: true,
),
home: const TapeCollectionHomePage(),
);
}
}
第三步:数据初始化
创建示例胶带数据:
void _initializeData() {
_tapeItems = [
TapeItem(
id: '1',
name: '樱花飞舞',
brand: 'mt',
series: '春日物语',
pattern: _getPatternTypeName(PatternType.floral),
width: 15.0,
length: 10.0,
remainingLength: 8.5,
color: '粉色',
purchaseDate: DateTime.now().subtract(const Duration(days: 30)),
price: 25.0,
purchasePlace: '淘宝',
notes: '春天限定款,图案很美',
tags: ['春天', '樱花', '限定', '粉色'],
imageUrl: 'sakura_tape.jpg',
isFavorite: true,
usageCount: 3,
lastUsedDate: DateTime.now().subtract(const Duration(days: 5)),
condition: '轻微使用',
),
// 更多胶带数据...
];
}
第四步:胶带列表页面
胶带卡片组件
Widget _buildTapeCard(TapeItem item) {
return Card(
elevation: 4,
margin: const EdgeInsets.only(bottom: 16),
child: InkWell(
onTap: () => _showTapeDetail(item),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题行
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.name,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
Text(
'${item.brand} • ${item.series}',
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 14,
),
),
],
),
),
IconButton(
icon: Icon(
item.isFavorite ? Icons.favorite : Icons.favorite_border,
color: item.isFavorite ? Colors.red : Colors.grey,
),
onPressed: () => _toggleFavorite(item),
),
],
),
// 图案和规格信息
Row(
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: _getPatternColor(item.pattern),
borderRadius: BorderRadius.circular(12),
),
child: Text(
item.pattern,
style: const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
),
// 更多信息显示...
],
),
// 剩余量进度条
LinearProgressIndicator(
value: item.remainingPercentage,
backgroundColor: Colors.grey.shade300,
valueColor: AlwaysStoppedAnimation<Color>(
item.isAlmostEmpty
? Colors.red
: item.isLowStock
? Colors.orange
: Colors.green,
),
),
],
),
),
),
);
}
智能状态检测
// 获取剩余百分比
double get remainingPercentage => remainingLength / length;
// 是否库存不足(少于20%)
bool get isLowStock => remainingPercentage < 0.2;
// 是否即将用完(少于10%)
bool get isAlmostEmpty => remainingPercentage < 0.1;
第五步:搜索和筛选功能
多维度筛选
List<TapeItem> _getFilteredTapeItems() {
return _tapeItems.where((item) {
// 搜索过滤
if (_searchQuery.isNotEmpty) {
final query = _searchQuery.toLowerCase();
if (!item.name.toLowerCase().contains(query) &&
!item.brand.toLowerCase().contains(query) &&
!item.series.toLowerCase().contains(query) &&
!item.pattern.toLowerCase().contains(query) &&
!item.tags.any((tag) => tag.toLowerCase().contains(query))) {
return false;
}
}
// 图案过滤
if (_selectedPattern != null &&
item.pattern != _getPatternTypeName(_selectedPattern!)) {
return false;
}
// 宽度过滤
if (_selectedWidth != null) {
final widthCategory = _getWidthCategory(item.width);
if (widthCategory != _selectedWidth) {
return false;
}
}
// 品牌过滤
if (_selectedBrand != null && item.brand != _selectedBrand) {
return false;
}
// 收藏过滤
if (_showFavoritesOnly && !item.isFavorite) {
return false;
}
// 库存不足过滤
if (_showLowStockOnly && !item.isLowStock) {
return false;
}
return true;
}).toList();
}
宽度分类算法
TapeWidth _getWidthCategory(double width) {
if (width <= 10) return TapeWidth.narrow;
if (width <= 20) return TapeWidth.medium;
if (width <= 30) return TapeWidth.wide;
return TapeWidth.extraWide;
}
第六步:使用记录功能
使用记录卡片
Widget _buildUsageRecordCard(UsageRecord record, TapeItem tape) {
return Card(
elevation: 2,
margin: const EdgeInsets.only(bottom: 12),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题行
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
tape.name,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
Text(
_formatDateTime(record.usageDate),
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 12,
),
),
],
),
),
// 满意度星星
Row(
children: List.generate(5, (index) {
return Icon(
index < record.satisfaction ? Icons.star : Icons.star_border,
color: Colors.amber,
size: 16,
);
}),
),
],
),
// 使用信息
Row(
children: [
_buildInfoItem(
Icons.straighten,
'用量',
'${record.lengthUsed}m',
Colors.blue,
),
_buildInfoItem(
Icons.work,
'项目',
record.project,
Colors.green,
),
_buildInfoItem(
Icons.label,
'用途',
record.purpose,
Colors.orange,
),
],
),
],
),
),
);
}
信息展示组件
Widget _buildInfoItem(IconData icon, String label, String value, Color color) {
return Column(
children: [
Icon(icon, size: 20, color: color),
const SizedBox(height: 4),
Text(
label,
style: TextStyle(
fontSize: 10,
color: Colors.grey.shade600,
),
),
Text(
value,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: color,
),
),
],
);
}
第七步:愿望清单功能
愿望清单卡片
Widget _buildWishlistCard(WishlistItem item) {
return Card(
elevation: 2,
margin: const EdgeInsets.only(bottom: 12),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题行
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.name,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
decoration: item.isPurchased
? TextDecoration.lineThrough
: null,
color: item.isPurchased
? Colors.grey.shade600
: Colors.black,
),
),
Text(
'${item.brand} • ${item.series}',
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 12,
),
),
],
),
),
// 优先级星星
Row(
children: List.generate(5, (index) {
return Icon(
index < item.priority ? Icons.star : Icons.star_border,
color: Colors.amber,
size: 16,
);
}),
),
],
),
// 价格和状态
Row(
children: [
Text(
'¥${item.estimatedPrice.toStringAsFixed(0)}',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: item.isPurchased ? Colors.grey : Colors.pink,
),
),
const Spacer(),
if (item.isPurchased)
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(12),
),
child: const Text(
'已购买',
style: TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
),
],
),
// 想要的原因
Text(
item.reason,
style: TextStyle(
color: Colors.grey.shade700,
fontSize: 14,
),
),
],
),
),
);
}
愿望清单管理功能
void _markAsPurchased(WishlistItem item) {
setState(() {
item.isPurchased = true;
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('${item.name} 已标记为已购买')),
);
}
void _removeFromWishlist(WishlistItem item) {
setState(() {
_wishlistItems.remove(item);
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('${item.name} 已从愿望清单中移除')),
);
}
第八步:统计分析功能
统计卡片组件
Widget _buildStatCard(String title, String value, IconData icon, Color color) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: color.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: color.withValues(alpha: 0.3)),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, color: color, size: 32),
const SizedBox(height: 8),
Text(
value,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: color,
),
),
Text(
title,
style: TextStyle(
fontSize: 12,
color: color,
),
textAlign: TextAlign.center,
),
],
),
);
}
数据统计计算
// 获取图案分布
Map<String, int> _getPatternDistribution() {
final distribution = <String, int>{};
for (final item in _tapeItems) {
distribution[item.pattern] = (distribution[item.pattern] ?? 0) + 1;
}
return Map.fromEntries(
distribution.entries.toList()..sort((a, b) => b.value.compareTo(a.value)));
}
// 获取品牌分布
Map<String, int> _getBrandDistribution() {
final distribution = <String, int>{};
for (final item in _tapeItems) {
distribution[item.brand] = (distribution[item.brand] ?? 0) + 1;
}
return Map.fromEntries(
distribution.entries.toList()..sort((a, b) => b.value.compareTo(a.value)));
}
// 获取唯一品牌列表
List<String> _getUniqueBrands() {
return _tapeItems.map((item) => item.brand).toSet().toList()..sort();
}
分布图表展示
// 图案分布图表
..._getPatternDistribution().entries.map((entry) {
final percentage = entry.value / _tapeItems.length;
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(entry.key),
Text('${entry.value}卷 (${(percentage * 100).toStringAsFixed(1)}%)'),
],
),
const SizedBox(height: 4),
LinearProgressIndicator(
value: percentage,
backgroundColor: Colors.grey.shade300,
valueColor: AlwaysStoppedAnimation<Color>(
_getPatternColor(entry.key),
),
),
],
),
);
}).take(5).toList(),
第九步:动画效果实现
淡入动画
void _setupAnimations() {
_fadeAnimationController = AnimationController(
duration: const Duration(milliseconds: 800),
vsync: this,
);
_fadeAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _fadeAnimationController,
curve: Curves.easeInOut,
));
_fadeAnimationController.forward();
}
// 在build方法中使用
body: FadeTransition(
opacity: _fadeAnimation,
child: IndexedStack(
index: _selectedIndex,
children: [
_buildTapeListPage(),
_buildUsageRecordsPage(),
_buildWishlistPage(),
_buildStatisticsPage(),
],
),
),
核心功能详解
1. 智能状态监控
应用提供了多种智能状态检测功能:
// 剩余量百分比计算
double get remainingPercentage => remainingLength / length;
// 库存不足检测(低于20%)
bool get isLowStock => remainingPercentage < 0.2;
// 即将用完检测(低于10%)
bool get isAlmostEmpty => remainingPercentage < 0.1;
2. 颜色主题系统
根据图案类型和颜色名称动态设置颜色:
Color _getPatternColor(String pattern) {
switch (pattern) {
case '纯色': return Colors.grey;
case '花卉': return Colors.pink;
case '几何': return Colors.blue;
case '卡通人物': return Colors.orange;
case '文字': return Colors.purple;
case '复古': return Colors.brown;
case '季节性': return Colors.green;
case '食物': return Colors.amber;
case '动物': return Colors.teal;
case '风景': return Colors.indigo;
default: return Colors.grey;
}
}
Color _getColorFromName(String colorName) {
switch (colorName) {
case '红色': return Colors.red;
case '粉色': return Colors.pink;
case '橙色': return Colors.orange;
case '黄色': return Colors.yellow;
case '绿色': return Colors.green;
case '蓝色': return Colors.blue;
case '紫色': return Colors.purple;
case '棕色': return Colors.brown;
case '黑色': return Colors.black;
case '白色': return Colors.grey;
case '薄荷绿': return Colors.teal;
default: return Colors.grey;
}
}
3. 状态颜色映射
Color _getConditionColor(String condition) {
switch (condition) {
case '全新': return Colors.green;
case '轻微使用': return Colors.blue;
case '半用': return Colors.orange;
case '快用完': return Colors.red;
default: return Colors.grey;
}
}
4. 搜索算法优化
bool _matchesSearchQuery(TapeItem item, String query) {
final lowerQuery = query.toLowerCase();
// 名称匹配
if (item.name.toLowerCase().contains(lowerQuery)) return true;
// 品牌匹配
if (item.brand.toLowerCase().contains(lowerQuery)) return true;
// 系列匹配
if (item.series.toLowerCase().contains(lowerQuery)) return true;
// 图案匹配
if (item.pattern.toLowerCase().contains(lowerQuery)) return true;
// 标签匹配
if (item.tags.any((tag) => tag.toLowerCase().contains(lowerQuery))) return true;
return false;
}
性能优化
1. 列表优化
使用ListView.builder实现虚拟滚动:
ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: filteredItems.length,
itemBuilder: (context, index) {
final item = filteredItems[index];
return _buildTapeCard(item);
},
)
2. 状态管理优化
合理使用setState,避免不必要的重建:
void _toggleFavorite(TapeItem item) {
setState(() {
item.isFavorite = !item.isFavorite;
});
// 只更新必要的UI部分
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(item.isFavorite ? '已添加到收藏' : '已从收藏中移除'),
duration: const Duration(seconds: 1),
),
);
}
3. 内存管理
及时释放动画控制器:
void dispose() {
_fadeAnimationController.dispose();
super.dispose();
}
4. 数据缓存策略
// 缓存筛选结果
List<TapeItem>? _cachedFilteredItems;
String? _lastSearchQuery;
List<TapeItem> _getFilteredTapeItems() {
// 如果搜索条件没有变化,返回缓存结果
if (_cachedFilteredItems != null && _lastSearchQuery == _searchQuery) {
return _cachedFilteredItems!;
}
// 重新计算筛选结果
_cachedFilteredItems = _tapeItems.where((item) {
// 筛选逻辑...
}).toList();
_lastSearchQuery = _searchQuery;
return _cachedFilteredItems!;
}
扩展功能
1. 数据持久化
使用shared_preferences保存数据:
dependencies:
shared_preferences: ^2.2.0
// 保存胶带数据
Future<void> _saveTapeData() async {
final prefs = await SharedPreferences.getInstance();
final tapeJson = _tapeItems.map((item) => item.toJson()).toList();
await prefs.setString('tape_items', jsonEncode(tapeJson));
}
// 加载胶带数据
Future<void> _loadTapeData() async {
final prefs = await SharedPreferences.getInstance();
final tapeJsonString = prefs.getString('tape_items');
if (tapeJsonString != null) {
final tapeJsonList = jsonDecode(tapeJsonString) as List;
_tapeItems = tapeJsonList
.map((json) => TapeItem.fromJson(json))
.toList();
}
}
2. 图片上传功能
集成image_picker插件:
dependencies:
image_picker: ^1.0.4
Future<void> _pickImage() async {
final picker = ImagePicker();
final pickedFile = await picker.pickImage(source: ImageSource.gallery);
if (pickedFile != null) {
setState(() {
// 更新胶带图片
});
}
}
3. 二维码扫描功能
使用mobile_scanner扫描胶带条码:
dependencies:
mobile_scanner: ^3.5.2
Future<void> _scanBarcode() async {
// 实现条码扫描功能
// 自动识别胶带信息
}
4. 数据导出功能
导出收藏清单为Excel或PDF:
dependencies:
excel: ^2.1.0
pdf: ^3.10.4
Future<void> _exportToExcel() async {
var excel = Excel.createExcel();
Sheet sheetObject = excel['胶带收藏清单'];
// 添加表头
sheetObject.cell(CellIndex.indexByString("A1")).value = "名称";
sheetObject.cell(CellIndex.indexByString("B1")).value = "品牌";
sheetObject.cell(CellIndex.indexByString("C1")).value = "价格";
// 添加数据
for (int i = 0; i < _tapeItems.length; i++) {
final item = _tapeItems[i];
sheetObject.cell(CellIndex.indexByString("A${i + 2}")).value = item.name;
sheetObject.cell(CellIndex.indexByString("B${i + 2}")).value = item.brand;
sheetObject.cell(CellIndex.indexByString("C${i + 2}")).value = item.price;
}
// 保存文件
var fileBytes = excel.save();
// 保存到本地文件系统
}
5. 社交分享功能
分享收藏和使用心得:
dependencies:
share_plus: ^7.2.1
Future<void> _shareCollection() async {
final text = '我的手账胶带收藏:共${_tapeItems.length}卷,总价值¥${_tapeItems.fold(0.0, (sum, item) => sum + item.price).toStringAsFixed(0)}';
await Share.share(text);
}
6. 提醒通知功能
库存不足和购买提醒:
dependencies:
flutter_local_notifications: ^16.1.0
// 设置库存不足提醒
Future<void> _scheduleLowStockReminder() async {
final lowStockItems = _tapeItems.where((item) => item.isLowStock).toList();
if (lowStockItems.isNotEmpty) {
// 发送本地通知
await flutterLocalNotificationsPlugin.show(
0,
'库存提醒',
'您有${lowStockItems.length}卷胶带库存不足',
const NotificationDetails(),
);
}
}
测试策略
1. 单元测试
测试核心业务逻辑:
// test/tape_item_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:tape_collection_app/models/tape_item.dart';
void main() {
group('TapeItem Tests', () {
test('should calculate remaining percentage correctly', () {
final tape = TapeItem(
// 初始化参数...
length: 10.0,
remainingLength: 5.0,
);
expect(tape.remainingPercentage, equals(0.5));
});
test('should detect low stock correctly', () {
final tape = TapeItem(
// 初始化参数...
length: 10.0,
remainingLength: 1.0, // 10%
);
expect(tape.isLowStock, isTrue);
expect(tape.isAlmostEmpty, isTrue);
});
});
}
2. Widget测试
测试UI组件:
// test/widget_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:tape_collection_app/main.dart';
void main() {
testWidgets('App should display navigation bar', (WidgetTester tester) async {
await tester.pumpWidget(const MyApp());
expect(find.byType(NavigationBar), findsOneWidget);
expect(find.text('收藏'), findsOneWidget);
expect(find.text('使用记录'), findsOneWidget);
expect(find.text('愿望清单'), findsOneWidget);
expect(find.text('统计'), findsOneWidget);
});
testWidgets('Should toggle favorite status', (WidgetTester tester) async {
await tester.pumpWidget(const MyApp());
// 查找收藏按钮并点击
final favoriteButton = find.byIcon(Icons.favorite_border).first;
await tester.tap(favoriteButton);
await tester.pump();
// 验证状态变化
expect(find.byIcon(Icons.favorite), findsWidgets);
});
}
3. 集成测试
测试完整用户流程:
// integration_test/app_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:tape_collection_app/main.dart' as app;
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('App Integration Tests', () {
testWidgets('Complete user flow test', (WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
// 测试搜索功能
await tester.tap(find.byIcon(Icons.search));
await tester.pumpAndSettle();
await tester.enterText(find.byType(TextField), '樱花');
await tester.tap(find.text('搜索'));
await tester.pumpAndSettle();
// 验证搜索结果
expect(find.text('樱花飞舞'), findsOneWidget);
// 测试导航
await tester.tap(find.text('愿望清单'));
await tester.pumpAndSettle();
expect(find.text('愿望清单'), findsWidgets);
});
});
}
部署和发布
1. Android发布
# 生成签名密钥
keytool -genkey -v -keystore ~/key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key
# 配置android/app/build.gradle
android {
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
# 构建APK
flutter build apk --release
2. iOS发布
# 构建iOS应用
flutter build ios --release
# 使用Xcode进行代码签名和发布
open ios/Runner.xcworkspace
3. Web发布
# 构建Web应用
flutter build web --release
# 部署到服务器
# 将build/web目录内容上传到Web服务器
总结
本教程详细介绍了如何使用Flutter开发一个功能完整的手账胶带收藏本应用。应用包含了胶带收藏管理、使用记录追踪、愿望清单管理、统计分析等核心功能,采用了Material Design 3设计规范,提供了良好的用户体验。
通过本项目的学习,你将掌握:
- Flutter应用架构设计
- 复杂数据模型的设计和管理
- 多页面导航和状态管理
- 搜索和筛选功能的实现
- 数据统计和可视化
- 动画效果的应用
- 性能优化技巧
- 测试策略和部署流程
这个应用不仅适合手账爱好者使用,也为其他收藏类应用的开发提供了很好的参考模板。你可以根据自己的需求进行功能扩展和定制,比如添加数据同步、社交分享、AI推荐等高级功能。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐

所有评论(0)