feat:增加Minio工具类
This commit is contained in:
@@ -51,11 +51,11 @@ Future<List<String>> queryFoodNameListApi() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> addRecordApi(Record record) {
|
Future<bool> addRecordApi(FoodRecord record) {
|
||||||
return HttpUtil().post<bool>("/food/record", data: record);
|
return HttpUtil().post<bool>("/food/record", data: record);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> updateRecordApi(int id, Record record) {
|
Future<bool> updateRecordApi(int id, FoodRecord record) {
|
||||||
return HttpUtil().put<bool>("/food/record/$id", data: record);
|
return HttpUtil().put<bool>("/food/record/$id", data: record);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,11 +83,11 @@ Future<bool> deleteRecipeFavouriteApi(int id) {
|
|||||||
return HttpUtil().delete<bool>("/food/recipe/$id/like");
|
return HttpUtil().delete<bool>("/food/recipe/$id/like");
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Record>> queryRecordApi(String startDate, String endDate) {
|
Future<List<FoodRecord>> queryRecordApi(String startDate, String endDate) {
|
||||||
return HttpUtil().get<List<Record>>(
|
return HttpUtil().get<List<FoodRecord>>(
|
||||||
"/food/record",
|
"/food/record",
|
||||||
queryParameters: {"startDate": startDate, "endDate": endDate},
|
queryParameters: {"startDate": startDate, "endDate": endDate},
|
||||||
converter: (data) => convertListResponse<Record>(data, Record.fromJson),
|
converter: (data) => convertListResponse<FoodRecord>(data, FoodRecord.fromJson),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,26 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
|
import 'package:food_hub_app/provider/food_provider.dart';
|
||||||
import 'package:food_hub_app/utils/sp_util.dart';
|
import 'package:food_hub_app/utils/sp_util.dart';
|
||||||
import 'package:food_hub_app/views/home.dart';
|
import 'package:food_hub_app/views/home.dart';
|
||||||
import 'package:food_hub_app/views/login.dart';
|
import 'package:food_hub_app/views/login.dart';
|
||||||
import 'package:food_hub_app/views/record_form.dart';
|
import 'package:food_hub_app/views/record_form.dart';
|
||||||
import 'package:food_hub_app/views/recipe_detail.dart';
|
import 'package:food_hub_app/views/recipe_detail.dart';
|
||||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
FormBuilderLocalizations.delegate.load(const Locale('zh', 'CN'));
|
FormBuilderLocalizations.delegate.load(const Locale('zh', 'CN'));
|
||||||
await SPUtil.init();
|
await SPUtil.init();
|
||||||
runApp(const MyApp());
|
runApp(
|
||||||
|
MultiProvider(
|
||||||
|
providers: [
|
||||||
|
ChangeNotifierProvider(create: (context) => FoodProvider()),
|
||||||
|
],
|
||||||
|
child: MyApp(),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ class RecipeMaterial {
|
|||||||
String name;
|
String name;
|
||||||
String amount;
|
String amount;
|
||||||
|
|
||||||
RecipeMaterial({required this.type, required this.name, required this.amount});
|
RecipeMaterial({
|
||||||
|
required this.type,
|
||||||
|
required this.name,
|
||||||
|
required this.amount,
|
||||||
|
});
|
||||||
|
|
||||||
factory RecipeMaterial.fromJson(Map<String, dynamic> json) =>
|
factory RecipeMaterial.fromJson(Map<String, dynamic> json) =>
|
||||||
_$RecipeMaterialFromJson(json);
|
_$RecipeMaterialFromJson(json);
|
||||||
@@ -24,9 +28,14 @@ class RecipeStep {
|
|||||||
String content;
|
String content;
|
||||||
String imageUrl;
|
String imageUrl;
|
||||||
|
|
||||||
RecipeStep({required this.sort, required this.content, required this.imageUrl});
|
RecipeStep({
|
||||||
|
required this.sort,
|
||||||
|
required this.content,
|
||||||
|
required this.imageUrl,
|
||||||
|
});
|
||||||
|
|
||||||
factory RecipeStep.fromJson(Map<String, dynamic> json) => _$RecipeStepFromJson(json);
|
factory RecipeStep.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$RecipeStepFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$RecipeStepToJson(this);
|
Map<String, dynamic> toJson() => _$RecipeStepToJson(this);
|
||||||
}
|
}
|
||||||
@@ -56,7 +65,7 @@ class RecipeComment {
|
|||||||
|
|
||||||
/// 成果信息
|
/// 成果信息
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class Record {
|
class FoodRecord {
|
||||||
int? id;
|
int? id;
|
||||||
String name;
|
String name;
|
||||||
String category;
|
String category;
|
||||||
@@ -64,7 +73,7 @@ class Record {
|
|||||||
String date;
|
String date;
|
||||||
String imageUrl;
|
String imageUrl;
|
||||||
|
|
||||||
Record({
|
FoodRecord({
|
||||||
this.id,
|
this.id,
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.category,
|
required this.category,
|
||||||
@@ -73,20 +82,31 @@ class Record {
|
|||||||
required this.imageUrl,
|
required this.imageUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Record.fromJson(Map<String, dynamic> json) => _$RecordFromJson(json);
|
factory FoodRecord.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$RecordFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$RecordToJson(this);
|
Map<String, dynamic> toJson() => _$RecordToJson(this);
|
||||||
|
|
||||||
|
static FoodRecord getEmpty() {
|
||||||
|
return FoodRecord(
|
||||||
|
id: 0,
|
||||||
|
name: '',
|
||||||
|
category: '',
|
||||||
|
person: 0,
|
||||||
|
date: '',
|
||||||
|
imageUrl: '',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class RecipeQuery {
|
class RecipeQuery {
|
||||||
String category;
|
String category;
|
||||||
|
|
||||||
RecipeQuery({
|
RecipeQuery({required this.category});
|
||||||
required this.category
|
|
||||||
});
|
|
||||||
|
|
||||||
factory RecipeQuery.fromJson(Map<String, dynamic> json) => _$RecipeQueryFromJson(json);
|
factory RecipeQuery.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$RecipeQueryFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$RecipeQueryToJson(this);
|
Map<String, dynamic> toJson() => _$RecipeQueryToJson(this);
|
||||||
}
|
}
|
||||||
@@ -105,7 +125,7 @@ class Recipe {
|
|||||||
String avatar;
|
String avatar;
|
||||||
List<RecipeMaterial> materialList;
|
List<RecipeMaterial> materialList;
|
||||||
List<RecipeStep> stepList;
|
List<RecipeStep> stepList;
|
||||||
List<Record> recordList;
|
List<FoodRecord> recordList;
|
||||||
List<int> likeList;
|
List<int> likeList;
|
||||||
int likeCount;
|
int likeCount;
|
||||||
List<int> favouriteList;
|
List<int> favouriteList;
|
||||||
@@ -146,7 +166,7 @@ class RecipeSummary {
|
|||||||
String category;
|
String category;
|
||||||
double recommendRate;
|
double recommendRate;
|
||||||
bool isShare;
|
bool isShare;
|
||||||
List<Record> recordList;
|
List<FoodRecord> recordList;
|
||||||
int likeCount;
|
int likeCount;
|
||||||
int favouriteCount;
|
int favouriteCount;
|
||||||
int commentCount;
|
int commentCount;
|
||||||
@@ -169,7 +189,8 @@ class RecipeSummary {
|
|||||||
required this.commentCount,
|
required this.commentCount,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory RecipeSummary.fromJson(Map<String, dynamic> json) => _$RecipeSummaryFromJson(json);
|
factory RecipeSummary.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$RecipeSummaryFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$RecipeSummaryToJson(this);
|
Map<String, dynamic> toJson() => _$RecipeSummaryToJson(this);
|
||||||
}
|
}
|
||||||
@@ -187,7 +208,7 @@ class RecipeDetail {
|
|||||||
String avatar;
|
String avatar;
|
||||||
List<RecipeMaterial> materialList;
|
List<RecipeMaterial> materialList;
|
||||||
List<RecipeStep> stepList;
|
List<RecipeStep> stepList;
|
||||||
List<Record> recordList;
|
List<FoodRecord> recordList;
|
||||||
List<int> likeList;
|
List<int> likeList;
|
||||||
List<int> favouriteList;
|
List<int> favouriteList;
|
||||||
List<RecipeComment> commentList;
|
List<RecipeComment> commentList;
|
||||||
@@ -210,7 +231,8 @@ class RecipeDetail {
|
|||||||
required this.commentList,
|
required this.commentList,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory RecipeDetail.fromJson(Map<String, dynamic> json) => _$RecipeDetailFromJson(json);
|
factory RecipeDetail.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$RecipeDetailFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$RecipeDetailToJson(this);
|
Map<String, dynamic> toJson() => _$RecipeDetailToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ Map<String, dynamic> _$RecipeCommentToJson(RecipeComment instance) =>
|
|||||||
'date': instance.date,
|
'date': instance.date,
|
||||||
};
|
};
|
||||||
|
|
||||||
Record _$RecordFromJson(Map<String, dynamic> json) => Record(
|
FoodRecord _$RecordFromJson(Map<String, dynamic> json) => FoodRecord(
|
||||||
id: (json['id'] as num?)?.toInt(),
|
id: (json['id'] as num?)?.toInt(),
|
||||||
name: json['name'] as String,
|
name: json['name'] as String,
|
||||||
category: json['category'] as String,
|
category: json['category'] as String,
|
||||||
@@ -60,7 +60,7 @@ Record _$RecordFromJson(Map<String, dynamic> json) => Record(
|
|||||||
imageUrl: json['imageUrl'] as String,
|
imageUrl: json['imageUrl'] as String,
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$RecordToJson(Record instance) => <String, dynamic>{
|
Map<String, dynamic> _$RecordToJson(FoodRecord instance) => <String, dynamic>{
|
||||||
'id': instance.id,
|
'id': instance.id,
|
||||||
'name': instance.name,
|
'name': instance.name,
|
||||||
'category': instance.category,
|
'category': instance.category,
|
||||||
@@ -95,7 +95,7 @@ Recipe _$RecipeFromJson(Map<String, dynamic> json) => Recipe(
|
|||||||
.toList(),
|
.toList(),
|
||||||
recordList:
|
recordList:
|
||||||
(json['recordList'] as List<dynamic>)
|
(json['recordList'] as List<dynamic>)
|
||||||
.map((e) => Record.fromJson(e as Map<String, dynamic>))
|
.map((e) => FoodRecord.fromJson(e as Map<String, dynamic>))
|
||||||
.toList(),
|
.toList(),
|
||||||
likeList:
|
likeList:
|
||||||
(json['likeList'] as List<dynamic>)
|
(json['likeList'] as List<dynamic>)
|
||||||
@@ -147,7 +147,7 @@ RecipeSummary _$RecipeSummaryFromJson(Map<String, dynamic> json) =>
|
|||||||
avatar: json['avatar'] as String,
|
avatar: json['avatar'] as String,
|
||||||
recordList:
|
recordList:
|
||||||
(json['recordList'] as List<dynamic>)
|
(json['recordList'] as List<dynamic>)
|
||||||
.map((e) => Record.fromJson(e as Map<String, dynamic>))
|
.map((e) => FoodRecord.fromJson(e as Map<String, dynamic>))
|
||||||
.toList(),
|
.toList(),
|
||||||
likeCount: (json['likeCount'] as num).toInt(),
|
likeCount: (json['likeCount'] as num).toInt(),
|
||||||
favouriteCount: (json['favouriteCount'] as num).toInt(),
|
favouriteCount: (json['favouriteCount'] as num).toInt(),
|
||||||
@@ -190,7 +190,7 @@ RecipeDetail _$RecipeDetailFromJson(Map<String, dynamic> json) => RecipeDetail(
|
|||||||
.toList(),
|
.toList(),
|
||||||
recordList:
|
recordList:
|
||||||
(json['recordList'] as List<dynamic>)
|
(json['recordList'] as List<dynamic>)
|
||||||
.map((e) => Record.fromJson(e as Map<String, dynamic>))
|
.map((e) => FoodRecord.fromJson(e as Map<String, dynamic>))
|
||||||
.toList(),
|
.toList(),
|
||||||
likeList:
|
likeList:
|
||||||
(json['likeList'] as List<dynamic>)
|
(json['likeList'] as List<dynamic>)
|
||||||
|
|||||||
18
lib/provider/food_provider.dart
Normal file
18
lib/provider/food_provider.dart
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:food_hub_app/models/recipe.dart';
|
||||||
|
|
||||||
|
class FoodProvider with ChangeNotifier {
|
||||||
|
late FoodRecord _recordFormItem;
|
||||||
|
|
||||||
|
FoodRecord get recordFormItem => _recordFormItem;
|
||||||
|
|
||||||
|
void resetRecordForm() {
|
||||||
|
_recordFormItem = FoodRecord.getEmpty();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void initRecordForm(FoodRecord record) {
|
||||||
|
_recordFormItem = record;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
57
lib/utils/minio_utils.dart
Normal file
57
lib/utils/minio_utils.dart
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'package:crypto/crypto.dart';
|
||||||
|
import 'package:file_picker/file_picker.dart';
|
||||||
|
|
||||||
|
import 'package:minio/io.dart';
|
||||||
|
import 'package:minio/minio.dart';
|
||||||
|
|
||||||
|
class MinIOHelper {
|
||||||
|
static final MinIOHelper _instance = MinIOHelper._internal();
|
||||||
|
|
||||||
|
factory MinIOHelper() => _instance;
|
||||||
|
|
||||||
|
final String rustfsIp = '14.103.235.151';
|
||||||
|
final String rustfsFileUrl = 'http://14.103.235.151:9100';
|
||||||
|
final String bucketName = 'flisp';
|
||||||
|
|
||||||
|
MinIOHelper._internal() {
|
||||||
|
_minio = Minio(
|
||||||
|
endPoint: rustfsIp,
|
||||||
|
port: 9100,
|
||||||
|
accessKey: "tHSFfcDW8qpCzKa2Xg6Y",
|
||||||
|
secretKey: "oq79EeYJ4jdczRp2IHUMCnbKtSw58NgDlG3sOkvX",
|
||||||
|
useSSL: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late Minio _minio;
|
||||||
|
|
||||||
|
Future<String> uploadFile({
|
||||||
|
required PlatformFile file,
|
||||||
|
Function(double)? onProgress,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
String hashName = await _generateMD5HashName(file.path!);
|
||||||
|
String fileName = '$hashName${_getFileExtension(file.name)}';
|
||||||
|
|
||||||
|
await _minio.fPutObject(bucketName, fileName, file.path!);
|
||||||
|
return fileName;
|
||||||
|
} catch (e) {
|
||||||
|
throw Exception('文件上传失败: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getFileExtension(String fileName) {
|
||||||
|
if (fileName.contains('.')) {
|
||||||
|
return '.${fileName.split('.').last.toLowerCase()}';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> _generateMD5HashName(String filePath) async {
|
||||||
|
final file = File(filePath);
|
||||||
|
final bytes = await file.readAsBytes();
|
||||||
|
final hash = md5.convert(bytes);
|
||||||
|
return hash.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -71,7 +71,7 @@ class _RecipeDetailState extends State<RecipeDetailPage> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: Text('菜谱信息')),
|
appBar: AppBar(title: Text('菜谱信息')),
|
||||||
backgroundColor: Color(0xFFF4F4F4),
|
backgroundColor: Color(0xFFF5F5F5),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Padding(padding: EdgeInsets.all(5), child: _buildRecipeDetail()),
|
child: Padding(padding: EdgeInsets.all(5), child: _buildRecipeDetail()),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||||
|
import 'package:food_hub_app/provider/food_provider.dart';
|
||||||
|
import 'package:food_hub_app/utils/minio_utils.dart';
|
||||||
import 'package:food_hub_app/widgets/common/form.dart';
|
import 'package:food_hub_app/widgets/common/form.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:food_hub_app/widgets/common/image.dart';
|
||||||
|
import 'package:food_hub_app/widgets/common/index.dart';
|
||||||
|
import 'package:food_hub_app/models/recipe.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class RecordFormPage extends StatefulWidget {
|
class RecordFormPage extends StatefulWidget {
|
||||||
const RecordFormPage({super.key});
|
const RecordFormPage({super.key});
|
||||||
|
|
||||||
@@ -17,9 +24,7 @@ class _RecordFormPageState extends State<RecordFormPage> {
|
|||||||
final _focusNode = FocusNode();
|
final _focusNode = FocusNode();
|
||||||
static const String _nameField = 'name';
|
static const String _nameField = 'name';
|
||||||
static const String _dateField = 'date';
|
static const String _dateField = 'date';
|
||||||
|
File? imageFile;
|
||||||
final ImagePicker _picker = ImagePicker();
|
|
||||||
File? imageUrl; // 改为单张图片变量
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
@@ -29,12 +34,12 @@ class _RecordFormPageState extends State<RecordFormPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('新增记录', style: TextStyle(color: Colors.white)),
|
title: const Text('新增记录', style: TextStyle(color: Colors.white)),
|
||||||
backgroundColor: theme.primaryColor,
|
backgroundColor: colors.primary,
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: const Icon(Icons.arrow_back, color: Colors.white),
|
icon: const Icon(Icons.arrow_back, color: Colors.white),
|
||||||
onPressed: () => Navigator.pop(context),
|
onPressed: () => Navigator.pop(context),
|
||||||
@@ -43,24 +48,16 @@ class _RecordFormPageState extends State<RecordFormPage> {
|
|||||||
backgroundColor: const Color(0xFFF5F5F5),
|
backgroundColor: const Color(0xFFF5F5F5),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(5),
|
padding: EdgeInsets.all(5),
|
||||||
child: Card(
|
child: buildCard(context: context, child: _buildFormBuilder()),
|
||||||
elevation: 0,
|
|
||||||
color: Colors.white,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(10),
|
|
||||||
child: _buildFormBuilder(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildFormBuilder() {
|
Widget _buildFormBuilder() {
|
||||||
|
final foodProvider = context.watch<FoodProvider>();
|
||||||
|
|
||||||
return FormBuilder(
|
return FormBuilder(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
@@ -82,7 +79,10 @@ class _RecordFormPageState extends State<RecordFormPage> {
|
|||||||
_buildImageUploadArea(),
|
_buildImageUploadArea(),
|
||||||
|
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
buildFormButtonGroup(context: context, onConfirm: () => _submitForm),
|
buildFormButtonGroup(
|
||||||
|
context: context,
|
||||||
|
onConfirm: () => _submitForm(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -93,7 +93,11 @@ class _RecordFormPageState extends State<RecordFormPage> {
|
|||||||
return FormBuilderTextField(
|
return FormBuilderTextField(
|
||||||
name: _nameField,
|
name: _nameField,
|
||||||
focusNode: _focusNode,
|
focusNode: _focusNode,
|
||||||
decoration: buildInputDecoration(context: context, hintText: '请输入菜谱名称'),
|
decoration: buildInputDecoration(
|
||||||
|
context: context,
|
||||||
|
hintText: '请输入菜谱名称',
|
||||||
|
prefixIcon: const Icon(Icons.title, size: 20, color: Color(0xFF86909C)),
|
||||||
|
),
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
return '请输入菜谱名称';
|
return '请输入菜谱名称';
|
||||||
@@ -132,10 +136,10 @@ class _RecordFormPageState extends State<RecordFormPage> {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (imageUrl != null)
|
if (imageFile != null)
|
||||||
_buildImagePreviewItem()
|
_buildImagePreviewItem()
|
||||||
else
|
else
|
||||||
_buildImageUploadButton(),
|
buildImageUploadButton(onPickImage: _pickImage),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -147,109 +151,8 @@ class _RecordFormPageState extends State<RecordFormPage> {
|
|||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
GestureDetector(
|
buildFileImage(context, imageFile!),
|
||||||
onTap: () => _showImagePreview(),
|
buildDeleteImage(onRemoveImage: _removeImage),
|
||||||
child: Image(
|
|
||||||
image: FileImage(imageUrl!),
|
|
||||||
width: 120,
|
|
||||||
height: 120,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
loadingBuilder: (context, child, loadingProgress) {
|
|
||||||
if (loadingProgress == null) return child;
|
|
||||||
return Container(
|
|
||||||
width: 120,
|
|
||||||
height: 120,
|
|
||||||
color: Colors.grey[100],
|
|
||||||
child: const Center(
|
|
||||||
child: CircularProgressIndicator(strokeWidth: 2),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Positioned(
|
|
||||||
top: 6,
|
|
||||||
right: 6,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: _removeImage,
|
|
||||||
child: Container(
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: Colors.red,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black26,
|
|
||||||
blurRadius: 2,
|
|
||||||
spreadRadius: 0,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: const Icon(Icons.close, color: Colors.white, size: 16),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showImagePreview() {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
barrierColor: Colors.black87, // 半透明黑色背景
|
|
||||||
builder:
|
|
||||||
(context) => Dialog(
|
|
||||||
backgroundColor: Colors.transparent,
|
|
||||||
elevation: 0,
|
|
||||||
insetPadding: const EdgeInsets.all(16),
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () => Navigator.pop(context), // 点击空白处关闭
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
// 预览图添加轻微阴影
|
|
||||||
boxShadow: [BoxShadow(color: Colors.black38, blurRadius: 10)],
|
|
||||||
),
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
child: Image.file(
|
|
||||||
imageUrl!,
|
|
||||||
fit: BoxFit.contain, // 保持图片比例
|
|
||||||
height: MediaQuery.of(context).size.height * 0.7, // 限制最大高度
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 图片上传按钮
|
|
||||||
Widget _buildImageUploadButton() {
|
|
||||||
return InkWell(
|
|
||||||
onTap: _pickImage,
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
child: Container(
|
|
||||||
width: 96,
|
|
||||||
height: 96,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFFF2F3F5),
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
border: Border.all(color: const Color(0xFFDCDFE6), width: 1),
|
|
||||||
),
|
|
||||||
child: const Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Icon(Icons.add, color: Color(0xFF86909C), size: 24),
|
|
||||||
SizedBox(height: 6),
|
|
||||||
Text(
|
|
||||||
'添加图片',
|
|
||||||
style: TextStyle(color: Color(0xFF86909C), fontSize: 13),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -263,19 +166,6 @@ class _RecordFormPageState extends State<RecordFormPage> {
|
|||||||
initialDate: DateTime.now(),
|
initialDate: DateTime.now(),
|
||||||
firstDate: DateTime(2000),
|
firstDate: DateTime(2000),
|
||||||
lastDate: DateTime(2100),
|
lastDate: DateTime(2100),
|
||||||
builder:
|
|
||||||
(context, child) => Theme(
|
|
||||||
data: ThemeData.light().copyWith(
|
|
||||||
primaryColor: Theme.of(context).primaryColor,
|
|
||||||
colorScheme: ColorScheme.light(
|
|
||||||
primary: Theme.of(context).primaryColor,
|
|
||||||
),
|
|
||||||
buttonTheme: const ButtonThemeData(
|
|
||||||
textTheme: ButtonTextTheme.primary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: child!,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (picked != null) {
|
if (picked != null) {
|
||||||
@@ -287,10 +177,17 @@ class _RecordFormPageState extends State<RecordFormPage> {
|
|||||||
|
|
||||||
/// 选择图片(限制单张)
|
/// 选择图片(限制单张)
|
||||||
Future<void> _pickImage() async {
|
Future<void> _pickImage() async {
|
||||||
final XFile? image = await _picker.pickImage(source: ImageSource.gallery);
|
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
||||||
if (image != null) {
|
type: FileType.custom,
|
||||||
|
allowedExtensions: ['jpg', 'jpeg', 'png'],
|
||||||
|
allowMultiple: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != null) {
|
||||||
|
PlatformFile file = result.files.first;
|
||||||
|
final fileName = await MinIOHelper().uploadFile(file: file);
|
||||||
setState(() {
|
setState(() {
|
||||||
imageUrl = File(image.path); // 直接覆盖现有图片
|
// imageFile = File(image.path);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -298,14 +195,14 @@ class _RecordFormPageState extends State<RecordFormPage> {
|
|||||||
/// 移除图片
|
/// 移除图片
|
||||||
void _removeImage() {
|
void _removeImage() {
|
||||||
setState(() {
|
setState(() {
|
||||||
imageUrl = null;
|
imageFile = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 提交表单
|
/// 提交表单
|
||||||
void _submitForm() {
|
void _submitForm() {
|
||||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||||
if (imageUrl == null) {
|
if (imageFile == null) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
const SnackBar(
|
||||||
content: Text('请上传图片'),
|
content: Text('请上传图片'),
|
||||||
@@ -318,7 +215,7 @@ class _RecordFormPageState extends State<RecordFormPage> {
|
|||||||
|
|
||||||
final formData = {
|
final formData = {
|
||||||
..._formKey.currentState!.value,
|
..._formKey.currentState!.value,
|
||||||
'imageUrl': imageUrl?.path, // 单张图片路径
|
'imageUrl': imageFile?.path,
|
||||||
};
|
};
|
||||||
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ Widget _buildCancelButton(BuildContext context) {
|
|||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
foregroundColor: const Color(0xFF4E5969),
|
foregroundColor: const Color(0xFF4E5969),
|
||||||
side: const BorderSide(color: Color(0xFFDCDFE6)),
|
side: const BorderSide(color: Color(0xFFDCDFE6)),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 15),
|
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
),
|
),
|
||||||
@@ -90,10 +89,9 @@ Widget _buildCancelButton(BuildContext context) {
|
|||||||
/// 提交按钮
|
/// 提交按钮
|
||||||
Widget _buildSubmitButton(BuildContext context, VoidCallback onConfirm) {
|
Widget _buildSubmitButton(BuildContext context, VoidCallback onConfirm) {
|
||||||
return ElevatedButton(
|
return ElevatedButton(
|
||||||
onPressed: onConfirm,
|
onPressed: () => onConfirm(),
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: Theme.of(context).primaryColor, // 使用传入的context获取主题
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 15),
|
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:photo_view/photo_view.dart';
|
import 'package:photo_view/photo_view.dart';
|
||||||
import 'package:photo_view/photo_view_gallery.dart';
|
import 'package:photo_view/photo_view_gallery.dart';
|
||||||
@@ -88,19 +90,35 @@ class _ImagePreviewPageState extends State<ImagePreviewPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget buildFileImage(BuildContext context, File imageFile) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () => showFullScreenImage(context, FileImage(imageFile)),
|
||||||
|
child: Image(
|
||||||
|
image: FileImage(imageFile),
|
||||||
|
width: 120,
|
||||||
|
height: 120,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
loadingBuilder: (context, child, loadingProgress) {
|
||||||
|
if (loadingProgress == null) return child;
|
||||||
|
return buildImageLoadingIndicator(loadingProgress);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget buildNetworkImage(BuildContext context, String url) {
|
Widget buildNetworkImage(BuildContext context, String url) {
|
||||||
return ClipRRect(
|
return ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_showFullScreenImage(context, url);
|
showFullScreenImage(context, NetworkImage(url));
|
||||||
},
|
},
|
||||||
child: Image.network(
|
child: Image.network(
|
||||||
url,
|
url,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
loadingBuilder: (context, child, loadingProgress) {
|
loadingBuilder: (context, child, loadingProgress) {
|
||||||
if (loadingProgress == null) return child;
|
if (loadingProgress == null) return child;
|
||||||
return _buildLoadingIndicator(loadingProgress);
|
return buildImageLoadingIndicator(loadingProgress);
|
||||||
},
|
},
|
||||||
errorBuilder: (context, error, stackTrace) => _buildErrorImage(),
|
errorBuilder: (context, error, stackTrace) => _buildErrorImage(),
|
||||||
),
|
),
|
||||||
@@ -108,7 +126,7 @@ Widget buildNetworkImage(BuildContext context, String url) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLoadingIndicator(ImageChunkEvent? loadingProgress) {
|
Widget buildImageLoadingIndicator(ImageChunkEvent? loadingProgress) {
|
||||||
return Center(
|
return Center(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 30,
|
width: 30,
|
||||||
@@ -131,15 +149,14 @@ Widget _buildErrorImage() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildPhotoView(String url) {
|
Widget _buildPhotoView(ImageProvider imageProvider) {
|
||||||
return PhotoView(
|
return PhotoView(
|
||||||
imageProvider: NetworkImage(url),
|
imageProvider: imageProvider,
|
||||||
backgroundDecoration: const BoxDecoration(color: Colors.transparent),
|
backgroundDecoration: const BoxDecoration(color: Colors.transparent),
|
||||||
minScale: PhotoViewComputedScale.contained,
|
minScale: PhotoViewComputedScale.contained,
|
||||||
maxScale: PhotoViewComputedScale.covered * 2,
|
maxScale: PhotoViewComputedScale.covered * 2,
|
||||||
initialScale: PhotoViewComputedScale.contained,
|
initialScale: PhotoViewComputedScale.contained,
|
||||||
heroAttributes: PhotoViewHeroAttributes(tag: url),
|
loadingBuilder: (context, event) => buildImageLoadingIndicator(event),
|
||||||
loadingBuilder: (context, event) => _buildLoadingIndicator(event),
|
|
||||||
errorBuilder: (context, error, stackTrace) => _buildErrorImage(),
|
errorBuilder: (context, error, stackTrace) => _buildErrorImage(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -155,7 +172,7 @@ Widget _buildCloseImage(BuildContext context) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showFullScreenImage(BuildContext context, String url) {
|
void showFullScreenImage(BuildContext context, ImageProvider imageProvider) {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
PageRouteBuilder(
|
PageRouteBuilder(
|
||||||
opaque: false,
|
opaque: false,
|
||||||
@@ -169,7 +186,7 @@ void _showFullScreenImage(BuildContext context, String url) {
|
|||||||
body: Stack(
|
body: Stack(
|
||||||
children: [
|
children: [
|
||||||
// 可缩放图片
|
// 可缩放图片
|
||||||
Positioned.fill(child: _buildPhotoView(url)),
|
Positioned.fill(child: _buildPhotoView(imageProvider)),
|
||||||
// 关闭按钮
|
// 关闭按钮
|
||||||
_buildCloseImage(context),
|
_buildCloseImage(context),
|
||||||
],
|
],
|
||||||
@@ -179,3 +196,46 @@ void _showFullScreenImage(BuildContext context, String url) {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget buildImageUploadButton({required VoidCallback onPickImage}) {
|
||||||
|
return InkWell(
|
||||||
|
onTap: onPickImage,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
child: Container(
|
||||||
|
width: 96,
|
||||||
|
height: 96,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(color: Colors.black),
|
||||||
|
),
|
||||||
|
child: const Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.add, color: Color(0xFF86909C)),
|
||||||
|
SizedBox(height: 6),
|
||||||
|
Text('添加图片', style: TextStyle(color: Color(0xFF86909C))),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget buildDeleteImage({required VoidCallback onRemoveImage}) {
|
||||||
|
return Positioned(
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: onRemoveImage,
|
||||||
|
child: Container(
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: Colors.red,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: const Icon(Icons.close, color: Colors.white, size: 16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ class _RecipeCalendarState extends State<RecipeCalendar> {
|
|||||||
DateTime _selectedDay = DateTime.now();
|
DateTime _selectedDay = DateTime.now();
|
||||||
DateTime _focusedDay = DateTime.now();
|
DateTime _focusedDay = DateTime.now();
|
||||||
|
|
||||||
List<Record> recordList = [];
|
List<FoodRecord> recordList = [];
|
||||||
List<Record> selectRecordList = [];
|
List<FoodRecord> selectRecordList = [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -109,7 +109,7 @@ class _RecipeCalendarState extends State<RecipeCalendar> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget recipeRecordItem(BuildContext context, Record record) {
|
Widget recipeRecordItem(BuildContext context, FoodRecord record) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
return Card(
|
return Card(
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class RecipeTimeline extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _RecipeTimeline extends State<RecipeTimeline> {
|
class _RecipeTimeline extends State<RecipeTimeline> {
|
||||||
List<Record> recordList = [];
|
List<FoodRecord> recordList = [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -30,7 +30,7 @@ class _RecipeTimeline extends State<RecipeTimeline> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildTimelineCard(BuildContext context, Record record) {
|
Widget buildTimelineCard(BuildContext context, FoodRecord record) {
|
||||||
final imageUrls = ['${AppConfig.baseApiUrl}/${record.imageUrl}'];
|
final imageUrls = ['${AppConfig.baseApiUrl}/${record.imageUrl}'];
|
||||||
|
|
||||||
void imageTapClick() {
|
void imageTapClick() {
|
||||||
@@ -79,7 +79,7 @@ class _RecipeTimeline extends State<RecipeTimeline> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildTimeline(BuildContext context, List<Record> recordList) {
|
Widget buildTimeline(BuildContext context, List<FoodRecord> recordList) {
|
||||||
if (recordList.isEmpty) {
|
if (recordList.isEmpty) {
|
||||||
return buildEmptyData();
|
return buildEmptyData();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -5,10 +5,12 @@
|
|||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
import file_picker
|
||||||
import file_selector_macos
|
import file_selector_macos
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
|
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
|
||||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
78
pubspec.lock
78
pubspec.lock
@@ -41,6 +41,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.2"
|
||||||
|
buffer:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: buffer
|
||||||
|
sha256: "389da2ec2c16283c8787e0adaede82b1842102f8c8aae2f49003a766c5c6b3d1"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.3"
|
||||||
build:
|
build:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -162,13 +170,13 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.4+2"
|
version: "0.3.4+2"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: crypto
|
name: crypto
|
||||||
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
|
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.6"
|
version: "3.0.7"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -185,6 +193,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.0"
|
||||||
|
dbus:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: dbus
|
||||||
|
sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.11"
|
||||||
dio:
|
dio:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -233,6 +249,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.1"
|
version: "7.0.1"
|
||||||
|
file_picker:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: file_picker
|
||||||
|
sha256: f8f4ea435f791ab1f817b4e338ed958cb3d04ba43d6736ffc39958d950754967
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "10.3.6"
|
||||||
file_selector_linux:
|
file_selector_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -589,6 +613,22 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.0.0"
|
||||||
|
minio:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: minio
|
||||||
|
sha256: ee2ce47766e46c7d164f960f2f5ed6a9a82844d877f6b82574f6876ec50c56d1
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "3.5.8"
|
||||||
|
nested:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: nested
|
||||||
|
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -645,6 +685,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.3.0"
|
||||||
|
petitparser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: petitparser
|
||||||
|
sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "6.1.0"
|
||||||
photo_view:
|
photo_view:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -677,6 +725,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.1"
|
version: "1.5.1"
|
||||||
|
provider:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: provider
|
||||||
|
sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "6.1.5+1"
|
||||||
pub_semver:
|
pub_semver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -970,6 +1026,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.3"
|
version: "3.0.3"
|
||||||
|
win32:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: win32
|
||||||
|
sha256: "329edf97fdd893e0f1e3b9e88d6a0e627128cc17cc316a8d67fda8f1451178ba"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "5.13.0"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -978,6 +1042,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
|
xml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xml
|
||||||
|
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "6.5.0"
|
||||||
yaml:
|
yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ dependencies:
|
|||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.8
|
cupertino_icons: ^1.0.8
|
||||||
dio: ^5.7.0
|
dio: ^5.7.0
|
||||||
|
provider: ^6.1.1
|
||||||
timelines_plus: ^1.0.7
|
timelines_plus: ^1.0.7
|
||||||
table_calendar: ^3.1.3
|
table_calendar: ^3.1.3
|
||||||
toggle_switch: ^2.3.0
|
toggle_switch: ^2.3.0
|
||||||
@@ -51,6 +52,9 @@ dependencies:
|
|||||||
flutter_carousel_widget: ^3.1.0
|
flutter_carousel_widget: ^3.1.0
|
||||||
easy_refresh: ^3.4.0
|
easy_refresh: ^3.4.0
|
||||||
syncfusion_flutter_charts: ^30.1.41
|
syncfusion_flutter_charts: ^30.1.41
|
||||||
|
minio: ^3.5.8
|
||||||
|
crypto: ^3.0.7
|
||||||
|
file_picker: ^10.3.3
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
tdesign_flutter_adaptation: 3.16.0
|
tdesign_flutter_adaptation: 3.16.0
|
||||||
|
|||||||
Reference in New Issue
Block a user