feat(app): 商业化门店页 + 收尾联调
- commercial_page:形象会员订阅占位 + 合作门店列表(造型/服装筛选,佣金政策展示) - avatar_viewer 精简为 MVP 占位实现(移除 three_dart/flutter_gl 依赖:flutter_gl 0.0.21 在 Flutter 3.44 下无法编译,v2 接入 GLB 时引入,接入方式注释保留) - 全链路冒烟验证通过:注册→照片→衣橱→身形→化身(172cm→体型模板4)→生成(LLM 预筛→全低分触发兜底→3 套方案)→选定主方案→3 视角效果图→收藏→门店列表
This commit is contained in:
@@ -1,10 +1,178 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class CommercialPage extends StatelessWidget {
|
||||
import '../../core/auth/auth_provider.dart';
|
||||
import '../../shared/widgets/error_view.dart';
|
||||
import '../../shared/widgets/loading_view.dart';
|
||||
|
||||
class PartnerStoreInfo {
|
||||
final int id;
|
||||
final String name;
|
||||
final int type; // 1 造型/发型店,2 服装店
|
||||
final String address;
|
||||
final String commissionPolicy;
|
||||
|
||||
const PartnerStoreInfo({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.type,
|
||||
required this.address,
|
||||
required this.commissionPolicy,
|
||||
});
|
||||
}
|
||||
|
||||
class StoreNotifier extends AsyncNotifier<List<PartnerStoreInfo>> {
|
||||
@override
|
||||
Future<List<PartnerStoreInfo>> build() async {
|
||||
final api = ref.read(apiClientProvider);
|
||||
final list = await api.get<List<dynamic>>('/partner-store/list');
|
||||
return list
|
||||
.map((e) => PartnerStoreInfo(
|
||||
id: (e['id'] as num).toInt(),
|
||||
name: e['name'] as String? ?? '',
|
||||
type: (e['type'] as num?)?.toInt() ?? 1,
|
||||
address: e['address'] as String? ?? '',
|
||||
commissionPolicy: e['commission_policy'] as String? ?? '',
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
state = const AsyncLoading();
|
||||
state = await AsyncValue.guard(build);
|
||||
}
|
||||
}
|
||||
|
||||
final storeProvider =
|
||||
AsyncNotifierProvider<StoreNotifier, List<PartnerStoreInfo>>(
|
||||
StoreNotifier.new);
|
||||
|
||||
const _storeTypeLabels = {1: '造型', 2: '服装'};
|
||||
|
||||
/// 商业化:订阅权益 + 合作门店(佣金导流)
|
||||
class CommercialPage extends ConsumerStatefulWidget {
|
||||
const CommercialPage({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<CommercialPage> createState() => _CommercialPageState();
|
||||
}
|
||||
|
||||
class _CommercialPageState extends ConsumerState<CommercialPage> {
|
||||
int? _typeFilter;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Center(child: Text('门店电商(开发中)'));
|
||||
final stores = ref.watch(storeProvider);
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('门店与服务')),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
// 订阅卡片(MVP 占位,支付接入后启用)
|
||||
Card(
|
||||
color: scheme.primaryContainer,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.workspace_premium,
|
||||
size: 36, color: scheme.primary),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('形象会员',
|
||||
style: TextStyle(
|
||||
fontSize: 16, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'会员专享:无限次效果图生成 · 优先 AI 方案 · 门店专属折扣(接入支付后开通)',
|
||||
style: TextStyle(
|
||||
fontSize: 12, color: scheme.primary),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
Text('合作门店',
|
||||
style: TextStyle(
|
||||
fontSize: 15, fontWeight: FontWeight.bold)),
|
||||
const Spacer(),
|
||||
ChoiceChip(
|
||||
label: const Text('全部'),
|
||||
selected: _typeFilter == null,
|
||||
onSelected: (_) => setState(() => _typeFilter = null),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
for (final entry in _storeTypeLabels.entries) ...[
|
||||
ChoiceChip(
|
||||
label: Text(entry.value),
|
||||
selected: _typeFilter == entry.key,
|
||||
onSelected: (_) =>
|
||||
setState(() => _typeFilter = entry.key),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
stores.when(
|
||||
loading: () => const Padding(
|
||||
padding: EdgeInsets.only(top: 32),
|
||||
child: LoadingView(text: '加载门店...')),
|
||||
error: (e, _) => Padding(
|
||||
padding: const EdgeInsets.only(top: 32),
|
||||
child: ErrorView(
|
||||
message: e.toString().replaceFirst('Exception: ', ''),
|
||||
onRetry: () => ref.read(storeProvider.notifier).refresh(),
|
||||
),
|
||||
),
|
||||
data: (list) {
|
||||
final shown = _typeFilter == null
|
||||
? list
|
||||
: list.where((s) => s.type == _typeFilter).toList();
|
||||
if (shown.isEmpty) {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.only(top: 32),
|
||||
child: Center(
|
||||
child: Text('附近暂无可合作门店',
|
||||
style: TextStyle(color: Colors.grey))),
|
||||
);
|
||||
}
|
||||
return Column(
|
||||
children: [
|
||||
for (final s in shown) ...[
|
||||
Card(
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: scheme.primaryContainer,
|
||||
child: Icon(s.type == 1
|
||||
? Icons.content_cut
|
||||
: Icons.checkroom),
|
||||
),
|
||||
title: Text(s.name),
|
||||
subtitle: Text('${s.address}\n${s.commissionPolicy}'),
|
||||
isThreeLine: true,
|
||||
trailing: const Icon(Icons.chevron_right,
|
||||
color: Colors.grey),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gl/flutter_gl.dart';
|
||||
import 'package:three_dart/three_dart.dart' as three;
|
||||
import 'package:three_dart_jsm/three_dart_jsm.dart' as three_jsm;
|
||||
|
||||
import '../../core/config/app_config.dart';
|
||||
|
||||
/// 是否启用 three_dart 3D 渲染。
|
||||
/// MVP 阶段服务端尚无预烘焙 GLB 资产(/workspace/templates/*.glb 为构建产物),
|
||||
/// 默认关闭展示信息占位;接入真实 GLB 资源后置为 true 即可启用。
|
||||
const bool kEnable3D = false;
|
||||
|
||||
/// 3D 化身查看组件:three_dart 渲染 GLB,加载失败/未启用时展示信息占位
|
||||
/// 3D 化身查看组件(MVP 占位实现)
|
||||
///
|
||||
/// v2 接入真实 3D 渲染的方式:
|
||||
/// 1. 服务端提供预烘焙 GLB 资产(/workspace/templates/avatar_f{face}_b{body}_s{skin}.glb)
|
||||
/// 2. pubspec 引入 three_dart ^0.0.16 + three_dart_jsm ^0.0.10 + flutter_gl ^0.0.20
|
||||
/// (注意 flutter_gl 0.0.21 在 Flutter 3.44 下无法编译,需锁 0.0.20)
|
||||
/// 3. 用 flutter_gl 初始化 GL 上下文 + GLTFLoader 加载 GLB + 自动旋转渲染,
|
||||
/// 参考 three_dart 0.0.16 的 example/lib/webgl_loader_glb.dart
|
||||
class AvatarViewer extends StatelessWidget {
|
||||
final String? glbUrl;
|
||||
final String title;
|
||||
@@ -28,182 +22,6 @@ class AvatarViewer extends StatelessWidget {
|
||||
this.height = 360,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!kEnable3D || glbUrl == null || glbUrl!.isEmpty) {
|
||||
return _AvatarPlaceholder(
|
||||
title: title, subtitle: subtitle, height: height);
|
||||
}
|
||||
return _ThreeDAvatarViewer(
|
||||
url: AppConfig.resolveUrl(glbUrl!),
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
height: height,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// three_dart 实现:flutter_gl 初始化 + GLTFLoader 加载 + 自动旋转渲染
|
||||
class _ThreeDAvatarViewer extends StatefulWidget {
|
||||
final String url;
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final double height;
|
||||
|
||||
const _ThreeDAvatarViewer({
|
||||
required this.url,
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
required this.height,
|
||||
});
|
||||
|
||||
@override
|
||||
State<_ThreeDAvatarViewer> createState() => _ThreeDAvatarViewerState();
|
||||
}
|
||||
|
||||
class _ThreeDAvatarViewerState extends State<_ThreeDAvatarViewer> {
|
||||
FlutterGlPlugin? _gl;
|
||||
three.WebGLRenderer? _renderer;
|
||||
three.Scene? _scene;
|
||||
three.Camera? _camera;
|
||||
three.Object3D? _model;
|
||||
dynamic _sourceTexture;
|
||||
double _width = 300;
|
||||
double _dpr = 1;
|
||||
bool _ready = false;
|
||||
bool _failed = false;
|
||||
bool _disposed = false;
|
||||
Timer? _timer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => _init());
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_disposed = true;
|
||||
_timer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _init() async {
|
||||
final mq = MediaQuery.of(context);
|
||||
_width = mq.size.width;
|
||||
_dpr = mq.devicePixelRatio;
|
||||
try {
|
||||
final gl = FlutterGlPlugin();
|
||||
await gl.initialize(options: {
|
||||
'antialias': true,
|
||||
'alpha': false,
|
||||
'width': _width.toInt(),
|
||||
'height': widget.height.toInt(),
|
||||
'dpr': _dpr,
|
||||
});
|
||||
await gl.prepareContext();
|
||||
|
||||
final renderer = three.WebGLRenderer({
|
||||
'width': _width,
|
||||
'height': widget.height,
|
||||
'gl': gl.gl,
|
||||
'antialias': true,
|
||||
'canvas': gl.element,
|
||||
});
|
||||
renderer.setPixelRatio(_dpr);
|
||||
renderer.setSize(_width, widget.height, false);
|
||||
|
||||
final target = three.WebGLMultisampleRenderTarget(
|
||||
(_width * _dpr).toInt(), (widget.height * _dpr).toInt(),
|
||||
three.WebGLRenderTargetOptions({'format': three.RGBAFormat}));
|
||||
target.samples = 4;
|
||||
renderer.setRenderTarget(target);
|
||||
final sourceTexture = renderer.getRenderTargetGLTexture(target);
|
||||
|
||||
await _loadModel(gl, renderer);
|
||||
|
||||
_gl = gl;
|
||||
_renderer = renderer;
|
||||
_sourceTexture = sourceTexture;
|
||||
if (mounted) setState(() => _ready = true);
|
||||
_timer = Timer.periodic(
|
||||
const Duration(milliseconds: 33), (_) => _render());
|
||||
} catch (e) {
|
||||
debugPrint('3D 渲染不可用,回退占位:$e');
|
||||
if (mounted) setState(() => _failed = true);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadModel(
|
||||
FlutterGlPlugin gl, three.WebGLRenderer renderer) async {
|
||||
final scene = three.Scene();
|
||||
final camera = three.PerspectiveCamera(45, _width / widget.height, 0.1, 100);
|
||||
camera.position.set(0, 1.4, 3.2);
|
||||
scene.add(three.AmbientLight(0xffffff, 0.9));
|
||||
final keyLight = three.DirectionalLight(0xffffff, 0.9);
|
||||
keyLight.position.set(2, 4, 3);
|
||||
scene.add(keyLight);
|
||||
scene.add(camera);
|
||||
camera.lookAt(three.Vector3(0, 1, 0));
|
||||
|
||||
final loader = three_jsm.GLTFLoader(null);
|
||||
final result = await loader.loadAsync(widget.url);
|
||||
final model = result['scene'] as three.Object3D?;
|
||||
if (model != null) {
|
||||
model.rotation.y = 0.6;
|
||||
scene.add(model);
|
||||
}
|
||||
|
||||
_scene = scene;
|
||||
_camera = camera;
|
||||
_model = model;
|
||||
}
|
||||
|
||||
void _render() {
|
||||
if (_disposed || _renderer == null || _scene == null || _camera == null) {
|
||||
return;
|
||||
}
|
||||
final model = _model;
|
||||
if (model != null) {
|
||||
model.rotation.y += 0.01; // 自动旋转
|
||||
}
|
||||
_renderer!.render(_scene!, _camera!);
|
||||
_gl!.gl.flush();
|
||||
if (!kIsWeb) {
|
||||
_gl!.updateTexture(_sourceTexture);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_failed) {
|
||||
return _AvatarPlaceholder(
|
||||
title: widget.title, subtitle: widget.subtitle, height: widget.height);
|
||||
}
|
||||
return SizedBox(
|
||||
height: widget.height,
|
||||
width: double.infinity,
|
||||
child: _ready
|
||||
? (kIsWeb
|
||||
? HtmlElementView(viewType: _gl!.textureId!.toString())
|
||||
: Texture(textureId: _gl!.textureId!))
|
||||
: const Center(child: CircularProgressIndicator()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 占位:GLB 资产未接入或加载失败时展示化身信息
|
||||
class _AvatarPlaceholder extends StatelessWidget {
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final double height;
|
||||
|
||||
const _AvatarPlaceholder({
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
required this.height,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
@@ -214,7 +32,10 @@ class _AvatarPlaceholder extends StatelessWidget {
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [scheme.primaryContainer, scheme.primary.withValues(alpha: 0.3)],
|
||||
colors: [
|
||||
scheme.primaryContainer,
|
||||
scheme.primary.withValues(alpha: 0.3),
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
|
||||
-144
@@ -17,14 +17,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "12.1.0"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.6.1"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -57,14 +49,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: charcode
|
||||
sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
cli_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -121,14 +105,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
csslib:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: csslib
|
||||
sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -222,46 +198,6 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_gl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_gl
|
||||
sha256: de74c88f77228f47dd280e2092b50eb49fe1fc008de74047d195a27f2db0b491
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.0.21"
|
||||
flutter_gl_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_gl_macos
|
||||
sha256: "62aa244d4aa9127115df651baec070893718dd27571c8dbca5374dbcb9fd4849"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.0.5"
|
||||
flutter_gl_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_gl_platform_interface
|
||||
sha256: "03062491fac26d0fda80703e4a01894ecc4fec4f7e5cec131ef021fbf46b2fda"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.0.4"
|
||||
flutter_gl_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_gl_web
|
||||
sha256: "005dc72618ee14659dff7dfe72d6b1fce0efdda745cfebfe4618ce7ac2044af9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.0.5"
|
||||
flutter_gl_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_gl_windows
|
||||
sha256: cd9259fb8178863de9e667129f73447fa174447a3c4abd16defa07af648850e4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.0.4"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
@@ -328,14 +264,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "17.3.0"
|
||||
html:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: html
|
||||
sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.15.6"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -360,14 +288,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.2"
|
||||
image:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image
|
||||
sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.0"
|
||||
image_picker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -528,14 +448,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
opentype_dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: opentype_dart
|
||||
sha256: "4bd96aeed494289a87e92bde20afe60f59648dcef253c0a7159b65ffa23899dc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.0.1"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -576,14 +488,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: petitparser
|
||||
sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.2"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -805,22 +709,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.17"
|
||||
three_dart:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: three_dart
|
||||
sha256: "102ff2cc65c2dcb805166c7dc5fa00a9a3252b5aec34fb435cc558a7487dc40b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.0.16"
|
||||
three_dart_jsm:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: three_dart_jsm
|
||||
sha256: "26a71aff4aa842ac8178d2ba8e64b997c434ce9f604fa4fa3ee49f343d2c84b4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.0.10"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -829,30 +717,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
typr_dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typr_dart
|
||||
sha256: e8aa717c1445ceccd77bd6ba471683c8e17a9b14797ac09db3bd52d6fbd2fe7b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.0.2"
|
||||
universal_html:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: universal_html
|
||||
sha256: c0bcae5c733c60f26c7dfc88b10b0fd27cbcc45cb7492311cdaa6067e21c9cd4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
universal_io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: universal_io
|
||||
sha256: f63cbc48103236abf48e345e07a03ce5757ea86285ed313a6a032596ed9301e2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -925,14 +789,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xml
|
||||
sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.6.1"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
+2
-3
@@ -40,9 +40,8 @@ dependencies:
|
||||
image_picker: ^1.2.3
|
||||
shared_preferences: ^2.5.5
|
||||
fluttertoast: ^9.1.0
|
||||
three_dart: ^0.0.16
|
||||
three_dart_jsm: ^0.0.10
|
||||
flutter_gl: ^0.0.20
|
||||
# v2 3D 渲染接入时引入:three_dart ^0.0.16 + three_dart_jsm ^0.0.10 + flutter_gl ^0.0.20
|
||||
# (flutter_gl 0.0.21 在 Flutter 3.44 下无法编译:platformViewRegistry 已移除)
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user