पुन: प्रयोज्य विगेट्स बनाने के लिए कार्यों और कक्षाओं के बीच अंतर क्या है?


125

मैंने महसूस किया है कि स्टेटलेसविजेट को उपवर्ग के बजाय सादे कार्यों का उपयोग करके विजेट बनाना संभव है । एक उदाहरण यह होगा:

Widget function({ String title, VoidCallback callback }) {
  return GestureDetector(
    onTap: callback,
    child: // some widget
  );
}

यह दिलचस्प है क्योंकि इसे पूर्ण-विकसित वर्ग की तुलना में कहीं कम कोड की आवश्यकता होती है । उदाहरण:

class SomeWidget extends StatelessWidget {
  final VoidCallback callback;
  final String title;

  const SomeWidget({Key key, this.callback, this.title}) : super(key: key);

  @override
  Widget build(BuildContext context) {
      return GestureDetector(
        onTap: callback,
        child: // some widget
      );
  }
}

तो मैं सोच रहा था: क्या विगेट्स बनाने के लिए फ़ंक्शन और कक्षाओं के बीच सिंटैक्स के अलावा कोई अंतर है? और क्या कार्यों का उपयोग करना एक अच्छा अभ्यास है?


मुझे यह मुद्दा मेरी समझ के लिए बहुत उपयोगी लगा। reddit.com/r/FlutterDev/comments/avhvco/…
रॉकेट

जवाबों:


172

TL; DR: पुन: प्रयोज्य विजेट-ट्री बनाने के लिए फ़ंक्शंस पर वर्गों का उपयोग करना पसंद करते हैं ।


संपादित करें : कुछ गलतफहमी के लिए बनाने के लिए: यह समस्याओं के कारण कार्यों के बारे में नहीं है, लेकिन कुछ को हल करने वाली कक्षाएं हैं।

यदि कोई फ़ंक्शन समान कार्य कर सकता है तो स्पंदन में स्टेटलेसविजेट नहीं होगा ।

इसी तरह, इसे मुख्य रूप से सार्वजनिक विजेट पर निर्देशित किया जाता है, जिसका पुन: उपयोग किया जाता है। यह केवल एक बार उपयोग किए जाने के लिए किए गए निजी कार्यों के लिए बहुत मायने नहीं रखता है - हालांकि इस व्यवहार के बारे में पता होना अभी भी अच्छा है।


कक्षाओं के बजाय फ़ंक्शंस का उपयोग करने के बीच एक महत्वपूर्ण अंतर है, अर्थात: फ़्रेम फ़ंक्शंस से अनजान है, लेकिन कक्षाओं को देख सकता है।

निम्नलिखित "विजेट" फ़ंक्शन पर विचार करें:

Widget functionWidget({ Widget child}) {
  return Container(child: child);
}

इस तरह से इस्तेमाल किया:

functionWidget(
  child: functionWidget(),
);

और यह कक्षा के बराबर है:

class ClassWidget extends StatelessWidget {
  final Widget child;

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

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

उस तरह इस्तेमाल किया:

new ClassWidget(
  child: new ClassWidget(),
);

कागज पर, दोनों बिल्कुल एक ही काम करते दिखते हैं: 2 बनाएं Container, एक के साथ दूसरे में नेस्टेड। लेकिन वास्तविकता थोड़ी अलग है।

कार्यों के मामले में, उत्पन्न विजेट ट्री इस तरह दिखता है:

Container
  Container

कक्षाओं के साथ, विजेट ट्री है:

ClassWidget
  Container
    ClassWidget
      Container

यह महत्वपूर्ण है क्योंकि यह बदलता है कि विजेट को अपडेट करते समय फ्रेमवर्क कैसे व्यवहार करता है।

वह क्यों मायने रखता है

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

इसमें कोई गारंटी नहीं है कि फ़ंक्शंस का उपयोग करके आपके पास बग होंगे , लेकिन कक्षाओं का उपयोग करके, आपको इन मुद्दों का सामना नहीं करने की गारंटी है।

यहां डार्टपैड पर कुछ इंटरेक्टिव उदाहरण दिए गए हैं जिनसे आप मुद्दों को बेहतर ढंग से समझने के लिए खुद को चला सकते हैं:

  • https://dartpad.dev/1870e726d7e04699bc8f9d78ba71da35
    इस उदाहरण से पता चलता है कि कैसे अपने ऐप को कार्यों में विभाजित करके, आप गलती से चीजों को तोड़ सकते हैं जैसेAnimatedSwitcher

