स्पंदन: सही ढंग से एक इनहेरिटेड विजेट का उपयोग कैसे करें?


103

InheritedWidget का उपयोग करने का सही तरीका क्या है? अब तक मैं समझ गया था कि यह आपको विजेट पेड़ के नीचे डेटा प्रचार करने का मौका देता है। चरम में अगर आप रूटविडगेट के रूप में हैं, तो यह सभी मार्गों पर पेड़ में सभी विजेट से सुलभ होगा, जो ठीक है क्योंकि किसी भी तरह मुझे ग्लोबल्स या सिंग्लेटनों का सहारा लिए बिना अपने विजेट के लिए अपने व्यूमॉडल / मॉडल को सुलभ बनाना होगा।

लेकिन InheritedWidget अपरिवर्तनीय है, तो मैं इसे कैसे अपडेट कर सकता हूं? और अधिक महत्वपूर्ण यह है कि मेरे स्टेटफुल विजेट कैसे अपने सबस्ट्रेट्स के पुनर्निर्माण के लिए ट्रिगर होते हैं?

दुर्भाग्य से प्रलेखन यहाँ बहुत अस्पष्ट है और बहुत से चर्चा के बाद किसी को भी वास्तव में यह पता नहीं लगता है कि इसका उपयोग करने का सही तरीका क्या है।

मैं ब्रायन एगन से एक उद्धरण जोड़ता हूं:

हां, मैं इसे पेड़ के नीचे डेटा के प्रचार के तरीके के रूप में देखता हूं। API डॉक्स से मुझे क्या भ्रमित करने वाला लगता है:

"इनहेरिट किए गए विजेट, जब इस तरह से संदर्भित होते हैं, तो उपभोक्ता को फिर से निर्माण करने का कारण होगा जब विरासत में मिला विजेट ही राज्य को बदलता है।"

जब मैंने पहली बार यह पढ़ा, तो मैंने सोचा:

मैं InheritedWidget में कुछ डेटा भर सकता हूं और बाद में इसे म्यूट कर सकता हूं। जब वह उत्परिवर्तन होता है, तो वह सभी विजेट को फिर से बनाएगा जो मेरे InheritedWidget को संदर्भित करता है जो मुझे मिला:

InheritedWidget की स्थिति को म्यूट करने के लिए, आपको इसे StatefulWidget में लपेटने की आवश्यकता होती है, फिर आप वास्तव में StatefulWidget की स्थिति को म्यूट कर देते हैं और इस डेटा को InheritedWidget पर नीचे भेज देते हैं, जो डेटा को सभी बच्चों को सौंप देता है। हालाँकि, उस स्थिति में, यह स्टेटफुलवगेट के नीचे पूरे पेड़ को फिर से बनाना लगता है, न कि केवल इनहेरिटडीडगेट को संदर्भित करने वाले विजेट। क्या वो सही है? या यह किसी भी तरह से पता चलेगा कि InheritedWidget को अपडेट करने वाले विजेट को कैसे छोड़ें यदि updatehouldNotify गलत रिटर्न देता है?

जवाबों:


108

समस्या आपके उद्धरण से आती है, जो गलत है।

जैसा कि आपने कहा, InheritedWidgets अन्य विगेट्स की तरह अपरिवर्तनीय हैं। इसलिए वे अपडेट नहीं करते हैं । उन्हें नए सिरे से बनाया गया है।

बात यह है: InheritedWidget केवल एक सरल विजेट है जो डेटा को पकड़े रहने के अलावा कुछ नहीं करता है । इसमें अपडेट या जो भी हो उसका कोई तर्क नहीं है। लेकिन, किसी भी अन्य विगेट्स की तरह, यह एक के साथ जुड़ा हुआ है Element। और अंदाज लगाइये क्या? यह बात आपस में जुड़ी हुई है और जब भी संभव होगा तब इसका इस्तेमाल किया जाएगा!

सही उद्धरण होगा:

InheritedWidget, जब इस तरह से संदर्भित किया जाता है, तो InheritedWidget एक InheritedElement परिवर्तनों से संबंधित होने पर उपभोक्ता को फिर से बनाने का कारण होगा ।

विगेट्स / एलिमेंट्स / रेंडरबॉक्स एक साथ कैसे प्लग किए जाते हैं, इस बारे में बहुत अच्छी बात है । लेकिन संक्षेप में, वे इस तरह हैं (बाएं आपका विशिष्ट विजेट है, मध्य 'तत्व' है, और दाएं 'रेंडर बॉक्स' हैं):

यहां छवि विवरण दर्ज करें

