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


143

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

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

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

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

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



4
에서 제공 문서 여기에 "바람직하지 가치를 창출하기 위해 .value 생성자를 사용하는 이유 자세한 내용에 대해 설명이 유래의 답변을 참조하십시오."라는 정보 링크 그러나 여기 또는 귀하의 답변에 가치 생성자를 언급하지 않았습니다. 다른 곳을 연결하려고 했습니까?
Suragch

답변:


225

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

  • 루트 팝 / 푸시
  • 키보드 모양 또는 방향 변경으로 인한 화면 크기 조정
  • 부모 위젯이 자식을 재생성
  • 위젯이 의존하는 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핫 리로드 기능을 중단하므로 권장하지 않지만 클래스의 필드에 저장할 수도 있습니다 .


2
subtree클래스 필드에 저장하면 핫 리로드가 중단되는 이유를 설명해 주 시겠습니까?
mFeinstein

4
내가 가진 문제 StreamBuilder는 키보드가 나타나면 화면이 바뀌므로 경로를 다시 작성해야한다는 것입니다. 따라서 StreamBuilder재 구축되고 새 StreamBuilder것이 생성되어에 구독합니다 stream. 때 StreamBuilderA와 구독은 stream, snapshot.connectionState이됩니다 ConnectionState.waiting내 코드 반환 a를 만드는 CircularProgressIndicator다음 snapshot.connectionState데이터가있을 경우 변경하고, 내 코드가 다른 물건으로 화면 깜박임을 만드는 다른 위젯을 반환합니다.
mFeinstein

1
나는를 만들고 StatefulWidget, streamon을 구독 initState()하고 currentWidgetwith setState()stream새로운 데이터를 currentWidget보내면서 build()메소드로 전달 하기로 결정했다 . 더 나은 해결책이 있습니까?
mFeinstein

1
조금 혼란 스러워요. 당신은 당신의 자신의 질문에 대답하고 있지만, 내용에서 보이지 않습니다.
sgon00

8
어, 빌드가 HTTP 메소드를 호출해서는 안된다는 것은 실제적인 예제를 완전히 물리칩니다 FutureBuilder.
TheGeekZn

6

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

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

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

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

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

0

플러터도 있습니다 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,
  ),
);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.