Flutter의 부모-자식 커뮤니케이션

소개



많은 개발자들이 매우 초기에 중첩된 구성 요소 간에 데이터를 공유하는 개념을 도입했으며 이는 복잡한 사용자 인터페이스를 구축하는 데 필요한 기본 개념입니다.

개발 중 간혹 발생하는 상황은 외부에서 자식 구성 요소 내부의 함수를 호출하는 방법입니다.

예: 상위 구성 요소에서 양식 제출, 장바구니 내용을 빈 상태로 설정 등

제어 장치



자식 함수 호출을 처리하는 일반적인 방법인 컨트롤러는 자주 사용되며 심지어 Flutter docs 에서 참조됩니다.

여러 함수 속성이 있는 객체가 자식에게 전달되고 그 결과 전달된 함수의 기본 동작을 재정의합니다. 상위 구성 요소에서 해당 함수를 호출하면 하위 구성 요소에 직접적인 영향을 미칩니다.

class Controller {
  void Function()? doSomething;
}

class Parent extends StatelessWidget {
  final Controller _controller = Controller();

  Parent({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Child(controller: _controller),
        ElevatedButton(
          onPressed: () {
            _controller.doSomething!();
          },
          child: Text("Press me!"),
        )
      ],
    );
  }
}

class Child extends StatefulWidget {
  final Controller controller;

  Child({
    Key? key,
    required this.controller,
  }) : super(key: key);

  @override
  State<Child> createState() => _ChildState(controller);
}

class _ChildState extends State<Child> {
  void doSomething() {
    print("Hello!");
  }

  _ChildState(Controller _controller) {
    _controller.doSomething = doSomething;
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}


글로벌키



키의 개념은 여러 UI 프레임워크에서 공유되며 본질적으로 키는 기존 사용자 인터페이스를 업데이트하기 위해 애플리케이션에서 내부적으로 사용되는 고유 식별자입니다.

구성 요소/위젯 트리를 확인하고 새 항목과 비교하면 변경이 필요한 항목만 격리하여 보다 쉽게 ​​업데이트할 수 있습니다.

또한 키를 사용하여 응용 프로그램의 어디에서나 위젯을 직접 조작할 수 있지만(악명 높은 전역 변수인 새로운 개발자의 일반적인 함정과 유사하게 함) 부주의하게 사용하면 많은 문제가 발생합니다. 그러나 이와 같은 간단한 경우에는 거의 위협이 되지 않습니다.

class Parent extends StatelessWidget {
  final GlobalKey<_ChildState> _childKey = GlobalKey();

  Parent({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Child(key: _childKey),
        ElevatedButton(
          onPressed: () {
            _childKey.currentState!.doSomething();
          },
          child: Text("Press me!"),
        )
      ],
    );
  }
}

class Child extends StatefulWidget {
  Child({Key? key}) : super(key: key);

  @override
  State<Child> createState() => _ChildState();
}

class _ChildState extends State<Child> {
  void doSomething() {
    print("Hello!");
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}


상태 관리 패키지



일부 상용구 및 추가 설정이 포함된 메서드, 많은 상태 관리 패키지, 심지어 Flutter 자체도 깊이 중첩된 구성 요소에 일부 이벤트를 알리는 방법을 제공합니다.

이 경우에는 RiverPod 을 사용합니다.

class Notifier extends StateNotifier<bool> {
  Notifier() : super(false);

  void toggle() {
    state = !state;
  }
}

final provider = StateNotifierProvider<Notifier, bool>((ref) {
  return Notifier();
});

class Parent extends ConsumerWidget {
  const Parent({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Column(
      children: [
        Child(),
        ElevatedButton(
          onPressed: () {
            ref.read(provider.notifier).toggle();
          },
          child: Text("Press me!"),
        )
      ],
    );
  }
}

class Child extends ConsumerWidget {
  const Child({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    void doSomething() {
      print("Hello!");
    }

    ref.listen<bool>(provider, (bool? oldVal, bool newVal) {
      doSomething();
    });

    return Container();
  }
}


일부 전역 내부 데이터(이 경우 부울 값)를 저장하고 자식 및 부모 구성 요소에서 사용되는 Notifier 클래스가 선언됩니다.

자식 구성 요소는 변경 사항을 듣고 해당 기능 중 하나를 호출하여 변경 사항에 반응하는 반면 부모 구성 요소는 알림 상태를 변경하여 변경 사항을 동작으로 설정합니다.

결론



하위 구성 요소에서 함수를 호출하는 것은 때때로 많은 설정이 필요하지 않은 필수 프로세스입니다.

이러한 방법 중 하나를 사용할 수 있지만 상황과 선택한 앱 아키텍처에 따라 다릅니다.

좋은 웹페이지 즐겨찾기