अवांछित विजेट निर्माण से कैसे निपटें?


143

विभिन्न कारणों से, कभी-कभी buildमेरे विजेट की विधि को फिर से कहा जाता है।

मुझे पता है कि ऐसा इसलिए होता है क्योंकि एक अभिभावक अपडेट करता है। लेकिन यह अवांछित प्रभाव का कारण बनता है। एक विशिष्ट स्थिति जहां यह समस्याओं का कारण बनता है, FutureBuilderइस तरह से उपयोग करते समय:

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

इस उदाहरण में, यदि निर्माण विधि को फिर से बुलाया जाना था, तो यह एक और http अनुरोध को ट्रिगर करेगा। जो अवांछित है।

इसे देखते हुए, अवांछित निर्माण से कैसे निपटें? यह कॉल को रोकने का कोई तरीका है?


1
इस पोस्ट आप मदद कर सकते हैं .. /programming/53223469/flutter-statelesswidget-build-called-multiple-times/55626839#55626839
बनी

4
में प्रदाता प्रलेखन आप यहाँ कह लिंक "इस stackoverflow जवाब जो अधिक जानकारी में बताता है कि क्यों .Value निर्माता का उपयोग कर मान बनाने के लिए अवांछित है या नहीं।" हालाँकि, आप यहाँ या आपके उत्तर में मूल्य निर्माता का उल्लेख नहीं करते हैं। क्या आपका मतलब कहीं और लिंक करना था?
सुरगाछ

जवाबों:


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
      },
    );
  }
}

यह मैं पहले से जानता हूं। मैं यहां आया क्योंकि मैं वास्तव में पुनर्वित्त का अनुकूलन करना चाहता हूं

अपने बच्चों को भी बनाने के लिए मजबूर किए बिना एक विजेट को फिर से बनाने में सक्षम बनाना संभव है।

जब एक विजेट का उदाहरण समान रहता है; स्पंदन उद्देश्यपूर्वक बच्चों का पुनर्निर्माण नहीं करेगा। तात्पर्य यह है कि आप अनावश्यक विजेट को रोकने के लिए अपने विजेट ट्री के कुछ हिस्सों को कैश कर सकते हैं।

सबसे आसान तरीका डार्ट 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,
        ],
      );
    },
  );
}

इस उदाहरण में जब स्ट्रीमब्यूलर को नए मूल्यों के बारे में सूचित किया जाता है, subtreeभले ही स्ट्रीमब्यूलर / कॉलम न करें। ऐसा इसलिए होता है, क्योंकि बंद होने के लिए धन्यवाद, MyWidgetपरिवर्तन नहीं हुआ।

एनिमेशन में इस पैटर्न का बहुत उपयोग किया जाता है। विशिष्ट उपयोग हैं AnimatedBuilderऔर सभी संक्रमण जैसे कि AlignTransition

आप subtreeअपनी कक्षा के एक क्षेत्र में भी स्टोर कर सकते हैं , हालांकि कम अनुशंसित है क्योंकि यह हॉट-रीलोड सुविधा को तोड़ता है।


2
क्या आप बता सकते हैं कि subtreeक्लास फील्ड में स्टोर करने से हॉट-रीलोड क्यों टूटता है?
mFeinstein

4
मेरे साथ एक समस्या यह StreamBuilderहै कि जब कीबोर्ड स्क्रीन बदलता है, तो मार्गों को फिर से बनाना पड़ता है। तो StreamBuilderफिर से बनाया गया है और एक नया StreamBuilderबनाया गया है और यह सदस्यता लेता है stream। जब एक StreamBuilderग्राहक बन जाता है stream, तो snapshot.connectionStateवह बन जाता है ConnectionState.waitingजो मेरे कोड को वापस कर देता है CircularProgressIndicator, और फिर snapshot.connectionStateडेटा होने पर बदल जाता है, और मेरा कोड एक अलग विजेट लौटाएगा, जो स्क्रीन को अलग-अलग सामान के साथ झिलमिलाहट देता है।
mFeinstein

1
मैं एक बनाने का फैसला किया StatefulWidget, की सदस्यता streamपर initState()और सेट currentWidgetके साथ setState()के रूप में streamनए डेटा भेजता है, गुजर currentWidgetकरने के लिए build()विधि। क्या इसका बेहतर समाधान है?
mFeinstein

1
मैं थोड़ा उलझन में हूं। आप अपने स्वयं के प्रश्न का उत्तर दे रहे हैं, हालांकि सामग्री से, यह ऐसा नहीं दिखता है।
sagon00

8
उह, यह कहते हुए कि एक बिल्ड को एक HTTP पद्धति को कॉल नहीं करना चाहिए पूरी तरह से एक के व्यावहारिक उदाहरण को हरा देता है FutureBuilder
TheGeekZn

6

आप इन तरीकों का उपयोग करके अवांछित बिल्ड कॉलिंग को रोक सकते हैं

1) यूआई के अलग-अलग छोटे हिस्से के लिए बच्चे को स्टेटफुल क्लास बनाएं

2) प्रदाता लाइब्रेरी का उपयोग करें , इसलिए इसका उपयोग करके आप अवांछित बिल्ड मेथड कॉलिंग को रोक सकते हैं

। इन नीचे की स्थिति में मेथड कॉल का उपयोग करें

  • InitState पर कॉल करने के बाद
  • कॉल करने के बाद didUpdateWidget
  • जब setState () कहा जाता है।
  • जब कीबोर्ड खुला हो
  • जब स्क्रीन ओरिएंटेशन बदल गया
  • पैरेंट विजेट का निर्माण होता है, फिर चाइल्ड विजेट भी पुनर्निर्माण होता है

0

स्पंदन भी है ValueListenableBuilder<T> class । यह आपको अपने उद्देश्य के लिए आवश्यक कुछ विगेट्स के पुनर्निर्माण की अनुमति देता है और महंगे विजेट को छोड़ देता है।

आप यहां दस्तावेज़ देख सकते हैं ValueListenableBuilder फ़्लटर डॉक्स
या बस नमूना कोड नीचे:

  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,
  ),
);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.