feat:增加dio网络请求
This commit is contained in:
16
lib/api/recipe.dart
Normal file
16
lib/api/recipe.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
import 'package:food_hub_app/models/recipe.dart';
|
||||
import 'package:food_hub_app/utils/http_util.dart';
|
||||
|
||||
Future<Recipe> queryRecipeByIdApi(int id) {
|
||||
return HttpUtil().get<Recipe>(
|
||||
"/food/recipe/$id",
|
||||
converter: (data) => Recipe.fromJson(data),
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<Recipe>> queryRecipeByUserApi(int id) {
|
||||
return HttpUtil().get<List<Recipe>>(
|
||||
"/food/recipe/user/$id",
|
||||
converter: (data) => data.map((item) => Recipe.fromJson(item)).toList()
|
||||
);
|
||||
}
|
||||
@@ -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<Session?> loginApi(String username, String password) {
|
||||
Future<Session> loginApi(String username, String password) {
|
||||
return HttpUtil().post<Session>(
|
||||
"/session",
|
||||
queryParameters: {"username": username, "password": password},
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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<String, dynamic> json) =>
|
||||
_$MaterialFromJson(json);
|
||||
|
||||
Map<String, dynamic> 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<String, dynamic> json) =>
|
||||
_$StepFromJson(json);
|
||||
|
||||
Map<String, dynamic> 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<String, dynamic> json) =>
|
||||
_$CommentFromJson(json);
|
||||
|
||||
Map<String, dynamic> 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<String, dynamic> json) =>
|
||||
_$RecordFromJson(json);
|
||||
|
||||
Map<String, dynamic> 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<Material> materialList;
|
||||
List<Step> stepList;
|
||||
List<Record> recordList;
|
||||
List<int>? likeList;
|
||||
int? likeCount;
|
||||
List<int>? favouriteList;
|
||||
int? favouriteCount;
|
||||
List<Comment>? 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<String, dynamic> json) =>
|
||||
_$RecipeFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$RecipeToJson(this);
|
||||
}
|
||||
|
||||
enum ViewType { recipe, calendar, timeline }
|
||||
|
||||
125
lib/models/recipe.g.dart
Normal file
125
lib/models/recipe.g.dart
Normal file
@@ -0,0 +1,125 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'recipe.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
Material _$MaterialFromJson(Map<String, dynamic> json) => Material(
|
||||
type: json['type'] as String,
|
||||
name: json['name'] as String,
|
||||
amount: json['amount'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$MaterialToJson(Material instance) => <String, dynamic>{
|
||||
'type': instance.type,
|
||||
'name': instance.name,
|
||||
'amount': instance.amount,
|
||||
};
|
||||
|
||||
Step _$StepFromJson(Map<String, dynamic> json) => Step(
|
||||
sort: (json['sort'] as num).toInt(),
|
||||
content: json['content'] as String,
|
||||
imageUrl: json['imageUrl'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$StepToJson(Step instance) => <String, dynamic>{
|
||||
'sort': instance.sort,
|
||||
'content': instance.content,
|
||||
'imageUrl': instance.imageUrl,
|
||||
};
|
||||
|
||||
Comment _$CommentFromJson(Map<String, dynamic> 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<String, dynamic> _$CommentToJson(Comment instance) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'username': instance.username,
|
||||
'avatar': instance.avatar,
|
||||
'content': instance.content,
|
||||
'date': instance.date,
|
||||
};
|
||||
|
||||
Record _$RecordFromJson(Map<String, dynamic> 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<String, dynamic> _$RecordToJson(Record instance) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'name': instance.name,
|
||||
'category': instance.category,
|
||||
'person': instance.person,
|
||||
'date': instance.date,
|
||||
'imageUrl': instance.imageUrl,
|
||||
};
|
||||
|
||||
Recipe _$RecipeFromJson(Map<String, dynamic> 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<dynamic>)
|
||||
.map((e) => Material.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
stepList:
|
||||
(json['stepList'] as List<dynamic>)
|
||||
.map((e) => Step.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
recordList:
|
||||
(json['recordList'] as List<dynamic>)
|
||||
.map((e) => Record.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
likeList:
|
||||
(json['likeList'] as List<dynamic>?)
|
||||
?.map((e) => (e as num).toInt())
|
||||
.toList(),
|
||||
likeCount: (json['likeCount'] as num?)?.toInt(),
|
||||
favouriteList:
|
||||
(json['favouriteList'] as List<dynamic>?)
|
||||
?.map((e) => (e as num).toInt())
|
||||
.toList(),
|
||||
favouriteCount: (json['favouriteCount'] as num?)?.toInt(),
|
||||
commentList:
|
||||
(json['commentList'] as List<dynamic>?)
|
||||
?.map((e) => Comment.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
commentCount: (json['commentCount'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$RecipeToJson(Recipe instance) => <String, dynamic>{
|
||||
'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,
|
||||
};
|
||||
@@ -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<String>? 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<String, dynamic> json) => _$SessionFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$SessionToJson(this);
|
||||
|
||||
@@ -7,16 +7,16 @@ part of 'session.dart';
|
||||
// **************************************************************************
|
||||
|
||||
SaTokenInfo _$SaTokenInfoFromJson(Map<String, dynamic> 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<String, dynamic> _$SaTokenInfoToJson(SaTokenInfo instance) =>
|
||||
};
|
||||
|
||||
User _$UserFromJson(Map<String, dynamic> 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<String, dynamic> json) => User(
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'username': instance.username,
|
||||
'gender': instance.gender,
|
||||
'phoneNumber': instance.phoneNumber,
|
||||
@@ -65,18 +66,11 @@ Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
|
||||
'job': instance.job,
|
||||
'tags': instance.tags,
|
||||
'description': instance.description,
|
||||
'id': instance.id,
|
||||
};
|
||||
|
||||
Session _$SessionFromJson(Map<String, dynamic> json) => Session(
|
||||
saToken:
|
||||
json['saToken'] == null
|
||||
? null
|
||||
: SaTokenInfo.fromJson(json['saToken'] as Map<String, dynamic>),
|
||||
userInfo:
|
||||
json['userInfo'] == null
|
||||
? null
|
||||
: User.fromJson(json['userInfo'] as Map<String, dynamic>),
|
||||
saToken: SaTokenInfo.fromJson(json['saToken'] as Map<String, dynamic>),
|
||||
userInfo: User.fromJson(json['userInfo'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$SessionToJson(Session instance) => <String, dynamic>{
|
||||
|
||||
@@ -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<String, dynamic>) {
|
||||
// 服务器返回标准JSON错误格式
|
||||
print(responseData['message']?.toString());
|
||||
showErrorToast('错误');
|
||||
} else {
|
||||
// 非JSON格式错误
|
||||
print('网络错误: ${e.message}');
|
||||
}
|
||||
}
|
||||
_handleError(e);
|
||||
return handler.next(e);
|
||||
},
|
||||
),
|
||||
@@ -79,7 +70,7 @@ class HttpUtil {
|
||||
}
|
||||
|
||||
// 基础请求方法(处理所有类型的请求)
|
||||
Future<T?> _request<T>(
|
||||
Future<T> _request<T>(
|
||||
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<T?> get<T>(
|
||||
Future<T> get<T>(
|
||||
String path, {
|
||||
Map<String, dynamic>? queryParameters,
|
||||
T Function(dynamic data)? converter,
|
||||
@@ -125,7 +110,7 @@ class HttpUtil {
|
||||
);
|
||||
|
||||
// POST请求
|
||||
Future<T?> post<T>(
|
||||
Future<T> post<T>(
|
||||
String path, {
|
||||
dynamic data,
|
||||
Map<String, dynamic>? queryParameters,
|
||||
@@ -139,7 +124,7 @@ class HttpUtil {
|
||||
);
|
||||
|
||||
// PUT请求
|
||||
Future<T?> put<T>(
|
||||
Future<T> put<T>(
|
||||
String path, {
|
||||
dynamic data,
|
||||
Map<String, dynamic>? queryParameters,
|
||||
@@ -153,7 +138,7 @@ class HttpUtil {
|
||||
);
|
||||
|
||||
// DELETE请求
|
||||
Future<T?> delete<T>(
|
||||
Future<T> delete<T>(
|
||||
String path, {
|
||||
dynamic data,
|
||||
Map<String, dynamic>? 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<String, dynamic> && 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';
|
||||
}
|
||||
}
|
||||
}
|
||||
61
lib/utils/sp_util.dart
Normal file
61
lib/utils/sp_util.dart
Normal file
@@ -0,0 +1,61 @@
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class SPUtil {
|
||||
static late SharedPreferences _prefs;
|
||||
|
||||
/// 初始化
|
||||
static Future<void> init() async {
|
||||
_prefs = await SharedPreferences.getInstance();
|
||||
}
|
||||
|
||||
/// 存储数据
|
||||
static Future<bool> 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<String>) 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<String> getStringList(String key, [List<String> defaultValue = const []]) {
|
||||
return _prefs.getStringList(key) ?? defaultValue;
|
||||
}
|
||||
|
||||
/// 删除数据
|
||||
static Future<bool> remove(String key) {
|
||||
return _prefs.remove(key);
|
||||
}
|
||||
|
||||
/// 清空所有数据
|
||||
static Future<bool> clear() {
|
||||
return _prefs.clear();
|
||||
}
|
||||
}
|
||||
@@ -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<LoginPage> {
|
||||
];
|
||||
|
||||
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<Recipe> recipeList = await queryRecipeByUserApi(691110779121733);
|
||||
// SPUtil.set("token", session?.saToken?.tokenValue);
|
||||
|
||||
// Navigator.pushNamed(context, '/home');
|
||||
return;
|
||||
|
||||
@@ -15,15 +15,9 @@ class RecordPage extends StatefulWidget {
|
||||
|
||||
class _RecordPageState extends State<RecordPage>
|
||||
with SingleTickerProviderStateMixin {
|
||||
final List<Recipe> 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<Recipe> recipeList = [];
|
||||
|
||||
final List<Record> recordList = [
|
||||
Record("韭菜炒鸡蛋", "家常菜", "2025-05-04", "https://picsum.photos/200"),
|
||||
Record("红烧肉", "家常菜", "2025-05-05", "https://picsum.photos/200"),
|
||||
];
|
||||
final List<Record> recordList = [];
|
||||
|
||||
late final TabController _tabController = TabController(
|
||||
length: 3,
|
||||
@@ -68,7 +62,7 @@ class _RecordPageState extends State<RecordPage>
|
||||
children: [
|
||||
RecipeList(recipeList: recipeList),
|
||||
RecipeCalendar(),
|
||||
RecipeTimeline(recipeList: recipeList),
|
||||
RecipeTimeline(recordList: recordList),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
],
|
||||
|
||||
@@ -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<Recipe> recipeList;
|
||||
final List<Record> 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,
|
||||
|
||||
@@ -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"))
|
||||
}
|
||||
|
||||
104
pubspec.lock
104
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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user