बात यह है: जब आप एक नए विजेट को तत्काल करते हैं; स्पंदन इसकी तुलना पुराने से करेगा। पुन: उपयोग करें यह "तत्व" है, जो एक रेंडरबॉक्स को इंगित करता है। और उत्परिवर्तित RenderBox गुण।


ओके, लेकिन यह मेरे सवाल का जवाब कैसे देता है?

जब एक इनहेरिटविडगेट को इंस्टेंट करना, और फिर कॉल करना context.inheritedWidgetOfExactType(या MyClass.ofजो मूल रूप से समान है); जो निहित है वह यह है कि यह Elementआपके साथ संबद्ध को सुनेगा InheritedWidget। और जब भी Elementएक नया विजेट मिलता है, तो यह किसी भी विजेट को रीफ्रेश करने के लिए मजबूर करेगा, जिसे पिछली विधि कहा जाता है।

संक्षेप में, जब आप किसी मौजूदा InheritedWidgetब्रांड को नए से बदलते हैं ; स्पंदन देखेगा कि यह बदल गया। और एक संभावित संशोधन के बाध्य विगेट्स को सूचित करेगा।

यदि आपको सब कुछ समझ में आ गया है, तो आपको पहले ही समाधान का अनुमान लगा लेना चाहिए:

जब भी कुछ बदले तो अपने InheritedWidgetअंदर लपेटें StatefulWidgetएक नया ब्रांड बनाएंगे InheritedWidget!

वास्तविक कोड में अंतिम परिणाम होगा:

class MyInherited extends StatefulWidget {
  static MyInheritedData of(BuildContext context) =>
      context.inheritFromWidgetOfExactType(MyInheritedData) as MyInheritedData;

  const MyInherited({Key key, this.child}) : super(key: key);

  final Widget child;

  @override
  _MyInheritedState createState() => _MyInheritedState();
}

class _MyInheritedState extends State<MyInherited> {
  String myField;

  void onMyFieldChange(String newValue) {
    setState(() {
      myField = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MyInheritedData(
      myField: myField,
      onMyFieldChange: onMyFieldChange,
      child: widget.child,
    );
  }
}

class MyInheritedData extends InheritedWidget {
  final String myField;
  final ValueChanged<String> onMyFieldChange;

  MyInheritedData({
    Key key,
    this.myField,
    this.onMyFieldChange,
    Widget child,
  }) : super(key: key, child: child);

  static MyInheritedData of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MyInheritedData>();
  }

  @override
  bool updateShouldNotify(MyInheritedData oldWidget) {
    return oldWidget.myField != myField ||
        oldWidget.onMyFieldChange != onMyFieldChange;
  }
}

लेकिन एक नया InheritedWidget बनाने से पूरे पेड़ का पुनर्निर्माण नहीं होगा?

नहीं, यह जरूरी नहीं होगा। जैसा कि आपका नया InheritedWidget संभावित रूप से पहले जैसा ही बच्चा हो सकता है। और सटीक रूप से, मेरा मतलब वही उदाहरण है। वे विजेट जिनके पास एक ही उदाहरण है, जो उनके पुनर्निर्माण से पहले नहीं थे।

और सबसे अधिक स्थिति में (आपके ऐप के मूल में विरासत में मिली सामग्री), विरासत में मिला विजेट स्थिर है । तो कोई अनावश्यक पुनर्निर्माण नहीं।


1
लेकिन एक नया InheritedWidget बनाने से पूरे पेड़ का पुनर्निर्माण नहीं होगा? फिर श्रोताओं की आवश्यकता क्यों?
थॉमस

1
आपकी पहली टिप्पणी के लिए, मैंने अपने उत्तर में तीसरा भाग जोड़ा। थकाऊ होने के रूप में: मैं असहमत हूं। एक कोड स्निपेट इसे काफी आसानी से उत्पन्न कर सकता है। और डेटा को एक्सेस करना कॉलिंग जितना ही सरल है MyInherited.of(context)
रेमी रूसेलेट

3
यकीन नहीं है कि आप रुचि रखते हैं, लेकिन इस तकनीक के साथ नमूना अपडेट किया गया है: github.com/brianegan/flutter_altecture_samples/tree/master/… यकीन के लिए अब थोड़ा कम दोहराव! यदि आपके पास उस क्रियान्वयन के लिए कोई अन्य सुझाव हैं, तो कोड समीक्षा पसंद करेंगे यदि आपके पास कभी भी कुछ क्षण खाली हों :) फिर भी इस लॉजिक क्रॉस प्लेटफॉर्म (स्पंदन और वेब) को साझा करने का सबसे अच्छा तरीका जानने की कोशिश कर रहा है और यह सुनिश्चित करने योग्य है कि ( विशेष रूप से async सामान)।
brianegan

