Kodokon kodokon.com

अनुकूली: LayoutBuilder, MediaQuery और प्लेटफ़ॉर्म

ऐसे इंटरफ़ेस बनाएँ जो पैरेंट की बाध्यताओं, विंडो और हर OS की परंपराओं के अनुसार ढल जाएँ।

9 मिनट · 3 प्रश्न

इस पाठ को Kodokon में खोलें

MediaQuery, लॉजिकल विंडो का वर्णन करता है, भौतिक स्क्रीन का नहीं: आकार, padding (नॉच, सिस्टम बार), viewInsets (कीबोर्ड), textScaler... एक क्लासिक गड्ढा: MediaQuery.of(context) विजेट को इन सभी प्रॉपर्टीज़ की सदस्यता दिला देता है। जब कीबोर्ड खुलता है, तो viewInsets बदलता है और हर सदस्यता वाला विजेट दोबारा बनता है - उनमें वे भी शामिल हैं जो केवल चौड़ाई पढ़ते हैं। Flutter 3.10 के बाद से, MediaQueryData एक InheritedModel के ज़रिए उजागर किया जाता है: लक्षित एक्सेसर MediaQuery.sizeOf(context), paddingOf, viewInsetsOf... विजेट को केवल माँगे गए पहलू की सदस्यता दिलाते हैं। विशेषज्ञ की सहज प्रतिक्रिया: अपने कोडबेस से .of(context).size को निकाल बाहर करें।

DART
import 'package:flutter/material.dart';

class AdaptiveGrid extends StatelessWidget {
  const AdaptiveGrid({super.key});

  @override
  Widget build(BuildContext context) {
    final width = MediaQuery.sizeOf(context).width;
    final columns = switch (width) {
      >= 900 => 4,
      >= 600 => 3,
      _ => 2,
    };
    return GridView.builder(
      gridDelegate:
          SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: columns,
      ),
      itemCount: 24,
      itemBuilder: (context, i) => Card(
        child: Center(child: Text('Tile $i')),
      ),
    );
  }
}
sizeOf + रिलेशनल पैटर्न: पढ़ने योग्य ब्रेकपॉइंट।

MediaQuery इस प्रश्न का उत्तर देता है "विंडो कितनी बड़ी है?"; LayoutBuilder इसका उत्तर देता है "मेरा पैरेंट मुझे कितनी जगह देता है?"। यह सूक्ष्म अंतर पुनः प्रयोग योग्य कंपोनेंट के लिए निर्णायक है: एक पैनल जो कभी पूरी स्क्रीन पर, कभी 320 px के कॉलम में दिखाया जाता है, उसे विंडो पर नहीं, बल्कि अपनी बाध्यताओं पर प्रतिक्रिया देनी चाहिए - वरना जब वह असल में तंग जगह में हो, तब भी उसे लगेगा कि वह किसी टैबलेट पर है। एक उपयोगी आंतरिक विवरण: LayoutBuilder का builder लेआउट चरण के दौरान चलता है, जब बाध्यताएँ ज्ञात होती हैं; इसलिए आप इसके अंदर कोई समकालिक setState ट्रिगर नहीं कर सकते, फ़्रेमवर्क इसे मना करता है।

DART
import 'package:flutter/material.dart';

class MasterDetail extends StatelessWidget {
  const MasterDetail({
    super.key,
    required this.list,
    required this.detail,
  });

  final Widget list;
  final Widget detail;

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth < 720) {
          return list;
        }
        return Row(
          children: [
            SizedBox(width: 320, child: list),
            const VerticalDivider(width: 1),
            Expanded(child: detail),
          ],
        );
      },
    );
  }
}
कंपोनेंट को जितनी जगह मिलती है उसके अनुसार वह ढल जाता है, चाहे उसे कहीं भी रखा जाए।

iOS/Android के अंतरों के लिए, विजेट की ओर सत्य का स्रोत Theme.of(context).platform है: यह defaultTargetPlatform से व्युत्पन्न होता है और साथ ही override का सम्मान करता है - किसी परीक्षण से iOS रेंडरिंग को जाँचने या किसी अन्य प्लेटफ़ॉर्म का अनुकरण करने के लिए आवश्यक। कई अनुकूलन पहले से ही फ़्रेमवर्क में निर्मित हैं: स्क्रॉल फ़िज़िक्स (iOS का bounce बनाम Android का glow), पेज ट्रांज़िशन, किनारे से स्वाइप-कर-वापस-जाने का जेस्चर। .adaptive कंस्ट्रक्टर (Switch.adaptive, CircularProgressIndicator.adaptive) खुद-ब-खुद Cupertino रेंडरिंग पर स्विच कर जाते हैं। अपनी मैनुअल शाखाओं को सचमुच भिन्न परंपराओं के लिए ही सुरक्षित रखें, जैसे क्रियाओं का लेबलिंग या डायलॉग बटन की स्थिति।

DART
import 'package:flutter/material.dart';

class SettingsTile extends StatelessWidget {
  const SettingsTile({
    super.key,
    required this.value,
    required this.onChanged,
  });

  final bool value;
  final ValueChanged<bool> onChanged;

  @override
  Widget build(BuildContext context) {
    final platform = Theme.of(context).platform;
    final isApple = platform == TargetPlatform.iOS ||
        platform == TargetPlatform.macOS;
    return ListTile(
      title: Text(
        isApple ? 'Settings' : 'Preferences',
      ),
      trailing: Switch.adaptive(
        value: value,
        onChanged: onChanged,
      ),
    );
  }
}
Switch.adaptive, iOS/macOS पर एक Cupertino टॉगल रेंडर करता है।

ज्ञान जांच

सुनिश्चित करें कि आपको इस पाठ के मुख्य बिंदु याद हैं।

  1. MediaQuery.of(context) और MediaQuery.sizeOf(context) के बीच क्या अंतर है?
    • sizeOf विजेट को केवल आकार वाले पहलू की सदस्यता दिलाता है: कीबोर्ड खुलने से वह दोबारा नहीं बनता
    • sizeOf असली स्क्रीन पिक्सेल में भौतिक आकार लौटाता है
    • of अप्रचलित (deprecated) है और फ़्रेमवर्क से हटा दिया जाएगा
  2. किसी लेआउट को अनुकूलित करने के लिए आपको MediaQuery के बजाय LayoutBuilder को कब प्राथमिकता देनी चाहिए?
    • जब निर्णय पैरेंट द्वारा आवंटित जगह पर निर्भर करता हो, न कि विंडो के आकार पर
    • जब आपको डिवाइस का अभिविन्यास (orientation) जानना हो
    • जब आप किसी थीम बदलाव पर रीबिल्ड से बचना चाहते हों
    • कभी नहीं: दोनों आपस में विनिमेय हैं
  3. विजेट कोड में Platform.isIOS से क्यों बचें?
    • यह dart:io से आता है, जो वेब पर उपलब्ध नहीं है, और परीक्षणों तथा थीम द्वारा उपयोग किए जाने वाले override को अनदेखा करता है
    • यह defaultTargetPlatform की तुलना में उल्लेखनीय रूप से धीमा है
    • यह macOS पर true लौटाता है