點燈坊

失くすものさえない今が強くなるチャンスよ

CircularProgressIndicator 實現 API Loading

Sam Xiao's Avatar 2025-01-02

對於回應時間較長的 API,可使用 CircularProgressIndicator 顯示 API Loading 動畫。

Version

Flutter 3.24.5

Flutter

loading01

  • Android 與 iOS 都成功使用 CircularProgressIndicator 實現 API Loading 動畫

CircularProgressIndicator

import 'package:flutter/material.dart';

class Home extends StatefulWidget {
  const Home({super.key});

  
  State<Home> createState() => _Home();
}

class _Home extends State<Home> {
  bool _isLoading = false;

  Future<void> fetchAPI() async {
    try {
      await Future.delayed(
        Duration(seconds: 3),
      );
    } catch (e) {
      debugPrint('Error: $e');
    }
  }

  
  Widget build(BuildContext context) {
    var appBar = AppBar(
      title: Text('API Loading'),
    );

    var loading = Visibility(
      visible: _isLoading,
      child: Container(
        color: Colors.transparent,
        child: Center(
          child: CircularProgressIndicator(),
        ),
      ),
    );

    var elevatedButton = Center(
      child: ElevatedButton(
        onPressed: () async {
          setState(() => _isLoading = true);
          await fetchAPI();
          setState(() => _isLoading = false);
        },
        child: Text(
          'Fetch API',
        ),
      ),
    );

    var body = Stack(
      children: [
        elevatedButton,
        loading,
      ],
    );

    return Scaffold(
      appBar: appBar,
      body: body,
    );
  }
}

Line 11

bool _isLoading = false;
  • _isLoading:定義是否正在 API loading 的 state

Line 13

Future<void> fetchAPI() async {
  try {
    await Future.delayed(
      Duration(seconds: 3),
    );
  } catch (e) {
    debugPrint('Error: $e');
  }
}
  • 模擬呼叫 API 需要花 3 秒鐘

Line 52

var body = Stack(
  children: [
    elevatedButton,
    loading,
  ],
);
  • Stack:將一般 widget 與 loading 一起放在 Stack

Line 39

var elevatedButton = Center(
  child: ElevatedButton(
    onPressed: () async {
      setState(() => _isLoading = true);
      await fetchAPI();
      setState(() => _isLoading = false);
    },
    child: Text(
      'Fetch API',
    ),
  ),
);
  • elevatedButton:一般 button,負責呼叫 API
  • 在呼叫 API 前將 isLoading 設定為 true,開始顯示 API loading 動畫
  • 在 API 回應後將 isLoading 設定為 false,結束顯示 API loading 動畫

Line 29

var loading = Visibility(
  visible: _isLoading,
  child: Container(
    color: Colors.transparent,
    child: Center(
      child: CircularProgressIndicator(),
    ),
  ),
);
  • CircularProgressIndicator:API loading 時顯示轉圈圈動畫
  • Visibility:由 _isLoading state 控制是否顯示

Conclusion

  • 由於 loadingelevatedButton 置於同一個 Stack 內,因此當顯示 loading 時,elevatedButton 無法被按,還順便做了防呆避免重複呼叫 API