4
चूंकि updateShouldNotifyपरीक्षण हमेशा एक ही MyInheritedStateउदाहरण का उल्लेख कर रहा है, क्या यह हमेशा वापस नहीं आएगा false? निश्चित रूप से नया उदाहरण बनाने की buildविधि MyInheritedStateहै _MyInherited, लेकिन dataक्षेत्र हमेशा संदर्भ thisनहीं है? मुझे समस्या हो रही है ... अगर मैं सिर्फ हार्ड कोड काम करता हूं true
cdock

2
@ LCDock हाँ मेरा बुरा। याद नहीं है कि मैंने ऐसा क्यों किया क्योंकि यह स्पष्ट रूप से काम नहीं करेगा। यह सच है, धन्यवाद के संपादन से निश्चित है।
रमी रूसेलेट

20

टी एल; डॉ

अंदर भारी गणना का उपयोग न करें updateShouldNotify विधि और उपयोग स्थिरांक के बजाय नया जब एक विजेट बनाने


सबसे पहले, हमें यह समझना चाहिए कि एक विजेट, एलीमेंट और रेंडर ऑब्जेक्ट क्या हैं।

  1. रेंडर ऑब्जेक्ट्स वास्तव में स्क्रीन पर प्रस्तुत किए गए हैं। वे म्यूट हैं, पेंटिंग और लेआउट तर्क शामिल हैं। रेंडर ट्री वेब में डॉक्यूमेंट ऑब्जेक्ट मॉडल (DOM) के समान है और आप ट्री में DOM के रूप में रेंडर ऑब्जेक्ट देख सकते हैं
  2. विजेट - क्या प्रदान किया जाना चाहिए का एक विवरण है। वे अपरिवर्तनीय और सस्ते हैं। इसलिए यदि कोई विजेट "क्या?" (डिक्लेरेटिव अप्रोच) के सवाल का जवाब देता है, तो एक रेंडर ऑब्जेक्ट "हाउ?" (इंपीरियल अप्रोच) के सवाल का जवाब देता है। वेब से एक सादृश्य "वर्चुअल डोम" है।
  3. तत्व / BuildContext - विजेट और रेंडर ऑब्जेक्ट्स के बीच एक प्रॉक्सी है । इसमें पेड़ में एक विजेट की स्थिति के बारे में जानकारी शामिल है * और इसी विजेट को बदलने पर रेंडर ऑब्जेक्ट को कैसे अपडेट किया जाए।

अब हम InheritedWidget और BuildContext की विधि inheritFromWidgetOfExactType में गोता लगाने के लिए तैयार हैं ।

एक उदाहरण के रूप में, हम अनुशंसा करते हैं कि हम इस उदाहरण को इनहेरिटडीडगेट के बारे में फ़्लटर के दस्तावेज़ से मानते हैं:

class FrogColor extends InheritedWidget {
  const FrogColor({
    Key key,
    @required this.color,
    @required Widget child,
  })  : assert(color != null),
        assert(child != null),
        super(key: key, child: child);

  final Color color;

  static FrogColor of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(FrogColor);
  }

  @override
  bool updateShouldNotify(FrogColor old) {
    return color != old.color;
  }
}

InheritedWidget - सिर्फ एक विजेट जो हमारे मामले में लागू होता है एक महत्वपूर्ण विधि - updateShouldNotifyupdateShouldNotify - एक फ़ंक्शन जो एक पैरामीटर ओल्डविडगेट को स्वीकार करता है और एक बूलियन मान लौटाता है: सही या गलत।

किसी भी विजेट की तरह, InheritedWidget में एक तत्व तत्व होता है। यह InheritedElement है । हर बार जब हम एक नया विजेट बनाते हैं तो इनहेरिट की गई कॉल अपडेट को अपडेट करें ( एक पूर्वज पर कॉल सेट करें )। जब updateShouldNotify रिटर्न सच के माध्यम से InheritedElement दोहराता निर्भरता (?) और कॉल विधि didChangeDependencies उस पर।

इनहेरीटेडमेंट में निर्भरता कहाँ मिलती है ? यहाँ हमें inheritFromWidgetOfExactType मेथड को देखना चाहिए ।

inheritFromWidgetOfExactType - इस विधि में BuildContext और प्रत्येक तत्व के रूप में परिभाषित किया गया है BuildContext इंटरफ़ेस (तत्व == BuildContext)। इसलिए प्रत्येक तत्व में यह विधि है।

