Chương 15 · Performance, Build & Deploy

Performance, Build & Deploy

Chương cuối — đưa app từ "chạy trên máy mình""ship cho user". Đây là phần thường bị bỏ qua hoặc làm vội. Chương dài + nhiều technical detail iOS/Android.

Chương kết Flutter — hoàn thành Pillar 9 Programming Language.

Độ dài: ~1280 dòng Bài tập: 5 Quiz: 8 Prerequisites: Tất cả chương trước + DevOps pillar
🎯 Mục tiêu chương
  • Sử dụng Flutter DevTools toàn diện: Inspector, Performance, Memory, Network, Logging.
  • Hiểu Performance Overlay (GPU + UI thread).
  • Tối ưu rebuild: const, RepaintBoundary, selector pattern.
  • Build release: APK, App Bundle, IPA, macOS app, web.
  • Sign Android (keystore) + iOS (cert + provisioning).
  • Deploy: Google Play, App Store, Firebase App Distribution, TestFlight.
  • Web deploy: Firebase Hosting, Cloudflare Pages.
  • Flavor: dev/staging/prod build.
  • Crash reporting + Analytics.
  • App size optimization.

1. Performance Overlay

MaterialApp(
  showPerformanceOverlay: true,    // 2 bar overlay góc phải
)

Output 2 bar:

  • UI thread (Dart) — build/layout time. Code Dart bạn viết chạy ở đây.
  • Raster thread (GPU) — paint time. Skia/Impeller render lên screen.

Mỗi vạch = 1 frame (16.67ms ở 60Hz, 8.33ms ở 120Hz). Đỏ = vượt budget → jank.

⚠️ Đo perf trên Profile/Release, không Debug

Debug mode chậm hơn release 10-100x do JIT + debug overhead. Đo perf phải dùng Profile mode:

flutter run --profile

2. Flutter DevTools — 6 panel chính

2.1 Widget Inspector

  • Xem widget tree đầy đủ, click widget trên app → highlight trong tree.
  • Property inspect: size, position, style.
  • Toggle "Select Widget Mode" — tap app để select.
  • Show layout guideline (border + padding) — debug layout phức tạp.

2.2 Performance

  • Record CPU trace.
  • Flame chart hiển thị thời gian mỗi function call.
  • Identify slow widget build.
  • Frame chart: mỗi frame một bar màu — đỏ = jank.

2.3 CPU Profiler

  • Sampling profiler — đếm function chiếm CPU nhiều nhất.
  • Bottom-up / top-down view.

2.4 Memory

  • Heap snapshot.
  • Allocation timeline.
  • Detect leak: object không được GC sau khi widget unmount.

2.5 Network

  • Log HTTP request, response, timing.
  • Filter URL, status code.
  • Inspect headers + body.

2.6 Logging

import 'dart:developer';

log('fetched user', name: 'api', level: 800);
log('error', name: 'api', level: 1000, error: e, stackTrace: st);

// debugPrint tránh truncate Android log
debugPrint('long output...');

3. 6 lỗi perf phổ biến + fix

IssueFix
Rebuild quá nhiều (widget không thay đổi vẫn rebuild)const constructor + tách widget thành class riêng, BlocSelector / Provider.select
Opacity widget expensive (offscreen layer)Dùng AnimatedOpacity hoặc Color.withOpacity background
ClipPath repaint mỗi frameWrap với RepaintBoundary
Long list không lazyListView.builder thay ListView(children: [...])
Image lớn không resizeImage.network(..., cacheWidth: 200, cacheHeight: 200) hoặc ResizeImage
State update granular thiếuSelector / BlocSelector rebuild chỉ subset

const constructor — perf hint chính

// ❌
Text('Hello')

// ✅ — không rebuild khi parent rebuild
const Text('Hello')

// Bật lint
// analysis_options.yaml
linter:
  rules:
    - prefer_const_constructors
    - prefer_const_literals_to_create_immutables
    - prefer_const_declarations

ListView.builder lazy

