본문 바로가기
SW 개발/FrontEnd(Flutter)

Flutter Parent-Child Widget간 State 전달과 업데이트 방법

by Kibua20 2021. 12. 29.

Flutter Widget 간  State 전달 방법과 업데이트 방법을 정리합니다.  Flutter에서는 React Props와 State 관리 방식과 유사한 방식을 사용하고 있습니다.  Flutter에서는 크게 2가지 방법으로 분류할 수 있습니다. 

 

  • 부모 Widget에서 자식 Widget으로 State 전달
  • 자식 Widget에서 부모 Widget의 callback함수를 통해서 State update

Flutter Widget 간 State 전달 방식

 

부모 Widget에서 자식 Widget으로 State 전달 

부모 Widget에서 자식 Widget으로 state 전달하는 방법은 다음과 같은 3단계로 진행합니다.  

  1. 부모 Widget에서 자식 Widget으로 State 보내기 
  2. 자식 Widget에서 전달받을 state를 class member 변수로 등록하기 
  3. 자식 Widget에서는 전달받은 state의class 변수를 사용하기

부모 Widget에서 자식 Widget으로 State 전달

 

Flutter Application 생성 시 만든 코드를 예를 들면 다음과 같습니다.  MyApp (=부모 Widget)에서 MyHomePage(=자식 Widget)으로  title 이름의 state에 'Flutter Demo Home Page'값으로 parameter를 전달합니다. 

 MyHomePage(title: 'Flutter Demo Home Page'),

 

MyHomePage(=자식 Widget)에서는  class member에 title을 등록합니다.  일반적으로 부모로 전달받은  state는 자식 Widget에서 값을 변경하지 않기 때문에 final 변수를 사용합니다. 

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;
    ...
}

 

 MyHomePage의 State를 관리하는  _MyHomePageState에서는 MyHomePage의 class member를 사용하기 위해서 widget.title 값으로 접근해서 사용합니다.  widget이란 keyword를 통해서 MyHomePage class member을 접근 가능 주의해주세요. 


class _MyHomePageState extends State<MyHomePage> {  
  ...
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
    ....
  }
}

 

자식 Widget에서 부모 Widget으로 State 전달

자식 Widget에서 부모의 State를 직접 접근은 불 가능하며, 대신 부모 Widget에서 State처리 함수를 자식 Widget으로 전달하고 이를 호출하는 방식을 사용해야 합니다.  이에 대한 설명은 링크를 참조하시고, 방법을 요약하면 다음과 같습니다. 

 

  1. Parent Widget:  State 처리 함수 생성, State 처리 함수는 내부 변수를 직접 접근 가능하면 Rendering을 다시 하기 위해서는 setState() 함수를 반드시 호출해야 합니다. 
  2. Parent Widget:  State 처리 함수를 Child Widget으로 function parameter로 전달 
  3. Chid Widget: Parent Widget에서 전달한 function parameter를 class member로 등록합니다. 
  4. Child Widget: Parent Widget에서 전달받은 function을 실행합니다. 

자식 Widget에서 부모 Widget으로 State 전달

 

Demo 예제를 사용하면 Child Widget으로 Elevation Button을 Widget을 만들고 클릭 시 counter 값을 업데이트하는 예제로 설명하겠습니다. 

자식 Widget에서 부모 Widget으로 State 전달 - Sample demo

Parent Widget에서 _counter 값을 선언하고 setState() 함수를 호출하는 _incrementCounter() 함수를 생성합니다. 

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

 

Parent Widget에서 Child Widget인 SampelChildWidget으로  notifyParent 이름으로 _incrementCounter() 함수를 전달합니다.

SampleChildWidget(notifyParent: _incrementCounter)

 

ChildWigdet에서는 ParentWidget인 MyHomePage에서 전달한 notifyParent 인자 값을 선언하고 받습니다. 

class SampleChildWidget extends StatefulWidget {
  const SampleChildWidget({Key? key, required this.notifyParent}) : super(key: key);
  final Function() notifyParent;

  @override
  _SampleChildWidgetState createState() => _SampleChildWidgetState();
}

 

ChildWidget에서 전달받은 notifyParent 함수를 onPress Event를 받아서 호출합니다. 

class _SampleChildWidgetState extends State<SampleChildWidget> {
  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: widget.notifyParent,
      child: const Text('Child Widget'),
    );
  }
}

 

findAncestorStateOfType 함수를 사용하여 Child Widget에서 부모의 State 변경 

Child Widget에서 context의 findAncestorStateOfType()로 parent를 찾고  parent의 state 변경도 가능합니다. (참고 링크)  개인적으로는 부모의 State 값을 자식에서 변경할 수 있기 때문에 지양하는 방법입니다. 

 

 

본 포스팅에서 설명한 코드는 다음과 같고, GitHub https://github.com/kibua20/devDocs/tree/master/flutter/a01Basic (lib/main01.dart)에도 반영하였습니다.

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
            SampleChildWidget(notifyParent: _incrementCounter)
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

class SampleChildWidget extends StatefulWidget {
  const SampleChildWidget({Key? key, required this.notifyParent}) : super(key: key);
  final Function() notifyParent;

  @override
  _SampleChildWidgetState createState() => _SampleChildWidgetState();
}

class _SampleChildWidgetState extends State<SampleChildWidget> {
  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: widget.notifyParent,
      child: const Text('Child Widget'),
    );
  }
}

 

 

관련 글:

[SW 개발/FrontEnd(Flutter)] - Flutter 특징과 개발환경 설정 방법

[SW 개발/FrontEnd(Flutter)] - Android Studio와 Visual Code로 Flutter Project 프로젝트 생성

[SW 개발/FrontEnd(Flutter)] - Flutter Stateless와 Stateful Widget 개념과Life Cycle, 대표 Widget(MaterialApp, Scaffold)

[SW 개발/Python] - Selenium 4.0 개선 사항 정리 - WebDriver 자동 로딩 가능

[SW 개발/Android] - 안드로이드 adb 설치 및 설정 방법

[SW 개발/Data 분석 (RDB, NoSQL, Dataframe)] - Jupyter Notebook의 업그레이드: Jupyter Lab 설치 및 extension 사용법

[SW 개발/Data 분석 (RDB, NoSQL, Dataframe)] - Pandas Dataframe 처리 속도 향상을 위한 병렬 처리 방법: Swifter 모듈 (사용법 쉬움)

[SW 개발/Python] - Python 가상환경(Virtual Environment) 만들기 위한 virtualenv 명령어 및 실행 예제

[SW 개발/Data 분석 (RDB, NoSQL, Dataframe)] - Python Selenium과 BeautifulSoup을 활용하여 Google PlayStore 사용자 리뷰 (댓글) 가져오기 (Sample Code 포함)

[SW 개발/Python] - Python 자동 테스트를 위한 Pytest 사용법과 예제




댓글