feat:增加上传图片组件
This commit is contained in:
@@ -1,5 +1,13 @@
|
||||
allprojects {
|
||||
repositories {
|
||||
// 优先添加国内镜像
|
||||
maven { url = uri("https://maven.aliyun.com/repository/central") }
|
||||
maven { url = uri("https://maven.aliyun.com/repository/jcenter") }
|
||||
maven { url = uri("https://maven.aliyun.com/repository/google") }
|
||||
maven { url = uri("https://maven.aliyun.com/repository/gradle-plugin") }
|
||||
maven { url = uri("https://maven.aliyun.com/repository/public") }
|
||||
maven { url = uri("https://jitpack.io") }
|
||||
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
@@ -10,6 +10,14 @@ pluginManagement {
|
||||
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||
|
||||
repositories {
|
||||
// 国内镜像加速
|
||||
maven { url = uri("https://maven.aliyun.com/repository/central") }
|
||||
maven { url = uri("https://maven.aliyun.com/repository/jcenter") }
|
||||
maven { url = uri("https://maven.aliyun.com/repository/google") }
|
||||
maven { url = uri("https://maven.aliyun.com/repository/gradle-plugin") }
|
||||
maven { url = uri("https://maven.aliyun.com/repository/public") }
|
||||
maven { url = uri("https://jitpack.io") }
|
||||
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
|
||||
@@ -1,32 +1,15 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:food_hub_app/widgets/common/index.dart';
|
||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class RecordFormPage extends StatefulWidget {
|
||||
const RecordFormPage({super.key});
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Scaffold(
|
||||
// appBar: AppBar(title: Text('新页面'), backgroundColor: Colors.white),
|
||||
// backgroundColor: Color(0xFFF5F5F5),
|
||||
// body: Center(
|
||||
// child: Column(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// Text('这是新页面'),
|
||||
// ElevatedButton(
|
||||
// onPressed: () => Navigator.pop(context),
|
||||
// child: Text('返回'),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
@override
|
||||
State<RecordFormPage> createState() => _RecordFormPage();
|
||||
}
|
||||
@@ -34,6 +17,27 @@ class RecordFormPage extends StatefulWidget {
|
||||
class _RecordFormPage extends State<RecordFormPage> {
|
||||
final _formKey = GlobalKey<FormBuilderState>();
|
||||
|
||||
final ImagePicker _picker = ImagePicker();
|
||||
XFile? _selectedImage;
|
||||
|
||||
// 选择图片的方法
|
||||
Future<void> _pickImage(ImageSource source) async {
|
||||
try {
|
||||
final XFile? image = await _picker.pickImage(source: source);
|
||||
if (image != null) {
|
||||
setState(() {
|
||||
_selectedImage = image;
|
||||
// 更新表单字段的值
|
||||
_formKey.currentState?.fields['image']?.didChange(image.path);
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('选择图片失败: $e')));
|
||||
}
|
||||
}
|
||||
|
||||
void confirmClick(BuildContext context) {
|
||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||
showSuccessToast("新增记录成功");
|
||||
@@ -42,6 +46,87 @@ class _RecordFormPage extends State<RecordFormPage> {
|
||||
}
|
||||
}
|
||||
|
||||
// 显示图片操作弹窗(查看/删除)
|
||||
Future<void> _showImageActionDialog(BuildContext context) async {
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('图片操作'),
|
||||
actions: [
|
||||
// 查看图片
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
_previewImage(context);
|
||||
},
|
||||
child: const Text('查看'),
|
||||
),
|
||||
// 删除图片
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_selectedImage = null; // 清空选中的图片
|
||||
});
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text(
|
||||
'删除',
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 显示选择图片来源的弹窗(相册/相机)
|
||||
Future<void> _showImageSourceDialog(BuildContext context) async {
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('选择图片来源'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
_pickImage(ImageSource.gallery);
|
||||
},
|
||||
child: const Text('相册'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
_pickImage(ImageSource.camera);
|
||||
},
|
||||
child: const Text('相机'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 预览图片(全屏查看)
|
||||
void _previewImage(BuildContext context) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
body: Center(
|
||||
child: Image.file(
|
||||
File(_selectedImage!.path),
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
floatingActionButton: IconButton(
|
||||
icon: const Icon(Icons.close, color: Colors.white),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@@ -59,7 +144,7 @@ class _RecordFormPage extends State<RecordFormPage> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
formLabelText(labelText: '菜谱名称'),
|
||||
formLabelText(labelText: '菜谱名称', isRequired: true),
|
||||
const SizedBox(height: 5),
|
||||
FormBuilderTextField(
|
||||
name: 'name',
|
||||
@@ -73,7 +158,7 @@ class _RecordFormPage extends State<RecordFormPage> {
|
||||
keyboardType: TextInputType.name,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
formLabelText(labelText: '完成时间'),
|
||||
formLabelText(labelText: '完成时间', isRequired: true),
|
||||
const SizedBox(height: 5),
|
||||
FormBuilderDateTimePicker(
|
||||
name: 'date',
|
||||
@@ -87,6 +172,69 @@ class _RecordFormPage extends State<RecordFormPage> {
|
||||
format: DateFormat('yyyy-MM-dd'),
|
||||
// enabled: _formKey.currentState?.fields['date']?.enabled ?? true,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
formLabelText(labelText: '美食图片'),
|
||||
const SizedBox(height: 5),
|
||||
|
||||
// 图片选择区域(核心优化部分)
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
// 已选择图片时,显示操作弹窗;未选择时直接打开选择器
|
||||
if (_selectedImage != null) {
|
||||
_showImageActionDialog(context);
|
||||
} else {
|
||||
_showImageSourceDialog(context); // 弹出选择相册/相机的对话框
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
height: 200,
|
||||
width: 200,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: Colors.grey[300]!,
|
||||
width: 1,
|
||||
style: BorderStyle.solid,
|
||||
),
|
||||
// 有图片时显示图片,无图片时显示背景色
|
||||
color:
|
||||
_selectedImage == null ? Colors.grey[50] : null,
|
||||
image:
|
||||
_selectedImage != null
|
||||
? DecorationImage(
|
||||
image: FileImage(
|
||||
File(_selectedImage!.path),
|
||||
),
|
||||
fit: BoxFit.contain,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
// 初始状态显示+号;有图片时隐藏
|
||||
child:
|
||||
_selectedImage == null
|
||||
? Center(
|
||||
child: Icon(
|
||||
Icons.add,
|
||||
color: Colors.grey[400],
|
||||
size: 48, // 大号+号,视觉更清晰
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
|
||||
// 隐藏的表单字段(保留原功能)
|
||||
FormBuilderTextField(
|
||||
name: 'image',
|
||||
initialValue: _selectedImage?.path ?? '',
|
||||
enabled: false,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return '请选择一张图片';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
|
||||
Widget formLabelText({required String labelText}) {
|
||||
Widget formLabelText({required String labelText, bool isRequired = false}) {
|
||||
return Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(text: '* ', style: TextStyle(color: Colors.red)),
|
||||
if (isRequired)
|
||||
const TextSpan(text: '* ', style: TextStyle(color: Colors.red)),
|
||||
TextSpan(text: labelText),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <file_selector_linux/file_selector_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
||||
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
file_selector_linux
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import file_selector_macos
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||
}
|
||||
|
||||
152
pubspec.lock
152
pubspec.lock
@@ -41,6 +41,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
cross_file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cross_file
|
||||
sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.3.4+2"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -65,6 +73,38 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.3.2"
|
||||
file_selector_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file_selector_linux
|
||||
sha256: "54cbbd957e1156d29548c7d9b9ec0c0ebb6de0a90452198683a7d23aed617a33"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.9.3+2"
|
||||
file_selector_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file_selector_macos
|
||||
sha256: "8c9250b2bd2d8d4268e39c82543bacbaca0fda7d29e0728c3c4bbb7c820fd711"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.9.4+3"
|
||||
file_selector_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file_selector_platform_interface
|
||||
sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.6.2"
|
||||
file_selector_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file_selector_windows
|
||||
sha256: "320fcfb6f33caa90f0b58380489fc5ac05d99ee94b61aa96ec2bff0ba81d3c2b"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.9.3+4"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
@@ -107,6 +147,14 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_plugin_android_lifecycle:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_plugin_android_lifecycle
|
||||
sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.28"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@@ -133,6 +181,86 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "11.1.2"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "4.1.2"
|
||||
image_picker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: image_picker
|
||||
sha256: "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
image_picker_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_android
|
||||
sha256: "317a5d961cec5b34e777b9252393f2afbd23084aa6e60fcf601dcf6341b9ebeb"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.8.12+23"
|
||||
image_picker_for_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_for_web
|
||||
sha256: "717eb042ab08c40767684327be06a5d8dbb341fe791d514e4b92c7bbe1b7bb83"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.0.6"
|
||||
image_picker_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_ios
|
||||
sha256: "05da758e67bc7839e886b3959848aa6b44ff123ab4b28f67891008afe8ef9100"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.8.12+2"
|
||||
image_picker_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_linux
|
||||
sha256: "34a65f6740df08bbbeb0a1abd8e6d32107941fd4868f67a507b25601651022c9"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.2.1+2"
|
||||
image_picker_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_macos
|
||||
sha256: "1b90ebbd9dcf98fb6c1d01427e49a55bd96b5d67b8c67cf955d60a5de74207c1"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.2.1+2"
|
||||
image_picker_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_platform_interface
|
||||
sha256: "886d57f0be73c4b140004e78b9f28a8914a09e50c2d816bdd0520051a71236a0"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.10.1"
|
||||
image_picker_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_windows
|
||||
sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.2.1+1"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -197,6 +325,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.16.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -205,6 +341,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
simple_gesture_detector:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -282,6 +426,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.7"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -43,6 +43,7 @@ dependencies:
|
||||
flutter_form_builder: ^10.0.0
|
||||
form_builder_validators: ^11.1.2
|
||||
fluttertoast: ^8.2.2
|
||||
image_picker: ^1.1.2
|
||||
intl: ^0.19.0
|
||||
|
||||
dev_dependencies:
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <file_selector_windows/file_selector_windows.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
FileSelectorWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
file_selector_windows
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
||||
Reference in New Issue
Block a user