fix: 플로팅 네비게이션 바 렌더링 문제 해결

- SMS 화면에서 그림자가 제대로 표시되지 않던 문제 수정
- Stack 구조를 Container로 단순화하여 렌더링 최적화
- RenderFlex overflow 오류 해결 (패딩 및 아이콘 크기 조정)
- Android 패키지명 변경 및 빌드 설정 업데이트
This commit is contained in:
JiWoong Sul
2025-07-18 20:39:25 +09:00
parent 58727af659
commit 9f1d29c99d
15 changed files with 109 additions and 105 deletions

View File

@@ -6,7 +6,7 @@ plugins {
}
android {
namespace = "com.example.submanager"
namespace = "com.naturebridgeai.digitalrentmanager"
compileSdk = flutter.compileSdkVersion
ndkVersion = "27.0.12077973"
@@ -22,7 +22,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.submanager"
applicationId = "com.naturebridgeai.digitalrentmanager"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion

View File

@@ -1,4 +1,4 @@
package com.example.submanager
package com.naturebridgeai.digitalrentmanager
import io.flutter.embedding.android.FlutterActivity

View File

@@ -495,7 +495,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.submanager;
PRODUCT_BUNDLE_IDENTIFIER = com.naturebridgeai.digitalrentmanager;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@@ -512,7 +512,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.submanager.RunnerTests;
PRODUCT_BUNDLE_IDENTIFIER = com.naturebridgeai.digitalrentmanager.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -530,7 +530,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.submanager.RunnerTests;
PRODUCT_BUNDLE_IDENTIFIER = com.naturebridgeai.digitalrentmanager.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@@ -546,7 +546,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.submanager.RunnerTests;
PRODUCT_BUNDLE_IDENTIFIER = com.naturebridgeai.digitalrentmanager.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@@ -677,7 +677,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.submanager;
PRODUCT_BUNDLE_IDENTIFIER = com.naturebridgeai.digitalrentmanager;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -699,7 +699,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.submanager;
PRODUCT_BUNDLE_IDENTIFIER = com.naturebridgeai.digitalrentmanager;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;

View File

@@ -20,16 +20,20 @@ import 'navigation/app_navigation_observer.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'dart:io' show Platform;
import 'dart:async' show unawaited;
import 'utils/memory_manager.dart';
import 'utils/performance_optimizer.dart';
import 'navigator_key.dart';
// AdMob 활성화 플래그 (개발 중 false, 프로덕션 시 true로 변경)
const bool enableAdMob = false;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// 구글 모바일 광고 SDK 초기화 (웹이 아니고, Android/iOS에서만)
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
await MobileAds.instance.initialize();
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS) && enableAdMob) {
unawaited(MobileAds.instance.initialize());
}
// 성능 최적화 설정

View File

