commit e7bec3073e0200306d5023d98a1b01433e41e667
parent f42c4c7a3fe0eda2e6a6a0b8cfda7253f4def07a
Author: aditya-samal <samaladitya2004@gmail.com>
Date: Tue, 2 Dec 2025 00:03:59 +0530
Updated topbar
Diffstat:
7 files changed, 253 insertions(+), 252 deletions(-)
diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
- <string>12.0</string>
+ <string>13.0</string>
</dict>
</plist>
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
@@ -346,7 +346,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -472,7 +472,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -523,7 +523,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
@@ -54,6 +55,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
diff --git a/lib/ui/pages/project_board_page.dart b/lib/ui/pages/project_board_page.dart
@@ -161,6 +161,13 @@ class _ProjectBoardPageState extends State<ProjectBoardPage> {
onBack: () => Navigator.pop(context),
onProjectChanged: _onProjectChanged,
onSettingsPressed: () {},
+ isAlternateView: _showAlternateView,
+ onLayoutToggle: () {
+ setState(() {
+ _showAlternateView = !_showAlternateView;
+ if (!_showAlternateView) _loadImagesForSelected();
+ });
+ },
),
bottomNavigationBar: BottomBar(
currentTab: BottomBarItem.moodboard,
@@ -174,93 +181,6 @@ class _ProjectBoardPageState extends State<ProjectBoardPage> {
),
body: Column(
children: [
- Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: 16.0,
- vertical: 12.0,
- ),
- child: Row(
- children: [
- InkWell(
- onTap: () {
- setState(() {
- _showAlternateView = !_showAlternateView;
- if (!_showAlternateView) _loadImagesForSelected();
- });
- },
- borderRadius: BorderRadius.circular(20),
- child: Container(
- padding: const EdgeInsets.symmetric(
- horizontal: 16,
- vertical: 8,
- ),
- decoration: BoxDecoration(
- color: Variables.surfaceSubtle,
- borderRadius: BorderRadius.circular(20),
- border: Border.all(color: Variables.borderSubtle),
- ),
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- Icon(
- _showAlternateView
- ? Icons.dashboard
- : Icons.view_agenda_outlined,
- size: 18,
- color: Variables.textPrimary,
- ),
- const SizedBox(width: 8),
- Text(
- _showAlternateView ? "Categorized" : "All Images",
- style: Variables.bodyStyle.copyWith(
- fontWeight: FontWeight.w600,
- fontSize: 13,
- ),
- ),
- ],
- ),
- ),
- ),
- const Spacer(),
- if (_showAlternateView)
- InkWell(
- onTap: () {
- _alternatePageKey.currentState?.showFilterDialog();
- },
- borderRadius: BorderRadius.circular(20),
- child: Container(
- padding: const EdgeInsets.symmetric(
- horizontal: 16,
- vertical: 8,
- ),
- decoration: BoxDecoration(
- color: Variables.surfaceSubtle,
- borderRadius: BorderRadius.circular(20),
- border: Border.all(color: Variables.borderSubtle),
- ),
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- const Icon(
- Icons.tune_outlined,
- size: 18,
- color: Variables.textPrimary,
- ),
- const SizedBox(width: 8),
- Text(
- "Filter",
- style: Variables.bodyStyle.copyWith(
- fontWeight: FontWeight.w600,
- fontSize: 13,
- ),
- ),
- ],
- ),
- ),
- ),
- ],
- ),
- ),
Expanded(
child:
_showAlternateView
diff --git a/lib/ui/pages/project_board_page_alternate.dart b/lib/ui/pages/project_board_page_alternate.dart
@@ -221,14 +221,6 @@ class ProjectBoardPageAlternateState extends State<ProjectBoardPageAlternate> {
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
child: Row(
children: [
- Text(
- "ALL IMAGES (${_filteredImages.length})",
- style: Variables.captionStyle.copyWith(
- fontWeight: FontWeight.bold,
- letterSpacing: 1.2,
- color: Variables.textSecondary,
- ),
- ),
const Spacer(),
if (_selectedTags.isNotEmpty)
Container(
diff --git a/lib/ui/pages/project_detail_page.dart b/lib/ui/pages/project_detail_page.dart
@@ -216,6 +216,8 @@ class _ProjectDetailPageState extends State<ProjectDetailPage> {
),
backgroundColor: const Color(0xFFFAFAFA),
elevation: 0,
+ leadingWidth: 40,
+ titleSpacing: 4,
actions: [
IconButton(
icon: SvgPicture.asset(
@@ -509,46 +511,44 @@ class _ProjectDetailPageState extends State<ProjectDetailPage> {
width: 1,
),
),
+ clipBehavior: Clip.antiAlias,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Three images side by side (or fewer if not available)
Container(
height: 115,
- padding: const EdgeInsets.all(4),
+ padding: const EdgeInsets.only(bottom: 4),
child: Row(
children: [
// Show up to 3 images
- for (int i = 0; i < 3; i++)
+ for (int i = 0; i < 3; i++) ...[
Expanded(
- child: Padding(
- padding: EdgeInsets.only(
- right: i < 2 ? 4 : 0,
- ),
- 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: 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],
+ ),
),
),
+ if (i < 2) const SizedBox(width: 4),
+ ],
],
),
),
// Event title
Padding(
- padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
+ padding: const EdgeInsets.fromLTRB(4, 16, 16, 16),
child: Text(
event.title,
style: const TextStyle(
diff --git a/lib/ui/widgets/top_bar.dart b/lib/ui/widgets/top_bar.dart
@@ -10,6 +10,11 @@ class TopBar extends StatefulWidget implements PreferredSizeWidget {
final Function(ProjectModel)? onProjectChanged;
final VoidCallback? onSettingsPressed;
final String? titleOverride;
+ final VoidCallback? onLayoutPressed;
+ final VoidCallback? onFilterPressed;
+ final VoidCallback? onAIPressed;
+ final bool? isAlternateView; // For toggle state
+ final VoidCallback? onLayoutToggle; // For toggle callback
const TopBar({
super.key,
@@ -18,13 +23,18 @@ class TopBar extends StatefulWidget implements PreferredSizeWidget {
this.onProjectChanged,
this.onSettingsPressed,
this.titleOverride,
+ this.onLayoutPressed,
+ this.onFilterPressed,
+ this.onAIPressed,
+ this.isAlternateView,
+ this.onLayoutToggle,
});
@override
State<TopBar> createState() => _TopBarState();
@override
- Size get preferredSize => const Size.fromHeight(kToolbarHeight);
+ Size get preferredSize => const Size.fromHeight(88.0); // Height for two rows
}
class _TopBarState extends State<TopBar> {
@@ -91,154 +101,231 @@ class _TopBarState extends State<TopBar> {
Widget build(BuildContext context) {
final textScaler = MediaQuery.of(context).textScaler;
return Container(
- color: Variables.background,
+ color: Colors.white, // White background
child: SafeArea(
bottom: false,
- child: SizedBox(
- height: kToolbarHeight,
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 4),
- child: Row(
- children: [
- // 1. Back Button
- if (widget.onBack != null) ...[
- IconButton(
- icon: const Icon(
- Icons.arrow_back,
- color: Variables.textPrimary,
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ // First Row: Title and Settings
+ Container(
+ height: 48.0, // py-[12px] = 24px + 24px content
+ padding: const EdgeInsets.symmetric(horizontal: 16.0),
+ child: Row(
+ children: [
+ // Back Button
+ if (widget.onBack != null) ...[
+ GestureDetector(
+ onTap: widget.onBack,
+ child: Container(
+ width: 24,
+ height: 24,
+ alignment: Alignment.center,
+ child: const Icon(
+ Icons.arrow_back,
+ size: 20,
+ color: Variables.textPrimary,
+ ),
+ ),
),
- onPressed: widget.onBack,
- ),
- const SizedBox(width: 8),
- ],
-
- // 2. Title & Selector
- Expanded(
- child:
- widget.titleOverride != null
- ? Padding(
- padding: const EdgeInsets.only(left: 8.0),
- child: Text(
- widget.titleOverride!,
+ const SizedBox(width: 8),
+ ],
+ // 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 &&
+ _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,
- ),
- )
- : (!_isLoading &&
- _currentProject != null &&
- _rootProject != null)
- ? Row(
- children: [
- if (widget.onBack == null)
- const SizedBox(width: 12),
- Flexible(
- child: Text(
- _rootProject!.title,
- style: const TextStyle(
- fontFamily: 'GeneralSans',
- fontSize: 20,
- fontWeight: FontWeight.w500,
- color: Variables.textPrimary,
- ),
- overflow: TextOverflow.ellipsis,
- ),
- ),
- const SizedBox(width: 8),
- ],
- )
- : const SizedBox(),
- ),
- 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(16),
- ),
- offset: const Offset(0, 38),
- child: Container(
- padding: const EdgeInsets.symmetric(
- horizontal: 14,
- vertical: 10,
- ),
- decoration: BoxDecoration(
- color: Variables.surfaceSubtle,
- borderRadius: BorderRadius.circular(16),
+ )
+ : const SizedBox(),
+ ),
+ // Settings Icon
+ GestureDetector(
+ onTap: widget.onSettingsPressed,
+ child: SvgPicture.asset(
+ 'assets/icons/settings-line.svg',
+ width: 24,
+ height: 24,
+ colorFilter: const ColorFilter.mode(
+ Variables.textPrimary,
+ BlendMode.srcIn,
),
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- Text(
- _currentProject!.id == _rootProject!.id
- ? "Main"
- : _currentProject!.title,
- style: Variables.bodyStyle.copyWith(
- fontFamily: 'GeneralSans',
- fontSize: 15 * textScaler.scale(1.1),
- fontWeight: FontWeight.w500,
- color: Variables.textPrimary,
+ ),
+ ),
+ ],
+ ),
+ ),
+ // Second Row: Global Dropdown and Action Buttons
+ 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: 8,
+ ),
+ decoration: BoxDecoration(
+ color: Variables.borderSubtle, // #e4e4e7
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ _currentProject!.id == _rootProject!.id
+ ? "Global"
+ : _currentProject!.title,
+ style: const TextStyle(
+ fontFamily: 'GeneralSans',
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ height: 24 / 16,
+ color: Variables.textPrimary,
+ ),
+ ),
+ const SizedBox(width: 8),
+ const Icon(
+ Icons.keyboard_arrow_down_rounded,
+ size: 20,
+ color: Variables.textPrimary,
+ ),
+ ],
),
),
- const SizedBox(width: 6),
- const Icon(
- Icons.keyboard_arrow_down_rounded,
- size: 18,
+ 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();
+ },
+ ),
+ const SizedBox(width: 8),
+ // Layout Icon Button (Toggle between All Images/Categorized)
+ GestureDetector(
+ onTap: widget.onLayoutToggle ?? widget.onLayoutPressed,
+ child: Container(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 16,
+ vertical: 8,
+ ),
+ decoration: BoxDecoration(
+ color: Variables.borderSubtle,
+ borderRadius: BorderRadius.circular(1000),
+ ),
+ child: Icon(
+ widget.isAlternateView == true
+ ? Icons.dashboard
+ : Icons.view_agenda_outlined,
+ 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 ? "Main Project" : project.title,
- style: Variables.bodyStyle.copyWith(
- fontFamily: 'GeneralSans',
- fontWeight:
- isSelected
- ? FontWeight.bold
- : FontWeight.normal,
- color:
- isSelected
- ? Variables.textPrimary
- : Variables.textSecondary,
- ),
- ),
- );
- }).toList();
- },
+ ],
),
- const SizedBox(width: 4),
-
- // 3. Settings Icon
- IconButton(
- icon: SvgPicture.asset(
- 'assets/icons/settings-line.svg',
- width: 24,
- height: 24,
- colorFilter: const ColorFilter.mode(
- Variables.textPrimary,
- BlendMode.srcIn,
- ),
+ const Spacer(),
+ // Right group: Filter and AI/Sparkle Buttons
+ Row(
+ children: [
+ // Filter Icon Button
+ 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,
+ ),
+ ),
+ ),
+ const SizedBox(width: 8),
+ // AI/Sparkle Icon Button
+ 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,
+ ),
+ ),
+ ),
+ ],
),
- onPressed: widget.onSettingsPressed,
- ),
- ],
+ ],
+ ),
),
- ),
+ ],
),
),
);