[flutter] 원치 않는 위젯 빌드를 처리하는 방법?

여러 가지 이유로 때때로 build위젯 의 메소드가 다시 호출됩니다.

부모가 업데이트했기 때문에 발생한다는 것을 알고 있습니다. 그러나 이로 인해 원하지 않는 효과가 발생합니다. FutureBuilder이 방법으로 문제를 일으키는 일반적인 상황은 다음과 같습니다.

@override
Widget build(BuildContext context) {
  return FutureBuilder(
    future: httpCall(),
    builder: (context, snapshot) {
      // create some layout here
    },
  );
}

이 예제에서 빌드 메소드를 다시 호출하면 다른 http 요청이 트리거됩니다. 바람직하지 않은 것입니다.

이것을 고려하여 원치 않는 빌드를 처리하는 방법은 무엇입니까? 빌드 호출을 방지하는 방법이 있습니까?



답변

빌드 방법은 그것이 있어야하는 방식으로 설계되어 부작용없이 / 순수 . 많은 외부 요소가 다음과 같은 새 위젯 빌드를 트리거 할 수 있기 때문입니다.

  • 루트 팝 / 푸시
  • 키보드 모양 또는 방향 변경으로 인한 화면 크기 조정
  • 부모 위젯이 자식을 재생성
  • 위젯이 의존하는 InheritedWidget ( Class.of(context)패턴) 변경

이는 build메소드가 http 호출을 트리거하거나 상태를 수정 해서는 안됨을 의미합니다 .


이것은 질문과 어떤 관련이 있습니까?

당신이 직면하고있는 문제는 빌드 메소드에 부작용이 있거나 순수하지 않으므로 외부 빌드 호출이 번거 롭다는 것입니다.

빌드 호출을 방지하는 대신 빌드 메소드를 순수하게 만들어서 영향없이 언제든지 호출 할 수 있도록해야합니다.

예제의 경우 위젯을 위젯으로 변환 한 StatefulWidget다음 에 대한 HTTP 호출을 추출 initState하십시오 State.

class Example extends StatefulWidget {
  @override
  _ExampleState createState() => _ExampleState();
}

class _ExampleState extends State<Example> {
  Future<int> future;

  @override
  void initState() {
    future = Future.value(42);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: future,
      builder: (context, snapshot) {
        // create some layout here
      },
    );
  }
}

나는 이것을 이미 알고있다. 나는 때문에 여기 온 진짜 최적화 재 구축하려는

또한 자식을 만들지 않고도 위젯을 다시 작성할 수 있습니다.

위젯 인스턴스가 동일하게 유지되는 경우 Flutter는 의도적으로 어린이를 재건하지 않습니다. 불필요한 재 빌드를 방지하기 위해 위젯 트리의 일부를 캐시 할 수 있음을 의미합니다.

가장 쉬운 방법은 다트 const생성자 를 사용하는 것입니다 .

@override
Widget build(BuildContext context) {
  return const DecoratedBox(
    decoration: BoxDecoration(),
    child: Text("Hello World"),
  );
}

const키워드 덕분에 DecoratedBox빌드가 수백 번 호출 되더라도 인스턴스 는 동일하게 유지됩니다.

그러나 동일한 결과를 수동으로 얻을 수 있습니다.

@override
Widget build(BuildContext context) {
  final subtree = MyWidget(
    child: Text("Hello World")
  );

  return StreamBuilder<String>(
    stream: stream,
    initialData: "Foo",
    builder: (context, snapshot) {
      return Column(
        children: <Widget>[
          Text(snapshot.data),
          subtree,
        ],
      );
    },
  );
}

이 예제에서 StreamBuilder에 새 값이 통보 subtree되면 StreamBuilder / Column이 수행하더라도 다시 작성하지 않습니다. 폐쇄 덕분에 인스턴스가 MyWidget변경되지 않았기 때문에 발생합니다 .

이 패턴은 애니메이션에서 많이 사용됩니다. 일반적인 용도 AnimatedBuilder는와 같은 모든 전환 AlignTransition입니다.

subtree핫 리로드 기능을 중단하므로 권장하지 않지만 클래스의 필드에 저장할 수도 있습니다 .


답변

이런 식으로 원치 않는 빌드 호출을 막을 수 있습니다

1) UI의 개별 작은 부분에 대한 자식 Statefull 클래스를 만듭니다.

2) 공급자 라이브러리를 사용 하므로 원하지 않는 빌드 메소드 호출을 중지 할 수 있습니다

.이 상황에서는 빌드 메소드 호출

  • initState를 호출 한 후
  • didUpdateWidget을 호출 한 후
  • setState () 가 호출 될 때
  • 키보드가 열려있을 때
  • 화면 방향이 변경된 경우
  • 부모 위젯은 빌드되고 자식 위젯도 다시 빌드됩니다.

답변

플러터도 있습니다 ValueListenableBuilder<T> class . 목적에 필요한 일부 위젯 만 재구성하고 값 비싼 위젯을 건너 뛸 수 있습니다.

ValueListenableBuilder flutter 문서
또는 샘플 코드는 여기에서 확인할 수 있습니다.

  return Scaffold(
  appBar: AppBar(
    title: Text(widget.title)
  ),
  body: Center(
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Text('You have pushed the button this many times:'),
        ValueListenableBuilder(
          builder: (BuildContext context, int value, Widget child) {
            // This builder will only get called when the _counter
            // is updated.
            return Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: <Widget>[
                Text('$value'),
                child,
              ],
            );
          },
          valueListenable: _counter,
          // The child parameter is most helpful if the child is
          // expensive to build and does not depend on the value from
          // the notifier.
          child: goodJob,
        )
      ],
    ),
  ),
  floatingActionButton: FloatingActionButton(
    child: Icon(Icons.plus_one),
    onPressed: () => _counter.value += 1,
  ),
);


답변