Files
superport/lib/screens/common/widgets/standard_action_bar.dart
JiWoong Sul ca830063f0
Some checks failed
Flutter Test & Quality Check / Build APK (push) Has been cancelled
Flutter Test & Quality Check / Test on macos-latest (push) Has been cancelled
Flutter Test & Quality Check / Test on ubuntu-latest (push) Has been cancelled
feat: 백엔드 API 구조 변경 대응 및 시스템 안정성 대폭 향상
주요 변경사항:
- Company-Branch → 계층형 Company 구조 완전 마이그레이션
- Equipment 모델 필드명 표준화 (current_company_id → company_id)
- DropdownButton assertion 오류 완전 해결
- 지점 추가 드롭다운 페이지네이션 문제 해결 (20개→55개 전체 표시)
- Equipment 백엔드 API 데이터 활용도 40%→100% 달성
- 소프트 딜리트 시스템 안정성 향상

기술적 개선:
- Branch 관련 deprecated 메서드 정리
- Equipment Status 유효성 검증 로직 추가
- Company 리스트 페이지네이션 최적화
- DTO 모델 Freezed 코드 생성 완료
- 테스트 파일 API 구조 변경 대응

성과:
- Flutter 웹 빌드 성공 (컴파일 에러 0건)
- 백엔드 API 호환성 95% 달성
- 시스템 안정성 및 사용자 경험 대폭 개선
2025-08-20 19:09:03 +09:00

236 lines
6.9 KiB
Dart

import 'package:flutter/material.dart';
import 'package:superport/screens/common/theme_shadcn.dart';
import 'package:superport/screens/common/components/shadcn_components.dart';
/// 표준 액션바 위젯
///
/// 모든 리스트 화면에서 일관된 액션 버튼 배치와 상태 표시 제공
class StandardActionBar extends StatelessWidget {
final List<Widget> leftActions; // 왼쪽 액션 버튼들
final List<Widget> rightActions; // 오른쪽 액션 버튼들
final int? selectedCount; // 선택된 항목 수
final int totalCount; // 전체 항목 수
final VoidCallback? onRefresh; // 새로고침 콜백
final String? statusMessage; // 추가 상태 메시지
const StandardActionBar({
Key? key,
this.leftActions = const [],
this.rightActions = const [],
this.selectedCount,
required this.totalCount,
this.onRefresh,
this.statusMessage,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// 왼쪽 액션 버튼들
Row(
children: [
...leftActions.map((action) => Padding(
padding: const EdgeInsets.only(right: ShadcnTheme.spacing2),
child: action,
)),
],
),
// 오른쪽 상태 표시 및 액션들
Row(
children: [
// 추가 상태 메시지 (작은 글자 크기로 통일)
if (statusMessage != null) ...[
Text(statusMessage!, style: ShadcnTheme.bodySmall),
const SizedBox(width: ShadcnTheme.spacing3),
],
// 선택된 항목 수 표시
if (selectedCount != null && selectedCount! > 0) ...[
Container(
padding: const EdgeInsets.symmetric(
vertical: 8,
horizontal: 16,
),
decoration: BoxDecoration(
color: ShadcnTheme.primary.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(ShadcnTheme.radiusSm),
border: Border.all(color: ShadcnTheme.primary.withValues(alpha: 0.3)),
),
child: Text(
'$selectedCount개 선택됨',
style: TextStyle(
fontWeight: FontWeight.bold,
color: ShadcnTheme.primary,
),
),
),
const SizedBox(width: ShadcnTheme.spacing3),
],
// 전체 항목 수 표시 (statusMessage에 "총 X개"가 없을 때만 표시)
if (statusMessage == null || !statusMessage!.contains(''))
Container(
padding: const EdgeInsets.symmetric(
vertical: 6,
horizontal: 12,
),
decoration: BoxDecoration(
color: ShadcnTheme.muted.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(ShadcnTheme.radiusSm),
),
child: Text(
'$totalCount개',
style: ShadcnTheme.bodySmall,
),
),
// 새로고침 버튼
if (onRefresh != null) ...[
const SizedBox(width: ShadcnTheme.spacing3),
IconButton(
icon: const Icon(Icons.refresh),
onPressed: onRefresh,
tooltip: '새로고침',
iconSize: 20,
),
],
// 오른쪽 액션 버튼들
...rightActions.map((action) => Padding(
padding: const EdgeInsets.only(left: ShadcnTheme.spacing2),
child: action,
)),
],
),
],
);
}
}
/// 표준 액션 버튼 그룹
class StandardActionButtons {
/// 추가 버튼
static Widget addButton({
required String text,
required VoidCallback onPressed,
IconData icon = Icons.add,
}) {
return ShadcnButton(
text: text,
onPressed: onPressed,
variant: ShadcnButtonVariant.primary,
textColor: Colors.white,
icon: Icon(icon, size: 16),
);
}
/// 삭제 버튼
static Widget deleteButton({
required VoidCallback? onPressed,
bool enabled = true,
String text = '삭제',
}) {
return ShadcnButton(
text: text,
onPressed: enabled ? onPressed : null,
variant: enabled
? ShadcnButtonVariant.destructive
: ShadcnButtonVariant.secondary,
icon: const Icon(Icons.delete, size: 16),
);
}
/// 엑셀 내보내기 버튼
static Widget exportButton({
required VoidCallback onPressed,
String text = '엑셀 내보내기',
}) {
return ShadcnButton(
text: text,
onPressed: onPressed,
variant: ShadcnButtonVariant.secondary,
icon: const Icon(Icons.download, size: 16),
);
}
/// 엑셀 가져오기 버튼
static Widget importButton({
required VoidCallback onPressed,
String text = '엑셀 가져오기',
}) {
return ShadcnButton(
text: text,
onPressed: onPressed,
variant: ShadcnButtonVariant.secondary,
icon: const Icon(Icons.upload, size: 16),
);
}
/// 새로고침 버튼
static Widget refreshButton({
required VoidCallback onPressed,
String text = '새로고침',
}) {
return ShadcnButton(
text: text,
onPressed: onPressed,
variant: ShadcnButtonVariant.secondary,
icon: const Icon(Icons.refresh, size: 16),
);
}
/// 필터 초기화 버튼
static Widget clearFiltersButton({
required VoidCallback onPressed,
String text = '필터 초기화',
}) {
return ShadcnButton(
text: text,
onPressed: onPressed,
variant: ShadcnButtonVariant.ghost,
icon: const Icon(Icons.clear_all, size: 16),
);
}
}
/// 표준 필터 드롭다운
class StandardFilterDropdown<T> extends StatelessWidget {
final T value;
final List<DropdownMenuItem<T>> items;
final ValueChanged<T?> onChanged;
final String? hint;
const StandardFilterDropdown({
Key? key,
required this.value,
required this.items,
required this.onChanged,
this.hint,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
height: 40,
padding: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
color: ShadcnTheme.card,
border: Border.all(color: Colors.black),
borderRadius: BorderRadius.circular(ShadcnTheme.radiusMd),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<T>(
value: value,
items: items,
onChanged: onChanged,
hint: hint != null ? Text(hint!) : null,
style: ShadcnTheme.bodySmall,
icon: const Icon(Icons.arrow_drop_down, size: 20),
),
),
);
}
}