開發環境設定

環境需求

項目 最低版本 用途
Flutter SDK 3.38+ 核心框架(穩定版頻道)
Dart SDK 3.7+ 隨 Flutter SDK 附帶
Xcode 15+ iOS / macOS 建置
Android Studio Hedgehog+ Android 建置、模擬器管理
Chrome 最新版 Web 建置除錯
iDempiere 11+ ERP 伺服器,需安裝 idempiere-rest 外掛
IDE Android Studio 或 VS Code(含 Flutter/Dart 延伸模組)
Git 版本控制,採用 Conventional Commits 工作流程

安裝步驟

# 1. 複製專案
git clone https://github.com/topgiga/tw.idempiere.flutter.git
cd tw.idempiere.flutter

# 2. 安裝相依套件
flutter pub get

# 3. 產生程式碼(Freezed + json_serializable)
dart run build_runner build --delete-conflicting-outputs

# 4. 靜態分析(確認無錯誤)
flutter analyze

# 5. 執行測試
flutter test

執行應用程式

# Web(Chrome)
flutter run -d chrome

# macOS(需要 Xcode)
flutter run -d macos

# iOS(需要 Xcode + 裝置 / 模擬器)
flutter run -d ios

# Android(需要 Android Studio + 裝置 / 模擬器)
flutter run -d android

# Windows
flutter run -d windows

新增平台支援

如果專案缺少某個平台的設定,可以產生它:

flutter create --platforms=ios,android,macos,web .

程式碼產生

專案使用 Freezed(不可變模型)和 json_serializable(JSON 序列化)進行程式碼產生。修改任何標記 @freezed@JsonSerializable 的模型後,必須重新執行:

# 一次性建置
dart run build_runner build --delete-conflicting-outputs

# 開發期間的監聽模式(持續監控檔案變更)
dart run build_runner watch --delete-conflicting-outputs

何時需要重新執行?

  • 修改任何 @freezed 類別後
  • 修改任何 @JsonSerializable 類別後
  • 新增或修改模型類別後
  • 看到「Generated files missing」錯誤時

注意:模型類別 Locatorstate_notifier 套件(由 flutter_riverpod 重新匯出)的 Locator 名稱衝突。需要同時匯入兩者的檔案必須使用 hide Locator

import 'package:flutter_riverpod/flutter_riverpod.dart' hide Locator;
import '../../../core/models/locator.dart';

專案結構說明

lib/
  core/                    # 共用框架
    api/                   #   Dio HTTP 客戶端, 認證, 攔截器
    config/                #   品牌設定, 模組登錄, 儀表板
    constants/             #   API URL 模式
    document_framework/    #   設定驅動文件畫面(核心)
    router/                #   GoRouter + 認證轉導
    services/              #   FCM, 通知服務
    widgets/               #   共用小工具(列印按鈕等)
  features/                # 每個功能一個目錄
    sales_order/           #   設定驅動:1 個檔案 (sales_order_config.dart)
    requisition/           #   自訂:data/ + domain/ + presentation/
    booking/               #   自訂:日曆 UI
    ...
  l10n/                    # 本地化(ARB 檔案, 4 種語言)

伺服器設定

