返回網誌
效能2025年6月12日安安科技團隊閱讀時間 10 分鐘

優化 Flutter 應用程式效能:
10 個實證技巧

學習進階技巧,全面提升 Flutter 應用程式效能、減少記憶體用量,交付用戶喜愛、順滑如絲的 60fps 體驗。

Flutter 效能優化
// 簡報

效能足以決定一個 Flutter 應用程式的成敗。用戶期望流暢的捲動、即時的回應和省電的表現。即使應用程式再精美,一旦感覺遲緩或耗電嚴重,也難逃失敗。

在優化過數十個 Flutter 應用程式之後,我們整理出一批持續見效的最有效技巧。這些不只是紙上談兵的理論——而是經過實戰考驗、確實改善過生產環境中真實應用程式的策略。

01/10

處處使用 const constructor

這是效能優化中最垂手可得的成果。const constructor 會建立編譯期常數,讓 Flutter 可以進行更積極的優化,減少 widget 重建和記憶體分配。

const_constructors.dart
// ❌ Bad
Container(
  padding: EdgeInsets.all(16),
  child: Text('Hello'),
)

// ✅ Good
const Container(
  padding: const EdgeInsets.all(16),
  child: const Text('Hello'),
)
成效:在複雜 UI 中可減少多達 40% 的 widget 重建
02/10

實作高效的清單建構

處理長清單時,務必使用 ListView.builder() 而非 ListView()。它會按需建立 widget,而不是一次過全部建立,大幅減少記憶體用量和初始載入時間。

list_building.dart
// ❌ Bad - Creates all widgets at once
ListView(
  children: items.map((item) => ItemWidget(item)).toList(),
)

// ✅ Good - Creates widgets on demand
ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) => ItemWidget(items[index]),
)
成效:清單項目超過 1000 個時,可減少 80% 記憶體用量
03/10

優化圖片載入

圖片往往是最大的效能瓶頸。使用 cacheWidth 和 cacheHeight 在記憶體中調整圖片尺寸,避免 OOM 錯誤並提升捲動效能。

image_loading.dart
// ✅ Optimized image loading
Image.network(
  imageUrl,
  cacheWidth: 400,
  cacheHeight: 400,
  fit: BoxFit.cover,
)

// For hero images, use precacheImage
@override
void didChangeDependencies() {
  super.didChangeDependencies();
  precacheImage(NetworkImage(heroImageUrl), context);
}
成效:圖片密集的應用程式可減少 60-70% 記憶體用量
04/10

策略性使用 RepaintBoundary

RepaintBoundary 會為 widget 建立獨立圖層,避免 UI 其他部分更新時,複雜 widget 被不必要地重繪。適用於靜態內容或繪製成本高的 widget。

repaint_boundary.dart
// Wrap expensive widgets
RepaintBoundary(
  child: ComplexCustomPaintWidget(),
)

// Check paint performance
void checkNeedsRepaint() {
  final RenderRepaintBoundary boundary = 
    _repaintKey.currentContext!.findRenderObject() as RenderRepaintBoundary;
  
  print('Needs repaint: ${boundary.debugNeedsPaint}');
}
成效:可提升 30-50% 動畫效能
05/10

實作分頁與延遲載入

不要一次過載入所有資料。為清單實作分頁、為內容實作延遲載入,既能縮短初始載入時間、減少記憶體用量,也能改善用戶的體感效能。

paginated_list.dart
class PaginatedList extends StatefulWidget {
  @override
  _PaginatedListState createState() => _PaginatedListState();
}

class _PaginatedListState extends State<PaginatedList> {
  final _scrollController = ScrollController();
  List<Item> _items = [];
  bool _isLoading = false;
  int _page = 1;

  @override
  void initState() {
    super.initState();
    _loadMore();
    _scrollController.addListener(_onScroll);
  }

  void _onScroll() {
    if (_scrollController.position.pixels >= 
        _scrollController.position.maxScrollExtent * 0.8) {
      _loadMore();
    }
  }

  Future<void> _loadMore() async {
    if (_isLoading) return;
    setState(() => _isLoading = true);
    
    final newItems = await fetchItems(page: _page);
    setState(() {
      _items.addAll(newItems);
      _page++;
      _isLoading = false;
    });
  }
}
成效:大型資料集的初始載入時間可縮短 70%
06/10

盡量減少 widget 重建

善用 const widget、妥善運用 Key,並拆分 widget 以減少不必要的重建。把靜態部分抽取成獨立 widget,並使用 ValueListenableBuilder 做針對性更新。

counter_widget.dart
// ❌ Bad - Entire widget rebuilds
class CounterWidget extends StatefulWidget {
  @override
  _CounterWidgetState createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
  int _counter = 0;
  
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ExpensiveStaticWidget(), // Rebuilds unnecessarily
        Text('Count: $_counter'),
        ElevatedButton(
          onPressed: () => setState(() => _counter++),
          child: Text('Increment'),
        ),
      ],
    );
  }
}

// ✅ Good - Only counter rebuilds
class OptimizedCounterWidget extends StatelessWidget {
  final _counter = ValueNotifier<int>(0);
  
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const ExpensiveStaticWidget(), // Never rebuilds
        ValueListenableBuilder<int>(
          valueListenable: _counter,
          builder: (context, value, child) {
            return Text('Count: $value');
          },
        ),
        ElevatedButton(
          onPressed: () => _counter.value++,
          child: const Text('Increment'),
        ),
      ],
    );
  }
}
成效:複雜 UI 的重建時間可減少 50-80%
07/10

