diff --git a/lib/apis/recipe.dart b/lib/apis/recipe.dart index 184547a..1d70135 100644 --- a/lib/apis/recipe.dart +++ b/lib/apis/recipe.dart @@ -51,11 +51,11 @@ Future> queryFoodNameListApi() { ); } -Future addRecordApi(Record record) { +Future addRecordApi(FoodRecord record) { return HttpUtil().post("/food/record", data: record); } -Future updateRecordApi(int id, Record record) { +Future updateRecordApi(int id, FoodRecord record) { return HttpUtil().put("/food/record/$id", data: record); } @@ -83,11 +83,11 @@ Future deleteRecipeFavouriteApi(int id) { return HttpUtil().delete("/food/recipe/$id/like"); } -Future> queryRecordApi(String startDate, String endDate) { - return HttpUtil().get>( +Future> queryRecordApi(String startDate, String endDate) { + return HttpUtil().get>( "/food/record", queryParameters: {"startDate": startDate, "endDate": endDate}, - converter: (data) => convertListResponse(data, Record.fromJson), + converter: (data) => convertListResponse(data, FoodRecord.fromJson), ); } diff --git a/lib/main.dart b/lib/main.dart index a50733b..423fa2b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,18 +1,26 @@ import 'package:flutter/material.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/views/home.dart'; import 'package:food_hub_app/views/login.dart'; import 'package:food_hub_app/views/record_form.dart'; import 'package:food_hub_app/views/recipe_detail.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 { WidgetsFlutterBinding.ensureInitialized(); FormBuilderLocalizations.delegate.load(const Locale('zh', 'CN')); await SPUtil.init(); - runApp(const MyApp()); + runApp( + MultiProvider( + providers: [ + ChangeNotifierProvider(create: (context) => FoodProvider()), + ], + child: MyApp(), + ), + ); } class MyApp extends StatelessWidget { diff --git a/lib/models/recipe.dart b/lib/models/recipe.dart index 704f795..6ead219 100644 --- a/lib/models/recipe.dart +++ b/lib/models/recipe.dart @@ -9,7 +9,11 @@ class RecipeMaterial { String name; 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 json) => _$RecipeMaterialFromJson(json); @@ -24,9 +28,14 @@ class RecipeStep { String content; 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 json) => _$RecipeStepFromJson(json); + factory RecipeStep.fromJson(Map json) => + _$RecipeStepFromJson(json); Map toJson() => _$RecipeStepToJson(this); } @@ -56,7 +65,7 @@ class RecipeComment { /// 成果信息 @JsonSerializable() -class Record { +class FoodRecord { int? id; String name; String category; @@ -64,7 +73,7 @@ class Record { String date; String imageUrl; - Record({ + FoodRecord({ this.id, required this.name, required this.category, @@ -73,20 +82,31 @@ class Record { required this.imageUrl, }); - factory Record.fromJson(Map json) => _$RecordFromJson(json); + factory FoodRecord.fromJson(Map json) => + _$RecordFromJson(json); Map toJson() => _$RecordToJson(this); + + static FoodRecord getEmpty() { + return FoodRecord( + id: 0, + name: '', + category: '', + person: 0, + date: '', + imageUrl: '', + ); + } } @JsonSerializable() class RecipeQuery { String category; - RecipeQuery({ - required this.category - }); + RecipeQuery({required this.category}); - factory RecipeQuery.fromJson(Map json) => _$RecipeQueryFromJson(json); + factory RecipeQuery.fromJson(Map json) => + _$RecipeQueryFromJson(json); Map toJson() => _$RecipeQueryToJson(this); } @@ -105,7 +125,7 @@ class Recipe { String avatar; List materialList; List stepList; - List recordList; + List recordList; List likeList; int likeCount; List favouriteList; @@ -146,7 +166,7 @@ class RecipeSummary { String category; double recommendRate; bool isShare; - List recordList; + List recordList; int likeCount; int favouriteCount; int commentCount; @@ -169,7 +189,8 @@ class RecipeSummary { required this.commentCount, }); - factory RecipeSummary.fromJson(Map json) => _$RecipeSummaryFromJson(json); + factory RecipeSummary.fromJson(Map json) => + _$RecipeSummaryFromJson(json); Map toJson() => _$RecipeSummaryToJson(this); } @@ -187,7 +208,7 @@ class RecipeDetail { String avatar; List materialList; List stepList; - List recordList; + List recordList; List likeList; List favouriteList; List commentList; @@ -210,7 +231,8 @@ class RecipeDetail { required this.commentList, }); - factory RecipeDetail.fromJson(Map json) => _$RecipeDetailFromJson(json); + factory RecipeDetail.fromJson(Map json) => + _$RecipeDetailFromJson(json); Map toJson() => _$RecipeDetailToJson(this); } diff --git a/lib/models/recipe.g.dart b/lib/models/recipe.g.dart index 3891b61..8861150 100644 --- a/lib/models/recipe.g.dart +++ b/lib/models/recipe.g.dart @@ -51,7 +51,7 @@ Map _$RecipeCommentToJson(RecipeComment instance) => 'date': instance.date, }; -Record _$RecordFromJson(Map json) => Record( +FoodRecord _$RecordFromJson(Map json) => FoodRecord( id: (json['id'] as num?)?.toInt(), name: json['name'] as String, category: json['category'] as String, @@ -60,7 +60,7 @@ Record _$RecordFromJson(Map json) => Record( imageUrl: json['imageUrl'] as String, ); -Map _$RecordToJson(Record instance) => { +Map _$RecordToJson(FoodRecord instance) => { 'id': instance.id, 'name': instance.name, 'category': instance.category, @@ -95,7 +95,7 @@ Recipe _$RecipeFromJson(Map json) => Recipe( .toList(), recordList: (json['recordList'] as List) - .map((e) => Record.fromJson(e as Map)) + .map((e) => FoodRecord.fromJson(e as Map)) .toList(), likeList: (json['likeList'] as List) @@ -147,7 +147,7 @@ RecipeSummary _$RecipeSummaryFromJson(Map json) => avatar: json['avatar'] as String, recordList: (json['recordList'] as List) - .map((e) => Record.fromJson(e as Map)) + .map((e) => FoodRecord.fromJson(e as Map)) .toList(), likeCount: (json['likeCount'] as num).toInt(), favouriteCount: (json['favouriteCount'] as num).toInt(), @@ -190,7 +190,7 @@ RecipeDetail _$RecipeDetailFromJson(Map json) => RecipeDetail( .toList(), recordList: (json['recordList'] as List) - .map((e) => Record.fromJson(e as Map)) + .map((e) => FoodRecord.fromJson(e as Map)) .toList(), likeList: (json['likeList'] as List) diff --git a/lib/provider/food_provider.dart b/lib/provider/food_provider.dart new file mode 100644 index 0000000..6b960f1 --- /dev/null +++ b/lib/provider/food_provider.dart @@ -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(); + } +} diff --git a/lib/utils/minio_utils.dart b/lib/utils/minio_utils.dart new file mode 100644 index 0000000..0f50c97 --- /dev/null +++ b/lib/utils/minio_utils.dart @@ -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 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 _generateMD5HashName(String filePath) async { + final file = File(filePath); + final bytes = await file.readAsBytes(); + final hash = md5.convert(bytes); + return hash.toString(); + } +} diff --git a/lib/views/recipe_detail.dart b/lib/views/recipe_detail.dart index 0461de4..bb28c53 100644 --- a/lib/views/recipe_detail.dart +++ b/lib/views/recipe_detail.dart @@ -71,7 +71,7 @@ class _RecipeDetailState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('菜谱信息')), - backgroundColor: Color(0xFFF4F4F4), + backgroundColor: Color(0xFFF5F5F5), body: SingleChildScrollView( child: Padding(padding: EdgeInsets.all(5), child: _buildRecipeDetail()), ), diff --git a/lib/views/record_form.dart b/lib/views/record_form.dart index 881a03c..4cd0359 100644 --- a/lib/views/record_form.dart +++ b/lib/views/record_form.dart @@ -1,10 +1,17 @@ +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.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: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 'dart:io'; +import 'package:provider/provider.dart'; + class RecordFormPage extends StatefulWidget { const RecordFormPage({super.key}); @@ -17,9 +24,7 @@ class _RecordFormPageState extends State { final _focusNode = FocusNode(); static const String _nameField = 'name'; static const String _dateField = 'date'; - - final ImagePicker _picker = ImagePicker(); - File? imageUrl; // 改为单张图片变量 + File? imageFile; @override void dispose() { @@ -29,12 +34,12 @@ class _RecordFormPageState extends State { @override Widget build(BuildContext context) { - final theme = Theme.of(context); + final colors = Theme.of(context).colorScheme; return Scaffold( appBar: AppBar( title: const Text('新增记录', style: TextStyle(color: Colors.white)), - backgroundColor: theme.primaryColor, + backgroundColor: colors.primary, leading: IconButton( icon: const Icon(Icons.arrow_back, color: Colors.white), onPressed: () => Navigator.pop(context), @@ -43,24 +48,16 @@ class _RecordFormPageState extends State { backgroundColor: const Color(0xFFF5F5F5), body: SingleChildScrollView( child: Padding( - padding: const EdgeInsets.all(5), - child: Card( - elevation: 0, - color: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), - child: Padding( - padding: const EdgeInsets.all(10), - child: _buildFormBuilder(), - ), - ), + padding: EdgeInsets.all(5), + child: buildCard(context: context, child: _buildFormBuilder()), ), ), ); } Widget _buildFormBuilder() { + final foodProvider = context.watch(); + return FormBuilder( key: _formKey, autovalidateMode: AutovalidateMode.onUserInteraction, @@ -82,7 +79,10 @@ class _RecordFormPageState extends State { _buildImageUploadArea(), const SizedBox(height: 10), - buildFormButtonGroup(context: context, onConfirm: () => _submitForm), + buildFormButtonGroup( + context: context, + onConfirm: () => _submitForm(), + ), ], ), ); @@ -93,7 +93,11 @@ class _RecordFormPageState extends State { return FormBuilderTextField( name: _nameField, focusNode: _focusNode, - decoration: buildInputDecoration(context: context, hintText: '请输入菜谱名称'), + decoration: buildInputDecoration( + context: context, + hintText: '请输入菜谱名称', + prefixIcon: const Icon(Icons.title, size: 20, color: Color(0xFF86909C)), + ), validator: (value) { if (value == null || value.isEmpty) { return '请输入菜谱名称'; @@ -132,10 +136,10 @@ class _RecordFormPageState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (imageUrl != null) + if (imageFile != null) _buildImagePreviewItem() else - _buildImageUploadButton(), + buildImageUploadButton(onPickImage: _pickImage), ], ); } @@ -147,109 +151,8 @@ class _RecordFormPageState extends State { borderRadius: BorderRadius.circular(8), child: Stack( children: [ - GestureDetector( - onTap: () => _showImagePreview(), - 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), - ), + buildFileImage(context, imageFile!), + buildDeleteImage(onRemoveImage: _removeImage), ], ), ), @@ -263,19 +166,6 @@ class _RecordFormPageState extends State { initialDate: DateTime.now(), firstDate: DateTime(2000), 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) { @@ -287,10 +177,17 @@ class _RecordFormPageState extends State { /// 选择图片(限制单张) Future _pickImage() async { - final XFile? image = await _picker.pickImage(source: ImageSource.gallery); - if (image != null) { + FilePickerResult? result = await FilePicker.platform.pickFiles( + 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(() { - imageUrl = File(image.path); // 直接覆盖现有图片 + // imageFile = File(image.path); }); } } @@ -298,14 +195,14 @@ class _RecordFormPageState extends State { /// 移除图片 void _removeImage() { setState(() { - imageUrl = null; + imageFile = null; }); } /// 提交表单 void _submitForm() { if (_formKey.currentState?.saveAndValidate() ?? false) { - if (imageUrl == null) { + if (imageFile == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('请上传图片'), @@ -318,7 +215,7 @@ class _RecordFormPageState extends State { final formData = { ..._formKey.currentState!.value, - 'imageUrl': imageUrl?.path, // 单张图片路径 + 'imageUrl': imageFile?.path, }; ScaffoldMessenger.of(context).showSnackBar( diff --git a/lib/widgets/common/form.dart b/lib/widgets/common/form.dart index 8220ebd..dd3a4cd 100644 --- a/lib/widgets/common/form.dart +++ b/lib/widgets/common/form.dart @@ -79,7 +79,6 @@ Widget _buildCancelButton(BuildContext context) { backgroundColor: Colors.white, foregroundColor: const Color(0xFF4E5969), side: const BorderSide(color: Color(0xFFDCDFE6)), - padding: const EdgeInsets.symmetric(vertical: 15), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), elevation: 0, ), @@ -90,10 +89,9 @@ Widget _buildCancelButton(BuildContext context) { /// 提交按钮 Widget _buildSubmitButton(BuildContext context, VoidCallback onConfirm) { return ElevatedButton( - onPressed: onConfirm, + onPressed: () => onConfirm(), style: ElevatedButton.styleFrom( - backgroundColor: Theme.of(context).primaryColor, // 使用传入的context获取主题 - padding: const EdgeInsets.symmetric(vertical: 15), + backgroundColor: Theme.of(context).colorScheme.primary, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), elevation: 0, ), diff --git a/lib/widgets/common/image.dart b/lib/widgets/common/image.dart index 8fe0794..0b21140 100644 --- a/lib/widgets/common/image.dart +++ b/lib/widgets/common/image.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:photo_view/photo_view.dart'; import 'package:photo_view/photo_view_gallery.dart'; @@ -88,19 +90,35 @@ class _ImagePreviewPageState extends State { } } +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) { return ClipRRect( borderRadius: BorderRadius.circular(8), child: GestureDetector( onTap: () { - _showFullScreenImage(context, url); + showFullScreenImage(context, NetworkImage(url)); }, child: Image.network( url, fit: BoxFit.cover, loadingBuilder: (context, child, loadingProgress) { if (loadingProgress == null) return child; - return _buildLoadingIndicator(loadingProgress); + return buildImageLoadingIndicator(loadingProgress); }, 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( child: SizedBox( width: 30, @@ -131,15 +149,14 @@ Widget _buildErrorImage() { ); } -Widget _buildPhotoView(String url) { +Widget _buildPhotoView(ImageProvider imageProvider) { return PhotoView( - imageProvider: NetworkImage(url), + imageProvider: imageProvider, backgroundDecoration: const BoxDecoration(color: Colors.transparent), minScale: PhotoViewComputedScale.contained, maxScale: PhotoViewComputedScale.covered * 2, initialScale: PhotoViewComputedScale.contained, - heroAttributes: PhotoViewHeroAttributes(tag: url), - loadingBuilder: (context, event) => _buildLoadingIndicator(event), + loadingBuilder: (context, event) => buildImageLoadingIndicator(event), 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( PageRouteBuilder( opaque: false, @@ -169,7 +186,7 @@ void _showFullScreenImage(BuildContext context, String url) { body: Stack( children: [ // 可缩放图片 - Positioned.fill(child: _buildPhotoView(url)), + Positioned.fill(child: _buildPhotoView(imageProvider)), // 关闭按钮 _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), + ), + ), + ); +} diff --git a/lib/widgets/recipe/calendar.dart b/lib/widgets/recipe/calendar.dart index 7323304..0b7729c 100644 --- a/lib/widgets/recipe/calendar.dart +++ b/lib/widgets/recipe/calendar.dart @@ -17,8 +17,8 @@ class _RecipeCalendarState extends State { DateTime _selectedDay = DateTime.now(); DateTime _focusedDay = DateTime.now(); - List recordList = []; - List selectRecordList = []; + List recordList = []; + List selectRecordList = []; @override void initState() { @@ -109,7 +109,7 @@ class _RecipeCalendarState extends State { ); } - Widget recipeRecordItem(BuildContext context, Record record) { + Widget recipeRecordItem(BuildContext context, FoodRecord record) { final colors = Theme.of(context).colorScheme; return Card( diff --git a/lib/widgets/recipe/timeline.dart b/lib/widgets/recipe/timeline.dart index 16e6d2f..3ecaf15 100644 --- a/lib/widgets/recipe/timeline.dart +++ b/lib/widgets/recipe/timeline.dart @@ -15,7 +15,7 @@ class RecipeTimeline extends StatefulWidget { } class _RecipeTimeline extends State { - List recordList = []; + List recordList = []; @override void initState() { @@ -30,7 +30,7 @@ class _RecipeTimeline extends State { }); } - Widget buildTimelineCard(BuildContext context, Record record) { + Widget buildTimelineCard(BuildContext context, FoodRecord record) { final imageUrls = ['${AppConfig.baseApiUrl}/${record.imageUrl}']; void imageTapClick() { @@ -79,7 +79,7 @@ class _RecipeTimeline extends State { ); } - Widget buildTimeline(BuildContext context, List recordList) { + Widget buildTimeline(BuildContext context, List recordList) { if (recordList.isEmpty) { return buildEmptyData(); } else { diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index ab1fdba..c0be2ee 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,10 +5,12 @@ import FlutterMacOS import Foundation +import file_picker import file_selector_macos import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 2b6017f..b15fcb4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -41,6 +41,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted 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: dependency: transitive description: @@ -162,13 +170,13 @@ packages: source: hosted version: "0.3.4+2" crypto: - dependency: transitive + dependency: "direct main" description: name: crypto - sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.6" + version: "3.0.7" cupertino_icons: dependency: "direct main" description: @@ -185,6 +193,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted 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: dependency: "direct main" description: @@ -233,6 +249,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted 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: dependency: transitive description: @@ -589,6 +613,22 @@ packages: url: "https://pub.flutter-io.cn" source: hosted 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: dependency: transitive description: @@ -645,6 +685,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted 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: dependency: "direct main" description: @@ -677,6 +725,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted 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: dependency: transitive description: @@ -970,6 +1026,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted 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: dependency: transitive description: @@ -978,6 +1042,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted 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: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c83201b..843950f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,6 +36,7 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 dio: ^5.7.0 + provider: ^6.1.1 timelines_plus: ^1.0.7 table_calendar: ^3.1.3 toggle_switch: ^2.3.0 @@ -51,6 +52,9 @@ dependencies: flutter_carousel_widget: ^3.1.0 easy_refresh: ^3.4.0 syncfusion_flutter_charts: ^30.1.41 + minio: ^3.5.8 + crypto: ^3.0.7 + file_picker: ^10.3.3 dependency_overrides: tdesign_flutter_adaptation: 3.16.0