  • https://dartpad.dev/a869b21a2ebd2466b876a5997c9cf3f1
    यह उदाहरण दिखाता है कि वर्ग कैसे विजेट ट्री के अधिक दानेदार पुनर्निर्माण की अनुमति देते हैं, प्रदर्शन में सुधार करते हैं

  • https://dartpad.dev/06842ae9e4b82fad917acb88da108eee
    यह उदाहरण दिखाता है कि कैसे, फ़ंक्शंस का उपयोग करके, आप बिल्डकॉनटेक्स्ट का दुरुपयोग करने के लिए खुद को बेनकाब करते हैं और इनहेरिटडीडेट्स (जैसे थीम या प्रदाताओं) का उपयोग करते समय बग का सामना करते हैं।

निष्कर्ष

यहां फ़ंक्शंस और कक्षाओं का उपयोग करने के बीच अंतरों की एक क्यूरेट सूची है:

  1. क्लास:
  • प्रदर्शन अनुकूलन की अनुमति दें (कास्ट कंस्ट्रक्टर, अधिक दानेदार पुनर्निर्माण)
  • सुनिश्चित करें कि दो अलग-अलग लेआउट के बीच स्विच करना संसाधनों का सही ढंग से निपटान करता है (कार्य कुछ पिछली स्थिति का पुन: उपयोग कर सकते हैं)
  • यह सुनिश्चित करता है कि हॉट-रीलोड ठीक से काम करता है (फ़ंक्शंस का उपयोग करने से हॉट-रीलोड टूट सकता है) showDialogs & समान के )
  • विजेट इंस्पेक्टर में एकीकृत कर रहे हैं।
    • हम देखते हैं ClassWidget devtool द्वारा दिखाए गए विजेट-ट्री में , जो स्क्रीन पर क्या है यह समझने में मदद करता है
    • हम डिबगफ़िलप्रोपरेटी को ओवरराइड कर सकते हैं कि एक विजेट में दिए गए पैरामीटर क्या हैं
  • बेहतर त्रुटि संदेश
    यदि एक अपवाद होता है (जैसे कि प्रोवाइडरनॉटफ़ाउंड), तो फ्रेमवर्क आपको वर्तमान में निर्माण विजेट का नाम देगा। यदि आपने अपने विजेट ट्री को केवल फ़ंक्शन + में विभाजित किया है Builder, तो आपकी त्रुटियों का सहायक नाम नहीं होगा
  • चाबियाँ परिभाषित कर सकते हैं
  • संदर्भ API का उपयोग कर सकते हैं
  1. कार्य:
  • कम कोड है (जो कोड-जनक functional_widget का उपयोग करके हल किया जा सकता है )

कुल मिलाकर, इन कारणों से विजेट्स के पुन: उपयोग के लिए कक्षाओं में फ़ंक्शन का उपयोग करना एक बुरा अभ्यास माना जाता है।
आप कर सकते हैं , लेकिन यह भविष्य में आपको काट सकता है।


टिप्पणियाँ विस्तारित चर्चा के लिए नहीं हैं; इस वार्तालाप को बातचीत में स्थानांतरित कर दिया गया है ।
शमूएल एलवाई

10

मैं पिछले 2 दिनों से इस मुद्दे पर शोध कर रहा हूं। मैं निम्नलिखित निष्कर्ष पर आया हूं: एप्लिकेशन के टुकड़ों को कार्यों में तोड़ देना ठीक है। यह सिर्फ आदर्श है कि उन कार्यों को वापस किया जाता है StatelessWidget, इसलिए अनुकूलन किए जा सकते हैं, जैसे कि बनाना StatelessWidget const, इसलिए यह पुनर्निर्माण नहीं करता है अगर यह नहीं करना है। उदाहरण के लिए, कोड का यह टुकड़ा पूरी तरह से मान्य है:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      ++_counter;
    });
  }

  @override
  Widget build(BuildContext context) {
    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:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
            const MyWidgetClass(key: const Key('const')),
            MyWidgetClass(key: Key('non-const')),
            _buildSomeWidgets(_counter),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }

  Widget _buildSomeWidgets(int val) {
    print('${DateTime.now()} Rebuild _buildSomeWidgets');
    return const MyWidgetClass(key: Key('function'));

    // This is bad, because it would rebuild this every time
    // return Container(
    //   child: Text("hi"),
    // );
  }
}

