From c8f93f15a1e34cec19e9663c3b27dde857c9c710 Mon Sep 17 00:00:00 2001 From: Cxx0822 <1556464090@qq.com> Date: Tue, 15 Jul 2025 19:54:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0dio=E7=BD=91=E7=BB=9C?= =?UTF-8?q?=E8=AF=B7=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/api/recipe.dart | 16 ++ lib/api/session.dart | 4 +- lib/main.dart | 6 +- lib/models/recipe.dart | 156 +++++++++++++++--- lib/models/recipe.g.dart | 125 ++++++++++++++ lib/models/session.dart | 46 +++--- lib/models/session.g.dart | 32 ++-- lib/utils/{HttpUtil.dart => http_util.dart} | 93 +++++++---- lib/utils/sp_util.dart | 61 +++++++ lib/views/login.dart | 13 +- lib/views/record.dart | 12 +- lib/widgets/recipe/recipe_card.dart | 6 +- lib/widgets/recipe/recipe_timeline.dart | 18 +- macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 104 ++++++++++++ pubspec.yaml | 2 + 16 files changed, 570 insertions(+), 126 deletions(-) create mode 100644 lib/api/recipe.dart create mode 100644 lib/models/recipe.g.dart rename lib/utils/{HttpUtil.dart => http_util.dart} (66%) create mode 100644 lib/utils/sp_util.dart diff --git a/lib/api/recipe.dart b/lib/api/recipe.dart new file mode 100644 index 0000000..77b39bc --- /dev/null +++ b/lib/api/recipe.dart @@ -0,0 +1,16 @@ +import 'package:food_hub_app/models/recipe.dart'; +import 'package:food_hub_app/utils/http_util.dart'; + +Future queryRecipeByIdApi(int id) { + return HttpUtil().get( + "/food/recipe/$id", + converter: (data) => Recipe.fromJson(data), + ); +} + +Future> queryRecipeByUserApi(int id) { + return HttpUtil().get>( + "/food/recipe/user/$id", + converter: (data) => data.map((item) => Recipe.fromJson(item)).toList() + ); +} \ No newline at end of file diff --git a/lib/api/session.dart b/lib/api/session.dart index 1c2ddd4..616dc25 100644 --- a/lib/api/session.dart +++ b/lib/api/session.dart @@ -1,7 +1,7 @@ import 'package:food_hub_app/models/session.dart'; -import 'package:food_hub_app/utils/HttpUtil.dart'; +import 'package:food_hub_app/utils/http_util.dart'; -Future loginApi(String username, String password) { +Future loginApi(String username, String password) { return HttpUtil().post( "/session", queryParameters: {"username": username, "password": password}, diff --git a/lib/main.dart b/lib/main.dart index d968082..27f1643 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,12 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:food_hub_app/utils/sp_util.dart'; import 'package:food_hub_app/views/home.dart'; import 'package:food_hub_app/views/login.dart'; import 'package:food_hub_app/views/recordForm.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:shared_preferences/shared_preferences.dart'; -void main() { +void main() async { + WidgetsFlutterBinding.ensureInitialized(); FormBuilderLocalizations.delegate.load(const Locale('zh', 'CN')); + SPUtil.init(); runApp(const MyApp()); } diff --git a/lib/models/recipe.dart b/lib/models/recipe.dart index eb700b5..fa1dd7d 100644 --- a/lib/models/recipe.dart +++ b/lib/models/recipe.dart @@ -1,30 +1,140 @@ -class Recipe { - final String name; - final String category; - final String date; - final String imageUrl; - final int likeCount; - final int favoriteCount; - final int commentCount; +import 'package:json_annotation/json_annotation.dart'; - Recipe( - this.name, - this.category, - this.date, - this.imageUrl, - this.likeCount, - this.favoriteCount, - this.commentCount, - ); +part 'recipe.g.dart'; + +/// 食材信息 +@JsonSerializable() +class Material { + String type; + String name; + String amount; + + Material({ + required this.type, + required this.name, + required this.amount, + }); + + factory Material.fromJson(Map json) => + _$MaterialFromJson(json); + + Map toJson() => _$MaterialToJson(this); } -class Record { - final String name; - final String category; - final String date; - final String imageUrl; +/// 步骤信息 +@JsonSerializable() +class Step { + int sort; + String content; + String imageUrl; - Record(this.name, this.category, this.date, this.imageUrl); + Step({ + required this.sort, + required this.content, + required this.imageUrl, + }); + + factory Step.fromJson(Map json) => + _$StepFromJson(json); + + Map toJson() => _$StepToJson(this); +} + +/// 评论信息 +@JsonSerializable() +class Comment { + int id; + String username; + String avatar; + String content; + String date; + + Comment({ + required this.id, + required this.username, + required this.avatar, + required this.content, + required this.date, + }); + + factory Comment.fromJson(Map json) => + _$CommentFromJson(json); + + Map toJson() => _$CommentToJson(this); +} + +/// 成果信息 +@JsonSerializable() +class Record { + int id; + String name; + String? category; + int person; + String date; + String imageUrl; + + Record({ + required this.id, + required this.name, + this.category, + required this.person, + required this.date, + required this.imageUrl, + }); + + factory Record.fromJson(Map json) => + _$RecordFromJson(json); + + Map toJson() => _$RecordToJson(this); +} + +/// 菜谱信息 +@JsonSerializable() +class Recipe { + int? id; + String name; + String category; + double recommendRate; + String? remark; + bool isShare; + int? userId; + String? username; + String? avatar; + List materialList; + List stepList; + List recordList; + List? likeList; + int? likeCount; + List? favouriteList; + int? favouriteCount; + List? commentList; + int? commentCount; + + Recipe({ + this.id, + required this.name, + required this.category, + required this.recommendRate, + this.remark, + required this.isShare, + this.userId, + this.username, + this.avatar, + required this.materialList, + required this.stepList, + required this.recordList, + this.likeList, + this.likeCount, + this.favouriteList, + this.favouriteCount, + this.commentList, + this.commentCount, + }); + + factory Recipe.fromJson(Map json) => + _$RecipeFromJson(json); + + Map toJson() => _$RecipeToJson(this); } enum ViewType { recipe, calendar, timeline } diff --git a/lib/models/recipe.g.dart b/lib/models/recipe.g.dart new file mode 100644 index 0000000..2ad2e12 --- /dev/null +++ b/lib/models/recipe.g.dart @@ -0,0 +1,125 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'recipe.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Material _$MaterialFromJson(Map json) => Material( + type: json['type'] as String, + name: json['name'] as String, + amount: json['amount'] as String, +); + +Map _$MaterialToJson(Material instance) => { + 'type': instance.type, + 'name': instance.name, + 'amount': instance.amount, +}; + +Step _$StepFromJson(Map json) => Step( + sort: (json['sort'] as num).toInt(), + content: json['content'] as String, + imageUrl: json['imageUrl'] as String, +); + +Map _$StepToJson(Step instance) => { + 'sort': instance.sort, + 'content': instance.content, + 'imageUrl': instance.imageUrl, +}; + +Comment _$CommentFromJson(Map json) => Comment( + id: (json['id'] as num).toInt(), + username: json['username'] as String, + avatar: json['avatar'] as String, + content: json['content'] as String, + date: json['date'] as String, +); + +Map _$CommentToJson(Comment instance) => { + 'id': instance.id, + 'username': instance.username, + 'avatar': instance.avatar, + 'content': instance.content, + 'date': instance.date, +}; + +Record _$RecordFromJson(Map json) => Record( + id: (json['id'] as num).toInt(), + name: json['name'] as String, + category: json['category'] as String?, + person: (json['person'] as num).toInt(), + date: json['date'] as String, + imageUrl: json['imageUrl'] as String, +); + +Map _$RecordToJson(Record instance) => { + 'id': instance.id, + 'name': instance.name, + 'category': instance.category, + 'person': instance.person, + 'date': instance.date, + 'imageUrl': instance.imageUrl, +}; + +Recipe _$RecipeFromJson(Map json) => Recipe( + id: (json['id'] as num?)?.toInt(), + name: json['name'] as String, + category: json['category'] as String, + recommendRate: (json['recommendRate'] as num).toDouble(), + remark: json['remark'] as String?, + isShare: json['isShare'] as bool, + userId: (json['userId'] as num?)?.toInt(), + username: json['username'] as String?, + avatar: json['avatar'] as String?, + materialList: + (json['materialList'] as List) + .map((e) => Material.fromJson(e as Map)) + .toList(), + stepList: + (json['stepList'] as List) + .map((e) => Step.fromJson(e as Map)) + .toList(), + recordList: + (json['recordList'] as List) + .map((e) => Record.fromJson(e as Map)) + .toList(), + likeList: + (json['likeList'] as List?) + ?.map((e) => (e as num).toInt()) + .toList(), + likeCount: (json['likeCount'] as num?)?.toInt(), + favouriteList: + (json['favouriteList'] as List?) + ?.map((e) => (e as num).toInt()) + .toList(), + favouriteCount: (json['favouriteCount'] as num?)?.toInt(), + commentList: + (json['commentList'] as List?) + ?.map((e) => Comment.fromJson(e as Map)) + .toList(), + commentCount: (json['commentCount'] as num?)?.toInt(), +); + +Map _$RecipeToJson(Recipe instance) => { + 'id': instance.id, + 'name': instance.name, + 'category': instance.category, + 'recommendRate': instance.recommendRate, + 'remark': instance.remark, + 'isShare': instance.isShare, + 'userId': instance.userId, + 'username': instance.username, + 'avatar': instance.avatar, + 'materialList': instance.materialList, + 'stepList': instance.stepList, + 'recordList': instance.recordList, + 'likeList': instance.likeList, + 'likeCount': instance.likeCount, + 'favouriteList': instance.favouriteList, + 'favouriteCount': instance.favouriteCount, + 'commentList': instance.commentList, + 'commentCount': instance.commentCount, +}; diff --git a/lib/models/session.dart b/lib/models/session.dart index 32781f4..b6f7902 100644 --- a/lib/models/session.dart +++ b/lib/models/session.dart @@ -4,29 +4,29 @@ part 'session.g.dart'; @JsonSerializable() class SaTokenInfo { - String? tokenName; - String? tokenValue; - bool? isLogin; + String tokenName; + String tokenValue; + bool isLogin; dynamic loginId; - String? loginType; - int? tokenTimeout; - int? sessionTimeout; - int? tokenSessionTimeout; - int? tokenActiveTimeout; - String? loginDeviceType; + String loginType; + int tokenTimeout; + int sessionTimeout; + int tokenSessionTimeout; + int tokenActiveTimeout; + String loginDeviceType; String? tag; SaTokenInfo({ - this.tokenName, - this.tokenValue, - this.isLogin, - this.loginId, - this.loginType, - this.tokenTimeout, - this.sessionTimeout, - this.tokenSessionTimeout, - this.tokenActiveTimeout, - this.loginDeviceType, + required this.tokenName, + required this.tokenValue, + required this.isLogin, + required this.loginId, + required this.loginType, + required this.tokenTimeout, + required this.sessionTimeout, + required this.tokenSessionTimeout, + required this.tokenActiveTimeout, + required this.loginDeviceType, this.tag, }); @@ -36,6 +36,7 @@ class SaTokenInfo { @JsonSerializable() class User { + int? id; String? username; int? gender; String? phoneNumber; @@ -47,7 +48,6 @@ class User { String? job; List? tags; String? description; - String? id; User({ this.id, @@ -70,10 +70,10 @@ class User { @JsonSerializable() class Session { - SaTokenInfo? saToken; - User? userInfo; + SaTokenInfo saToken; + User userInfo; - Session({this.saToken, this.userInfo}); + Session({required this.saToken, required this.userInfo}); factory Session.fromJson(Map json) => _$SessionFromJson(json); Map toJson() => _$SessionToJson(this); diff --git a/lib/models/session.g.dart b/lib/models/session.g.dart index 7614793..554fa27 100644 --- a/lib/models/session.g.dart +++ b/lib/models/session.g.dart @@ -7,16 +7,16 @@ part of 'session.dart'; // ************************************************************************** SaTokenInfo _$SaTokenInfoFromJson(Map json) => SaTokenInfo( - tokenName: json['tokenName'] as String?, - tokenValue: json['tokenValue'] as String?, - isLogin: json['isLogin'] as bool?, + tokenName: json['tokenName'] as String, + tokenValue: json['tokenValue'] as String, + isLogin: json['isLogin'] as bool, loginId: json['loginId'], - loginType: json['loginType'] as String?, - tokenTimeout: (json['tokenTimeout'] as num?)?.toInt(), - sessionTimeout: (json['sessionTimeout'] as num?)?.toInt(), - tokenSessionTimeout: (json['tokenSessionTimeout'] as num?)?.toInt(), - tokenActiveTimeout: (json['tokenActiveTimeout'] as num?)?.toInt(), - loginDeviceType: json['loginDeviceType'] as String?, + loginType: json['loginType'] as String, + tokenTimeout: (json['tokenTimeout'] as num).toInt(), + sessionTimeout: (json['sessionTimeout'] as num).toInt(), + tokenSessionTimeout: (json['tokenSessionTimeout'] as num).toInt(), + tokenActiveTimeout: (json['tokenActiveTimeout'] as num).toInt(), + loginDeviceType: json['loginDeviceType'] as String, tag: json['tag'] as String?, ); @@ -36,7 +36,7 @@ Map _$SaTokenInfoToJson(SaTokenInfo instance) => }; User _$UserFromJson(Map json) => User( - id: json['id'] as String?, + id: (json['id'] as num?)?.toInt(), username: json['username'] as String?, gender: (json['gender'] as num?)?.toInt(), phoneNumber: json['phoneNumber'] as String?, @@ -54,6 +54,7 @@ User _$UserFromJson(Map json) => User( ); Map _$UserToJson(User instance) => { + 'id': instance.id, 'username': instance.username, 'gender': instance.gender, 'phoneNumber': instance.phoneNumber, @@ -65,18 +66,11 @@ Map _$UserToJson(User instance) => { 'job': instance.job, 'tags': instance.tags, 'description': instance.description, - 'id': instance.id, }; Session _$SessionFromJson(Map json) => Session( - saToken: - json['saToken'] == null - ? null - : SaTokenInfo.fromJson(json['saToken'] as Map), - userInfo: - json['userInfo'] == null - ? null - : User.fromJson(json['userInfo'] as Map), + saToken: SaTokenInfo.fromJson(json['saToken'] as Map), + userInfo: User.fromJson(json['userInfo'] as Map), ); Map _$SessionToJson(Session instance) => { diff --git a/lib/utils/HttpUtil.dart b/lib/utils/http_util.dart similarity index 66% rename from lib/utils/HttpUtil.dart rename to lib/utils/http_util.dart index 4168cc3..7c49fc1 100644 --- a/lib/utils/HttpUtil.dart +++ b/lib/utils/http_util.dart @@ -1,6 +1,6 @@ import 'package:dio/dio.dart'; +import 'package:food_hub_app/utils/sp_util.dart'; import 'package:food_hub_app/widgets/common/index.dart'; -import 'package:tdesign_flutter/tdesign_flutter.dart'; class HttpUtil { static final HttpUtil _instance = HttpUtil._internal(); @@ -38,8 +38,10 @@ class HttpUtil { if (options.data != null) { print("请求参数: ${options.data}"); } - // 可以在此添加token等操作 - // options.headers['Authorization'] = 'Bearer your_token'; + + if (SPUtil.getString('token').isNotEmpty) { + options.headers['satoken'] = SPUtil.getString('token'); + } return handler.next(options); }, ), @@ -60,18 +62,7 @@ class HttpUtil { _dio.interceptors.add( InterceptorsWrapper( onError: (DioException e, handler) { - if (e.response != null) { - final responseData = e.response?.data; - - if (responseData is Map) { - // 服务器返回标准JSON错误格式 - print(responseData['message']?.toString()); - showErrorToast('错误'); - } else { - // 非JSON格式错误 - print('网络错误: ${e.message}'); - } - } + _handleError(e); return handler.next(e); }, ), @@ -79,7 +70,7 @@ class HttpUtil { } // 基础请求方法(处理所有类型的请求) - Future _request( + Future _request( String path, { required String method, dynamic data, @@ -99,21 +90,15 @@ class HttpUtil { return converter(response.data); } - // 如果没有转换器且T是dynamic,直接返回原始数据 - if (T == dynamic) { - return response.data as T; - } - // 没有转换器时尝试直接返回(可能不安全,建议提供转换器) - return response.data as T?; + return response.data; } catch (e) { - _handleError(e); rethrow; } } // GET请求 - Future get( + Future get( String path, { Map? queryParameters, T Function(dynamic data)? converter, @@ -125,7 +110,7 @@ class HttpUtil { ); // POST请求 - Future post( + Future post( String path, { dynamic data, Map? queryParameters, @@ -139,7 +124,7 @@ class HttpUtil { ); // PUT请求 - Future put( + Future put( String path, { dynamic data, Map? queryParameters, @@ -153,7 +138,7 @@ class HttpUtil { ); // DELETE请求 - Future delete( + Future delete( String path, { dynamic data, Map? queryParameters, @@ -191,32 +176,68 @@ class HttpUtil { // 错误处理 void _handleError(dynamic error) { + String errorMessage = '未知错误'; if (error is DioException) { switch (error.type) { case DioExceptionType.connectionTimeout: - print("连接超时"); + errorMessage = '连接超时,请检查网络连接'; break; case DioExceptionType.sendTimeout: - print("发送超时"); + errorMessage = '发送超时,请检查网络连接'; break; case DioExceptionType.receiveTimeout: - print("接收超时"); + errorMessage = '接收超时,请检查网络连接'; break; case DioExceptionType.cancel: - print("请求取消"); + errorMessage = '请求已取消'; break; case DioExceptionType.badCertificate: - print("证书错误"); + errorMessage = '证书验证失败'; + break; case DioExceptionType.badResponse: - print("错误响应"); + // 处理HTTP响应错误(4xx, 5xx) + final statusCode = error.response?.statusCode ?? 0; + final responseData = error.response?.data; + + if (responseData is Map && responseData.containsKey('message')) { + // 服务器返回了自定义错误消息 + errorMessage = responseData['message']?.toString() ?? '未知错误'; + } else { + errorMessage = _getHttpErrorMessage(statusCode); + } + break; case DioExceptionType.connectionError: - print("连接错误"); + errorMessage = '网络连接错误,请检查网络设置'; + break; case DioExceptionType.unknown: - print("未知错误"); + if (error.error != null) { + errorMessage = '未知错误: ${error.error.toString()}'; + } + errorMessage = '发生未知错误'; break; } } else { - print("未知错误: $error"); + errorMessage = '非Dio错误: $error'; + } + + print(errorMessage); + showErrorToast(errorMessage); + } + + String _getHttpErrorMessage(int statusCode) { + // 根据HTTP状态码返回对应的错误消息 + switch (statusCode) { + case 400: return '错误请求,请检查参数'; + case 401: return '未授权,请登录'; + case 403: return '禁止访问,权限不足'; + case 404: return '资源不存在'; + case 405: return '方法不允许'; + case 408: return '请求超时'; + case 500: return '服务器内部错误'; + case 502: return '网关错误'; + case 503: return '服务不可用'; + case 504: return '网关超时'; + default: return 'HTTP错误: $statusCode'; } } } diff --git a/lib/utils/sp_util.dart b/lib/utils/sp_util.dart new file mode 100644 index 0000000..b255633 --- /dev/null +++ b/lib/utils/sp_util.dart @@ -0,0 +1,61 @@ +import 'package:shared_preferences/shared_preferences.dart'; + +class SPUtil { + static late SharedPreferences _prefs; + + /// 初始化 + static Future init() async { + _prefs = await SharedPreferences.getInstance(); + } + + /// 存储数据 + static Future set(String key, dynamic value) { + if (value is String) return _prefs.setString(key, value); + if (value is int) return _prefs.setInt(key, value); + if (value is bool) return _prefs.setBool(key, value); + if (value is double) return _prefs.setDouble(key, value); + if (value is List) return _prefs.setStringList(key, value); + throw ArgumentError('Unsupported value type: ${value.runtimeType}'); + } + + /// 获取数据 + static dynamic get(String key, [dynamic defaultValue]) { + if (!_prefs.containsKey(key)) return defaultValue; + return _prefs.get(key) ?? defaultValue; + } + + /// 获取字符串 + static String getString(String key, [String defaultValue = '']) { + return _prefs.getString(key) ?? defaultValue; + } + + /// 获取布尔值 + static bool getBool(String key, [bool defaultValue = false]) { + return _prefs.getBool(key) ?? defaultValue; + } + + /// 获取整数 + static int getInt(String key, [int defaultValue = 0]) { + return _prefs.getInt(key) ?? defaultValue; + } + + /// 获取浮点数 + static double getDouble(String key, [double defaultValue = 0.0]) { + return _prefs.getDouble(key) ?? defaultValue; + } + + /// 获取字符串列表 + static List getStringList(String key, [List defaultValue = const []]) { + return _prefs.getStringList(key) ?? defaultValue; + } + + /// 删除数据 + static Future remove(String key) { + return _prefs.remove(key); + } + + /// 清空所有数据 + static Future clear() { + return _prefs.clear(); + } +} \ No newline at end of file diff --git a/lib/views/login.dart b/lib/views/login.dart index a1d1c29..571adad 100644 --- a/lib/views/login.dart +++ b/lib/views/login.dart @@ -1,8 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:food_hub_app/api/recipe.dart'; import 'package:food_hub_app/api/session.dart'; +import 'package:food_hub_app/models/recipe.dart'; +import 'package:food_hub_app/models/session.dart'; +import 'package:food_hub_app/utils/sp_util.dart'; import 'package:food_hub_app/widgets/common/index.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart'; class LoginPage extends StatefulWidget { @@ -25,7 +30,13 @@ class _LoginPage extends State { ]; void loginClick(BuildContext context) async { - await loginApi("Cxx", "1232"); + SPUtil.clear(); + Session session = await loginApi("Cxx0822", "19940822Cxx"); + SPUtil.set('token', session.saToken.tokenValue); + + Recipe recipe = await queryRecipeByIdApi(614660173221957); + List recipeList = await queryRecipeByUserApi(691110779121733); + // SPUtil.set("token", session?.saToken?.tokenValue); // Navigator.pushNamed(context, '/home'); return; diff --git a/lib/views/record.dart b/lib/views/record.dart index d6449e7..3ef1bc6 100644 --- a/lib/views/record.dart +++ b/lib/views/record.dart @@ -15,15 +15,9 @@ class RecordPage extends StatefulWidget { class _RecordPageState extends State with SingleTickerProviderStateMixin { - final List recipeList = [ - Recipe("韭菜炒鸡蛋", "家常菜", "2025-05-04", "https://picsum.photos/200", 10, 2, 4), - Recipe("红烧肉", "家常菜", "2025-05-05", "https://picsum.photos/200", 12, 4, 7), - ]; + final List recipeList = []; - final List recordList = [ - Record("韭菜炒鸡蛋", "家常菜", "2025-05-04", "https://picsum.photos/200"), - Record("红烧肉", "家常菜", "2025-05-05", "https://picsum.photos/200"), - ]; + final List recordList = []; late final TabController _tabController = TabController( length: 3, @@ -68,7 +62,7 @@ class _RecordPageState extends State children: [ RecipeList(recipeList: recipeList), RecipeCalendar(), - RecipeTimeline(recipeList: recipeList), + RecipeTimeline(recordList: recordList), ], ), ), diff --git a/lib/widgets/recipe/recipe_card.dart b/lib/widgets/recipe/recipe_card.dart index 69d60fa..08d2817 100644 --- a/lib/widgets/recipe/recipe_card.dart +++ b/lib/widgets/recipe/recipe_card.dart @@ -18,7 +18,7 @@ class RecipeCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Image.network( - recipe.imageUrl, + recipe.recordList[0].imageUrl, height: 200, width: double.infinity, fit: BoxFit.contain, @@ -106,7 +106,7 @@ class RecipeCard extends StatelessWidget { const SizedBox(width: 12), _buildIconText( icon: Icons.star_outline, - text: recipe.favoriteCount.toString(), + text: recipe.favouriteCount.toString(), color: Colors.blue, ), const SizedBox(width: 12), @@ -121,7 +121,7 @@ class RecipeCard extends StatelessWidget { children: [ _buildIconText( icon: Icons.date_range, - text: recipe.date, + text: recipe.recordList[0].date, color: Colors.red, ), ], diff --git a/lib/widgets/recipe/recipe_timeline.dart b/lib/widgets/recipe/recipe_timeline.dart index 4efefe1..b1697f9 100644 --- a/lib/widgets/recipe/recipe_timeline.dart +++ b/lib/widgets/recipe/recipe_timeline.dart @@ -3,23 +3,23 @@ import 'package:food_hub_app/models/recipe.dart'; import 'package:timelines_plus/timelines_plus.dart'; class RecipeTimeline extends StatelessWidget { - final List recipeList; + final List recordList; - const RecipeTimeline({super.key, required this.recipeList}); + const RecipeTimeline({super.key, required this.recordList}); @override Widget build(BuildContext context) { return Timeline.tileBuilder( theme: TimelineThemeData(nodePosition: 0, indicatorPosition: 0), builder: TimelineTileBuilder.connected( - itemCount: recipeList.length, + itemCount: recordList.length, connectorBuilder: (context, index, type) => Connector.solidLine(thickness: 2), indicatorBuilder: (context, index) { return Indicator.dot(size: 12.0); }, contentsBuilder: (context, index) { - return TimelineCard(recipe: recipeList[index]); + return TimelineCard(record: recordList[index]); }, ), ); @@ -27,9 +27,9 @@ class RecipeTimeline extends StatelessWidget { } class TimelineCard extends StatelessWidget { - final Recipe recipe; + final Record record; - const TimelineCard({super.key, required this.recipe}); + const TimelineCard({super.key, required this.record}); @override Widget build(BuildContext context) { @@ -41,7 +41,7 @@ class TimelineCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - recipe.date, + record.date, style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), Card( @@ -51,10 +51,10 @@ class TimelineCard extends StatelessWidget { padding: EdgeInsets.all(10), child: Column( children: [ - Text(recipe.name, style: TextStyle(fontSize: 16)), + Text(record.name, style: TextStyle(fontSize: 16)), const SizedBox(height: 10), Image.network( - recipe.imageUrl, + record.imageUrl, width: double.infinity, height: 250, fit: BoxFit.contain, diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 14b5f7c..ab1fdba 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,7 +6,9 @@ import FlutterMacOS import Foundation import file_selector_macos +import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 3444216..7aab282 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -217,6 +217,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.3.2" + ffi: + dependency: transitive + description: + name: ffi + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.4" file: dependency: transitive description: @@ -605,6 +613,38 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.1.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.3.0" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.1.6" plugin_platform_interface: dependency: transitive description: @@ -637,6 +677,62 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.5.0" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.5.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.10" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.5.4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.1" shelf: dependency: transitive description: @@ -834,6 +930,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "3.0.3" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.0" yaml: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 7489cbd..cfbdd61 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -45,6 +45,7 @@ dependencies: tdesign_flutter: ^0.2.3 json_annotation: ^4.9.0 fluttertoast: ^8.2.0 + shared_preferences: ^2.3.0 dependency_overrides: tdesign_flutter_adaptation: 3.16.0 @@ -60,6 +61,7 @@ dev_dependencies: # package. See that file for information about deactivating specific lint # rules and activating additional ones. flutter_lints: ^5.0.0 + # flutter pub run build_runner build build_runner: ^2.4.5 json_serializable: ^6.7.1