import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; /// 品牌配置编辑(启动图 + Logo)。 /// /// 通过 [ImagePicker] 选择本地图片,并用「手机外框」预览: /// - 启动图为全屏背景; /// - Logo 为右下角角标。 /// /// [splashUrl]/[logoUrl] 为本地图片路径(占位),由父级保存。 class BrandingEditor extends StatefulWidget { const BrandingEditor({ super.key, this.initialSplashUrl, this.initialLogoUrl, }); final String? initialSplashUrl; final String? initialLogoUrl; @override State createState() => BrandingEditorState(); } /// 品牌编辑器状态(公开,供父级通过 GlobalKey 读取所选图片路径)。 class BrandingEditorState extends State { final ImagePicker _picker = ImagePicker(); String? _splashUrl; String? _logoUrl; @override void initState() { super.initState(); _splashUrl = widget.initialSplashUrl; _logoUrl = widget.initialLogoUrl; } Future _pickSplash() async { final xFile = await _picker.pickImage(source: ImageSource.gallery); if (xFile != null && mounted) { setState(() => _splashUrl = xFile.path); } } Future _pickLogo() async { final xFile = await _picker.pickImage(source: ImageSource.gallery); if (xFile != null && mounted) { setState(() => _logoUrl = xFile.path); } } @override Widget build(BuildContext context) { final theme = Theme.of(context); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: FilledButton.tonal( onPressed: _pickSplash, child: const Text('选择启动图'), ), ), const SizedBox(width: 12), Expanded( child: FilledButton.tonal( onPressed: _pickLogo, child: const Text('选择 Logo'), ), ), ], ), const SizedBox(height: 12), // 手机外框预览。 Center( child: Container( width: 200, height: 380, decoration: BoxDecoration( color: Colors.black, borderRadius: BorderRadius.circular(28), border: Border.all(color: Colors.black87, width: 8), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 12, offset: const Offset(0, 6), ), ], ), clipBehavior: Clip.antiAlias, child: Stack( children: [ // 启动图全屏背景。 if (_splashUrl != null && _splashUrl!.isNotEmpty) Positioned.fill( child: Image.network( _splashUrl!, fit: BoxFit.cover, errorBuilder: (_, __, ___) => Container( color: theme.colorScheme.primaryContainer, ), ), ) else Positioned.fill( child: Container( color: theme.colorScheme.primaryContainer, child: Center( child: Text( '启动图预览', style: TextStyle( color: theme.colorScheme.onPrimaryContainer, ), ), ), ), ), // 顶部状态栏占位。 Positioned( top: 8, left: 0, right: 0, child: Container( height: 18, color: Colors.black.withOpacity(0.25), ), ), // Logo 右下角角标。 if (_logoUrl != null && _logoUrl!.isNotEmpty) Positioned( right: 16, bottom: 24, child: Container( width: 56, height: 56, decoration: const BoxDecoration( color: Colors.white, shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Colors.black26, blurRadius: 6, offset: Offset(0, 2), ), ], ), child: ClipOval( child: Image.network( _logoUrl!, fit: BoxFit.cover, errorBuilder: (_, __, ___) => const Icon(Icons.image, size: 28), ), ), ), ), ], ), ), ), ], ); } /// 当前启动图本地路径(未选返回 null)。 String? get splashUrl => _splashUrl; /// 当前 Logo 本地路径(未选返回 null)。 String? get logoUrl => _logoUrl; }