應用程式透過 REST API 連接 iDempiere。首次啟動時:

  1. 輸入 iDempiere 伺服器 URL(例如 https://your-server/api
  2. 使用 iDempiere 帳號密碼登入
  3. 選擇 Client、Role、Organization

Firebase(選用)

推播通知需要 Firebase 設定。應用程式在沒有 Firebase 設定的情況下仍可正常運作 — FCM 功能在缺少設定時會優雅地停用。

開發工作流程

  1. main 建立功能分支:git checkout -b feat/your-feature main
  2. 進行開發 — 遵循程式碼慣例
  3. 提交前執行檢查:
    flutter analyze
    flutter test
  4. 使用 Conventional Commits 格式提交
  5. main 發起 Pull Request

Commit 慣例

前綴 用途 範例
feat: 新功能 feat: add Warehouse Transfer config
fix: 錯誤修正 fix: resolve OData filter for boolean columns
refactor: 重構 refactor: migrate Invoice to Document Framework
docs: 文件 docs: update architecture diagram
ci: CI/CD ci: add iOS build to workflow
chore: 雜項維護 chore: update dependencies

測試

# 執行所有測試
flutter test

# 執行特定測試檔案
flutter test test/core/api/api_client_test.dart

# 含覆蓋率報告
flutter test --coverage

程式碼慣例

  • flutter analyze 必須零問題通過
  • 盡可能使用 const 建構子
  • 優先使用 final 變數;已知型別時避免 var
  • 3 個以上參數的建構子使用命名參數
  • 設定檔命名:lib/features/<feature>/<feature>_config.dart
  • 自訂功能:lib/features/<feature>/{data,domain,presentation}/
  • 路由:/dashboard/<feature-name>(kebab-case)

Tech Stack 版本

Package Version Purpose
Flutter 3.38+ 核心框架
flutter_riverpod ^2.5.1 狀態管理
dio ^5.4.0 HTTP 客戶端
go_router ^14.2.0 路由導航
freezed + json_serializable ^2.5.2 / ^6.8.0 程式碼產生(不可變模型 + JSON 序列化)
flutter_secure_storage ^9.2.2 安全儲存(JWT token)
shared_preferences ^2.2.3 本地設定儲存
mobile_scanner ^5.1.1 條碼掃描
intl ^0.19.0 日期格式化 / 國際化
flutter_test + mocktail SDK / ^1.0.3 測試框架
fl_chart 圖表視覺化(折線、長條、圓餅、雷達)

測試指南

測試目錄結構

test/ 目錄鏡像 lib/features/ 的結構:

test/
├── core/
│   ├── api/
│   │   └── api_client_test.dart              # TokenStorage 單元測試
│   ├── models/
│   │   └── models_test.dart                  # Product JSON 反序列化
│   └── utils/
│       ├── odata_filter_builder_test.dart     # ODataFilter 建構器(22 個測試)
│       ├── odata_expand_builder_test.dart     # ODataExpand 建構器(8 個測試)
│       └── api_date_format_test.dart          # 日期格式化
├── features/
│   ├── login/
│   │   └── auth_notifier_test.dart           # AuthState sealed class 測試
│   ├── inventory/
│   │   └── inventory_repository_test.dart     # StorageOnHand 解析
│   ├── inbound/
│   │   └── inbound_repository_test.dart       # Movement 模型測試
│   ├── outbound/
│   │   └── outbound_repository_test.dart      # MovementLine 解析
│   └── rnd/
│       └── dispatch_repository_test.dart      # Dispatch CRUD + 篩選測試
├── integration/
│   ├── inbound_flow_test.dart                # 入庫表單驗證
│   └── outbound_flow_test.dart               # 出庫表單驗證
└── widget_test.dart                           # App 渲染佔位

測試執行命令

# 執行所有測試
flutter test

# 執行特定測試檔案
flutter test test/core/api/api_client_test.dart

# 含覆蓋率報告
flutter test --coverage

主要測試模式

  • Repository 測試 — 使用 mocktail mock API 客戶端,驗證 JSON 解析與 OData 查詢建構
  • Notifier 測試 — 驗證狀態轉換(如 AuthState 的 4 種狀態)
  • 整合測試 — 驗證表單驗證與完整業務流程
  • 工具類測試 — ODataFilter、ODataExpand 建構器的邊界案例

疑難排解補充

PrintFormat PDF 中文字型問題

問題:iDempiere PrintFormat 輸出 PDF 時中文顯示為空白(方塊/豆腐字)。此問題在 iDempiere Web Client 和 Flutter App 中均會發生。HTML 和 Excel 輸出正常,JasperReport PDF 也正常。

輸出格式 中文 備註
HTML 正常 瀏覽器使用系統字型
Excel 正常 Excel 使用系統字型
PDF(PrintFormat) 空白 iDempiere Web Client 和 Flutter App 均異常
PDF(JasperReport) 正常 有獨立 Font Extension 機制

根因:DefaultFontMapper 預設只有 14 個標準 PDF 字型(Helvetica、Times 等),不含中文。PdfGraphics2D 用此 mapper 將 Java AWT Font 轉為 PDF 字型,找不到中文字型時中文即消失。

解法:設定 PDF_FONT_DIR

  1. 建立字型目錄並放入中文 TTF:
    mkdir -p /opt/idempiere/fonts
    # 從 Jasper 字型 JAR 提取
    cd /tmp && unzip tw.ninniku.jasperfont_1.0.0.202308210256.jar
    unzip ireport-fonts.jar
    cp fonts/ARIALUNI.TTF /opt/idempiere/fonts/
  2. 在 System Configurator 設定 PDF_FONT_DIR = /opt/idempiere/fonts
  3. 重啟 iDempiere

排查清單:

  • PDF_FONT_DIR 值為絕對路徑、無多餘空白
  • 目錄存在且 iDempiere 程序有讀取權限
  • 目錄內有 .ttf.otf 檔案(非 .jar
  • 字型含中文字形(可用 fc-query 確認)
  • iDempiere 已重啟
  • PrintFormat 的 Print Font 名稱與 TTF 內字型名稱匹配
🌐 English Version

Prerequisites

Item Minimum Purpose
Flutter SDK 3.38+ Core framework (stable channel)
Dart SDK 3.7+ Bundled with Flutter SDK
Xcode 15+ iOS / macOS builds
Android Studio Hedgehog+ Android builds, emulator management
Chrome Latest Web build debugging
iDempiere 11+ ERP server with idempiere-rest plugin
IDE Android Studio or VS Code with Flutter/Dart extensions

Setup

git clone https://github.com/topgiga/tw.idempiere.flutter.git
cd tw.idempiere.flutter
flutter pub get
dart run build_runner build --delete-conflicting-outputs
flutter analyze
flutter test

Running

flutter run -d chrome      # Web
flutter run -d macos        # macOS
flutter run -d ios          # iOS
flutter run -d android      # Android
flutter run -d windows      # Windows

Code Generation

The project uses Freezed and json_serializable. After modifying any @freezed or @JsonSerializable model, re-run:

dart run build_runner build --delete-conflicting-outputs

# Watch mode:
dart run build_runner watch --delete-conflicting-outputs

Development Workflow

  1. Create a feature branch from main
  2. Make changes following code conventions
  3. Run flutter analyze and flutter test before committing
  4. Use Conventional Commits
  5. Open a Pull Request against main

Testing

flutter test                    # All tests
flutter test test/path/file.dart  # Specific file
flutter test --coverage          # With coverage
🇯🇵 日本語版

前提条件

項目 最低バージョン 用途
Flutter SDK 3.38+ コアフレームワーク(Stable チャネル)
Dart SDK 3.7+ Flutter SDK に同梱
Xcode 15+ iOS / macOS ビルド
Android Studio Hedgehog+ Android ビルド、エミュレータ管理
Chrome 最新版 Web ビルドのデバッグ
iDempiere 11+ idempiere-rest プラグイン付き ERP サーバー
IDE Android Studio または VS Code(Flutter/Dart 拡張機能付き)

セットアップ

git clone https://github.com/topgiga/tw.idempiere.flutter.git
cd tw.idempiere.flutter
flutter pub get
dart run build_runner build --delete-conflicting-outputs
flutter analyze
flutter test

実行

flutter run -d chrome      # Web
flutter run -d macos        # macOS
flutter run -d ios          # iOS
flutter run -d android      # Android
flutter run -d windows      # Windows

コード生成

本プロジェクトでは Freezedjson_serializable を使用しています。@freezed または @JsonSerializable のモデルを変更した後、以下を再実行してください。

dart run build_runner build --delete-conflicting-outputs

# ウォッチモード:
dart run build_runner watch --delete-conflicting-outputs

開発ワークフロー

  1. main からフィーチャーブランチを作成
  2. コーディング規約に従って変更を実施
  3. コミット前に flutter analyzeflutter test を実行
  4. Conventional Commits 形式でコミット
  5. main に対して Pull Request を作成

テスト

flutter test                    # 全テスト
flutter test test/path/file.dart  # 特定ファイル
flutter test --coverage          # カバレッジ付き

按 Enter 搜尋,ESC 關閉