subindev 님의 블로그

[Flutter] #11 콜백 함수의 이해 본문

앱개발/Flutter

[Flutter] #11 콜백 함수의 이해

subindev 2025. 1. 20. 09:30

 

 

 

1단계 - 기본 구조 작성

개념

  1. 부모-자식 위젯 구조를 이해합니다.
  2. 부모는 상태(state)를 관리하며, 자식은 단순히 UI 요소로 동작합니다.

코드 설명

  • 부모 위젯 ParentsView는 StatefulWidget으로 선언되어, 상태를 관리합니다.
  • 자식 위젯 ChildA, ChildB는 StatelessWidget으로 선언되어, 상태 없이 UI만 표시합니다.
  • InkWell 위젯을 통해 클릭 이벤트를 감지합니다.
  • 부모는 displayText라는 상태 변수에 따라 화면에 메시지를 출력합니다.
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: ParentsView(),
    );
  }
}

// 부모 클래스
class ParentsView extends StatefulWidget {
  const ParentsView({super.key});

  @override
  State<ParentsView> createState() => _ParentsViewState();
}

class _ParentsViewState extends State<ParentsView> {
  String displayText = '여기는 부모 위젯 변수야';

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: Column(
          children: [
            Expanded(flex: 1, child: Center(child: Text(displayText))),
            Expanded(flex: 1, child: ChildA()),
            Expanded(flex: 1, child: ChildB()),
          ],
        ),
      ),
    );
  }
}

// 자식 a
class ChildA extends StatelessWidget {
  const ChildA({super.key});

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(30.0),
      child: InkWell(
        onTap: () {
          print('child a 에 이벤트 발생 ');
        },
        child: Container(
          width: double.infinity,
          color: Colors.orange,
          child: Center(child: Text('CHILD A')),
        ),
      ),
    );
  }
}

// 자식 b
class ChildB extends StatelessWidget {
  const ChildB({super.key});

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(30.0),
      child: InkWell(
        onTap: () {
          print('child b 에 이벤트 발생 ');
        },
        child: Container(
          width: double.infinity,
          color: Colors.red,
          child: Center(child: Text('CHILD B')),
        ),
      ),
    );
  }
}

 

 


 

 

2단계 - 콜백 메서드로 이벤트 전달

개념

  1. 자식 위젯에서 발생한 이벤트를 부모로 전달합니다.
  2. Flutter에서는 이를 위해 콜백 함수를 활용합니다.

코드 설명

  • 부모는 자식에게 콜백 함수(onCallbackReceived)를 전달합니다.
  • 자식은 특정 이벤트(예: 클릭)가 발생했을 때 이 콜백 함수를 호출하여 부모에게 알립니다.
  • 부모는 전달받은 이벤트를 처리하며 상태를 업데이트합니다.
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: ParentsView(),
    );
  }
}

// 부모 클래스
class ParentsView extends StatefulWidget {
  const ParentsView({super.key});

  @override
  State<ParentsView> createState() => _ParentsViewState();
}

class _ParentsViewState extends State<ParentsView> {
  String displayText = '여기는 부모 위젯 변수야';

  // 메서드를 선언해 보자
  void handleChildEvent() {
    // build() 메서드 호출
    setState(() {
      displayText = '야 어딘지는 모르겠지만 자식 위젯에서 이벤트 발생';
    });
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: Column(
          children: [
            Expanded(flex: 1, child: Center(child: Text(displayText))),
            Expanded(
                flex: 1, child: ChildA(onCallbackReceived: handleChildEvent)),
            Expanded(
                flex: 1, child: ChildB(onCallbackReceived: handleChildEvent)),
          ],
        ),
      ),
    );
  }
}

// 자식 a
class ChildA extends StatelessWidget {
  const ChildA({required this.onCallbackReceived, super.key});

  final Function onCallbackReceived;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(30.0),
      child: InkWell(
        onTap: () {
          print('child a 에 이벤트 발생 ');
          // Fuction 데이터 타입
          // onCallbackReceived() ---> 호출
          onCallbackReceived();
        },
        child: Container(
          width: double.infinity,
          color: Colors.orange,
          child: Center(child: Text('CHILD A')),
        ),
      ),
    );
  }
}

// 자식 b
class ChildB extends StatelessWidget {
  const ChildB({required this.onCallbackReceived, super.key});

  final Function onCallbackReceived;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(30.0),
      child: InkWell(
        onTap: () {
          print('child b 에 이벤트 발생 ');
          onCallbackReceived();
        },
        child: Container(
          width: double.infinity,
          color: Colors.red,
          child: Center(child: Text('CHILD B')),
        ),
      ),
    );
  }
}

 

 

 


 

3단계 - 자식에서 데이터를 부모로 전달

개념

  1. 자식이 단순히 이벤트 발생을 알리는 것뿐만 아니라 데이터를 함께 전달합니다.
  2. 이를 위해 콜백 함수에 매개변수를 추가합니다.

코드 설명

  • 콜백 함수의 타입을 Function(String message)로 정의하여, 자식이 문자열 데이터를 부모로 전달할 수 있도록 설정합니다.
  • 자식 위젯은 이벤트 발생 시 데이터를 생성하여 부모에게 전달합니다.
  • 부모는 전달받은 데이터를 setState로 상태를 업데이트합니다.
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: ParentsView(),
    );
  }
}

// 부모 클래스
class ParentsView extends StatefulWidget {
  const ParentsView({super.key});

  @override
  State<ParentsView> createState() => _ParentsViewState();
}

class _ParentsViewState extends State<ParentsView> {
  String displayText = '여기는 부모 위젯 변수야';

  // 메서드를 선언해 보자
  void handleChildEvent(String message) {
    // build() 메서드 호출
    setState(() {
      displayText = message;
    });
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: Column(
          children: [
            Expanded(flex: 1, child: Center(child: Text(displayText))),
            Expanded(
                flex: 1, child: ChildA(onCallbackReceived: handleChildEvent)),
            Expanded(
                flex: 1, child: ChildB(onCallbackReceived: handleChildEvent)),
          ],
        ),
      ),
    );
  }
}

// 자식 a
class ChildA extends StatelessWidget {
  const ChildA({required this.onCallbackReceived, super.key});

  final Function(String message) onCallbackReceived;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(30.0),
      child: InkWell(
        onTap: () {
          print('child a 에 이벤트 발생 ');
          // Fuction 데이터 타입
          // onCallbackReceived() ---> 호출
          onCallbackReceived('A가 연산한 데이터를 전달 했음');
        },
        child: Container(
          width: double.infinity,
          color: Colors.orange,
          child: Center(child: Text('CHILD A')),
        ),
      ),
    );
  }
}

// 자식 b
class ChildB extends StatelessWidget {
  const ChildB({required this.onCallbackReceived, super.key});

  final Function(String message) onCallbackReceived;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(30.0),
      child: InkWell(
        onTap: () {
          print('child b 에 이벤트 발생 ');
          onCallbackReceived('자식 b가 연산한 데이터 전달');
        },
        child: Container(
          width: double.infinity,
          color: Colors.red,
          child: Center(child: Text('CHILD B')),
        ),
      ),
    );
  }
}