Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fa02fdbfd8 | |||
| 211fafc7e1 | |||
| 5278bea940 | |||
| 0bc3f4b702 |
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.0.21] - 2026-06-07
|
||||
|
||||
### 新功能
|
||||
- 商品详情页添加照片时,手机端(Android/iOS)支持直接拍照或从相册选择
|
||||
|
||||
## [1.0.20] - 2026-06-07
|
||||
|
||||
### 修复
|
||||
- 修复 Android 客户端登录页提示「服务不可达」、无法连接服务器的问题(正式版安装包此前缺少联网权限)
|
||||
|
||||
## [1.0.19] - 2026-06-07
|
||||
|
||||
### 改进
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- release 包走此主清单,必须显式声明联网权限;debug/profile 清单由 Flutter 模板自动添加,
|
||||
release 不带会导致所有网络请求失败(登录页显示「服务不可达」)。 -->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<application
|
||||
android:label="岩美酒库"
|
||||
android:name="${applicationName}"
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
<dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>用于拍摄商品照片</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>用于从相册选择商品照片</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import '../../core/utils/dialog_util.dart';
|
||||
import 'dart:typed_data';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -91,7 +92,56 @@ class _ProductDetailScreenState extends ConsumerState<ProductDetailScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
// 移动端(Android/iOS)支持拍照,用 image_picker;桌面/Web 用 file_picker 选文件。
|
||||
// 用 defaultTargetPlatform 而非 dart:io 的 Platform,避免在参与 Web 编译的文件里引入 dart:io。
|
||||
bool get _isMobile =>
|
||||
!kIsWeb &&
|
||||
(defaultTargetPlatform == TargetPlatform.android ||
|
||||
defaultTargetPlatform == TargetPlatform.iOS);
|
||||
|
||||
Future<void> _pickAndUpload() async {
|
||||
if (_isMobile) {
|
||||
await _pickFromCameraOrGallery();
|
||||
} else {
|
||||
await _pickFromFiles();
|
||||
}
|
||||
}
|
||||
|
||||
/// 移动端:底部弹出「拍照 / 从相册选择」
|
||||
Future<void> _pickFromCameraOrGallery() async {
|
||||
final source = await showModalBottomSheet<ImageSource>(
|
||||
context: context,
|
||||
builder: (ctx) => SafeArea(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.camera_alt_outlined),
|
||||
title: const Text('拍照'),
|
||||
onTap: () => Navigator.pop(ctx, ImageSource.camera),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.photo_library_outlined),
|
||||
title: const Text('从相册选择'),
|
||||
onTap: () => Navigator.pop(ctx, ImageSource.gallery),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
if (source == null) return;
|
||||
|
||||
final XFile? xfile = await ImagePicker().pickImage(
|
||||
source: source,
|
||||
maxWidth: 2000,
|
||||
imageQuality: 85,
|
||||
);
|
||||
if (xfile == null) return;
|
||||
await _uploadImage(filePath: xfile.path, fileName: xfile.name);
|
||||
}
|
||||
|
||||
/// 桌面 / Web:文件选择器
|
||||
Future<void> _pickFromFiles() async {
|
||||
final result = await FilePicker.platform.pickFiles(
|
||||
type: FileType.image,
|
||||
allowMultiple: false,
|
||||
@@ -102,13 +152,17 @@ class _ProductDetailScreenState extends ConsumerState<ProductDetailScreen> {
|
||||
final String? filePath = kIsWeb ? null : file.path;
|
||||
final fileBytes = file.bytes;
|
||||
if (filePath == null && fileBytes == null) return;
|
||||
await _uploadImage(
|
||||
filePath: filePath, bytes: fileBytes, fileName: file.name);
|
||||
}
|
||||
|
||||
Future<void> _uploadImage(
|
||||
{String? filePath, Uint8List? bytes, required String fileName}) async {
|
||||
setState(() => _uploading = true);
|
||||
try {
|
||||
final img = await ref
|
||||
.read(productRepositoryProvider)
|
||||
.uploadImage(_product!.id, filePath,
|
||||
bytes: fileBytes, fileName: file.name);
|
||||
final img = await ref.read(productRepositoryProvider).uploadImage(
|
||||
_product!.id, filePath,
|
||||
bytes: bytes, fileName: fileName);
|
||||
_updateImages([..._product!.images, img]);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
|
||||
@@ -6,6 +6,7 @@ import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import file_picker
|
||||
import file_selector_macos
|
||||
import package_info_plus
|
||||
import printing
|
||||
import shared_preferences_foundation
|
||||
@@ -13,6 +14,7 @@ import url_launcher_macos
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
|
||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||
PrintingPlugin.register(with: registry.registrar(forPlugin: "PrintingPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
|
||||
@@ -161,6 +161,38 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.3.7"
|
||||
file_selector_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file_selector_linux
|
||||
sha256: "2567f398e06ac72dcf2e98a0c95df2a9edd03c2c2e0cacd4780f20cdf56263a0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.4"
|
||||
file_selector_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file_selector_macos
|
||||
sha256: "5e0bbe9c312416f1787a68259ea1505b52f258c587f12920422671807c4d618a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.5"
|
||||
file_selector_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file_selector_platform_interface
|
||||
sha256: "35e0bd61ebcdb91a3505813b055b09b79dfdc7d0aee9c09a7ba59ae4bb13dc85"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.7.0"
|
||||
file_selector_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file_selector_windows
|
||||
sha256: "62197474ae75893a62df75939c777763d39c2bc5f73ce5b88497208bc269abfd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.3+5"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
@@ -253,6 +285,70 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.3.0"
|
||||
image_picker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: image_picker
|
||||
sha256: "91c025426c2881c551100bce834e201c835a170151545f58d17da5180ca7d9ac"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
image_picker_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_android
|
||||
sha256: "6f3a1995eafb000333174fae92202622033b0ee7fd917a6cd3730295264df84a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.13+19"
|
||||
image_picker_for_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_for_web
|
||||
sha256: "66257a3191ab360d23a55c8241c91a6e329d31e94efa7be9cf7a212e65850214"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
image_picker_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_ios
|
||||
sha256: b9c4a438a9ff4f60808c9cf0039b93a42bb6c2211ef6ebb647394b2b3fa84588
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.13+6"
|
||||
image_picker_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_linux
|
||||
sha256: "1f81c5f2046b9ab724f85523e4af65be1d47b038160a8c8deed909762c308ed4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.2"
|
||||
image_picker_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_macos
|
||||
sha256: "86f0f15a309de7e1a552c12df9ce5b59fe927e71385329355aec4776c6a8ec91"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.2+1"
|
||||
image_picker_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_platform_interface
|
||||
sha256: "567e056716333a1647c64bb6bd873cff7622233a5c3f694be28a583d4715690c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.1"
|
||||
image_picker_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_windows
|
||||
sha256: d248c86554a72b5495a31c56f060cf73a41c7ff541689327b1a7dbccc33adfae
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.2"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@@ -25,6 +25,7 @@ dependencies:
|
||||
printing: ^5.13.0
|
||||
pdf: ^3.10.8
|
||||
web: ^1.1.0
|
||||
image_picker: ^1.2.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
@@ -6,10 +6,13 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <file_selector_windows/file_selector_windows.h>
|
||||
#include <printing/printing_plugin.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
FileSelectorWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
||||
PrintingPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("PrintingPlugin"));
|
||||
UrlLauncherWindowsRegisterWithRegistrar(
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
file_selector_windows
|
||||
printing
|
||||
url_launcher_windows
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user