commit b04800d0a5ef4cf944514446fdbae47869f09506
parent eba934413bf3c55de786272ffb12fc9b680f4b32
Author: maydayv7 <maydayv7@gmail.com>
Date: Thu, 4 Dec 2025 13:47:08 +0530
Format all dart files
Diffstat:
18 files changed, 1099 insertions(+), 909 deletions(-)
diff --git a/lib/data/models/project_model.dart b/lib/data/models/project_model.dart
@@ -61,6 +61,4 @@ class ProjectModel {
: [],
);
}
-
-
}
diff --git a/lib/services/analysis_queue_manager.dart b/lib/services/analysis_queue_manager.dart
@@ -124,10 +124,10 @@ class AnalysisQueueManager {
}
final appDir = await getApplicationDocumentsDirectory();
-
- final cropDirPath = p.join(appDir.path, 'crops');
+
+ final cropDirPath = p.join(appDir.path, 'crops');
final cropDir = Directory(cropDirPath);
-
+
if (!await cropDir.exists()) {
await cropDir.create(recursive: true);
}
@@ -187,14 +187,14 @@ class AnalysisQueueManager {
}
await _noteRepo.updateNote(
- note.id!,
+ note.id!,
status: 'analyzing',
- cropFilePath: cropPath
+ cropFilePath: cropPath,
);
// 3. Analysis
debugPrint("[Queue]: Analyzing Note ${note.id} tag: ${note.category}");
-
+
// Now when this calls generateAsset internally, the DB lookup will succeed
final result = await ImageAnalyzerService.analyzeSelected(cropPath, [
note.category,
@@ -204,7 +204,7 @@ class AnalysisQueueManager {
if (result['success'] == true && result['data'] != null) {
final resultsMap = result['data']['results'] ?? {};
final jsonString = jsonEncode(resultsMap);
-
+
await _noteRepo.updateNote(
note.id!,
analysisData: jsonString,
diff --git a/lib/services/analyze/image_analyzer.dart b/lib/services/analyze/image_analyzer.dart
@@ -364,7 +364,7 @@ class ImageAnalyzerService {
'Lighting': {"scores": results[5]['scores']},
'Colour Palette': {
"scores": results[1]['scores'],
- "palette": results[1]['palette']
+ "palette": results[1]['palette'],
},
'Emotions': {"scores": results[4]['scores']},
'Era': {"scores": results[6]['scores']},
diff --git a/lib/services/file_service.dart b/lib/services/file_service.dart
@@ -85,5 +85,5 @@ class FileService {
Future<void> renameFile(String id, String newName) async {
await _repo.updateDetails(id, name: newName);
- }
+ }
}
diff --git a/lib/services/flask_service.dart b/lib/services/flask_service.dart
@@ -53,10 +53,12 @@ class FlaskService {
debugPrint("🔗 [Pipeline] Starting Sketch-to-Image...");
// 1. Analyze Sketch (Use cached description if available)
- final String? sketchDescription = imageDescription ?? await describeImage(
- imagePath: sketchPath,
- prompt: '<MORE_DETAILED_CAPTION>',
- );
+ final String? sketchDescription =
+ imageDescription ??
+ await describeImage(
+ imagePath: sketchPath,
+ prompt: '<MORE_DETAILED_CAPTION>',
+ );
if (sketchDescription == null) {
debugPrint("❌ [Pipeline] Failed: Could not analyze sketch.");
@@ -81,13 +83,14 @@ class FlaskService {
);
if (magicPrompt != null) {
- await _logToFile("debug_magic_prompt.txt", magicPrompt);
- debugPrint("🐛 [DEBUG] 4. Magic Prompt: $magicPrompt");
+ await _logToFile("debug_magic_prompt.txt", magicPrompt);
+ debugPrint("🐛 [DEBUG] 4. Magic Prompt: $magicPrompt");
} else {
- debugPrint("🐛 [DEBUG] 4. Magic Prompt: null");
+ debugPrint("🐛 [DEBUG] 4. Magic Prompt: null");
}
- final String globalPrompt = magicPrompt ?? "$userPrompt. The image features: $sketchDescription";
+ final String globalPrompt =
+ magicPrompt ?? "$userPrompt. The image features: $sketchDescription";
debugPrint("🔗 [Pipeline] Generating base image...");
@@ -142,10 +145,10 @@ class FlaskService {
);
if (magicPrompt != null) {
- await _logToFile("debug_magic_prompt.txt", magicPrompt);
- debugPrint("🐛 [DEBUG] 4. Magic Prompt: $magicPrompt");
+ await _logToFile("debug_magic_prompt.txt", magicPrompt);
+ debugPrint("🐛 [DEBUG] 4. Magic Prompt: $magicPrompt");
} else {
- debugPrint("🐛 [DEBUG] 4. Magic Prompt: null");
+ debugPrint("🐛 [DEBUG] 4. Magic Prompt: null");
}
final String globalPrompt =
diff --git a/lib/services/python_service.dart b/lib/services/python_service.dart
@@ -3,9 +3,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
class PythonService {
- static const MethodChannel _channel = MethodChannel(
- 'com.creek.ui/methods',
- );
+ static const MethodChannel _channel = MethodChannel('com.creek.ui/methods');
// 1. Layout Analysis (OpenCV)
Future<Map<String, dynamic>> analyzeLayout(String imagePath) async {
@@ -74,11 +72,14 @@ class PythonService {
required String userPrompt,
}) async {
try {
- final String? result = await _channel.invokeMethod('generateMagicPrompt', {
- 'stylesheetJson': stylesheetJson,
- 'caption': caption,
- 'userPrompt': userPrompt,
- });
+ final String? result = await _channel.invokeMethod(
+ 'generateMagicPrompt',
+ {
+ 'stylesheetJson': stylesheetJson,
+ 'caption': caption,
+ 'userPrompt': userPrompt,
+ },
+ );
return result;
} catch (e) {
debugPrint("Magic Prompt Generation Error: $e");
diff --git a/lib/services/stylesheet_service.dart b/lib/services/stylesheet_service.dart
@@ -50,10 +50,25 @@ class StylesheetService {
return StylesheetData(
colors: colors,
fonts: fonts,
- graphics: _extractStrings(data, ['Graphics', 'graphics'], valueKey: 'path'),
- compositions: _extractStrings(data, ['Compositions', 'Composition', 'compositions']),
- materialLook: _extractStrings(data, ['Material look', 'Material Look', 'material_look']),
- textures: _extractStrings(data, ['Textures', 'Background/Texture', 'textures']),
+ graphics: _extractStrings(data, [
+ 'Graphics',
+ 'graphics',
+ ], valueKey: 'path'),
+ compositions: _extractStrings(data, [
+ 'Compositions',
+ 'Composition',
+ 'compositions',
+ ]),
+ materialLook: _extractStrings(data, [
+ 'Material look',
+ 'Material Look',
+ 'material_look',
+ ]),
+ textures: _extractStrings(data, [
+ 'Textures',
+ 'Background/Texture',
+ 'textures',
+ ]),
lighting: _extractStrings(data, ['Lighting', 'lighting']),
style: _extractStrings(data, ['Style', 'style']),
era: _extractStrings(data, ['Era/Cultural Reference', 'Era', 'era']),
@@ -133,7 +148,12 @@ class StylesheetService {
// Extracts font names and resolves them to valid Google Font strings.
List<String> _extractFonts(Map<String, dynamic> data) {
- dynamic fontData = _findValue(data, ['Typography', 'fonts', 'typography', 'Fonts']);
+ dynamic fontData = _findValue(data, [
+ 'Typography',
+ 'fonts',
+ 'typography',
+ 'Fonts',
+ ]);
if (fontData == null) return [];
List<String> rawNames = [];
@@ -160,7 +180,7 @@ class StylesheetService {
'Colour Palette',
'Color Palette',
'colors',
- 'Colors'
+ 'Colors',
]);
if (colorData == null) return [];
@@ -185,7 +205,11 @@ class StylesheetService {
// Generic helper to extract a list of strings from various keys
// Supports extraction from [{ "label": "val" }] or ["val"] or "val"
- List<String> _extractStrings(Map<String, dynamic> data, List<String> keys, {String valueKey = 'label'}) {
+ List<String> _extractStrings(
+ Map<String, dynamic> data,
+ List<String> keys, {
+ String valueKey = 'label',
+ }) {
dynamic rawData = _findValue(data, keys);
if (rawData == null) return [];
diff --git a/lib/ui/pages/canvas_page.dart b/lib/ui/pages/canvas_page.dart
@@ -1290,25 +1290,50 @@ class _CanvasPageState extends State<CanvasPage> {
right: 0,
child: Center(
child: Container(
- padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
+ padding: const EdgeInsets.symmetric(
+ horizontal: 16,
+ vertical: 8,
+ ),
decoration: BoxDecoration(
color: Colors.black87,
borderRadius: BorderRadius.circular(30),
boxShadow: [
- BoxShadow(color: Colors.black26, blurRadius: 8, offset: Offset(0, 2))
- ]
+ BoxShadow(
+ color: Colors.black26,
+ blurRadius: 8,
+ offset: Offset(0, 2),
+ ),
+ ],
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (_isRemovingBg) ...[
- const SizedBox(width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white)),
- const SizedBox(width: 8),
- const Text("Removing...", style: TextStyle(color: Colors.white, fontFamily: 'GeneralSans', fontSize: 14)),
+ const SizedBox(
+ width: 16,
+ height: 16,
+ child: CircularProgressIndicator(
+ strokeWidth: 2,
+ color: Colors.white,
+ ),
+ ),
+ const SizedBox(width: 8),
+ const Text(
+ "Removing...",
+ style: TextStyle(
+ color: Colors.white,
+ fontFamily: 'GeneralSans',
+ fontSize: 14,
+ ),
+ ),
] else ...[
- const Text(
+ const Text(
"Remove background?",
- style: TextStyle(color: Colors.white, fontFamily: 'GeneralSans', fontSize: 14),
+ style: TextStyle(
+ color: Colors.white,
+ fontFamily: 'GeneralSans',
+ fontSize: 14,
+ ),
),
const SizedBox(width: 8),
InkWell(
@@ -1319,15 +1344,26 @@ class _CanvasPageState extends State<CanvasPage> {
color: Colors.white,
shape: BoxShape.circle,
),
- child: const Icon(Icons.check, size: 16, color: Colors.black),
+ child: const Icon(
+ Icons.check,
+ size: 16,
+ color: Colors.black,
+ ),
),
),
const SizedBox(width: 8),
InkWell(
- onTap: () => setState(() => _showBgRemovalBanner = false),
- child: const Icon(Icons.close, size: 18, color: Colors.white54),
+ onTap:
+ () => setState(
+ () => _showBgRemovalBanner = false,
+ ),
+ child: const Icon(
+ Icons.close,
+ size: 18,
+ color: Colors.white54,
+ ),
),
- ]
+ ],
],
),
),
@@ -1525,7 +1561,7 @@ class _CanvasPageState extends State<CanvasPage> {
Future<void> _confirmRemoveBackground() async {
if (_bgRemovalTargetId == null || _bgRemovalTargetPath == null) return;
-
+
_bgRemovalBannerTimer?.cancel();
setState(() {
@@ -1534,35 +1570,39 @@ class _CanvasPageState extends State<CanvasPage> {
try {
// Call service to remove background
- final newPath = await FlaskService().generateAsset(imagePath: _bgRemovalTargetPath!);
+ final newPath = await FlaskService().generateAsset(
+ imagePath: _bgRemovalTargetPath!,
+ );
if (newPath != null && mounted) {
// Find the element and update its content
final index = elements.indexWhere((e) => e['id'] == _bgRemovalTargetId);
if (index != -1) {
- final oldState = _getCurrentState();
- setState(() {
- elements[index]['content'] = newPath; // Replace with transparent png
- _showBgRemovalBanner = false;
- _isRemovingBg = false;
- _hasUnsavedChanges = true;
- });
- _recordChange(oldState);
+ final oldState = _getCurrentState();
+ setState(() {
+ elements[index]['content'] =
+ newPath; // Replace with transparent png
+ _showBgRemovalBanner = false;
+ _isRemovingBg = false;
+ _hasUnsavedChanges = true;
+ });
+ _recordChange(oldState);
}
}
} catch (e) {
debugPrint("BG Removal Failed: $e");
} finally {
- if (mounted) setState(() {
- _isRemovingBg = false;
- _showBgRemovalBanner = false;
- });
+ if (mounted)
+ setState(() {
+ _isRemovingBg = false;
+ _showBgRemovalBanner = false;
+ });
}
}
Future<void> _processInpainting(String prompt, String modelId) async {
// Lock editing and hide keyboard
- FocusScope.of(context).unfocus();
+ FocusScope.of(context).unfocus();
setState(() => _isInpainting = true);
_resetInactivityTimer();
@@ -1591,15 +1631,15 @@ class _CanvasPageState extends State<CanvasPage> {
// CASE 1: INPAINTING (When hasImageLayers is TRUE)
if (hasImageLayers) {
if (modelId == 'inpaint_api') {
- // Call API Inpainting
- newImageUrl = await FlaskService().inpaintApiImage(
+ // Call API Inpainting
+ newImageUrl = await FlaskService().inpaintApiImage(
imagePath: _tempBaseImage!.path,
maskPath: maskFile.path,
prompt: prompt,
);
} else {
- // Local Inpainting
- newImageUrl = await FlaskService().inpaintImage(
+ // Local Inpainting
+ newImageUrl = await FlaskService().inpaintImage(
imagePath: _tempBaseImage!.path,
maskPath: maskFile.path,
prompt: prompt,
@@ -1608,15 +1648,14 @@ class _CanvasPageState extends State<CanvasPage> {
}
// CASE 2: SKETCH TO IMAGE (When hasImageLayers is FALSE)
else {
- if(modelId == 'sketch_fusion'){
+ if (modelId == 'sketch_fusion') {
newImageUrl = await FlaskService().sketchToImage(
projectId: widget.projectId,
sketchPath: _tempBaseImage!.path,
userPrompt: prompt,
imageDescription: _aiDescription,
);
- }
- else if(modelId == 'sketch_advanced'){
+ } else if (modelId == 'sketch_advanced') {
newImageUrl = await FlaskService().sketchToImageAPI(
projectId: widget.projectId,
sketchPath: _tempBaseImage!.path,
@@ -1624,8 +1663,7 @@ class _CanvasPageState extends State<CanvasPage> {
option: 1,
imageDescription: _aiDescription,
);
- }
- else if (modelId == 'sketch_creative') {
+ } else if (modelId == 'sketch_creative') {
newImageUrl = await FlaskService().sketchToImageAPI(
projectId: widget.projectId,
sketchPath: _tempBaseImage!.path,
diff --git a/lib/ui/pages/canvas_toolbar/magic_draw_overlay.dart b/lib/ui/pages/canvas_toolbar/magic_draw_overlay.dart
@@ -81,13 +81,13 @@ class _MagicDrawToolsState extends State<MagicDrawTools> {
// --- 2. LIST FOR SKETCH TO IMAGE (When No Image Exists) ---
final List<AIModelOption> _sketchModels = [
+ AIModelOption(id: 'sketch_advanced', name: 'Nano Banana', badge: 'Premium'),
+ AIModelOption(id: 'sketch_creative', name: 'FLUX Dev', badge: 'Fast'),
AIModelOption(
- id: 'sketch_advanced',
- name: 'Nano Banana',
- badge: 'Premium',
+ id: 'sketch_fusion',
+ name: 'Stable Diffusion v1.5',
+ badge: null,
),
- AIModelOption(id: 'sketch_creative', name: 'FLUX Dev', badge: 'Fast'),
- AIModelOption(id: 'sketch_fusion', name: 'Stable Diffusion v1.5', badge: null),
];
@override
diff --git a/lib/ui/pages/home_page.dart b/lib/ui/pages/home_page.dart
@@ -198,65 +198,84 @@ class _HomePageState extends State<HomePage> {
final didRename = await showDialog<bool>(
context: context,
- builder: (ctx) => AlertDialog(
- backgroundColor: theme.cardColor,
- title: Text(
- 'Rename Project',
- style: TextStyle(
- fontFamily: 'GeneralSans',
- fontSize: 18,
- color: theme.colorScheme.onSurface,
- ),
- ),
- content: TextField(
- controller: controller,
- autofocus: true,
- style: TextStyle(fontFamily: 'GeneralSans', color: theme.colorScheme.onSurface),
- decoration: InputDecoration(
- hintText: 'Enter new name',
- hintStyle: TextStyle(color: theme.colorScheme.onSurface.withValues(alpha: 0.5)),
- enabledBorder: UnderlineInputBorder(
- borderSide: BorderSide(color: theme.colorScheme.primary.withValues(alpha: 0.5)),
+ builder:
+ (ctx) => AlertDialog(
+ backgroundColor: theme.cardColor,
+ title: Text(
+ 'Rename Project',
+ style: TextStyle(
+ fontFamily: 'GeneralSans',
+ fontSize: 18,
+ color: theme.colorScheme.onSurface,
+ ),
),
- focusedBorder: UnderlineInputBorder(
- borderSide: BorderSide(color: theme.colorScheme.primary),
+ content: TextField(
+ controller: controller,
+ autofocus: true,
+ style: TextStyle(
+ fontFamily: 'GeneralSans',
+ color: theme.colorScheme.onSurface,
+ ),
+ decoration: InputDecoration(
+ hintText: 'Enter new name',
+ hintStyle: TextStyle(
+ color: theme.colorScheme.onSurface.withValues(alpha: 0.5),
+ ),
+ enabledBorder: UnderlineInputBorder(
+ borderSide: BorderSide(
+ color: theme.colorScheme.primary.withValues(alpha: 0.5),
+ ),
+ ),
+ focusedBorder: UnderlineInputBorder(
+ borderSide: BorderSide(color: theme.colorScheme.primary),
+ ),
+ ),
+ textCapitalization: TextCapitalization.sentences,
),
+ actions: [
+ TextButton(
+ child: Text(
+ 'Cancel',
+ style: TextStyle(
+ color: theme.colorScheme.onSurface.withValues(alpha: 0.7),
+ ),
+ ),
+ onPressed: () => Navigator.pop(ctx, false),
+ ),
+ TextButton(
+ child: Text(
+ 'Save',
+ style: TextStyle(color: theme.colorScheme.primary),
+ ),
+ onPressed: () {
+ if (controller.text.trim().isNotEmpty) {
+ Navigator.pop(ctx, true);
+ }
+ },
+ ),
+ ],
),
- textCapitalization: TextCapitalization.sentences,
- ),
- actions: [
- TextButton(
- child: Text('Cancel', style: TextStyle(color: theme.colorScheme.onSurface.withValues(alpha: 0.7))),
- onPressed: () => Navigator.pop(ctx, false),
- ),
- TextButton(
- child: Text('Save', style: TextStyle(color: theme.colorScheme.primary)),
- onPressed: () {
- if (controller.text.trim().isNotEmpty) {
- Navigator.pop(ctx, true);
- }
- },
- ),
- ],
- ),
);
if (didRename == true && project.id != null) {
setState(() => _isLoading = true);
try {
- await _projectService.updateProjectDetails(project.id!, title: controller.text.trim());
+ await _projectService.updateProjectDetails(
+ project.id!,
+ title: controller.text.trim(),
+ );
if (mounted) {
- ScaffoldMessenger.of(context).showSnackBar(
- const SnackBar(content: Text('Project renamed')),
- );
+ ScaffoldMessenger.of(
+ context,
+ ).showSnackBar(const SnackBar(content: Text('Project renamed')));
}
_loadData();
} catch (e) {
if (mounted) {
setState(() => _isLoading = false);
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(content: Text('Error renaming: $e')),
- );
+ ScaffoldMessenger.of(
+ context,
+ ).showSnackBar(SnackBar(content: Text('Error renaming: $e')));
}
}
}
@@ -267,31 +286,43 @@ class _HomePageState extends State<HomePage> {
final theme = Theme.of(context);
final confirm = await showDialog<bool>(
context: context,
- builder: (ctx) => AlertDialog(
- backgroundColor: theme.cardColor,
- title: Text(
- 'Delete Project',
- style: TextStyle(
- fontFamily: 'GeneralSans',
- fontSize: 18,
- color: theme.colorScheme.onSurface,
- ),
- ),
- content: Text(
- 'Are you sure you want to delete "${project.title}"? This cannot be undone.',
- style: TextStyle(fontFamily: 'GeneralSans', color: theme.colorScheme.onSurface.withValues(alpha: 0.8)),
- ),
- actions: [
- TextButton(
- child: Text('Cancel', style: TextStyle(color: theme.colorScheme.onSurface.withValues(alpha: 0.7))),
- onPressed: () => Navigator.pop(ctx, false),
- ),
- TextButton(
- child: const Text('Delete', style: TextStyle(color: Colors.red)),
- onPressed: () => Navigator.pop(ctx, true),
+ builder:
+ (ctx) => AlertDialog(
+ backgroundColor: theme.cardColor,
+ title: Text(
+ 'Delete Project',
+ style: TextStyle(
+ fontFamily: 'GeneralSans',
+ fontSize: 18,
+ color: theme.colorScheme.onSurface,
+ ),
+ ),
+ content: Text(
+ 'Are you sure you want to delete "${project.title}"? This cannot be undone.',
+ style: TextStyle(
+ fontFamily: 'GeneralSans',
+ color: theme.colorScheme.onSurface.withValues(alpha: 0.8),
+ ),
+ ),
+ actions: [
+ TextButton(
+ child: Text(
+ 'Cancel',
+ style: TextStyle(
+ color: theme.colorScheme.onSurface.withValues(alpha: 0.7),
+ ),
+ ),
+ onPressed: () => Navigator.pop(ctx, false),
+ ),
+ TextButton(
+ child: const Text(
+ 'Delete',
+ style: TextStyle(color: Colors.red),
+ ),
+ onPressed: () => Navigator.pop(ctx, true),
+ ),
+ ],
),
- ],
- ),
);
if (confirm == true && project.id != null) {
@@ -307,9 +338,9 @@ class _HomePageState extends State<HomePage> {
} catch (e) {
if (mounted) {
setState(() => _isLoading = false);
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(content: Text('Error deleting project: $e')),
- );
+ ScaffoldMessenger.of(
+ context,
+ ).showSnackBar(SnackBar(content: Text('Error deleting project: $e')));
}
}
}
@@ -347,12 +378,13 @@ class _HomePageState extends State<HomePage> {
Navigator.push(
context,
MaterialPageRoute(
- builder: (_) => CanvasPage(
- projectId: file.projectId,
- width: width,
- height: height,
- existingFile: file,
- ),
+ builder:
+ (_) => CanvasPage(
+ projectId: file.projectId,
+ width: width,
+ height: height,
+ existingFile: file,
+ ),
),
).then((_) => _loadData());
}
@@ -575,7 +607,7 @@ class _HomePageState extends State<HomePage> {
onRename: () => _renameProject(p),
onDelete: () => _deleteProject(p),
isHorizontal: false,
- showGrid: false,
+ showGrid: false,
),
),
),
@@ -594,24 +626,23 @@ class _HomePageState extends State<HomePage> {
),
),
const SizedBox(height: 8),
- ...filteredFiles.map(
- (f) {
- final meta = _fileMetadata[f.id] ?? {};
- return Padding(
- padding: const EdgeInsets.only(bottom: 12),
- child: _FileCard(
- file: f,
- theme: theme,
- isDark: isDark,
- breadcrumb: _getProjectBreadcrumb(f),
- dimensions: meta['dimensions'] ?? 'Unknown',
- previewPath: meta['preview'] ?? '',
- timeAgo: _formatTimeAgo(f.lastUpdated),
- onTap: () => _openFile(f),
- ),
- );
- },
- ),
+ ...filteredFiles.map((f) {
+ final meta = _fileMetadata[f.id] ?? {};
+ return Padding(
+ padding: const EdgeInsets.only(bottom: 12),
+ child: _FileCard(
+ file: f,
+ theme: theme,
+ isDark: isDark,
+ breadcrumb: _getProjectBreadcrumb(f),
+ dimensions:
+ meta['dimensions'] ?? 'Unknown',
+ previewPath: meta['preview'] ?? '',
+ timeAgo: _formatTimeAgo(f.lastUpdated),
+ onTap: () => _openFile(f),
+ ),
+ );
+ }),
],
]),
),
@@ -634,33 +665,27 @@ class _HomePageState extends State<HomePage> {
),
),
const SizedBox(height: 12),
- ...(_recentFiles
- .take(2)
- .map(
- (file) {
- final meta = _fileMetadata[file.id] ?? {};
- return Padding(
- padding: const EdgeInsets.only(
- bottom: 12,
- ),
- child: _FileCard(
- file: file,
- theme: theme,
- isDark: isDark,
- breadcrumb: _getProjectBreadcrumb(
- file,
- ),
- dimensions: meta['dimensions'] ?? 'Unknown',
- previewPath: meta['preview'] ?? '',
- timeAgo: _formatTimeAgo(
- file.lastUpdated,
- ),
- onTap: () => _openFile(file),
- ),
- );
- },
- )
- .toList()),
+ ...(_recentFiles.take(2).map((file) {
+ final meta = _fileMetadata[file.id] ?? {};
+ return Padding(
+ padding: const EdgeInsets.only(
+ bottom: 12,
+ ),
+ child: _FileCard(
+ file: file,
+ theme: theme,
+ isDark: isDark,
+ breadcrumb: _getProjectBreadcrumb(file),
+ dimensions:
+ meta['dimensions'] ?? 'Unknown',
+ previewPath: meta['preview'] ?? '',
+ timeAgo: _formatTimeAgo(
+ file.lastUpdated,
+ ),
+ onTap: () => _openFile(file),
+ ),
+ );
+ }).toList()),
const SizedBox(height: 24),
],
_buildSectionHeader(
@@ -673,37 +698,41 @@ class _HomePageState extends State<HomePage> {
),
),
const SizedBox(height: 12),
-
+
// Show "Create Project" if empty, else list
_allProjects.isEmpty
? _buildCreateProjectCard(theme, isDark)
: SizedBox(
- height: 140, // Reduced height
- child: ListView.builder(
- scrollDirection: Axis.horizontal,
- itemCount: _allProjects.length,
- itemBuilder: (context, index) {
- final project = _allProjects[index];
- return Padding(
- padding: const EdgeInsets.only(
- right: 12,
- ),
- child: _ProjectCard(
- project: project,
- theme: theme,
- isDark: isDark,
- previewImages:
- _projectPreviews[project.id] ??
- [],
- onTap: () => _openProject(project),
- onRename: () => _renameProject(project),
- onDelete: () => _deleteProject(project),
- showGrid: false,
- ),
- );
- },
- ),
+ height: 140, // Reduced height
+ child: ListView.builder(
+ scrollDirection: Axis.horizontal,
+ itemCount: _allProjects.length,
+ itemBuilder: (context, index) {
+ final project = _allProjects[index];
+ return Padding(
+ padding: const EdgeInsets.only(
+ right: 12,
+ ),
+ child: _ProjectCard(
+ project: project,
+ theme: theme,
+ isDark: isDark,
+ previewImages:
+ _projectPreviews[project
+ .id] ??
+ [],
+ onTap:
+ () => _openProject(project),
+ onRename:
+ () => _renameProject(project),
+ onDelete:
+ () => _deleteProject(project),
+ showGrid: false,
+ ),
+ );
+ },
),
+ ),
const SizedBox(height: 24),
_buildSectionHeader(
'Explore templates',
@@ -722,14 +751,15 @@ class _HomePageState extends State<HomePage> {
),
),
),
- floatingActionButton: _allProjects.isEmpty
- ? null
- : FloatingActionButton(
- onPressed: _createNewProject,
- backgroundColor: isDark ? Colors.grey[900] : Colors.grey[900],
- foregroundColor: Colors.white,
- child: const Icon(Icons.add, size: 24),
- ),
+ floatingActionButton:
+ _allProjects.isEmpty
+ ? null
+ : FloatingActionButton(
+ onPressed: _createNewProject,
+ backgroundColor: isDark ? Colors.grey[900] : Colors.grey[900],
+ foregroundColor: Colors.white,
+ child: const Icon(Icons.add, size: 24),
+ ),
);
}
@@ -774,7 +804,7 @@ class _HomePageState extends State<HomePage> {
return GestureDetector(
onTap: _createNewProject,
child: Container(
- height: 130,
+ height: 130,
width: 130,
margin: const EdgeInsets.only(right: 12),
decoration: BoxDecoration(
@@ -941,14 +971,14 @@ class _FileCard extends StatelessWidget {
borderRadius: BorderRadius.circular(8),
),
child: Row(
- crossAxisAlignment: CrossAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: 88,
height: 88,
child: Center(
child: Container(
- width: 80,
+ width: 80,
height: 80,
decoration: BoxDecoration(
color: isDark ? Colors.grey[800] : Colors.grey[200],
@@ -963,34 +993,36 @@ class _FileCard extends StatelessWidget {
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
- child: previewPath.isNotEmpty
- ? Image.file(
- File(previewPath),
- fit: BoxFit.cover,
- errorBuilder: (context, error, stackTrace) =>
- Container(
- color: isDark ? Colors.grey[800] : Colors.grey[200],
- child: Icon(
- Icons.broken_image,
- size: 20,
- color:
- theme.colorScheme.onSurface.withValues(
- alpha: 0.3,
+ child:
+ previewPath.isNotEmpty
+ ? Image.file(
+ File(previewPath),
+ fit: BoxFit.cover,
+ errorBuilder:
+ (context, error, stackTrace) => Container(
+ color:
+ isDark
+ ? Colors.grey[800]
+ : Colors.grey[200],
+ child: Icon(
+ Icons.broken_image,
+ size: 20,
+ color: theme.colorScheme.onSurface
+ .withValues(alpha: 0.3),
),
- ),
- ),
- )
- : Container(
- color: isDark ? Colors.grey[800] : Colors.grey[200],
- child: Icon(
- Icons.image,
- size: 24,
- color:
- theme.colorScheme.onSurface.withValues(
- alpha: 0.3,
),
+ )
+ : Container(
+ color:
+ isDark ? Colors.grey[800] : Colors.grey[200],
+ child: Icon(
+ Icons.image,
+ size: 24,
+ color: theme.colorScheme.onSurface.withValues(
+ alpha: 0.3,
+ ),
+ ),
),
- ),
),
),
),
@@ -1001,7 +1033,8 @@ class _FileCard extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
- mainAxisSize: MainAxisSize.min, // Keep height minimal for centering
+ mainAxisSize:
+ MainAxisSize.min, // Keep height minimal for centering
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -1125,40 +1158,46 @@ class _ProjectCard extends StatelessWidget {
children: [
Expanded(
child: ClipRRect(
- borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
- child: showGrid
- ? Padding(
- padding: const EdgeInsets.all(4.0),
- child: _buildGridPreview(),
- )
- : (previewImages.isNotEmpty
- ? Image.file(
- File(previewImages.first),
- fit: BoxFit.cover,
- width: double.infinity,
- errorBuilder: (context, error, stackTrace) =>
- Container(
- color: isDark ? Colors.grey[800] : Colors.grey[200],
- child: Icon(
- Icons.broken_image,
- color: theme.colorScheme.onSurface.withValues(
- alpha: 0.3,
- ),
- ),
- ),
- )
- : Container(
- color: isDark ? Colors.grey[800] : Colors.grey[200],
- child: Center(
- child: Icon(
- Icons.folder_outlined,
- size: 32,
- color: theme.colorScheme.onSurface.withValues(
- alpha: 0.3,
+ borderRadius: const BorderRadius.vertical(
+ top: Radius.circular(12),
+ ),
+ child:
+ showGrid
+ ? Padding(
+ padding: const EdgeInsets.all(4.0),
+ child: _buildGridPreview(),
+ )
+ : (previewImages.isNotEmpty
+ ? Image.file(
+ File(previewImages.first),
+ fit: BoxFit.cover,
+ width: double.infinity,
+ errorBuilder:
+ (context, error, stackTrace) => Container(
+ color:
+ isDark
+ ? Colors.grey[800]
+ : Colors.grey[200],
+ child: Icon(
+ Icons.broken_image,
+ color: theme.colorScheme.onSurface
+ .withValues(alpha: 0.3),
+ ),
+ ),
+ )
+ : Container(
+ color:
+ isDark ? Colors.grey[800] : Colors.grey[200],
+ child: Center(
+ child: Icon(
+ Icons.folder_outlined,
+ size: 32,
+ color: theme.colorScheme.onSurface.withValues(
+ alpha: 0.3,
+ ),
),
),
- ),
- )),
+ )),
),
),
Padding(
@@ -1186,7 +1225,9 @@ class _ProjectCard extends StatelessWidget {
icon: Icon(
Icons.more_vert,
size: 16,
- color: theme.colorScheme.onSurface.withValues(alpha: 0.6),
+ color: theme.colorScheme.onSurface.withValues(
+ alpha: 0.6,
+ ),
),
padding: EdgeInsets.zero,
shape: RoundedRectangleBorder(
@@ -1199,28 +1240,42 @@ class _ProjectCard extends StatelessWidget {
onDelete!();
}
},
- itemBuilder: (context) => [
- const PopupMenuItem(
- value: 'rename',
- child: Row(
- children: [
- Icon(Icons.edit_outlined, size: 18),
- SizedBox(width: 12),
- Text('Rename', style: TextStyle(fontFamily: 'GeneralSans')),
- ],
- ),
- ),
- const PopupMenuItem(
- value: 'delete',
- child: Row(
- children: [
- Icon(Icons.delete_outline, size: 18, color: Colors.red),
- SizedBox(width: 12),
- Text('Delete', style: TextStyle(fontFamily: 'GeneralSans', color: Colors.red)),
- ],
- ),
- ),
- ],
+ itemBuilder:
+ (context) => [
+ const PopupMenuItem(
+ value: 'rename',
+ child: Row(
+ children: [
+ Icon(Icons.edit_outlined, size: 18),
+ SizedBox(width: 12),
+ Text(
+ 'Rename',
+ style: TextStyle(fontFamily: 'GeneralSans'),
+ ),
+ ],
+ ),
+ ),
+ const PopupMenuItem(
+ value: 'delete',
+ child: Row(
+ children: [
+ Icon(
+ Icons.delete_outline,
+ size: 18,
+ color: Colors.red,
+ ),
+ SizedBox(width: 12),
+ Text(
+ 'Delete',
+ style: TextStyle(
+ fontFamily: 'GeneralSans',
+ color: Colors.red,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
),
),
],
@@ -1282,16 +1337,21 @@ class _ProjectCard extends StatelessWidget {
borderRadius: BorderRadius.circular(6), // Rounded grid items
),
clipBehavior: Clip.antiAlias,
- child: imagePath != null
- ? Image.file(
- File(imagePath),
- fit: BoxFit.cover,
- width: double.infinity,
- height: double.infinity,
- errorBuilder: (context, error, stackTrace) =>
- const Icon(Icons.broken_image, size: 16, color: Colors.grey),
- )
- : null, // Empty placeholder
+ child:
+ imagePath != null
+ ? Image.file(
+ File(imagePath),
+ fit: BoxFit.cover,
+ width: double.infinity,
+ height: double.infinity,
+ errorBuilder:
+ (context, error, stackTrace) => const Icon(
+ Icons.broken_image,
+ size: 16,
+ color: Colors.grey,
+ ),
+ )
+ : null, // Empty placeholder
);
}
}
@@ -1348,12 +1408,12 @@ class _SeeAllPageState extends State<_SeeAllPage> {
List<String> eventImages = [];
// Get 1 image from up to 4 distinct events
for (final event in events.take(4)) {
- if (event.id != null) {
- final imgs = await widget.imageRepo.getImages(event.id!);
- if (imgs.isNotEmpty) {
- eventImages.add(imgs.first.filePath);
- }
- }
+ if (event.id != null) {
+ final imgs = await widget.imageRepo.getImages(event.id!);
+ if (imgs.isNotEmpty) {
+ eventImages.add(imgs.first.filePath);
+ }
+ }
}
_projectPreviews[p.id!] = eventImages;
}
@@ -1446,63 +1506,65 @@ class _SeeAllPageState extends State<_SeeAllPage> {
elevation: 0,
iconTheme: IconThemeData(color: theme.colorScheme.onSurface),
),
- body: _isLoading
- ? const Center(child: CircularProgressIndicator())
- : Padding(
- padding: const EdgeInsets.all(16.0),
- child: widget.isProjects
- ? GridView.builder(
- gridDelegate:
- const SliverGridDelegateWithFixedCrossAxisCount(
- crossAxisCount: 2,
- childAspectRatio: 0.8,
- crossAxisSpacing: 12,
- mainAxisSpacing: 12,
- ),
- itemCount: _items.length,
- itemBuilder: (context, index) {
- final project = _items[index] as ProjectModel;
- return _ProjectCard(
- project: project,
- theme: theme,
- isDark: isDark,
- previewImages: _projectPreviews[project.id] ?? [],
- onTap: () => widget.onProjectTap(project),
- // Chain callbacks, reload data on completion
- onRename: () async {
- await widget.onProjectRename(project);
- _fetchData();
+ body:
+ _isLoading
+ ? const Center(child: CircularProgressIndicator())
+ : Padding(
+ padding: const EdgeInsets.all(16.0),
+ child:
+ widget.isProjects
+ ? GridView.builder(
+ gridDelegate:
+ const SliverGridDelegateWithFixedCrossAxisCount(
+ crossAxisCount: 2,
+ childAspectRatio: 0.8,
+ crossAxisSpacing: 12,
+ mainAxisSpacing: 12,
+ ),
+ itemCount: _items.length,
+ itemBuilder: (context, index) {
+ final project = _items[index] as ProjectModel;
+ return _ProjectCard(
+ project: project,
+ theme: theme,
+ isDark: isDark,
+ previewImages: _projectPreviews[project.id] ?? [],
+ onTap: () => widget.onProjectTap(project),
+ // Chain callbacks, reload data on completion
+ onRename: () async {
+ await widget.onProjectRename(project);
+ _fetchData();
+ },
+ onDelete: () async {
+ await widget.onProjectDelete(project);
+ _fetchData();
+ },
+ isHorizontal: false,
+ showGrid: true,
+ );
},
- onDelete: () async {
- await widget.onProjectDelete(project);
- _fetchData();
+ )
+ : ListView.builder(
+ itemCount: _items.length,
+ itemBuilder: (context, index) {
+ final file = _items[index] as FileModel;
+ final meta = _fileMetadata[file.id] ?? {};
+ return Padding(
+ padding: const EdgeInsets.only(bottom: 12),
+ child: _FileCard(
+ file: file,
+ theme: theme,
+ isDark: isDark,
+ breadcrumb: _getBreadcrumb(file),
+ dimensions: meta['dimensions'] ?? 'Unknown',
+ previewPath: meta['preview'] ?? '',
+ timeAgo: _timeAgo(file.lastUpdated),
+ onTap: () => widget.onFileTap(file),
+ ),
+ );
},
- isHorizontal: false,
- showGrid: true,
- );
- },
- )
- : ListView.builder(
- itemCount: _items.length,
- itemBuilder: (context, index) {
- final file = _items[index] as FileModel;
- final meta = _fileMetadata[file.id] ?? {};
- return Padding(
- padding: const EdgeInsets.only(bottom: 12),
- child: _FileCard(
- file: file,
- theme: theme,
- isDark: isDark,
- breadcrumb: _getBreadcrumb(file),
- dimensions: meta['dimensions'] ?? 'Unknown',
- previewPath: meta['preview'] ?? '',
- timeAgo: _timeAgo(file.lastUpdated),
- onTap: () => widget.onFileTap(file),
- ),
- );
- },
- ),
- ),
+ ),
+ ),
);
}
}
diff --git a/lib/ui/pages/image_save_page.dart b/lib/ui/pages/image_save_page.dart
@@ -566,7 +566,8 @@ class _ImageSavePageState extends State<ImageSavePage> {
height: 16 / 12,
),
decoration: InputDecoration(
- hintText: "I love the serif font and how it is used...",
+ hintText:
+ "I love the serif font and how it is used...",
hintStyle: const TextStyle(
fontFamily: 'GeneralSans',
fontSize: 12,
@@ -703,10 +704,11 @@ class _ImageSavePageState extends State<ImageSavePage> {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
- builder: (_) => ProjectBoardPage(
- projectId: widget.projectId,
- initialShowAlternateView: true, // Show grid view
- ),
+ builder:
+ (_) => ProjectBoardPage(
+ projectId: widget.projectId,
+ initialShowAlternateView: true, // Show grid view
+ ),
),
(route) => false, // Remove all previous routes
);
@@ -764,21 +766,13 @@ class _ImageSavePageState extends State<ImageSavePage> {
// CONFIRM SELECTION BUTTON (Visible only in resizing mode)
if (_isResizing && _finalSelectionRect != null)
IconButton(
- icon: const Icon(
- Icons.check,
- color: Color(0xFF7C86FF),
- size: 24,
- ),
+ icon: const Icon(Icons.check, color: Color(0xFF7C86FF), size: 24),
onPressed: _confirmSelectionAndShowModal,
),
// CANCEL SELECTION BUTTON (Visible only in drawing/resizing mode)
if (_isDrawMode || _isResizing)
IconButton(
- icon: const Icon(
- Icons.close,
- color: Colors.black87,
- size: 24,
- ),
+ icon: const Icon(Icons.close, color: Colors.black87, size: 24),
onPressed: _resetSelectionMode,
),
],
diff --git a/lib/ui/pages/project_board_page.dart b/lib/ui/pages/project_board_page.dart
@@ -180,10 +180,11 @@ class _ProjectBoardPageState extends State<ProjectBoardPage> {
Navigator.push(
context,
MaterialPageRoute(
- builder: (_) => StylesheetPage(
- projectId: _currentProject!.id!,
- autoGenerate: true,
- ),
+ builder:
+ (_) => StylesheetPage(
+ projectId: _currentProject!.id!,
+ autoGenerate: true,
+ ),
),
);
},
diff --git a/lib/ui/pages/project_detail_page.dart b/lib/ui/pages/project_detail_page.dart
@@ -92,226 +92,229 @@ class _ProjectDetailPageState extends State<ProjectDetailPage> {
context: context,
backgroundColor: Colors.transparent,
isScrollControlled: true,
- builder: (context) => Container(
- decoration: const BoxDecoration(
- color: Color(0xFFFAFAFA),
- borderRadius: BorderRadius.only(
- topLeft: Radius.circular(24),
- topRight: Radius.circular(24),
- ),
- ),
- child: Padding(
- padding: EdgeInsets.only(
- bottom: MediaQuery.of(context).viewInsets.bottom,
- ),
- child: Column(
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- // Drag handle
- Container(
- padding: const EdgeInsets.only(top: 8, bottom: 16),
- child: Center(
- child: Container(
- width: 48,
- height: 2,
- decoration: BoxDecoration(
- color: const Color(0xFF71717B),
- borderRadius: BorderRadius.circular(100),
- ),
- ),
- ),
+ builder:
+ (context) => Container(
+ decoration: const BoxDecoration(
+ color: Color(0xFFFAFAFA),
+ borderRadius: BorderRadius.only(
+ topLeft: Radius.circular(24),
+ topRight: Radius.circular(24),
),
- // Header section
- Container(
- padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
- decoration: const BoxDecoration(
- border: Border(
- bottom: BorderSide(
- color: Color(0xFFE4E4E7),
- width: 1,
- ),
- ),
- ),
- child: const Text(
- "Event Details",
- style: TextStyle(
- fontFamily: 'GeneralSans',
- fontSize: 14,
- fontWeight: FontWeight.normal,
- color: Color(0xFF27272A),
- height: 20 / 14,
- ),
- ),
+ ),
+ child: Padding(
+ padding: EdgeInsets.only(
+ bottom: MediaQuery.of(context).viewInsets.bottom,
),
- // Content section
- Padding(
- padding: const EdgeInsets.all(16),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- // Name field
- Container(
- height: 40,
- decoration: BoxDecoration(
- color: const Color(0xFFE4E4E7),
- borderRadius: BorderRadius.circular(8),
- ),
- child: TextField(
- controller: nameController,
- autofocus: true,
- style: const TextStyle(
- fontFamily: 'GeneralSans',
- fontSize: 12,
- fontWeight: FontWeight.normal,
- color: Color(0xFF27272A),
- height: 16 / 12,
- ),
- decoration: const InputDecoration(
- hintText: "Add Name*",
- hintStyle: TextStyle(
- fontFamily: 'GeneralSans',
- fontSize: 12,
- fontWeight: FontWeight.normal,
- color: Color(0xFF71717B),
- height: 16 / 12,
- ),
- contentPadding: EdgeInsets.symmetric(
- horizontal: 16,
- vertical: 12,
- ),
- border: InputBorder.none,
- enabledBorder: InputBorder.none,
- focusedBorder: InputBorder.none,
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // Drag handle
+ Container(
+ padding: const EdgeInsets.only(top: 8, bottom: 16),
+ child: Center(
+ child: Container(
+ width: 48,
+ height: 2,
+ decoration: BoxDecoration(
+ color: const Color(0xFF71717B),
+ borderRadius: BorderRadius.circular(100),
),
),
),
- const SizedBox(height: 16),
- // Description field
- Container(
- height: 80,
- decoration: BoxDecoration(
- color: const Color(0xFFE4E4E7),
- borderRadius: BorderRadius.circular(8),
+ ),
+ // Header section
+ Container(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 16,
+ vertical: 8,
+ ),
+ decoration: const BoxDecoration(
+ border: Border(
+ bottom: BorderSide(color: Color(0xFFE4E4E7), width: 1),
),
- child: TextField(
- controller: descriptionController,
- maxLines: null,
- expands: true,
- textAlignVertical: TextAlignVertical.top,
- style: const TextStyle(
- fontFamily: 'GeneralSans',
- fontSize: 12,
- fontWeight: FontWeight.normal,
- color: Color(0xFF27272A),
- height: 16 / 12,
- ),
- decoration: const InputDecoration(
- hintText: "Add Description",
- hintStyle: TextStyle(
- fontFamily: 'GeneralSans',
- fontSize: 12,
- fontWeight: FontWeight.normal,
- color: Color(0xFF71717B),
- height: 16 / 12,
- ),
- contentPadding: EdgeInsets.symmetric(
- horizontal: 16,
- vertical: 12,
- ),
- border: InputBorder.none,
- enabledBorder: InputBorder.none,
- focusedBorder: InputBorder.none,
- ),
+ ),
+ child: const Text(
+ "Event Details",
+ style: TextStyle(
+ fontFamily: 'GeneralSans',
+ fontSize: 14,
+ fontWeight: FontWeight.normal,
+ color: Color(0xFF27272A),
+ height: 20 / 14,
),
),
- ],
- ),
- ),
- // Bottom actions
- Container(
- padding: const EdgeInsets.fromLTRB(16, 16, 16, 32),
- child: Row(
- children: [
- // Cancel button
- Expanded(
- child: GestureDetector(
- onTap: () => Navigator.pop(context),
- child: Container(
+ ),
+ // Content section
+ Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // Name field
+ Container(
height: 40,
decoration: BoxDecoration(
color: const Color(0xFFE4E4E7),
- borderRadius: BorderRadius.circular(1000),
+ borderRadius: BorderRadius.circular(8),
),
- child: const Center(
- child: Text(
- "Cancel",
- style: TextStyle(
+ child: TextField(
+ controller: nameController,
+ autofocus: true,
+ style: const TextStyle(
+ fontFamily: 'GeneralSans',
+ fontSize: 12,
+ fontWeight: FontWeight.normal,
+ color: Color(0xFF27272A),
+ height: 16 / 12,
+ ),
+ decoration: const InputDecoration(
+ hintText: "Add Name*",
+ hintStyle: TextStyle(
fontFamily: 'GeneralSans',
- fontSize: 14,
- fontWeight: FontWeight.w500,
- color: Color(0xFF27272A),
- height: 20 / 14,
- letterSpacing: 0.25,
+ fontSize: 12,
+ fontWeight: FontWeight.normal,
+ color: Color(0xFF71717B),
+ height: 16 / 12,
+ ),
+ contentPadding: EdgeInsets.symmetric(
+ horizontal: 16,
+ vertical: 12,
),
+ border: InputBorder.none,
+ enabledBorder: InputBorder.none,
+ focusedBorder: InputBorder.none,
),
),
),
- ),
- ),
- const SizedBox(width: 8),
- // Add Event button
- Expanded(
- child: GestureDetector(
- onTap: () async {
- if (nameController.text.trim().isNotEmpty) {
- try {
- await _projectService.createProject(
- nameController.text.trim(),
- description:
- descriptionController.text.trim().isEmpty
- ? null
- : descriptionController.text.trim(),
- parentId: _project!.id,
- );
- if (context.mounted) {
- Navigator.pop(context);
- _loadData(); // Refresh to show new event
- }
- } catch (e) {
- debugPrint("Error creating event: $e");
- }
- }
- },
- child: Container(
- height: 40,
+ const SizedBox(height: 16),
+ // Description field
+ Container(
+ height: 80,
decoration: BoxDecoration(
- color: const Color(0xFF27272A),
- borderRadius: BorderRadius.circular(1000),
+ color: const Color(0xFFE4E4E7),
+ borderRadius: BorderRadius.circular(8),
),
- child: const Center(
- child: Text(
- "Add Event",
- style: TextStyle(
+ child: TextField(
+ controller: descriptionController,
+ maxLines: null,
+ expands: true,
+ textAlignVertical: TextAlignVertical.top,
+ style: const TextStyle(
+ fontFamily: 'GeneralSans',
+ fontSize: 12,
+ fontWeight: FontWeight.normal,
+ color: Color(0xFF27272A),
+ height: 16 / 12,
+ ),
+ decoration: const InputDecoration(
+ hintText: "Add Description",
+ hintStyle: TextStyle(
fontFamily: 'GeneralSans',
- fontSize: 14,
- fontWeight: FontWeight.w500,
- color: Color(0xFFFAFAFA),
- height: 20 / 14,
- letterSpacing: 0.25,
+ fontSize: 12,
+ fontWeight: FontWeight.normal,
+ color: Color(0xFF71717B),
+ height: 16 / 12,
),
+ contentPadding: EdgeInsets.symmetric(
+ horizontal: 16,
+ vertical: 12,
+ ),
+ border: InputBorder.none,
+ enabledBorder: InputBorder.none,
+ focusedBorder: InputBorder.none,
),
),
),
- ),
+ ],
),
- ],
- ),
+ ),
+ // Bottom actions
+ Container(
+ padding: const EdgeInsets.fromLTRB(16, 16, 16, 32),
+ child: Row(
+ children: [
+ // Cancel button
+ Expanded(
+ child: GestureDetector(
+ onTap: () => Navigator.pop(context),
+ child: Container(
+ height: 40,
+ decoration: BoxDecoration(
+ color: const Color(0xFFE4E4E7),
+ borderRadius: BorderRadius.circular(1000),
+ ),
+ child: const Center(
+ child: Text(
+ "Cancel",
+ style: TextStyle(
+ fontFamily: 'GeneralSans',
+ fontSize: 14,
+ fontWeight: FontWeight.w500,
+ color: Color(0xFF27272A),
+ height: 20 / 14,
+ letterSpacing: 0.25,
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ const SizedBox(width: 8),
+ // Add Event button
+ Expanded(
+ child: GestureDetector(
+ onTap: () async {
+ if (nameController.text.trim().isNotEmpty) {
+ try {
+ await _projectService.createProject(
+ nameController.text.trim(),
+ description:
+ descriptionController.text
+ .trim()
+ .isEmpty
+ ? null
+ : descriptionController.text.trim(),
+ parentId: _project!.id,
+ );
+ if (context.mounted) {
+ Navigator.pop(context);
+ _loadData(); // Refresh to show new event
+ }
+ } catch (e) {
+ debugPrint("Error creating event: $e");
+ }
+ }
+ },
+ child: Container(
+ height: 40,
+ decoration: BoxDecoration(
+ color: const Color(0xFF27272A),
+ borderRadius: BorderRadius.circular(1000),
+ ),
+ child: const Center(
+ child: Text(
+ "Add Event",
+ style: TextStyle(
+ fontFamily: 'GeneralSans',
+ fontSize: 14,
+ fontWeight: FontWeight.w500,
+ color: Color(0xFFFAFAFA),
+ height: 20 / 14,
+ letterSpacing: 0.25,
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
),
- ],
+ ),
),
- ),
- ),
);
}
@@ -395,7 +398,7 @@ class _ProjectDetailPageState extends State<ProjectDetailPage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 16),
-
+
// Action Cards Row
_buildActionCardsRow(_project!.id!),
@@ -415,13 +418,15 @@ class _ProjectDetailPageState extends State<ProjectDetailPage> {
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(12),
- border: Border.all(
- color: Colors.grey[300]!,
- ),
+ border: Border.all(color: Colors.grey[300]!),
),
child: Column(
children: [
- Icon(Icons.event_note, size: 48, color: Colors.grey[400]),
+ Icon(
+ Icons.event_note,
+ size: 48,
+ color: Colors.grey[400],
+ ),
const SizedBox(height: 8),
Text(
"No events created yet",
@@ -439,9 +444,7 @@ class _ProjectDetailPageState extends State<ProjectDetailPage> {
separatorBuilder: (_, __) => const SizedBox(height: 16),
itemBuilder: (context, index) {
final event = _events[index];
- return Center(
- child: _buildEventCard(event),
- );
+ return Center(child: _buildEventCard(event));
},
),
@@ -499,10 +502,7 @@ class _ProjectDetailPageState extends State<ProjectDetailPage> {
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
- border: Border.all(
- color: const Color(0xFFE4E4E7),
- width: 1,
- ),
+ border: Border.all(color: const Color(0xFFE4E4E7), width: 1),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -530,9 +530,7 @@ class _ProjectDetailPageState extends State<ProjectDetailPage> {
blobImage,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
- return Container(
- color: const Color(0xFF27272A),
- );
+ return Container(color: const Color(0xFF27272A));
},
),
),
@@ -606,12 +604,7 @@ class _ProjectDetailPageState extends State<ProjectDetailPage> {
return Container(
padding: const EdgeInsets.only(bottom: 8, top: 16),
decoration: const BoxDecoration(
- border: Border(
- bottom: BorderSide(
- color: Color(0xFFE4E4E7),
- width: 1,
- ),
- ),
+ border: Border(bottom: BorderSide(color: Color(0xFFE4E4E7), width: 1)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -652,7 +645,7 @@ class _ProjectDetailPageState extends State<ProjectDetailPage> {
Widget _buildEventCard(ProjectModel event) {
final eventImages = _eventImages[event.id] ?? [];
-
+
return GestureDetector(
onTap: () {
if (event.id != null) {
@@ -663,10 +656,7 @@ class _ProjectDetailPageState extends State<ProjectDetailPage> {
width: 328,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
- border: Border.all(
- color: const Color(0xFFE4E4E7),
- width: 1,
- ),
+ border: Border.all(color: const Color(0xFFE4E4E7), width: 1),
),
clipBehavior: Clip.antiAlias,
child: Column(
@@ -683,19 +673,16 @@ class _ProjectDetailPageState extends State<ProjectDetailPage> {
Expanded(
child: ClipRRect(
borderRadius: BorderRadius.circular(4),
- child: i < eventImages.length
- ? Image.file(
- File(eventImages[i].filePath),
- fit: BoxFit.cover,
- errorBuilder: (context, error, stackTrace) {
- return Container(
- color: Colors.grey[300],
- );
- },
- )
- : Container(
- color: Colors.grey[200],
- ),
+ child:
+ i < eventImages.length
+ ? Image.file(
+ File(eventImages[i].filePath),
+ fit: BoxFit.cover,
+ errorBuilder: (context, error, stackTrace) {
+ return Container(color: Colors.grey[300]);
+ },
+ )
+ : Container(color: Colors.grey[200]),
),
),
if (i < 2) const SizedBox(width: 4),
@@ -722,4 +709,4 @@ class _ProjectDetailPageState extends State<ProjectDetailPage> {
),
);
}
-}
-\ No newline at end of file
+}
diff --git a/lib/ui/pages/project_file_page.dart b/lib/ui/pages/project_file_page.dart
@@ -398,9 +398,6 @@ class _ProjectFilePageState extends State<ProjectFilePage> {
);
}
-
-
-
// ---------------------------------------
// BREADCRUMB
// ---------------------------------------
@@ -531,7 +528,9 @@ class _ProjectFilePageState extends State<ProjectFilePage> {
),
const SizedBox(height: 16),
Text(
- _search.isEmpty ? "No files yet" : "No files found",
+ _search.isEmpty
+ ? "No files yet"
+ : "No files found",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
@@ -626,38 +625,40 @@ class _ProjectFilePageState extends State<ProjectFilePage> {
_eventFiles.isEmpty
? Container(
- width: double.infinity,
- padding: const EdgeInsets.symmetric(vertical: 48),
- child: Column(
- children: [
- Icon(
- Icons.insert_drive_file_outlined,
- size: 64,
- color: Colors.grey[400],
- ),
- const SizedBox(height: 16),
- Text(
- "No files in this event yet",
- style: TextStyle(
- fontSize: 16,
- fontWeight: FontWeight.w500,
- fontFamily: 'GeneralSans',
- color: Colors.grey[600],
- ),
+ width: double.infinity,
+ padding: const EdgeInsets.symmetric(
+ vertical: 48,
+ ),
+ child: Column(
+ children: [
+ Icon(
+ Icons.insert_drive_file_outlined,
+ size: 64,
+ color: Colors.grey[400],
+ ),
+ const SizedBox(height: 16),
+ Text(
+ "No files in this event yet",
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ fontFamily: 'GeneralSans',
+ color: Colors.grey[600],
),
- const SizedBox(height: 8),
- Text(
- "Create a file to add it to this event",
- style: TextStyle(
- fontSize: 14,
- fontFamily: 'GeneralSans',
- color: Colors.grey[500],
- ),
- textAlign: TextAlign.center,
+ ),
+ const SizedBox(height: 8),
+ Text(
+ "Create a file to add it to this event",
+ style: TextStyle(
+ fontSize: 14,
+ fontFamily: 'GeneralSans',
+ color: Colors.grey[500],
),
- ],
- ),
- )
+ textAlign: TextAlign.center,
+ ),
+ ],
+ ),
+ )
: Column(
children:
_eventFiles
@@ -746,7 +747,6 @@ class _ProjectFilePageState extends State<ProjectFilePage> {
);
}
-
// ---------------------------------------
// FILTER
// ---------------------------------------
diff --git a/lib/ui/pages/share_handler_page.dart b/lib/ui/pages/share_handler_page.dart
@@ -82,10 +82,7 @@ class _ShareHandlerPageState extends State<ShareHandlerPage> {
Navigator.pushReplacement(
context,
MaterialPageRoute(
- builder:
- (_) => ShareToFilePage(
- sharedImage: tempFiles.first,
- ),
+ builder: (_) => ShareToFilePage(sharedImage: tempFiles.first),
),
);
} else {
diff --git a/lib/ui/pages/share_to_file_page.dart b/lib/ui/pages/share_to_file_page.dart
@@ -253,8 +253,6 @@ class _ShareToFilePageState extends State<ShareToFilePage> {
}
}
-
-
String _formatDate(DateTime date) {
final now = DateTime.now();
final diff = now.difference(date);
@@ -574,7 +572,6 @@ class _ShareToFilePageState extends State<ShareToFilePage> {
);
}
-
Widget _buildFileItem(FileModel file) {
final preview = _resolvePreviewPath(file);
diff --git a/lib/ui/pages/stylesheet_page.dart b/lib/ui/pages/stylesheet_page.dart
@@ -19,7 +19,11 @@ class StylesheetPage extends StatefulWidget {
final int projectId;
final bool autoGenerate;
- const StylesheetPage({super.key, required this.projectId, this.autoGenerate = false});
+ const StylesheetPage({
+ super.key,
+ required this.projectId,
+ this.autoGenerate = false,
+ });
@override
State<StylesheetPage> createState() => _StylesheetPageState();
@@ -95,14 +99,19 @@ class _StylesheetPageState extends State<StylesheetPage> {
String _formatLabel(String label) {
String clean = label.replaceAll(RegExp(r'[-_]'), ' ');
List<String> words = clean.split(' ');
- return words.map((w) {
- if (w.isEmpty) return '';
- return w[0].toUpperCase() + w.substring(1).toLowerCase();
- }).join(' ');
+ return words
+ .map((w) {
+ if (w.isEmpty) return '';
+ return w[0].toUpperCase() + w.substring(1).toLowerCase();
+ })
+ .join(' ');
}
String? _findAssetPath(Map<String, String> assetMap, String label) {
- String normalized = label.toLowerCase().replaceAll(RegExp(r'[^a-z0-9]'), '');
+ String normalized = label.toLowerCase().replaceAll(
+ RegExp(r'[^a-z0-9]'),
+ '',
+ );
return assetMap[normalized];
}
@@ -151,15 +160,27 @@ class _StylesheetPageState extends State<StylesheetPage> {
try {
final images = await ImageRepo().getImages(_currentProjectId);
- final List<String> analysisData = images.map((img) => img.analysisData).where((data) => data != null && data.isNotEmpty).cast<String>().toList();
+ final List<String> analysisData =
+ images
+ .map((img) => img.analysisData)
+ .where((data) => data != null && data.isNotEmpty)
+ .cast<String>()
+ .toList();
final notes = await NoteRepo().getNotesByProjectId(_currentProjectId);
- final List<String> noteAnalysisData = notes.map((n) => n.analysisData).where((data) => data != null && data.isNotEmpty).cast<String>().toList();
+ final List<String> noteAnalysisData =
+ notes
+ .map((n) => n.analysisData)
+ .where((data) => data != null && data.isNotEmpty)
+ .cast<String>()
+ .toList();
analysisData.addAll(noteAnalysisData);
if (analysisData.isEmpty) {
if (mounted) {
- ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("No analyzed images or notes found.")));
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(content: Text("No analyzed images or notes found.")),
+ );
}
return;
}
@@ -185,21 +206,28 @@ class _StylesheetPageState extends State<StylesheetPage> {
appBar: TopBar(
currentProjectId: _currentProjectId,
onBack: () => Navigator.of(context).pop(),
- onProjectChanged: (p) => setState(() {
- _currentProjectId = p.id!;
- _stylesheetData = null;
- _projectAssets = [];
- _logoPaths = [];
- _loadSavedStylesheet();
- }),
+ onProjectChanged:
+ (p) => setState(() {
+ _currentProjectId = p.id!;
+ _stylesheetData = null;
+ _projectAssets = [];
+ _logoPaths = [];
+ _loadSavedStylesheet();
+ }),
onSettingsPressed: () {},
),
- body: _isLoading
- ? const Center(child: CircularProgressIndicator(color: Variables.textPrimary))
- : (_stylesheetData == null && _rawJsonString == null)
+ body:
+ _isLoading
+ ? const Center(
+ child: CircularProgressIndicator(color: Variables.textPrimary),
+ )
+ : (_stylesheetData == null && _rawJsonString == null)
? _buildEmptyState()
: _buildContent(),
- bottomNavigationBar: BottomBar(currentTab: BottomBarItem.stylesheet, projectId: _currentProjectId),
+ bottomNavigationBar: BottomBar(
+ currentTab: BottomBarItem.stylesheet,
+ projectId: _currentProjectId,
+ ),
);
}
@@ -233,19 +261,22 @@ class _StylesheetPageState extends State<StylesheetPage> {
children: [
Center(child: _buildGenerateButton("Regenerate Stylesheet")),
const SizedBox(height: 24),
-
+
_buildLogosSection(null),
- if (data.graphics.isNotEmpty || _projectAssets.isNotEmpty) _buildGraphicsSection(data.graphics),
+ if (data.graphics.isNotEmpty || _projectAssets.isNotEmpty)
+ _buildGraphicsSection(data.graphics),
if (data.colors.isNotEmpty) _buildColorsSection(data.colors),
if (data.fonts.isNotEmpty) _buildFontsSection(data.fonts),
- if (data.compositions.isNotEmpty) _buildCompositionsSection(data.compositions),
- if (data.materialLook.isNotEmpty) _buildMaterialLookSection(data.materialLook),
+ if (data.compositions.isNotEmpty)
+ _buildCompositionsSection(data.compositions),
+ if (data.materialLook.isNotEmpty)
+ _buildMaterialLookSection(data.materialLook),
if (data.textures.isNotEmpty) _buildTexturesSection(data.textures),
if (data.lighting.isNotEmpty) _buildLightingSection(data.lighting),
if (data.style.isNotEmpty) _buildStyleSection(data.style),
if (data.era.isNotEmpty) _buildEraSection(data.era),
if (data.emotions.isNotEmpty) _buildEmotionsSection(data.emotions),
-
+
const SizedBox(height: 24),
],
),
@@ -259,8 +290,8 @@ class _StylesheetPageState extends State<StylesheetPage> {
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
decoration: BoxDecoration(
- color: Variables.textPrimary,
- borderRadius: BorderRadius.circular(112)
+ color: Variables.textPrimary,
+ borderRadius: BorderRadius.circular(112),
),
child: Row(
mainAxisSize: MainAxisSize.min,
@@ -270,9 +301,12 @@ class _StylesheetPageState extends State<StylesheetPage> {
const SizedBox(width: 8),
SvgPicture.asset(
'assets/icons/generate_icon.svg',
- width: 18,
+ width: 18,
height: 18,
- colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn),
+ colorFilter: const ColorFilter.mode(
+ Colors.white,
+ BlendMode.srcIn,
+ ),
),
],
),
@@ -281,7 +315,11 @@ class _StylesheetPageState extends State<StylesheetPage> {
}
// Modified to support a custom trailing widget (e.g. Eye Icon)
- Widget _buildSectionHeader(String title, {bool showArrow = true, Widget? trailing}) {
+ Widget _buildSectionHeader(
+ String title, {
+ bool showArrow = true,
+ Widget? trailing,
+ }) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Row(
@@ -308,7 +346,10 @@ class _StylesheetPageState extends State<StylesheetPage> {
'assets/icons/arrow-left-s-line.svg',
width: 24,
height: 24,
- colorFilter: const ColorFilter.mode(Variables.textPrimary, BlendMode.srcIn),
+ colorFilter: const ColorFilter.mode(
+ Variables.textPrimary,
+ BlendMode.srcIn,
+ ),
),
),
],
@@ -319,7 +360,9 @@ class _StylesheetPageState extends State<StylesheetPage> {
// --- Logos ---
Future<void> _pickLogo() async {
try {
- final XFile? pickedFile = await _picker.pickImage(source: ImageSource.gallery);
+ final XFile? pickedFile = await _picker.pickImage(
+ source: ImageSource.gallery,
+ );
if (pickedFile != null) {
setState(() {
_logoPaths.add(pickedFile.path);
@@ -335,10 +378,13 @@ class _StylesheetPageState extends State<StylesheetPage> {
if (data is List) {
for (var item in data) {
- if (item is Map && item.containsKey('path')) logoPaths.add(item['path'].toString());
- else if (item is String) logoPaths.add(item);
+ if (item is Map && item.containsKey('path'))
+ logoPaths.add(item['path'].toString());
+ else if (item is String)
+ logoPaths.add(item);
}
- } else if (data is String) logoPaths.add(data);
+ } else if (data is String)
+ logoPaths.add(data);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -364,8 +410,12 @@ class _StylesheetPageState extends State<StylesheetPage> {
child: Center(
child: SvgPicture.asset(
'assets/icons/add-line.svg',
- width: 20, height: 20,
- colorFilter: const ColorFilter.mode(Variables.textSecondary, BlendMode.srcIn),
+ width: 20,
+ height: 20,
+ colorFilter: const ColorFilter.mode(
+ Variables.textSecondary,
+ BlendMode.srcIn,
+ ),
),
),
),
@@ -397,7 +447,8 @@ class _StylesheetPageState extends State<StylesheetPage> {
scrollDirection: Axis.horizontal,
itemCount: graphicPaths.length,
separatorBuilder: (_, __) => const SizedBox(width: 8),
- itemBuilder: (context, index) => _buildGraphicCard(graphicPaths[index]),
+ itemBuilder:
+ (context, index) => _buildGraphicCard(graphicPaths[index]),
),
),
const SizedBox(height: 16),
@@ -418,14 +469,19 @@ class _StylesheetPageState extends State<StylesheetPage> {
color: Variables.surfaceSubtle,
),
clipBehavior: Clip.antiAlias,
- child: file != null
- ? Image.file(file, fit: BoxFit.cover)
- : Container(
- color: Variables.surfaceSubtle,
- child: const Center(
- child: Icon(Icons.broken_image, color: Colors.grey, size: 20),
+ child:
+ file != null
+ ? Image.file(file, fit: BoxFit.cover)
+ : Container(
+ color: Variables.surfaceSubtle,
+ child: const Center(
+ child: Icon(
+ Icons.broken_image,
+ color: Colors.grey,
+ size: 20,
+ ),
+ ),
),
- ),
);
},
);
@@ -478,19 +534,22 @@ class _StylesheetPageState extends State<StylesheetPage> {
Text(
"Aa",
style: sampleStyle.copyWith(
- fontSize: 28,
- height: 1.2,
- fontWeight: FontWeight.w400,
+ fontSize: 28,
+ height: 1.2,
+ fontWeight: FontWeight.w400,
color: Variables.textPrimary,
),
),
Text(
- displayFontName,
+ displayFontName,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
- fontFamily: 'GeneralSans', fontSize: 12, height: 16 / 12,
- color: Variables.textSecondary, fontWeight: FontWeight.w400,
+ fontFamily: 'GeneralSans',
+ fontSize: 12,
+ height: 16 / 12,
+ color: Variables.textSecondary,
+ fontWeight: FontWeight.w400,
),
),
],
@@ -507,7 +566,7 @@ class _StylesheetPageState extends State<StylesheetPage> {
children: [
// Using custom trailing widget for the Eye Icon toggle
_buildSectionHeader(
- "Colors",
+ "Colors",
showArrow: false,
trailing: GestureDetector(
onTap: () => setState(() => _showHexCodes = !_showHexCodes),
@@ -519,8 +578,10 @@ class _StylesheetPageState extends State<StylesheetPage> {
),
),
Wrap(
- spacing: 8, runSpacing: 8,
- children: colors.take(5).map((color) => _buildColorSwatch(color)).toList(),
+ spacing: 8,
+ runSpacing: 8,
+ children:
+ colors.take(5).map((color) => _buildColorSwatch(color)).toList(),
),
const SizedBox(height: 16),
],
@@ -528,25 +589,32 @@ class _StylesheetPageState extends State<StylesheetPage> {
}
Widget _buildColorSwatch(Color color) {
- final String hexCode = '#${color.value.toRadixString(16).padLeft(8, '0').substring(2).toUpperCase()}';
- final Color textColor = color.computeLuminance() > 0.5 ? Colors.black : Colors.white;
+ final String hexCode =
+ '#${color.value.toRadixString(16).padLeft(8, '0').substring(2).toUpperCase()}';
+ final Color textColor =
+ color.computeLuminance() > 0.5 ? Colors.black : Colors.white;
return Container(
- width: 104, height: 52,
- decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(8)),
+ width: 104,
+ height: 52,
+ decoration: BoxDecoration(
+ color: color,
+ borderRadius: BorderRadius.circular(8),
+ ),
alignment: Alignment.center,
// Only show text if _showHexCodes is true
- child: _showHexCodes
- ? Text(
- hexCode,
- style: TextStyle(
- fontFamily: 'GeneralSans',
- fontSize: 13,
- fontWeight: FontWeight.w500,
- color: textColor,
- ),
- )
- : null,
+ child:
+ _showHexCodes
+ ? Text(
+ hexCode,
+ style: TextStyle(
+ fontFamily: 'GeneralSans',
+ fontSize: 13,
+ fontWeight: FontWeight.w500,
+ color: textColor,
+ ),
+ )
+ : null,
);
}
@@ -570,11 +638,14 @@ class _StylesheetPageState extends State<StylesheetPage> {
child: Image.asset(
assetPath,
fit: BoxFit.cover,
- errorBuilder: (c, o, s) => Container(color: Variables.surfaceSubtle),
+ errorBuilder:
+ (c, o, s) => Container(color: Variables.surfaceSubtle),
),
),
Positioned(
- left: 0, right: 0, bottom: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
height: 40,
child: Container(
decoration: BoxDecoration(
@@ -587,7 +658,9 @@ class _StylesheetPageState extends State<StylesheetPage> {
),
),
Positioned(
- left: 4, right: 4, bottom: 4,
+ left: 4,
+ right: 4,
+ bottom: 4,
child: Text(
formattedLabel,
style: const TextStyle(
@@ -628,7 +701,11 @@ class _StylesheetPageState extends State<StylesheetPage> {
}
}
- Widget _buildAttributeSection(String title, List<String> items, Map<String, String>? assetMap) {
+ Widget _buildAttributeSection(
+ String title,
+ List<String> items,
+ Map<String, String>? assetMap,
+ ) {
if (items.isEmpty) return const SizedBox();
return Column(
@@ -643,7 +720,8 @@ class _StylesheetPageState extends State<StylesheetPage> {
separatorBuilder: (_, __) => const SizedBox(width: 8),
itemBuilder: (context, index) {
String label = items[index];
- String? assetPath = assetMap != null ? _findAssetPath(assetMap, label) : null;
+ String? assetPath =
+ assetMap != null ? _findAssetPath(assetMap, label) : null;
return _buildUnifiedCard(label, assetPath);
},
),
diff --git a/lib/ui/widgets/top_bar.dart b/lib/ui/widgets/top_bar.dart
@@ -36,9 +36,10 @@ class TopBar extends StatefulWidget implements PreferredSizeWidget {
State<TopBar> createState() => _TopBarState();
@override
- Size get preferredSize => hideSecondRow
- ? const Size.fromHeight(48.0) // Height for single row
- : const Size.fromHeight(88.0); // Height for two rows
+ Size get preferredSize =>
+ hideSecondRow
+ ? const Size.fromHeight(48.0) // Height for single row
+ : const Size.fromHeight(88.0); // Height for two rows
}
class _TopBarState extends State<TopBar> {
@@ -136,32 +137,33 @@ class _TopBarState extends State<TopBar> {
],
// Title
Expanded(
- child: widget.titleOverride != null
- ? Text(
- widget.titleOverride!,
- style: const TextStyle(
- fontFamily: 'GeneralSans',
- fontSize: 20,
- fontWeight: FontWeight.w500,
- height: 24 / 20, // line-height: 24px
- color: Variables.textPrimary,
- ),
- overflow: TextOverflow.ellipsis,
- )
- : (!_isLoading &&
+ child:
+ widget.titleOverride != null
+ ? Text(
+ widget.titleOverride!,
+ style: const TextStyle(
+ fontFamily: 'GeneralSans',
+ fontSize: 20,
+ fontWeight: FontWeight.w500,
+ height: 24 / 20, // line-height: 24px
+ color: Variables.textPrimary,
+ ),
+ overflow: TextOverflow.ellipsis,
+ )
+ : (!_isLoading &&
_currentProject != null &&
_rootProject != null)
? Text(
- _rootProject!.title,
- style: const TextStyle(
- fontFamily: 'GeneralSans',
- fontSize: 20,
- fontWeight: FontWeight.w500,
- height: 24 / 20,
- color: Variables.textPrimary,
- ),
- overflow: TextOverflow.ellipsis,
- )
+ _rootProject!.title,
+ style: const TextStyle(
+ fontFamily: 'GeneralSans',
+ fontSize: 20,
+ fontWeight: FontWeight.w500,
+ height: 24 / 20,
+ color: Variables.textPrimary,
+ ),
+ overflow: TextOverflow.ellipsis,
+ )
: const SizedBox(),
),
// Settings Icon (only show if second row is visible)
@@ -184,140 +186,92 @@ class _TopBarState extends State<TopBar> {
// Second Row: Global Dropdown and Action Buttons
if (!widget.hideSecondRow)
Container(
- height: 40.0, // py-[8px] = 16px + 24px content
- padding: const EdgeInsets.symmetric(horizontal: 16.0),
- child: Row(
- children: [
- // Left group: Global Dropdown and Layout Button
- Row(
- children: [
- // Global Dropdown
- if (!_isLoading &&
- _currentProject != null &&
- _rootProject != null)
- PopupMenuButton<ProjectModel>(
- padding: EdgeInsets.zero,
- onSelected: (project) =>
- widget.onProjectChanged?.call(project),
- color: Colors.white,
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(8),
- ),
- offset: const Offset(0, 42),
- child: Container(
- padding: const EdgeInsets.symmetric(
- horizontal: 16,
- vertical: 10,
- ),
- decoration: BoxDecoration(
- color: Variables.borderSubtle, // #e4e4e7
- borderRadius: BorderRadius.circular(1000),
- ),
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- Text(
- _currentProject!.id == _rootProject!.id
- ? "Global"
- : _currentProject!.title,
- style: const TextStyle(
- fontFamily: 'GeneralSans',
- fontSize: 14,
- fontWeight: FontWeight.w500,
- height: 20 / 14,
- color: Variables.textPrimary,
- ),
- ),
- const SizedBox(width: 8),
- const Icon(
- Icons.keyboard_arrow_down_rounded,
- size: 20,
- color: Variables.textPrimary,
- ),
- ],
- ),
- ),
- itemBuilder: (context) {
- return _contextList.map((project) {
- final isSelected = project.id == _currentProject!.id;
- final isRoot = project.id == _rootProject!.id;
- return PopupMenuItem<ProjectModel>(
- value: project,
- child: Text(
- isRoot ? "Global" : project.title,
- style: Variables.bodyStyle.copyWith(
- fontFamily: 'GeneralSans',
- fontWeight: isSelected
- ? FontWeight.bold
- : FontWeight.normal,
- color: isSelected
- ? Variables.textPrimary
- : Variables.textSecondary,
- ),
- ),
- );
- }).toList();
- },
- ),
- // Layout Icon Button (Toggle between All Images/Categorized) or Edit Icon
- if (widget.onLayoutToggle != null || widget.onLayoutPressed != null) ...[
- const SizedBox(width: 8),
- GestureDetector(
- onTap: widget.onLayoutToggle ?? widget.onLayoutPressed,
- child: Container(
- padding: const EdgeInsets.symmetric(
- horizontal: 16,
- vertical: 10,
- ),
- decoration: BoxDecoration(
- color: Variables.borderSubtle,
- borderRadius: BorderRadius.circular(1000),
- ),
- child: Icon(
- widget.onLayoutToggle != null
- ? (widget.isAlternateView == true
- ? Icons.dashboard
- : Icons.view_agenda_outlined)
- : Icons.edit, // Use edit icon when only onLayoutPressed is provided
- size: 20,
- color: Variables.textPrimary,
- ),
- ),
- ),
- ],
- ],
- ),
- const Spacer(),
- // Right group: Filter and AI/Sparkle Buttons
- if (widget.onFilterPressed != null || widget.onAIPressed != null)
+ height: 40.0, // py-[8px] = 16px + 24px content
+ padding: const EdgeInsets.symmetric(horizontal: 16.0),
+ child: Row(
+ children: [
+ // Left group: Global Dropdown and Layout Button
Row(
children: [
- // Filter Icon Button
- if (widget.onFilterPressed != null)
- GestureDetector(
- onTap: widget.onFilterPressed,
+ // Global Dropdown
+ if (!_isLoading &&
+ _currentProject != null &&
+ _rootProject != null)
+ PopupMenuButton<ProjectModel>(
+ padding: EdgeInsets.zero,
+ onSelected:
+ (project) =>
+ widget.onProjectChanged?.call(project),
+ color: Colors.white,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(8),
+ ),
+ offset: const Offset(0, 42),
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 10,
),
decoration: BoxDecoration(
- color: Variables.borderSubtle,
+ color: Variables.borderSubtle, // #e4e4e7
borderRadius: BorderRadius.circular(1000),
),
- child: const Icon(
- Icons.tune,
- size: 20,
- color: Variables.textPrimary,
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ _currentProject!.id == _rootProject!.id
+ ? "Global"
+ : _currentProject!.title,
+ style: const TextStyle(
+ fontFamily: 'GeneralSans',
+ fontSize: 14,
+ fontWeight: FontWeight.w500,
+ height: 20 / 14,
+ color: Variables.textPrimary,
+ ),
+ ),
+ const SizedBox(width: 8),
+ const Icon(
+ Icons.keyboard_arrow_down_rounded,
+ size: 20,
+ color: Variables.textPrimary,
+ ),
+ ],
),
),
+ itemBuilder: (context) {
+ return _contextList.map((project) {
+ final isSelected =
+ project.id == _currentProject!.id;
+ final isRoot = project.id == _rootProject!.id;
+ return PopupMenuItem<ProjectModel>(
+ value: project,
+ child: Text(
+ isRoot ? "Global" : project.title,
+ style: Variables.bodyStyle.copyWith(
+ fontFamily: 'GeneralSans',
+ fontWeight:
+ isSelected
+ ? FontWeight.bold
+ : FontWeight.normal,
+ color:
+ isSelected
+ ? Variables.textPrimary
+ : Variables.textSecondary,
+ ),
+ ),
+ );
+ }).toList();
+ },
),
- if (widget.onFilterPressed != null && widget.onAIPressed != null)
+ // Layout Icon Button (Toggle between All Images/Categorized) or Edit Icon
+ if (widget.onLayoutToggle != null ||
+ widget.onLayoutPressed != null) ...[
const SizedBox(width: 8),
- // AI/Sparkle Icon Button
- if (widget.onAIPressed != null)
GestureDetector(
- onTap: widget.onAIPressed,
+ onTap:
+ widget.onLayoutToggle ?? widget.onLayoutPressed,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 16,
@@ -327,18 +281,75 @@ class _TopBarState extends State<TopBar> {
color: Variables.borderSubtle,
borderRadius: BorderRadius.circular(1000),
),
- child: const Icon(
- Icons.auto_awesome,
+ child: Icon(
+ widget.onLayoutToggle != null
+ ? (widget.isAlternateView == true
+ ? Icons.dashboard
+ : Icons.view_agenda_outlined)
+ : Icons
+ .edit, // Use edit icon when only onLayoutPressed is provided
size: 20,
color: Variables.textPrimary,
),
),
),
+ ],
],
),
- ],
+ const Spacer(),
+ // Right group: Filter and AI/Sparkle Buttons
+ if (widget.onFilterPressed != null ||
+ widget.onAIPressed != null)
+ Row(
+ children: [
+ // Filter Icon Button
+ if (widget.onFilterPressed != null)
+ GestureDetector(
+ onTap: widget.onFilterPressed,
+ child: Container(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 16,
+ vertical: 10,
+ ),
+ decoration: BoxDecoration(
+ color: Variables.borderSubtle,
+ borderRadius: BorderRadius.circular(1000),
+ ),
+ child: const Icon(
+ Icons.tune,
+ size: 20,
+ color: Variables.textPrimary,
+ ),
+ ),
+ ),
+ if (widget.onFilterPressed != null &&
+ widget.onAIPressed != null)
+ const SizedBox(width: 8),
+ // AI/Sparkle Icon Button
+ if (widget.onAIPressed != null)
+ GestureDetector(
+ onTap: widget.onAIPressed,
+ child: Container(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 16,
+ vertical: 10,
+ ),
+ decoration: BoxDecoration(
+ color: Variables.borderSubtle,
+ borderRadius: BorderRadius.circular(1000),
+ ),
+ child: const Icon(
+ Icons.auto_awesome,
+ size: 20,
+ color: Variables.textPrimary,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
),
- ),
],
),
),