@@ -25,7 +25,7 @@ class AdaptiveTheme {
scaffoldBackgroundColor: const Color(0xFF121212),
cardTheme: CardTheme(
cardTheme: CardThemeData(
color: const Color(0xFF1E1E1E),
elevation: 2,
shadowColor: Colors.black.withValues(alpha: 0.3),
@@ -235,7 +235,7 @@ class AdaptiveTheme {
),
),
cardTheme: CardTheme(
cardTheme: CardThemeData(
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),

View File

@@ -18,7 +18,7 @@ class AppTheme {
scaffoldBackgroundColor: AppColors.backgroundColor,
// 카드 스타일 - 글래스모피즘 효과
cardTheme: CardTheme(
cardTheme: CardThemeData(
color: AppColors.glassCard,
elevation: 0,
shadowColor: AppColors.shadowBlack,
@@ -307,7 +307,7 @@ class AppTheme {
),
// 탭바 스타일
tabBarTheme: const TabBarTheme(
tabBarTheme: const TabBarThemeData(
labelColor: AppColors.primaryColor,
unselectedLabelColor: AppColors.textSecondary,
indicatorColor: AppColors.primaryColor,

View File

@@ -75,66 +75,61 @@ class _FloatingNavigationBarState extends State<FloatingNavigationBar>
offset: Offset(0, 100 * (1 - _animation.value)),
child: Opacity(
opacity: _animation.value,
child: Stack(
children: [
// 흰색 배경 레이어 (완전 불투명)
Container(
decoration: BoxDecoration(
color: AppColors.surfaceColor,
borderRadius: BorderRadius.circular(24),
boxShadow: const [
BoxShadow(
color: AppColors.shadowBlack,
blurRadius: 20,
spreadRadius: -5,
offset: Offset(0, 10),
),
],
child: Container(
margin: const EdgeInsets.all(4), // 그림자 공간 확보
decoration: BoxDecoration(
color: AppColors.surfaceColor,
borderRadius: BorderRadius.circular(24),
boxShadow: const [
BoxShadow(
color: AppColors.shadowBlack,
blurRadius: 12,
spreadRadius: 0,
offset: Offset(0, 4),
),
],
),
child: GlassmorphismCard(
padding:
const EdgeInsets.symmetric(vertical: 8, horizontal: 8),
borderRadius: 24,
blur: 10.0,
backgroundColor: Colors.transparent,
boxShadow: const [], // 그림자는 Container에서 처리
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_NavigationItem(
icon: Icons.home_rounded,
label: AppLocalizations.of(context).home,
isSelected: widget.selectedIndex == 0,
onTap: () => _onItemTapped(0),
),
_NavigationItem(
icon: Icons.analytics_rounded,
label: AppLocalizations.of(context).analysis,
isSelected: widget.selectedIndex == 1,
onTap: () => _onItemTapped(1),
),
_AddButton(
onTap: () => _onItemTapped(2),
),
if (!PlatformHelper.isIOS)
_NavigationItem(
icon: Icons.qr_code_scanner_rounded,
label: AppLocalizations.of(context).smsScanLabel,
isSelected: widget.selectedIndex == 3,
onTap: () => _onItemTapped(3),
),
_NavigationItem(
icon: Icons.settings_rounded,
label: AppLocalizations.of(context).settings,
isSelected: PlatformHelper.isIOS ? widget.selectedIndex == 3 : widget.selectedIndex == 4,
onTap: () => _onItemTapped(PlatformHelper.isIOS ? 3 : 4),
),
],
),
// 글래스모피즘 레이어 (시각적 효과)
GlassmorphismCard(
padding:
const EdgeInsets.symmetric(vertical: 8, horizontal: 8),
borderRadius: 24,
blur: 10.0,
backgroundColor: Colors.transparent,
boxShadow: const [], // 그림자는 배경 레이어에서 처리
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_NavigationItem(
icon: Icons.home_rounded,
label: AppLocalizations.of(context).home,
isSelected: widget.selectedIndex == 0,
onTap: () => _onItemTapped(0),
),
_NavigationItem(
icon: Icons.analytics_rounded,
label: AppLocalizations.of(context).analysis,
isSelected: widget.selectedIndex == 1,
onTap: () => _onItemTapped(1),
),
_AddButton(
onTap: () => _onItemTapped(2),
),
if (!PlatformHelper.isIOS)
_NavigationItem(
icon: Icons.qr_code_scanner_rounded,
label: AppLocalizations.of(context).smsScanLabel,
isSelected: widget.selectedIndex == 3,
onTap: () => _onItemTapped(3),
),
_NavigationItem(
icon: Icons.settings_rounded,
label: AppLocalizations.of(context).settings,
isSelected: PlatformHelper.isIOS ? widget.selectedIndex == 3 : widget.selectedIndex == 4,
onTap: () => _onItemTapped(PlatformHelper.isIOS ? 3 : 4),
),
],
),
),
],
),
),
),
),
@@ -169,7 +164,7 @@ class _NavigationItem extends StatelessWidget {
borderRadius: BorderRadius.circular(12),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: isSelected
? AppColors.primaryColor.withValues(alpha: 0.1)
@@ -184,14 +179,14 @@ class _NavigationItem extends StatelessWidget {
child: Icon(
icon,
color: isSelected ? AppColors.primaryColor : AppColors.navyGray,
size: isSelected ? 26 : 24,
size: isSelected ? 24 : 22,
),
),
const SizedBox(height: 4),
const SizedBox(height: 2),
AnimatedDefaultTextStyle(
duration: const Duration(milliseconds: 200),
style: TextStyle(
fontSize: 11,
fontSize: 10,
fontWeight: isSelected ? FontWeight.w600 : FontWeight.w500,
color: isSelected ? AppColors.primaryColor : AppColors.navyGray,
),

View File

@@ -153,15 +153,14 @@ class _GlassmorphicScaffoldState extends State<GlassmorphicScaffold>
Widget _buildBackground(List<Color> gradientColors) {
return Positioned.fill(
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
AppColors.backgroundColor,
...gradientColors.map((color) => color.withValues(alpha: 0.05)).toList(),
AppColors.backgroundColor,
],
color: AppColors.backgroundColor, // 베이스 색상 추가
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: gradientColors.map((color) => color.withOpacity(0.3)).toList(),
),
),
),
),

View File

@@ -3,6 +3,7 @@ import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'dart:io' show Platform;
import 'glassmorphism_card.dart';
import '../main.dart' show enableAdMob;
/// 구글 네이티브 광고 위젯 (AdMob NativeAd)
/// SRP에 따라 광고 전용 위젯으로 분리
@@ -162,6 +163,11 @@ class _NativeAdWidgetState extends State<NativeAdWidget> {
@override
Widget build(BuildContext context) {
// AdMob이 비활성화된 경우 빈 컨테이너 반환
if (!enableAdMob) {
return const SizedBox.shrink();
}
// 웹 환경인 경우 플레이스홀더 표시
if (kIsWeb) {
return _buildWebPlaceholder();

View File

@@ -7,7 +7,7 @@ project(runner LANGUAGES CXX)
set(BINARY_NAME "submanager")
# The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "com.example.submanager")
set(APPLICATION_ID "com.naturebridgeai.digitalrentmanager")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.

View File

@@ -479,7 +479,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.submanager.RunnerTests;
PRODUCT_BUNDLE_IDENTIFIER = com.naturebridgeai.digitalrentmanager.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/submanager.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/submanager";
@@ -494,7 +494,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.submanager.RunnerTests;
PRODUCT_BUNDLE_IDENTIFIER = com.naturebridgeai.digitalrentmanager.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/submanager.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/submanager";
@@ -509,7 +509,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.submanager.RunnerTests;
PRODUCT_BUNDLE_IDENTIFIER = com.naturebridgeai.digitalrentmanager.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/submanager.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/submanager";

View File

@@ -8,7 +8,7 @@
PRODUCT_NAME = submanager
// The application's bundle identifier
PRODUCT_BUNDLE_IDENTIFIER = com.example.submanager
PRODUCT_BUNDLE_IDENTIFIER = com.naturebridgeai.digitalrentmanager
// The copyright displayed in application information
PRODUCT_COPYRIGHT = Copyright © 2025 com.example. All rights reserved.
PRODUCT_COPYRIGHT = Copyright © 2025 com.naturebridgeai. All rights reserved.

View File

@@ -50,10 +50,10 @@ packages:
dependency: transitive
description:
name: async
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
url: "https://pub.dev"
source: hosted
version: "2.12.0"
version: "2.13.0"
boolean_selector:
dependency: transitive
description:
@@ -266,10 +266,10 @@ packages:
dependency: transitive
description:
name: fake_async
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev"
source: hosted
version: "1.3.2"
version: "1.3.3"
ffi:
dependency: transitive
description:
@@ -566,10 +566,10 @@ packages:
dependency: "direct main"
description:
name: intl
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
url: "https://pub.dev"
source: hosted
version: "0.19.0"
version: "0.20.2"
io:
dependency: transitive
description:
@@ -598,10 +598,10 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
url: "https://pub.dev"
source: hosted
version: "10.0.8"
version: "10.0.9"
leak_tracker_flutter_testing:
dependency: transitive
description:
@@ -1299,10 +1299,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
url: "https://pub.dev"
source: hosted
version: "14.3.1"
version: "15.0.0"
watcher:
dependency: transitive
description:

View File

@@ -20,7 +20,7 @@ dependencies:
http: ^1.4.0
qr_flutter: ^4.1.0
url_launcher: ^6.2.4
intl: ^0.19.0
intl: ^0.20.2
permission_handler: ^11.3.0
uuid: ^4.2.1
timezone: ^0.9.2

View File

@@ -89,11 +89,11 @@ BEGIN
BEGIN
BLOCK "040904e4"
BEGIN
VALUE "CompanyName", "com.example" "\0"
VALUE "CompanyName", "com.naturebridgeai" "\0"
VALUE "FileDescription", "submanager" "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "submanager" "\0"
VALUE "LegalCopyright", "Copyright (C) 2025 com.example. All rights reserved." "\0"
VALUE "LegalCopyright", "Copyright (C) 2025 com.naturebridgeai. All rights reserved." "\0"
VALUE "OriginalFilename", "submanager.exe" "\0"
VALUE "ProductName", "submanager" "\0"
VALUE "ProductVersion", VERSION_AS_STRING "\0"