// ❌ — build 1000 widget ngay từ đầu
ListView(children: items.map((i) => ItemTile(i)).toList())

// ✅ — chỉ build widget visible
ListView.builder(
  itemCount: items.length,
  itemBuilder: (ctx, i) => ItemTile(items[i]),
)

Image cacheWidth

// ❌ — load full size 4000x4000
Image.network(url)

// ✅ — resize ở memory, hiển thị 200x200
Image.network(url, cacheWidth: 200, cacheHeight: 200)

4. Build mode — Debug / Profile / Release

DebugProfileRelease
CompileJITAOTAOT
Hot reloadKhôngKhông
AssertionBậtBậtTắt
DevToolsFullPerformance onlyKhông
PerfChậmGần productionProduction
Lệnhflutter runflutter run --profileflutter build apk --release

5. Build Android APK

flutter build apk --release
# Output: build/app/outputs/flutter-apk/app-release.apk

# Split per ABI (giảm 50% size — mỗi ABI 1 APK)
flutter build apk --release --split-per-abi
# Output:
#   app-arm64-v8a-release.apk
#   app-armeabi-v7a-release.apk
#   app-x86_64-release.apk

6. Android App Bundle (AAB)

flutter build appbundle --release
# Output: build/app/outputs/bundle/release/app-release.aab

Google Play khuyến nghị AAB. Google Play split per-device download — user tải nhỏ hơn.

7. Android signing

Generate keystore

keytool -genkey -v -keystore ~/upload-keystore.jks \
  -keyalg RSA -keysize 2048 -validity 10000 -alias upload

# Nhập password + thông tin (CN, OU, O, City, State, Country)
# OUTPUT: upload-keystore.jks

android/key.properties

storePassword=your_store_password
keyPassword=your_key_password
keyAlias=upload
storeFile=/Users/yourname/upload-keystore.jks

KHÔNG commit file này — add vào .gitignore.

Update android/app/build.gradle

def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}

android {
    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
            storePassword keystoreProperties['storePassword']
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }
}
⚠️ Backup keystore!

Mất keystore = không update app trên Play được. Backup multiple location (Google Drive, USB, password manager). Mất keystore phải release app mới với package name khác → mất user, review.

8. Build iOS IPA

flutter build ipa --release
# Output: build/ios/ipa/Runner.ipa

9. iOS signing

iOS signing là pain point. Cần:

  1. Apple Developer account ($99/năm).
  2. App ID — register Bundle ID trên Apple Developer Portal.
  3. Certificate — Development + Distribution (.p12).
  4. Provisioning Profile — link App ID + Certificate.
  5. Team — Xcode Signing & Capabilities.

Automatic vs Manual signing

  • Automatic: Xcode tự handle. Pros: dễ. Cons: đôi khi vẽ cert sai, đặc biệt với CI.
  • Manual: control đầy đủ. Pros: reliable cho CI. Cons: setup phức tạp.

Production team thường dùng fastlane match để sync cert + profile qua git encrypted.

10. macOS app

flutter build macos --release
# Output: build/macos/Build/Products/Release/MyApp.app

# Notarize cho distribute outside App Store
xcrun notarytool submit MyApp.app.zip --keychain-profile "AC_PASSWORD" --wait
xcrun stapler staple MyApp.app

11. Build Web

flutter build web --release
# Output: build/web/

CanvasKit vs HTML renderer

CanvasKitHTML
RenderWebAssembly + SkiaHTML/CSS + Canvas
Size~2MB libNhẹ hơn
ConsistencyPixel-perfect with mobileBrowser-native, có thể khác
SEOKém (canvas)Tốt hơn
Use caseApp giống mobile, animation phức tạpApp content-heavy, SEO matter
# Force HTML renderer
flutter build web --web-renderer html --release

# Force CanvasKit
flutter build web --web-renderer canvaskit --release

# Auto (default): CanvasKit trên desktop, HTML trên mobile

12. Web hosting

Firebase Hosting

