অযাচিত উইজেট বিল্ডের সাথে কীভাবে মোকাবিলা করবেন?


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-m
বানি

4
ইন প্রদানকারী ডকুমেন্টেশন আপনি এখানে বলার অপেক্ষা রাখে না লিঙ্ক "এই Stackoverflow উত্তর যা আরো বিস্তারিত ব্যাখ্যা দিয়েছে কেন .value কন্সট্রাকটর ব্যবহার করে মান তৈরি করতে অবাঞ্ছিত তা দেখুন।" তবে আপনি এখানে বা আপনার উত্তরে মান নির্মাতার উল্লেখ করবেন না। আপনি অন্য কোথাও লিঙ্ক বোঝাতে চেয়েছিলেন?
সুরগাচ

উত্তর:


224

বিল্ড পদ্ধতি এমনভাবে এটি হওয়া উচিত এ ডিজাইন করা হয়েছে বিশুদ্ধ / পার্শ্ব প্রতিক্রিয়া ছাড়াই । এটি কারণ অনেকগুলি বাহ্যিক কারণ একটি নতুন উইজেট বিল্ডকে ট্রিগার করতে পারে, যেমন:

  • রুট পপ / পুশ
  • স্ক্রীন আকার পরিবর্তন, সাধারণত কীবোর্ড উপস্থিতি বা অভিমুখী পরিবর্তনের কারণে
  • মূল উইজেট তার শিশুটিকে পুনরায় তৈরি করেছে
  • একটি উত্তরাধিকারী উইজেট উইজেট ( 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শ্রেণিকেন্দ্রের ক্ষেত্রটি কেন হট-লোড ভেঙে দেয়?
এমফাইনস্টাইন

4
আমার যে সমস্যাটি হচ্ছে StreamBuilderতা হ'ল যখন কীবোর্ডটি প্রদর্শিত হয় তখন স্ক্রিন পরিবর্তন হয়, তাই রুটগুলি আবার তৈরি করতে হয়। সুতরাং StreamBuilderপুনর্নির্মাণ করা হয় এবং একটি নতুন StreamBuilderতৈরি হয় এবং এটি সাবস্ক্রাইব করে stream। যখন StreamBuilderএকটিতে সাবস্ক্রাইব হয় stream, এটি snapshot.connectionStateহয়ে যায় ConnectionState.waitingযা আমার কোডটিকে একটি করে দেয় CircularProgressIndicatorএবং তারপরে snapshot.connectionStateডেটা থাকলেই পরিবর্তন হয় এবং আমার কোডটি একটি ভিন্ন উইজেট ফিরিয়ে দেয়, যা বিভিন্ন স্টাফ দিয়ে স্ক্রিনটিকে ঝাঁকুনিতে পরিণত করে।
এমফাইনস্টাইন

1
আমি না করার সিদ্ধান্ত নিয়েছে StatefulWidget, এর সাবস্ক্রাইব streamউপর initState()এবং সেট currentWidgetদিয়ে setState()যেমন streamনতুন তথ্য পাঠায়, ক্ষণস্থায়ী currentWidgetকরার build()পদ্ধতি। এর থেকে আরও ভাল সমাধান কি আছে?
এমফিনস্টাইন

1
আমি একটু বিভ্রান্ত। আপনি আপনার নিজের প্রশ্নের উত্তর দিচ্ছেন, তবে বিষয়বস্তু থেকে, এটি এর মতো দেখাচ্ছে না।
সাগোন00

8
উহ, বলছেন যে কোনও বিল্ডকে এইচটিটিপি পদ্ধতিতে কল করা উচিত নয় সম্পূর্ণরূপে এর এর বাস্তব ব্যবহারিক উদাহরণটিকে পরাস্ত করে FutureBuilder
TheGeekZn

6

আপনি এইভাবে ব্যবহার করে অযাচিত বিল্ডিং কলিং প্রতিরোধ করতে পারেন

1) UI এর স্বতন্ত্র অংশের জন্য চাইল্ড স্টেটফুল ক্লাস তৈরি করুন

2) সরবরাহকারী লাইব্রেরি ব্যবহার করুন , সুতরাং এটি ব্যবহার করে আপনি

নীচের পরিস্থিতিতে বিল্ড পদ্ধতি কলটি বন্ধ করতে পারেন অযাচিত বিল্ড পদ্ধতি কলিং বন্ধ করতে

  • থ্রিস্টেট কল করার পরে
  • DidUpdateWidget কল করার পরে
  • যখন সেটস্টেট () বলা হয়।
  • যখন কীবোর্ড খোলা থাকে
  • যখন স্ক্রিন ওরিয়েন্টেশন পরিবর্তিত হয়
  • প্যারেন্ট উইজেট বিল্ড হয় তারপর চাইল্ড উইজেটও আবার তৈরি করে

0

তোলাও আছে ValueListenableBuilder<T> class । এটি আপনাকে আপনার উদ্দেশ্যে প্রয়োজনীয় কয়েকটি উইজেটগুলি পুনর্নির্মাণ করতে এবং ব্যয়বহুল উইজেটগুলি এড়িয়ে যাওয়ার অনুমতি দেয়।

আপনি এখানে দস্তাবেজগুলি ভ্যালুলিস্টেবলবিল্ডার বিড়বিড়কারী ডক্স
বা নীচের স্যাম্পল কোডটি দেখতে পারেন :

  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.