के कोड को देखें inheritFromWidgetOfExactType:

final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
if (ancestor != null) {
  assert(ancestor is InheritedElement);
  return inheritFromElement(ancestor, aspect: aspect);
}

यहाँ हम _inheritedWidgets प्रकार में मैप किए गए पूर्वजों को खोजने का प्रयास करते हैं । यदि पूर्वज पाया जाता है, तो हम विरासत को कहते हैं ।

InheritFromElement के लिए कोड :

  InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
    assert(ancestor != null);
    _dependencies ??= HashSet<InheritedElement>();
    _dependencies.add(ancestor);
    ancestor.updateDependencies(this, aspect);
    return ancestor.widget;
  }
  1. हम पूर्वज को वर्तमान तत्व की निर्भरता के रूप में जोड़ते हैं (_d dependencies.add (पूर्वज))
  2. हम वर्तमान तत्व को पूर्वज की निर्भरता (ancestor.updateD dependencies (यह, पहलू)) से जोड़ते हैं
  3. हम inheritFromWidgetOfExactType के परिणाम के रूप में पूर्वजों का विजेट लौटाते हैं (पूर्वजों को वापस भेजें )

तो अब हम जानते हैं कि InheritedElement को अपनी निर्भरता कहाँ मिलती है।

अब doChangeD निर्भरता विधि पर नजर डालते हैं । प्रत्येक तत्व में यह विधि है:

  void didChangeDependencies() {
    assert(_active); // otherwise markNeedsBuild is a no-op
    assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));
    markNeedsBuild();
  }

जैसा कि हम देख सकते हैं कि यह विधि केवल एक तत्व को गंदे के रूप में चिह्नित करती है और इस तत्व को अगले फ्रेम पर फिर से बनाया जाना चाहिए। पुनर्निर्माण का मतलब है , कॉलस्पैंडिंग विजेट तत्व पर कॉल विधि का निर्माण

लेकिन जब मैं InheritedWidget का पुनर्निर्माण करता हूं, तो "उप-ट्री के पुनर्निर्माण के बारे में क्या"। यहां हमें याद रखना चाहिए कि विजेट अपरिवर्तनीय हैं और यदि आप नया विजेट बनाते हैं तो फ़्लटर उप-ट्री का पुनर्निर्माण करेगा। हम इसे कैसे ठीक कर सकते हैं?

  1. हाथों से कैश विजेट (मैन्युअल)
  2. Const का उपयोग करें क्योंकि const मान / वर्ग का केवल एक उदाहरण बनाते हैं

1
महान व्याख्या maksimr। यह बात मुझे सबसे अधिक भ्रमित करती है कि अगर विरासत में मिले सबटेड ट्री को फिर से बनाया जाए, तो इनकी जगह ले ली जाए, अपडेट का बिंदु क्या है?
पांडा वर्ल्ड

3

से डॉक्स :

[BuildContext.inheritFromWidgetOfExactType] दिए गए प्रकार के निकटतम विजेट को प्राप्त करता है, जो एक ठोस InheritedWidget उपवर्ग का प्रकार होना चाहिए, और इस बिल्ड संदर्भ को इस विजेट के साथ पंजीकृत करता है कि जब विजेट बदलता है (या उस प्रकार का एक नया विजेट पेश किया जाता है), या विजेट चला जाता है), यह निर्माण संदर्भ फिर से बनाया गया है ताकि यह उस विजेट से नए मान प्राप्त कर सके।

इसे आम तौर पर () स्थिर तरीकों, जैसे कि Theme.of से निहित कहा जाता है।

जैसा कि ओपी ने कहा, एक InheritedWidgetउदाहरण नहीं बदलता है ... लेकिन इसे विजेट ट्री में एक ही स्थान पर एक नए उदाहरण के साथ बदला जा सकता है। जब ऐसा होता है तो यह संभव है कि पंजीकृत विजेट को फिर से बनाया जाना चाहिए। InheritedWidget.updateShouldNotifyविधि इस निर्धारण करता है। (देखें: डॉक्स )

तो एक उदाहरण को कैसे बदला जा सकता है? एक InheritedWidgetउदाहरण एक द्वारा समाहित किया जा सकता है StatefulWidget, जो एक पुराने उदाहरण को नए उदाहरण से बदल सकता है।


-1

InheritedWidget ऐप के केंद्रीकृत डेटा को प्रबंधित करता है और इसे बच्चे को पास करता है, जैसे हम यहां बताए गए अनुसार कार्ट की गिनती को स्टोर कर सकते हैं :

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