티스토리 뷰

반응형

깔끔하고 효율적으로 구현하는 방법은 몇 가지 패턴과 Flutter의 생명주기를 활용하는 것입니다. 아래에 일반적인 접근법과 추천 방식을 설명할게요.

1. initState에서 초기화 처리하기

Flutter의 StatefulWidget에서 initState 메서드는 위젯이 처음 생성될 때 한 번만 호출되므로, 초기 데이터를 로딩하는 데 적합합니다. 예를 들어, 로컬 DB나 네트워크에서 데이터를 가져오는 비동기 작업을 여기서 시작할 수 있습니다.

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String? _initialData;

  @override
  void initState() {
    super.initState();
    _loadInitialData();
  }

  Future<void> _loadInitialData() async {
    try {
      // 로컬 DB (예: Hive, SQflite)나 네트워크 호출
      final data = await fetchDataFromNetwork(); // 네트워크 호출 예시
      setState(() {
        _initialData = data;
      });
    } catch (e) {
      print('Error loading data: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: _initialData == null
          ? Center(child: CircularProgressIndicator()) // 로딩 중
          : Text(_initialData!), // 데이터 표시
    );
  }
}
  • 장점: 간단하고 직관적이며, 위젯 단위로 초기화 로직을 관리 가능.
  • 주의점: 비동기 작업이 끝나기 전에 화면이 빌드되므로, 로딩 상태를 잘 처리해야 함 (예: CircularProgressIndicator).

2. FutureBuilder로 비동기 데이터 처리하기

FutureBuilder를 사용하면 비동기 작업의 상태(로딩, 성공, 에러)를 더 선언적으로 관리할 수 있습니다.

class MyHomePage extends StatelessWidget {
  Future<String> _fetchInitialData() async {
    // 네트워크나 로컬 DB에서 데이터 가져오기
    await Future.delayed(Duration(seconds: 2)); // 예시 딜레이
    return "Loaded Data";
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FutureBuilder<String>(
        future: _fetchInitialData(),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return Center(child: CircularProgressIndicator());
          } else if (snapshot.hasError) {
            return Center(child: Text('Error: ${snapshot.error}'));
          } else if (snapshot.hasData) {
            return Center(child: Text(snapshot.data!));
          }
          return Container(); // 기본값
        },
      ),
    );
  }
}
  • 장점: 로딩 상태를 명시적으로 처리하며, 코드가 깔끔함.
  • 주의점: future가 반복 호출되지 않도록 주의해야 함. 예를 들어, build 메서드에서 매번 새 Future를 생성하면 안 되니, Future를 변수로 캐싱하거나 initState에서 한 번만 호출되게 해야 함.

3. 앱 전역 초기화를 위한 main에서의 처리

앱 전체에 걸쳐 필요한 초기화(예: 전역 변수, 설정값 로딩)는 main 함수에서 처리할 수 있습니다. 이를 위해 runApp 전에 비동기 작업을 완료하도록 설계합니다.

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized(); // Flutter 바인딩 초기화 보장
  await _initializeApp(); // 앱 초기화 로직
  runApp(MyApp());
}

Future<void> _initializeApp() async {
  // 로컬 DB 초기화 (예: Hive 초기화)
  // 네트워크로 설정값 가져오기
  await Future.delayed(Duration(seconds: 1)); // 예시
  print("App initialized");
}
  • 장점: 앱 전역에서 필요한 초기화를 한 번에 처리 가능.
  • 주의점: UI 렌더링이 지연되므로, 너무 무거운 작업은 피하고 필수적인 초기화만 여기서 수행.

4. 상태 관리 도구 활용 (Provider, Riverpod 등)

더 복잡한 앱이라면 상태 관리 도구를 사용해 초기화 로직을 전역적으로 관리하는 게 좋습니다. 예를 들어, Provider를 사용한 방식은 다음과 같습니다.

void main() {
  runApp(
    ProviderScope( // 또는 ChangeNotifierProvider
      child: MyApp(),
    ),
  );
}

class AppState {
  String? initialData;

  Future<void> loadInitialData() async {
    initialData = await fetchDataFromNetwork();
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final appState = context.read<AppState>(); // 상태 가져오기
    appState.loadInitialData(); // 초기화 호출
    return Scaffold(
      body: Consumer<AppState>(
        builder: (context, state, child) {
          return state.initialData == null
              ? CircularProgressIndicator()
              : Text(state.initialData!);
        },
      ),
    );
  }
}
  • 장점: 상태를 전역적으로 관리하며, 초기화 로직을 재사용 가능.
  • 추천 상황: 앱 규모가 크거나 여러 화면에서 초기 데이터를 공유해야 할 때.

추천 접근법

  • 간단한 앱: initState + setState 또는 FutureBuilder 사용.
  • 중간 규모 앱: main에서 전역 초기화 + FutureBuilder로 화면 단위 초기화.
  • 대규모 앱: 상태 관리 도구(Provider, Riverpod, Bloc 등)를 활용해 초기화와 데이터 흐름을 통합 관리.

추가 팁

  1. 로딩 UI: 사용자 경험을 위해 로딩 중임을 알리는 UI(스피너 등)를 항상 제공하세요.
  2. 에러 처리: 네트워크나 DB 실패 시 사용자에게 피드백을 주고, 재시도 옵션을 고려하세요.
  3. 캐싱: 자주 사용하는 데이터는 로컬에 캐싱해 네트워크 호출을 줄이세요(예: SharedPreferences, Hive).

 

반응형

'Dart & Flutter > Widget & Package' 카테고리의 다른 글

Riverpod 공부  (0) 2024.07.03
Deferred Components  (0) 2023.03.20
카메라 사용하기  (0) 2023.02.16
iOS 와 안드로이드에서의 파일 저장 위치  (0) 2023.02.08
month의 week number를 계산하는 extention  (0) 2022.08.09
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함