class MyWidgetClass extends StatelessWidget {
  const MyWidgetClass({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print('${DateTime.now()} Rebuild MyWidgetClass $key');

    return Container(
      child: Text("hi"),
    );
  }
}

फ़ंक्शन का उपयोग पूरी तरह से ठीक है, क्योंकि यह एक रिटर्न देता है const StatelessWidget। कृपया मुझे सुधारें अगर मैं गलत हूं।


क्या कोई समझा सकता है कि मैंने जो कहा वह गलत क्यों है? मेरा मतलब है, मुझे लगता है कि यह गलत है क्योंकि नीचे दिया गया है।
सेर्गी इयाकोब

मैं वास्तव में आप से सहमत हूँ। मैं मतभेदों के बारे में अधिक विस्तृत विराम लिखने का इरादा कर रहा हूं, लेकिन इसके आस-पास कुछ भी नहीं देखा है। अपने तर्क से मुक्त होने के लिए स्वतंत्र महसूस करें क्योंकि मुझे लगता है कि विगेट्स v विधियों के पेशेवरों और विपक्षों को समझना महत्वपूर्ण है।
IIT

@SergiuIacob क्या हम constहर मामले के लिए स्टेटलेस क्लास के सामने उपयोग कर सकते हैं ? या क्या इसे कुछ मामलों में होना चाहिए? यदि हां, तो वो कौन हैं?
शाम

1
@aytunch मुझे नहीं लगता कि आप constहर जगह उपयोग कर सकते हैं । उदाहरण के लिए, यदि आपके पास एक ऐसा StatelessWidgetवर्ग है जो Textकिसी चर का मान देता है , और वह चर कहीं बदल जाता है, तो आपके StatelessWidgetपुन: निर्माण से होना चाहिए, इसलिए यह अलग मूल्य दिखा सकता है, इसलिए यह नहीं हो सकता है const। मुझे लगता है कि इसे लगाने का सुरक्षित तरीका यह है: जहां भी आप ऐसा कर सकते हैं, उपयोग करें const, यदि ऐसा करना सुरक्षित है।
सेर्गी इयाकोब

3
मैं बहस कर रहा हूं कि क्या इस सवाल का जवाब खुद देना है। स्वीकृत उत्तर स्पष्ट रूप से गलत है, लेकिन रमी ने फड़फड़ाने वाले समुदाय की कोशिश और मदद करने के लिए बहुत कुछ किया है, इसलिए लोग शायद उसके जवाबों की जांच नहीं करते हैं जितना कि किसी और का। यह सभी उतार-चढ़ाव से स्पष्ट हो सकता है। लोग बस अपना "सत्य का एकल स्रोत" चाहते हैं। :-)
डार्कनॉरटन

4

क्या कार्य करता है और क्या वर्ग करता है के बीच एक बड़ा अंतर था।


आइए मैं इसे बहुत खरोंच से समझाऊंगा। (केवल अनिवार्य के बारे में)

  • प्रोग्रामिंग इतिहास, हम सभी को सीधे मूल आदेशों (जैसे- विधानसभा) के साथ शुरू हुआ।

  • नेक्स्ट स्ट्रक्चर्ड प्रोग्रामिंग फ्लो कंट्रोल के साथ आया (जैसे-: अगर, स्विच, जबकि, आदि के लिए) यह प्रतिमान प्रोग्रामर को प्रोग्राम के प्रवाह को प्रभावी ढंग से नियंत्रित करने के लिए देता है और लूप द्वारा कोड लाइनों की इसकी न्यूनतम संख्या भी।

  • अगली प्रक्रियात्मक प्रोग्रामिंग आई और जो समूह प्रक्रियाओं (फंक्शंस) में निर्देश देते हैं। इससे प्रोग्रामर को दो बड़े लाभ हुए।

    1. अलग-अलग ब्लॉकों में बयान (संचालन)।

    2. इन ब्लॉकों का पुन: उपयोग कर सकते हैं। (कार्य)

लेकिन ऊपर के सभी प्रतिमानों ने प्रबंध अनुप्रयोगों के लिए एक समाधान नहीं दिया। प्रक्रियात्मक प्रोग्रामिंग भी केवल छोटे पैमाने के अनुप्रयोगों के लिए उपयोग कर सकते हैं। बड़े वेब एप्लिकेशन विकसित करने के लिए इसका उपयोग नहीं किया जा सकता है (जैसे: बैंकिंग, गूगल, यूट्यूब, फेसबुक, स्टिकओवरफ्लो आदि), एंड्रॉइड एसडीके, फ्लटर एसडीके और बहुत कुछ जैसे फ्रेमवर्क नहीं बना सकते ......