npm install -g firebase-tools
firebase login
firebase init hosting
# Choose: build/web as public, configure as SPA → yes

flutter build web --release
firebase deploy --only hosting

# URL: https://your-project.web.app

Alternatives

  • Cloudflare Pages — free, custom domain, CDN edge.
  • Netlify / Vercel — same idea, drag-drop hoặc git integration.
  • GitHub Pages — free, push build/web lên gh-pages branch.

13. Firebase App Distribution

Internal testing — upload AAB/IPA, invite tester qua email.

npm install -g firebase-tools
firebase login

# Upload Android AAB
firebase appdistribution:distribute build/app/outputs/bundle/release/app-release.aab \
  --app YOUR_FIREBASE_APP_ID \
  --groups testers

# Upload iOS IPA
firebase appdistribution:distribute build/ios/ipa/Runner.ipa \
  --app YOUR_FIREBASE_APP_ID \
  --groups testers

14. TestFlight (iOS beta)

  1. Build IPA: flutter build ipa --release.
  2. Upload qua Xcode Organizer hoặc Transporter app.
  3. App Store Connect → TestFlight → External Testing → add tester.
  4. Tester nhận email + cài qua TestFlight app.
  5. External tester cần Apple review (~1 ngày), internal không.

15. Google Play Console

4 testing track:

  • Internal testing — instant, max 100 tester.
  • Closed testing — invite, Google review ~1h.
  • Open testing — public beta link.
  • Production — live trên Play Store.
flutter build appbundle --release
# Upload manually qua Play Console UI
# Hoặc tự động: fastlane supply

16. Flavor — multi env

Build dev/staging/prod với config khác (bundle id, app name, API endpoint, Firebase project).

Android — productFlavors

// android/app/build.gradle
android {
    flavorDimensions "env"
    productFlavors {
        dev {
            dimension "env"
            applicationIdSuffix ".dev"
            resValue "string", "app_name", "MyApp Dev"
        }
        staging {
            dimension "env"
            applicationIdSuffix ".staging"
            resValue "string", "app_name", "MyApp Staging"
        }
        prod {
            dimension "env"
            resValue "string", "app_name", "MyApp"
        }
    }
}

iOS — Xcode schemes

iOS không có productFlavor như Android — cần tạo Xcode schemes thủ công. Steps: Product → Scheme → New Scheme → Duplicate "Runner" cho mỗi env.

Dart — entry point per flavor

// lib/main_dev.dart
import 'app.dart';

void main() => runApp(App(env: 'dev', apiUrl: 'https://api-dev.example.com'));

// lib/main_staging.dart
void main() => runApp(App(env: 'staging', apiUrl: 'https://api-staging.example.com'));

// lib/main_prod.dart
void main() => runApp(App(env: 'prod', apiUrl: 'https://api.example.com'));
# Run
flutter run --flavor dev -t lib/main_dev.dart
flutter run --flavor staging -t lib/main_staging.dart

# Build
flutter build apk --flavor prod --release -t lib/main_prod.dart

17. CI/CD intro

Options

  • fastlane — Ruby tool, mạnh nhất cho mobile. Match (signing), Pilot (TestFlight), Supply (Play).
  • Codemagic — Flutter-first CI, dễ setup.
  • Bitrise — mobile-focused CI.
  • GitHub Actions — free for open source, free tier limit.

GitHub Actions example

# .github/workflows/deploy.yml
name: Deploy
on:
  push:
    tags: ['v*']

jobs:
  android:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: subosito/flutter-action@v2
        with: { flutter-version: '3.22.0', channel: stable }

      - run: flutter pub get
      - run: flutter test
      - run: flutter build appbundle --release --flavor prod -t lib/main_prod.dart
        env:
          # decode keystore từ secret
          KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}

      - uses: actions/upload-artifact@v4
        with:
          name: aab
          path: build/app/outputs/bundle/prodRelease/app-prod-release.aab

  ios:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v4
      - uses: subosito/flutter-action@v2
      - run: flutter build ipa --release
      # Upload via fastlane pilot...

