Flutter Application/앱 설계

Flutter app 상태 관리 - state hoisting

sdchjjj 2024. 11. 28. 14:51
반응형

안녕하세요.

이번 포스팅에서는 flutter app에서 상태를 관리하는 방법 중 하나인 state hoisting에 대해 간단히 알아보겠습니다.

 

언어: dart

IDE: Android Studio

Framework: Flutter

Test device: Android


State Hoisting

React, Flutter와 같은 선언형 UI 프레임워크에서 주로 사용되는 개념입니다. 이는 상태(state: UI에 영향을 미치는 변경 가능한 data)를 더 상위의 컴포넌트나 컨텍스트로 이동시켜 여러 하위 컴포넌트에서 공유할 수 있도록 하는 것을 말합니다.

 

State Hoisting은 다음과 같은 경우에 주로 사용됩니다.

  1. 두 개 이상의 컴포넌트가 동일한 상태에 접근해야 할 때: 여러 곳에서 상태를 중복 관리하는 대신, 상태를 공통된 상위 컴포넌트로 끌어올려 한 곳에서 관리하도록 합니다. 이를 통해 상태 불일치를 방지할 수 있습니다.
  2. 상위 컴포넌트가 상태를 제어해야 할 때: 상위 컴포넌트가 하위 컴포넌트에서 발생하는 상태 변화를 관리하거나 이에 반응할 수 있습니다.
  3. 캡슐화와 재사용성: 하위 컴포넌트를 stateless 상태로 유지할 수 있고, 이를 재사용 가능한 컴포넌트로 활용할 수 있습니다.

아래의 포스팅에서 사용한 소스를 state hoisting 설계대로 수정해 보겠습니다.

https://it-of-fortune.tistory.com/47

 

Flutter 원형 프로그레스(circular progress bar)

안녕하세요.이번 포스팅에서는 flutter app 내에서 로딩 중인 상태를 보여줄 수 있는 방법 중 하나인 원형 프로그레스 바를 구현해 보겠습니다. 언어: dartIDE: Android StudioFramework: FlutterTest device: An

it-of-fortune.tistory.com

 

기본 상태의 소스입니다.

main.dart

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: LoadingExample(),
  ));
}

class LoadingExample extends StatefulWidget {
  @override
  _LoadingExampleState createState() => _LoadingExampleState();
}

class _LoadingExampleState extends State<LoadingExample> {
  bool _showMessage = false;
  bool _isLoading = false;

  void _setMessageVisibility() async {
    setState(() {
      _isLoading = true;
    });
    await Future.delayed(Duration(seconds: 3));
    setState(() {
      _isLoading = false;
      _showMessage = !_showMessage;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Delay Example')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: _setMessageVisibility,
              child: Text('Show message'),
            ),
            SizedBox(height: 20),

            if (_showMessage)
              Text(
                'Hello World',
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
              ),
          ],
        ),
      ),
    );
  }
}

 

먼저, parent widget으로부터 state를 받기 위한 child widget class를 만들어 줍니다.

main.dart

class childWidget extends StatelessWidget {
  final bool showMessage;
  final bool isLoading;
  final VoidCallback onButtonPressed;

  const childWidget({
    required this.showMessage,
    required this.isLoading,
    required this.onButtonPressed,
  });

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ElevatedButton(
            onPressed: onButtonPressed,
            child: Text('Show message'),
          ),
          SizedBox(height: 20),
          if (isLoading) CircularProgressIndicator(),
          if (showMessage)
            Text(
              'Hello World',
              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
        ],
      ),
    );
  }
}

이제 이 class가 생성되면서 showMessage과 isLoading 상태를 전달받고, 버튼 클릭 시 수행할 동작 또한 전달받습니다.

 

Parent widget class는 아래와 같이 수정해 줍니다.

main.dart

class _LoadingExampleState extends State<LoadingExample> {
  bool _showMessage = false;
  bool _isLoading = false;

  void _setMessageVisibility() async {
    setState(() {
      _isLoading = true;
    });
    await Future.delayed(Duration(seconds: 3));
    setState(() {
      _isLoading = false;
      _showMessage = !_showMessage;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('State Hoisting Example')),
      body: childWidget(
        showMessage: _showMessage,
        isLoading: _isLoading,
        onButtonPressed: _setMessageVisibility,
      ),
    );
  }
}

이 class에서는 상태를 set 해주는 부분만 있고, 이후 변화된 상태를 하위 widget에게 전달해 주면서 UI를 그려주게 됩니다.

 

아래는 전체 코드입니다.

main.dart

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: LoadingExample(),
  ));
}

class LoadingExample extends StatefulWidget {
  @override
  _LoadingExampleState createState() => _LoadingExampleState();
}

class _LoadingExampleState extends State<LoadingExample> {
  bool _showMessage = false;
  bool _isLoading = false;

  void _setMessageVisibility() async {
    setState(() {
      _isLoading = true;
    });
    await Future.delayed(Duration(seconds: 3));
    setState(() {
      _isLoading = false;
      _showMessage = !_showMessage;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('State Hoisting Example')),
      body: childWidget(
        showMessage: _showMessage,
        isLoading: _isLoading,
        onButtonPressed: _setMessageVisibility,
      ),
    );
  }
}

class childWidget extends StatelessWidget {
  final bool showMessage;
  final bool isLoading;
  final VoidCallback onButtonPressed;

  const childWidget({
    required this.showMessage,
    required this.isLoading,
    required this.onButtonPressed,
  });

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ElevatedButton(
            onPressed: onButtonPressed,
            child: Text('Show message'),
          ),
          SizedBox(height: 20),
          if (isLoading) CircularProgressIndicator(),
          if (showMessage)
            Text(
              'Hello World',
              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
        ],
      ),
    );
  }
}

 

사용자가 보는 동작 면에서는 수정하기 전과 차이가 없지만, 하위 컴포넌트를 생성해 주었으며 state hoisting을 활용하여 해당 컴포넌트의 상태를 관리하도록 설계된 코드입니다.

 

result

테스트 시 동작에도 문제가 없어 보입니다.

 

state hoisting은 분명 상태를 관리하기 적합한 방법 중 하나이지만, 관리해야 할 상태가 많아지거나 여러 계층의 컴포넌트에게 상태와 callback 함수를 전달해야 할 경우 오히려 코드가 복잡해지고 관리가 어려워질 수 있으므로 자신의 프로젝트 규모에 맞게 적용하는 것이 중요합니다.

 

이상 포스팅을 마치겠습니다.

 

감사합니다.

728x90
반응형

'Flutter Application > 앱 설계' 카테고리의 다른 글

Flutter 상태 관리 - Bloc pattern  (1) 2024.11.29