優化動畫

動畫請使用 AnimatedBuilder 而非 setState,確保只有動畫中的 widget 重建,而非整棵 widget 樹。同時要妥善 dispose 動畫,防止記憶體洩漏。

fade_box.dart
// ✅ Efficient animation
class FadeBox extends StatefulWidget {
  @override
  _FadeBoxState createState() => _FadeBoxState();
}

class _FadeBoxState extends State<FadeBox> 
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 1),
      vsync: this,
    );
    _animation = Tween<double>(
      begin: 0.0,
      end: 1.0,
    ).animate(CurvedAnimation(
      parent: _controller,
      curve: Curves.easeIn,
    ));
    _controller.forward();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _animation,
      builder: (context, child) {
        return Opacity(
          opacity: _animation.value,
          child: child,
        );
      },
      child: const ExpensiveWidget(), // Built only once
    );
  }
}
成效:動畫幀率由 40fps 提升至穩定 60fps
08/10

以 isolate 處理繁重運算

把繁重的運算移到 isolate,避免阻塞 UI 執行緒,包括 JSON 解析、圖像處理或複雜計算。讓主執行緒保持空閒,UI 更新才會流暢。

isolates.dart
import 'dart:isolate';

// Heavy computation in isolate
Future<List<ProcessedData>> processDataInIsolate(String rawData) async {
  final p = ReceivePort();
  await Isolate.spawn(_processData, p.sendPort);
  
  final response = await p.first;
  return response as List<ProcessedData>;
}

void _processData(SendPort p) {
  // Heavy processing here
  final processed = parseAndTransformData(largeDataSet);
  Isolate.exit(p, processed);
}

// Using compute for simpler cases
import 'package:flutter/foundation.dart';

Future<String> parseJson(String json) async {
  return await compute(_parseJsonInBackground, json);
}

String _parseJsonInBackground(String json) {
  // Parse large JSON
  final parsed = jsonDecode(json);
  return processData(parsed);
}
成效:避免超過 16ms 的操作凍結 UI
09/10

實作妥善的快取機制

為成本高的操作、API 回應和計算結果建立快取。圖片可使用 cached_network_image 之類的套件,並為經常存取的資料實作記憶體快取。

cache_manager.dart
// Simple memory cache implementation
class CacheManager {
  static final _cache = <String, CacheEntry>{};
  static const _maxCacheSize = 100;
  static const _defaultTTL = Duration(minutes: 5);

  static T? get<T>(String key) {
    final entry = _cache[key];
    if (entry == null || entry.isExpired) {
      _cache.remove(key);
      return null;
    }
    return entry.value as T;
  }

  static void set<T>(String key, T value, {Duration? ttl}) {
    if (_cache.length >= _maxCacheSize) {
      // Remove oldest entries
      _removeOldest();
    }
    
    _cache[key] = CacheEntry(
      value: value,
      expiry: DateTime.now().add(ttl ?? _defaultTTL),
    );
  }

  static void _removeOldest() {
    final sortedKeys = _cache.keys.toList()
      ..sort((a, b) => _cache[a]!.expiry.compareTo(_cache[b]!.expiry));
    
    for (var i = 0; i < _maxCacheSize ~/ 4; i++) {
      _cache.remove(sortedKeys[i]);
    }
  }
}

class CacheEntry {
  final dynamic value;
  final DateTime expiry;

  CacheEntry({required this.value, required this.expiry});

  bool get isExpired => DateTime.now().isAfter(expiry);
}
成效:API 呼叫減少 60%,回應時間改善 90%
10/10

剖析並量度一切

使用 Flutter DevTools 剖析你的應用程式。在開發階段啟用 performance overlay,追蹤幀渲染時間並找出卡頓。優化前後都要量度,以驗證改善成效。

performance_tracker.dart
// Enable performance overlay
MaterialApp(
  showPerformanceOverlay: true, // Shows FPS and frame time
  home: MyApp(),
)

// Track custom performance metrics
class PerformanceTracker {
  static final _timers = <String, Stopwatch>{};

  static void startTimer(String operation) {
    _timers[operation] = Stopwatch()..start();
  }

  static Duration? endTimer(String operation) {
    final timer = _timers[operation];
    if (timer == null) return null;
    
    timer.stop();
    final duration = timer.elapsed;
    _timers.remove(operation);
    
    print('$operation took: ${duration.inMilliseconds}ms');
    
    // Log slow operations
    if (duration.inMilliseconds > 16) {
      print('⚠️ SLOW OPERATION: $operation');
    }
    
    return duration;
  }
}

// Usage
PerformanceTracker.startTimer('expensive_build');
// ... expensive operation ...
PerformanceTracker.endTimer('expensive_build');
成效:找出造成 95% 效能問題的瓶頸
CHK.01調校

效能優化 檢查清單

建構優化

盡可能處處使用 const constructor
長清單使用 ListView.builder
拆分 widget 以減少重建
複雜 widget 使用 RepaintBoundary
為 widget 樹正確設置 Key

執行期優化

優化圖片載入與快取
以 isolate 處理繁重運算
實作分頁與延遲載入
為成本高的操作建立快取
剖析並量度效能
// 加速

需要協助優化你的應用程式?

我們的團隊專精於 Flutter 效能優化,曾協助多個應用程式達到 60fps 效能,並減少多達 70% 的記憶體用量。

聯絡我們
// 訊號增幅

分享這篇指南