18. Crash reporting

Capture exception runtime production. 2 options chính:

  • Firebase Crashlytics — free, tích hợp Firebase.
  • Sentry — feature richer, có self-host option.
flutter pub add firebase_core firebase_crashlytics
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  // Catch Flutter framework error
  FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError;

  // Catch async / platform error
  PlatformDispatcher.instance.onError = (e, st) {
    FirebaseCrashlytics.instance.recordError(e, st, fatal: true);
    return true;
  };

  runApp(const MyApp());
}

// Manual log
FirebaseCrashlytics.instance.log('user clicked checkout');
FirebaseCrashlytics.instance.setUserIdentifier(user.id);

19. Analytics

  • Firebase Analytics — free, integration với Firebase suite.
  • Mixpanel — event-based, free tier.
  • Amplitude — product analytics.
  • PostHog — open source, self-host được.
await FirebaseAnalytics.instance.logEvent(
  name: 'purchase',
  parameters: {'item_id': 'sku123', 'value': 99.99},
);

20. App size optimization

# Phân tích size
flutter build apk --analyze-size
flutter build appbundle --analyze-size

# Output: bảng size component, font, asset, code

Tips giảm size

  • --split-per-abi Android — 50% nhỏ hơn.
  • --tree-shake-icons — chỉ include Material icon dùng.
  • Compress image: WebP thay PNG, JPEG quality 70-80%.
  • Remove unused asset trong pubspec.yaml.
  • Font subset — chỉ ship glyph dùng (vd Vietnamese subset).
  • Audit dependencies — bỏ package nặng không dùng.

21. Launcher icons + splash

flutter_launcher_icons

flutter pub add --dev flutter_launcher_icons
# pubspec.yaml
flutter_launcher_icons:
  android: true
  ios: true
  image_path: "assets/icon/icon.png"   # 1024x1024 PNG
  adaptive_icon_background: "#FFFFFF"
  adaptive_icon_foreground: "assets/icon/foreground.png"
  web:
    generate: true
  macos:
    generate: true
dart run flutter_launcher_icons
# Sinh icon cho tất cả platform

flutter_native_splash

flutter_native_splash:
  color: "#FFFFFF"
  image: assets/splash.png
  android_12:
    image: assets/splash_android12.png
    color: "#FFFFFF"
dart run flutter_native_splash:create

22. Bài tập

Perf optimize ListView 1000 item

Bật Performance Overlay. Scroll ListView 1000 item — UI bar đỏ. Optimize bằng ListView.builder + const + RepaintBoundary. Đo lại, đỏ → xanh.

💡 Gợi ý đáp án

3 step: (1) ListView → ListView.builder, (2) const cho item widget, (3) RepaintBoundary wrap item heavy paint.

Build signed APK

Build release APK signed với keystore tự sinh. Cài lên Android device thực tế.

💡 Gợi ý đáp án

Tham khảo section 5-7. keytoolkey.propertiesbuild.gradle signingConfigs → flutter build apk --release → adb install.

TestFlight upload

Build IPA, upload TestFlight, gửi cho 1 tester thật.

💡 Gợi ý đáp án

Cần Apple Developer account ($99/năm). flutter build ipa → upload via Xcode Organizer → TestFlight UI invite tester email.

Setup 3 flavor

Setup 3 flavor (dev/staging/prod) với 3 API endpoint khác. Run + build từng flavor.

💡 Gợi ý đáp án

Tham khảo section 16. Android productFlavors + iOS schemes + 3 entry point Dart.

GitHub Actions CI

CI GitHub Actions: trigger merge to main → build APK + web → upload Firebase App Distribution.

💡 Gợi ý đáp án

Workflow file (section 17). Secret keystore (base64 encode). firebase-cli action cho App Distribution.

23. Quiz

Q1

APK vs AAB — Google Play khuyến nghị?

Xem đáp án

Đáp án: AAB (App Bundle). Google split per-device, download nhỏ hơn 50%+.

Q2