इसलिए इंजीनियर कार्यक्रमों को उचित तरीके से प्रबंधित करने के लिए बहुत अधिक शोध करते हैं।

  • अंत में ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग किसी भी पैमाने पर अनुप्रयोगों के प्रबंधन के लिए सभी समाधान के साथ आता है। (हैलो दुनिया से ट्रिलियन लोगों के लिए सिस्टम निर्माण जैसे-गूगल, अमेज़ॅन और आज 90% अनुप्रयोगों का उपयोग करके)।

  • ऑप में सभी एप्लिकेशन Objects.It के आसपास निर्मित होते हैं। इसका मतलब है कि एप्लिकेशन इन ऑब्जेक्ट्स का एक संग्रह है।

इसलिए वस्तुएँ किसी भी अनुप्रयोग के लिए आधारभूत इमारत होती हैं।

वर्ग (रनटाइम पर ऑब्जेक्ट) समूह डेटा और उन चर (डेटा) से संबंधित फ़ंक्शन। इसलिए डेटा और उनके संबंधित कार्यों की रचना।

[यहाँ मैं ऊप के बारे में समझाने वाला नहीं हूँ]


👈👈👈Ok अब स्पंदन ढांचे के लिए आ रहा है। L

डार्ट प्रक्रियात्मक और ऊप दोनों का समर्थन करते हैं लेकिन, फ़्लटर फ्रेमवर्क पूरी तरह से कक्षाओं (ऊप) का उपयोग करके बनाते हैं। (क्योंकि बड़े प्रबंधनीय फ्रेमवर्क प्रक्रियात्मक का उपयोग करके नहीं बना सकते हैं)

यहां मैं उन कारणों की सूची बनाऊंगा, जो वे विजेट बनाने के लिए कार्यों के बजाय कक्षाओं का उपयोग करते हैं


1 - ज्यादातर बार निर्माण विधि (चाइल्ड विजेट) सिंक्रोनस और एसिंक्रोनस फ़ंक्शन की कॉल संख्या होती है।

उदाहरण के लिए:

  • नेटवर्क छवि डाउनलोड करने के लिए
  • उपयोगकर्ता आदि से इनपुट प्राप्त करें

इसलिए बिल्ड मेथड को अलग क्लास के विजेट में रखने की जरूरत है (क्योंकि अन्य सभी तरीके बिल्ड () मेथड को एक क्लास में रख सकते हैं)


2 - विजेट क्लास का उपयोग करके आप बार-बार एक ही कोड लिखे बिना दूसरी कक्षा की संख्या बना सकते हैं (** इनहेरिटेंस का उपयोग ** (फैली हुई))।

और इनहेरिटेंस (विस्तार) और बहुरूपता (ओवरराइड) का उपयोग करके भी आप स्वयं के कस्टम वर्ग बना सकते हैं। (नीचे उदाहरण के लिए, वहां मैं MaterialPageRoute का विस्तार करके एनिमेशन को ओवरराइड (ओवरराइड) करूंगा (क्योंकि इसका डिफ़ॉल्ट संक्रमण मैं अनुकूलित करना चाहता हूं)। In

class MyCustomRoute<T> extends MaterialPageRoute<T> {
  MyCustomRoute({ WidgetBuilder builder, RouteSettings settings })
      : super(builder: builder, settings: settings);

  @override                                      //Customize transition
  Widget buildTransitions(BuildContext context,
      Animation<double> animation,
      Animation<double> secondaryAnimation,
      Widget child) {
    if (settings.isInitialRoute)
      return child;
    // Fades between routes. (If you don't want any animation, 
    // just return child.)
    return new FadeTransition(opacity: animation, child: child);
  }
}

3 - फ़ंक्शंस उनके मापदंडों के लिए शर्तों को नहीं जोड़ सकते हैं, लेकिन क्लास विजेट के निर्माता का उपयोग करके आप ऐसा कर सकते हैं।

नीचे कोड example (यह सुविधा फ्रेमवर्क विगेट्स द्वारा अत्यधिक उपयोग की जाती है)

const Scaffold({
    Key key,
    this.bottomNavigationBar,
    this.bottomSheet,
    this.backgroundColor,
    this.resizeToAvoidBottomPadding,
    this.resizeToAvoidBottomInset,
    this.primary = true,
    this.drawerDragStartBehavior = DragStartBehavior.start,
    this.extendBody = false,
    this.extendBodyBehindAppBar = false,
    this.drawerScrimColor,
    this.drawerEdgeDragWidth,
  }) : assert(primary != null),
       assert(extendBody != null),
       assert(extendBodyBehindAppBar != null),
       assert(drawerDragStartBehavior != null),
       super(key: key);

4 - फ़ंक्शंस का उपयोग नहीं किया जा सकता है और क्लास विजेट अपने कंस्ट्रक्टरों के लिए कॉस्ट का उपयोग कर सकते हैं। (जो मुख्य सूत्र के प्रदर्शन को प्रभावित करते हैं)


5 - आप एक ही वर्ग (एक वर्ग / वस्तुओं के उदाहरण) का उपयोग करके किसी भी स्वतंत्र विजेट को बना सकते हैं, लेकिन फ़ंक्शन स्वतंत्र विजेट (उदाहरण) नहीं बना सकता है, लेकिन पुन: उपयोग कर सकता है।

[प्रत्येक उदाहरण का अपना उदाहरण चर होता है और जो अन्य विजेट्स (ऑब्जेक्ट) से पूरी तरह से स्वतंत्र होता है, लेकिन फ़ंक्शन का स्थानीय वैरिएबल प्रत्येक फ़ंक्शन कॉल पर निर्भर होता है * (जिसका अर्थ है, जब आप किसी स्थानीय चर का मान बदलते हैं तो यह अन्य सभी भागों के लिए प्रभावित होता है) अनुप्रयोग जो इस फ़ंक्शन का उपयोग करता है)]


