ऐसे इंटरफ़ेस बनाएँ जो पैरेंट की बाध्यताओं, विंडो और हर OS की परंपराओं के अनुसार ढल जाएँ।
इस पाठ को Kodokon में खोलेंMediaQuery, लॉजिकल विंडो का वर्णन करता है, भौतिक स्क्रीन का नहीं: आकार, padding (नॉच, सिस्टम बार), viewInsets (कीबोर्ड), textScaler... एक क्लासिक गड्ढा: MediaQuery.of(context) विजेट को इन सभी प्रॉपर्टीज़ की सदस्यता दिला देता है। जब कीबोर्ड खुलता है, तो viewInsets बदलता है और हर सदस्यता वाला विजेट दोबारा बनता है - उनमें वे भी शामिल हैं जो केवल चौड़ाई पढ़ते हैं। Flutter 3.10 के बाद से, MediaQueryData एक InheritedModel के ज़रिए उजागर किया जाता है: लक्षित एक्सेसर MediaQuery.sizeOf(context), paddingOf, viewInsetsOf... विजेट को केवल माँगे गए पहलू की सदस्यता दिलाते हैं। विशेषज्ञ की सहज प्रतिक्रिया: अपने कोडबेस से .of(context).size को निकाल बाहर करें।
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')),
),
);
}
}MediaQuery इस प्रश्न का उत्तर देता है "विंडो कितनी बड़ी है?"; LayoutBuilder इसका उत्तर देता है "मेरा पैरेंट मुझे कितनी जगह देता है?"। यह सूक्ष्म अंतर पुनः प्रयोग योग्य कंपोनेंट के लिए निर्णायक है: एक पैनल जो कभी पूरी स्क्रीन पर, कभी 320 px के कॉलम में दिखाया जाता है, उसे विंडो पर नहीं, बल्कि अपनी बाध्यताओं पर प्रतिक्रिया देनी चाहिए - वरना जब वह असल में तंग जगह में हो, तब भी उसे लगेगा कि वह किसी टैबलेट पर है। एक उपयोगी आंतरिक विवरण: LayoutBuilder का builder लेआउट चरण के दौरान चलता है, जब बाध्यताएँ ज्ञात होती हैं; इसलिए आप इसके अंदर कोई समकालिक setState ट्रिगर नहीं कर सकते, फ़्रेमवर्क इसे मना करता है।
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 रेंडरिंग पर स्विच कर जाते हैं। अपनी मैनुअल शाखाओं को सचमुच भिन्न परंपराओं के लिए ही सुरक्षित रखें, जैसे क्रियाओं का लेबलिंग या डायलॉग बटन की स्थिति।
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,
),
);
}
}