Profile mode khác Release?

Xem đáp án

Đáp án: Profile = AOT + profile flag (cho DevTools track). Slight slower than Release. Dùng để đo perf gần production. Không hot reload.

Q3

Web CanvasKit vs HTML — chọn nào?

Xem đáp án

Đáp án: CanvasKit feature đầy đủ, giống mobile (~2MB). HTML nhẹ, SEO tốt, một số widget không support hoàn hảo. Default: Flutter pick auto.

Q4

iOS auto signing — vấn đề?

Xem đáp án

Đáp án: Cần đăng nhập Apple Developer, đôi khi vẽ cert sai. Manual signing đáng tin cho CI. fastlane match là solution mature.

Q5

App size lớn — trick giảm?

Xem đáp án

Đáp án: --split-per-abi, --tree-shake-icons, remove unused asset, dùng WebP, font subset, audit deps.

Q6

Flavor Android setup file nào?

Xem đáp án

Đáp án: android/app/build.gradle trong android { flavorDimensions ... productFlavors { dev {}, staging {}, prod {} } }.

Q7

Firebase Crashlytics capture exception nào?

Xem đáp án

Đáp án: Native crash + Dart uncaught. Cần setup FlutterError.onError + PlatformDispatcher.instance.onError.

Q8

Hot reload hoạt động mode nào?

Xem đáp án

Đáp án: Chỉ Debug (JIT). Profile/Release là AOT — code đã native binary.

24. Tổng kết

  • ✅ Performance Overlay 2 bar (UI + Raster), 16ms threshold.
  • ✅ DevTools 6 panel: Inspector, Performance, CPU, Memory, Network, Logging.
  • ✅ 6 lỗi perf phổ biến + fix.
  • ✅ Build mode: Debug (JIT, dev) / Profile (AOT, đo) / Release (AOT, ship).
  • ✅ Android: APK + AAB. AAB chuẩn cho Play.
  • ✅ Signing Android: keystore + key.properties + build.gradle signingConfigs.
  • ✅ iOS: IPA + Xcode signing (cert + provisioning).
  • ✅ Web: CanvasKit vs HTML renderer.
  • ✅ Deploy: Firebase Hosting / TestFlight / Play Console.
  • ✅ Flavor multi-env: Android productFlavors + iOS schemes.
  • ✅ CI/CD: fastlane, GitHub Actions, Codemagic.
  • ✅ Crash report: Crashlytics. Analytics: Firebase.
  • ✅ App size: split-per-abi, tree-shake-icons, WebP.
  • ✅ Launcher icons + splash via package.

25. Tổng kết Flutter curriculum

Bạn vừa hoàn thành 15 chương Flutter. Chương trình bao gồm:

  1. Setup, FVM, toolchain.
  2. Widget Tree mental model.
  3. Layout system, constraints.
  4. Material 3, theming, dark mode.
  5. Navigation, go_router, deep link.
  6. State foundation: setState + InheritedWidget.
  7. flutter_bloc deep dive.
  8. Provider / Riverpod / GetX comparison.
  9. Forms & input.
  10. Network: HTTP, Dio, Retrofit, repository pattern.
  11. Local storage: SharedPreferences → Drift, Hive, Isar.
  12. Animation: implicit / explicit / Hero / Lottie.
  13. Custom Paint & 3 cây render.
  14. Testing 4 level + CI.
  15. Performance + Deploy production.
🧠 Bước tiếp theo

Bạn đã có nền tảng để build production Flutter app. Bước tiếp theo:

  • Build side project end-to-end — apply 15 chương vào 1 app thật.
  • Đọc code mở các Flutter app lớn (Wonderous, Reflectly, FlutterFire samples).
  • Theo dõi Flutter team channel: YouTube, blog medium, Flutter Forward conference.
  • Đóng góp package mở trên pub.dev khi feel comfortable.

Hoàn thành Pillar 9 Programming Language — kết thúc 9 trụ cột IT Basic. Chúc bạn ship được app đầu tiên!