फ़ंक्शंस के वर्ग में कई फ़ायदे थे .. (ऊपर केवल कुछ उपयोग के मामले हैं)


🤯 माई फाइनल थॉट

इसलिए अपने एप्लिकेशन के बिल्डिंग ब्लॉक के रूप में फ़ंक्शंस का उपयोग न करें, उन्हें केवल संचालन करने के लिए उपयोग करें। जब आपके एप्लिकेशन को स्केलेबल मिलता है तो अन्यथा यह कई अनचाही समस्याओं का कारण बनता है ।

  • कार्य के छोटे हिस्से को करने के लिए कार्यों का उपयोग करें
  • अनुप्रयोग के निर्माण खंड के रूप में वर्ग का उपयोग करें (अनुप्रयोग का प्रबंधन)

📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍 📍📍📍📍📍📍📍

आप इसे (या लाइनों) संख्या के आधार पर कार्यक्रम की संख्या की गुणवत्ता को प्राप्त नहीं कर सकते

📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍 📍📍📍📍📍📍📍

पढ़ने के लिए धन्यवाद


Stackoverflow में आपका स्वागत है! मुझे वास्तव में यकीन नहीं है कि आप अपने उत्तर के साथ क्या व्यक्त करने की कोशिश कर रहे हैं। विजेट बनाने के लिए आप किसी फंक्शन का उपयोग कर सकते हैं। आपके विजेट ट्री में इनलाइन shrinkHelper() { return const SizedBox.shrink(); }का उपयोग करने के समान है const SizedBox.shrink(), और सहायक कार्यों का उपयोग करके आप एक स्थान पर नेस्टिंग की मात्रा को सीमित कर सकते हैं।
डार्कनॉरन

@DarkNeuron साझा करने के लिए धन्यवाद। मैं सहायक कार्यों का उपयोग करने की कोशिश करूंगा।
TDM

2

जब आप फ़्लटर विजेट को कॉल कर रहे हैं, तो सुनिश्चित करें कि आप कास्ट कीवर्ड का उपयोग करते हैं। उदाहरण के लिएconst MyListWidget();


9
क्या मुझे पता है कि यह ओपी प्रश्न का उत्तर कैसे देता है?
CopsOnRoad

2
जैसे मैंने देखा कि मैंने गलत खंड का उत्तर दिया। मैं डैनियल के सवाल का जवाब देने की कोशिश कर रहा था कि रिफैक्टर्ड स्टेटलेस विजेट बिल्ड विधि अभी भी कहा जा रहा है। constRefactored स्टेटलेस विजेट को कॉल करते समय कीवर्ड जोड़कर इसे केवल एक बार कॉल किया जाना चाहिए।
user4761410

1
ठीक। समझ गया। लोग इस उत्तर को अस्वीकार कर सकते हैं क्योंकि इसका ओपी प्रश्न से कोई लेना-देना नहीं है। इसलिए आपको इसे डिलीट कर देना चाहिए। किसी भी तरह तुम्हारा है।
CopsOnRoad
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.