[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"technologies":3,"\u002Fopen-source\u002Fflutter-adaptive-layout":167},[4,10,15,21,26,32,38,43,48,53,58,63,68,73,77,82,87,92,97,103,108,113,118,123,128,133,138,143,147,152,157,162],{"title":5,"description":6,"slug":7,"category":8,"icon":9},"Android","Каждое наше приложение выходит и на Android — платформенные API, фоновое выполнение, разрешения и требования магазина вместе с ними.","android","framework","logos:android-icon",{"title":11,"description":12,"slug":13,"category":8,"icon":14},"Angular","Фронтенд-фреймворк, в котором мы работаем, когда продукт уже на Angular, — строгая структура, подходящая большим долгоживущим приложениям.","angular","logos:angular-icon",{"title":16,"description":17,"slug":18,"category":19,"icon":20},"Dart","Язык, на котором написано каждое наше Flutter-приложение, — строгая null safety, полноценный pattern matching и компиляция в нативный код и веб.","dart","language","logos:dart",{"title":22,"description":23,"slug":24,"category":8,"icon":25},"Django","Python-фреймворк, который мы берём, когда продукту нужны админка, аутентификация и модель данных в первый же день, а не самописный сервис.","django","logos:django-icon",{"title":27,"description":28,"slug":29,"category":30,"icon":31},"Docker","Каждый наш сервис поставляется в виде контейнера, поэтому на ноутбуке, в CI и в продакшене работает один и тот же артефакт, а не три разных.","docker","infrastructure","logos:docker-icon",{"title":33,"description":34,"slug":35,"category":36,"icon":37},"Fastlane","Автоматизация релизов в нашей мобильной разработке — подпись, сборки и загрузка в сторы идут из CI, а не с ноутбука одного инженера в день релиза.","fastlane","tools","logos:fastlane",{"title":39,"description":40,"slug":41,"category":8,"icon":42},"Flutter","Наш основной мобильный стек с 2018 года — одна кодовая база на Dart для iOS, Android, веба и десктопа без отдельной команды под каждую платформу.","flutter","logos:flutter",{"title":44,"description":45,"slug":46,"category":36,"icon":47},"Git","Все проекты, к которым мы прикасаемся, живут в Git — ревью пул-реквестов, CI на каждой ветке и история, понятная и спустя месяцы.","git","logos:git-icon",{"title":49,"description":50,"slug":51,"category":19,"icon":52},"Go","Наш бэкенд-язык для real-time API и сервисов под нагрузкой — маленькие бинарники, быстрая сборка и конкурентность, которая остаётся читаемой.","go","logos:gopher",{"title":54,"description":55,"slug":56,"category":30,"icon":57},"Google Cloud","Облако, в котором работают наши продакшен-нагрузки, — управляемый Kubernetes, хранилище и сеть без серверов, которые надо обслуживать руками.","gcp","logos:google-cloud",{"title":59,"description":60,"slug":61,"category":36,"icon":62},"Gradle","Система сборки, через которую проходит каждый Android-релиз, — флейворы, конфигурации подписи и вся обвязка зависимостей под Flutter-приложением.","gradle","logos:gradle",{"title":64,"description":65,"slug":66,"category":30,"icon":67},"Helm","Так мы упаковываем деплой в Kubernetes — сервис, конфигурация, секреты и ingress как одна версионируемая единица, которую можно выкатить и откатить.","helm","logos:helm",{"title":69,"description":70,"slug":71,"category":19,"icon":72},"Kotlin","То, что мы берём, когда Flutter-приложению нужен настоящий Android под ним, — platform channels, фоновые задачи и интеграции с нативными SDK.","kotlin","logos:kotlin-icon",{"title":74,"description":75,"slug":76,"category":8,"icon":72},"Kotlin Multiplatform","Общая бизнес-логика для iOS и Android, а нативный интерфейс у каждой платформы свой, — альтернатива, когда Flutter не подходит.","kmp",{"title":78,"description":79,"slug":80,"category":30,"icon":81},"Kubernetes","Так мы запускаем сервисы в продакшене — деплой в Helm-чартах, откатываемые выкатки и масштабирование, для которого не нужен человек в три ночи.","kubernetes","logos:kubernetes",{"title":83,"description":84,"slug":85,"category":30,"icon":86},"NATS","Лёгкий обмен сообщениями между сервисами — publish\u002Fsubscribe и request\u002Freply без эксплуатационного веса полноценного кластера брокера.","nats","logos:nats-icon",{"title":88,"description":89,"slug":90,"category":8,"icon":91},"Nuxt","Vue с серверным рендерингом, роутингом и SEO из коробки — так мы делаем сайты и веб-приложения, которые должны быстро грузиться и индексироваться.","nuxt","logos:nuxt-icon",{"title":93,"description":94,"slug":95,"category":19,"icon":96},"PHP","Там, где PHP-бэкенд уже есть: дорабатываем его, интегрируемся с ним и делаем мобильные и веб-клиенты, которые он обслуживает.","php","logos:php",{"title":98,"description":99,"slug":100,"category":101,"icon":102},"PostgreSQL","Наша база данных по умолчанию — от проектирования схемы до настройки индексов мы берём её, если у продукта нет конкретной причины взять другую.","postgres","database","logos:postgresql",{"title":104,"description":105,"slug":106,"category":19,"icon":107},"Python","Наш язык для бэкендов, работы с данными и AI-интеграций — включая Python-биндинги, которые мы выпускаем к собственным инструментам на Rust.","python","logos:python",{"title":109,"description":110,"slug":111,"category":8,"icon":112},"React","Фронтенд-библиотека, в которой мы работаем, когда продукт уже на React, — компоненты, хуки и выросшая вокруг них экосистема.","react","logos:react",{"title":114,"description":115,"slug":116,"category":101,"icon":117},"Redis","Место для данных, которые должны быть быстрыми и которые не жалко пересчитать, — кэши, сессии, лимиты и очереди фоновых задач.","redis","logos:redis",{"title":119,"description":120,"slug":121,"category":19,"icon":122},"Ruby","Язык, на котором написана автоматизация наших мобильных релизов, — лейны Fastlane, кастомные действия и связка с CI, доставляющая сборки в сторы.","ruby","logos:ruby",{"title":124,"description":125,"slug":126,"category":19,"icon":127},"Rust","Куда мы идём, когда важны и скорость, и корректность, — рендеринг документов, CLI-инструменты и сервисы, которые обязаны оставаться быстрыми и предсказуемыми.","rust","simple-icons:rust",{"title":129,"description":130,"slug":131,"category":101,"icon":132},"SQLite","База данных, которая поставляется внутри приложения, — локальные кэши, offline-first хранилище и всё, что должно работать без сети.","sqlite","logos:sqlite",{"title":134,"description":135,"slug":136,"category":8,"icon":137},"Strapi","Headless CMS, к которой мы обращаемся, когда контент должны вести сами редакторы, — готовая админка и чистый API вместо самописных.","strapi","logos:strapi-icon",{"title":139,"description":140,"slug":141,"category":19,"icon":142},"Swift","То, что мы берём, когда Flutter-приложению нужен настоящий iOS под ним, — platform channels, интеграции нативных SDK, виджеты и App Clips.","swift","logos:swift",{"title":144,"description":145,"slug":146,"category":8,"icon":142},"SwiftUI","UI-фреймворк Apple, на котором мы делаем нативные поверхности, недоступные Flutter-приложению, — виджеты, App Clips и расширения.","swiftui",{"title":148,"description":149,"slug":150,"category":8,"icon":151},"Tailwind CSS","Так мы стилизуем весь фронтенд — утилитарные классы и дизайн-токены вместо таблицы стилей, которая только растёт и никогда не чистится.","tailwind","logos:tailwindcss-icon",{"title":153,"description":154,"slug":155,"category":19,"icon":156},"TypeScript","Стандарт для всего, что мы пишем для браузера и для Node-сервисов, — типы ловят ошибки интеграции раньше, чем код доходит до ревью.","typescript","logos:typescript-icon",{"title":158,"description":159,"slug":160,"category":8,"icon":161},"Vue","Наш фронтенд-фреймворк для админок, дашбордов мерчантов и веб-приложений, которые должны оставаться поддерживаемыми годами, а не спринтами.","vue","logos:vue",{"title":163,"description":164,"slug":165,"category":36,"icon":166},"Whisper","Открытая модель распознавания речи от OpenAI — движок транскрипции для голосового ввода, который работает и как бэкенд-сервис, и прямо на устройстве в приложении.","whisper","simple-icons:openai",{"id":168,"title":169,"body":170,"description":1004,"extension":1005,"links":1006,"meta":1010,"metaDescription":1011,"metaTitle":1012,"navigation":715,"ogDescription":1013,"path":1014,"repository":1015,"seo":1019,"slug":1018,"stem":1020,"technologies":1021,"updated":1022,"__hash__":1023,"stats":1024},"open_source_ru\u002Fopen-source\u002Fflutter-adaptive-layout.md","Flutter Adaptive Layout",{"type":171,"value":172,"toc":991},"minimark",[173,178,182,198,201,219,223,244,417,420,428,434,438,505,509,515,596,606,610,615,624,628,631,635,648,652,655,659,662,684,691,694,799,808,967,973,983,987],[174,175,177],"h2",{"id":176},"проблема","Проблема",[179,180,181],"p",{},"Приложению на Flutter, которое выходит на телефоны, планшеты и десктоп, недостаточно гибкой колонки. Экрану, который хорошо читается при ширине 390 логических пикселей, при 700 нужна центрированная карточка, а при 1200 — боковая панель. Flutter даёт измерения, но не даёт структуры, в которой их применять.",[179,183,184,185,189,190,193,194,197],{},"Поэтому проверки расползаются по коду. Здесь ",[186,187,188],"code",{},"MediaQuery.of(context).size.width > 600",", там ",[186,191,192],{},"LayoutBuilder",", тремя виджетами ниже — тернарный оператор внутри ",[186,195,196],{},"Padding",". Каждая по отдельности выглядит уместно, но вместе они образуют правила вёрстки, которые негде прочитать целиком.",[179,199,200],{},"Типичные проблемы:",[202,203,204,208,213,216],"ul",{},[205,206,207],"li",{},"Значения брейкпойнтов дублируются в десятках виджетов и расходятся по мере изменения дизайна",[205,209,210,212],{},[186,211,192],{}," сообщает ограничения родителя, тогда как нужен размер устройства",[205,214,215],{},"Вёрстка переключается на планшетный вариант, как только телефон поворачивают в альбомную ориентацию",[205,217,218],{},"В дереве виджетов невозможно увидеть логику адаптивности, не прочитав каждый дочерний виджет",[174,220,222],{"id":221},"как-flutter_adaptive_layout-решает-это","Как flutter_adaptive_layout решает это",[179,224,225,228,229,232,233,236,237,240,241,243],{},[186,226,227],{},"AdaptiveLayout"," принимает по одному билдеру на размер экрана и ",[186,230,231],{},"child",". Он измеряет устройство через ",[186,234,235],{},"MediaQuery",", сопоставляет результат с ",[186,238,239],{},"ScreenSize"," и строит только подходящий вариант — при этом ",[186,242,231],{}," создаётся один раз и передаётся в выбранный билдер.",[245,246,250],"pre",{"className":247,"code":248,"language":18,"meta":249,"style":249},"language-dart shiki shiki-themes material-theme-lighter github-light github-dark","AdaptiveLayout(\n  smallBuilder: (context, child) => child!,\n  mediumBuilder: (context, child) => Center(child: child),\n  largeBuilder: (context, child) => Row(\n    children: [const Sidebar(), Expanded(child: child!)],\n  ),\n  child: const MyHomePage(),\n)\n","",[186,251,252,264,296,325,346,385,393,411],{"__ignoreMap":249},[253,254,257,260],"span",{"class":255,"line":256},"line",1,[253,258,227],{"class":259},"sZMiF",[253,261,263],{"class":262},"su5hD","(\n",[253,265,267,270,274,277,281,284,287,290,293],{"class":255,"line":266},2,[253,268,269],{"class":262},"  smallBuilder",[253,271,273],{"class":272},"smGrS",":",[253,275,276],{"class":262}," (context",[253,278,280],{"class":279},"sP7_E",",",[253,282,283],{"class":262}," child) ",[253,285,286],{"class":272},"=>",[253,288,289],{"class":262}," child",[253,291,292],{"class":272},"!",[253,294,295],{"class":279},",\n",[253,297,299,302,304,306,308,310,312,315,318,320,323],{"class":255,"line":298},3,[253,300,301],{"class":262},"  mediumBuilder",[253,303,273],{"class":272},[253,305,276],{"class":262},[253,307,280],{"class":279},[253,309,283],{"class":262},[253,311,286],{"class":272},[253,313,314],{"class":259}," Center",[253,316,317],{"class":262},"(child",[253,319,273],{"class":272},[253,321,322],{"class":262}," child)",[253,324,295],{"class":279},[253,326,328,331,333,335,337,339,341,344],{"class":255,"line":327},4,[253,329,330],{"class":262},"  largeBuilder",[253,332,273],{"class":272},[253,334,276],{"class":262},[253,336,280],{"class":279},[253,338,283],{"class":262},[253,340,286],{"class":272},[253,342,343],{"class":259}," Row",[253,345,263],{"class":262},[253,347,349,352,354,357,361,364,367,369,372,374,376,378,380,383],{"class":255,"line":348},5,[253,350,351],{"class":262},"    children",[253,353,273],{"class":272},[253,355,356],{"class":262}," [",[253,358,360],{"class":359},"sbsja","const",[253,362,363],{"class":259}," Sidebar",[253,365,366],{"class":262},"()",[253,368,280],{"class":279},[253,370,371],{"class":259}," Expanded",[253,373,317],{"class":262},[253,375,273],{"class":272},[253,377,289],{"class":262},[253,379,292],{"class":272},[253,381,382],{"class":262},")]",[253,384,295],{"class":279},[253,386,388,391],{"class":255,"line":387},6,[253,389,390],{"class":262},"  )",[253,392,295],{"class":279},[253,394,396,399,401,404,407,409],{"class":255,"line":395},7,[253,397,398],{"class":262},"  child",[253,400,273],{"class":272},[253,402,403],{"class":359}," const",[253,405,406],{"class":259}," MyHomePage",[253,408,366],{"class":262},[253,410,295],{"class":279},[253,412,414],{"class":255,"line":413},8,[253,415,416],{"class":262},")\n",[179,418,419],{},"Решение об адаптивности теперь живёт в одном виджете, на вершине поддерева, которым оно управляет, и записано как три варианта вёрстки, а не как цепочка условий.",[179,421,422],{},[423,424],"img",{"alt":425,"src":426,"style":427},"Пример приложения на маленьком экране — iPhone 14","\u002Fopen-source\u002Fflutter-adaptive-layout\u002Fiphone_14.png","max-height: 500px; height: auto; width: auto;",[179,429,430],{},[423,431],{"alt":432,"src":433},"То же приложение на iPad Pro 12,9 дюйма: largeBuilder помещает контент в широкую карточку","\u002Fopen-source\u002Fflutter-adaptive-layout\u002Fipad_12_inch.png",[174,435,437],{"id":436},"что-умеет-пакет","Что умеет пакет",[202,439,440,462,468,476,486,496],{},[205,441,442,446,447,450,451,454,455,458,459,461],{},[443,444,445],"strong",{},"Вёрстка по размеру экрана"," — ",[186,448,449],{},"smallBuilder",", ",[186,452,453],{},"mediumBuilder"," и ",[186,456,457],{},"largeBuilder",", каждый необязателен: если билдера нет, рисуется ",[186,460,231],{},", поэтому можно описать только те размеры, которые важны",[205,463,464,467],{},[443,465,466],{},"Свои брейкпойнты"," — по умолчанию 400 и 600 логических пикселей, переопределяются для отдельного виджета или для всего приложения",[205,469,470,475],{},[443,471,472,474],{},[186,473,231],{}," строится один раз"," — при смене вёрстки контент оборачивается, а не пересобирается с нуля",[205,477,478,481,482,485],{},[443,479,480],{},"Устойчивость к повороту"," — классификация читает ",[186,483,484],{},"MediaQuery.of(context).size.shortestSide",", поэтому телефон остаётся маленьким экраном и в альбомной ориентации",[205,487,488,491,492,495],{},[443,489,490],{},"Подключаемая логика классификации"," — реализуйте ",[186,493,494],{},"ScreenSizeQualifier",", чтобы делить экраны по ширине, платформе, состоянию складного экрана или как принято в вашей дизайн-системе",[205,497,498,501,502,504],{},[443,499,500],{},"Без зависимостей"," — чистый Flutter поверх ",[186,503,235],{},", работает на iOS, Android, web, macOS, Windows и Linux",[174,506,508],{"id":507},"как-определяется-размер-экрана","Как определяется размер экрана",[179,510,511,514],{},[186,512,513],{},"BreakpointsQualifier"," по умолчанию сравнивает короткую сторону с двумя брейкпойнтами, причём включительно: значение, равное брейкпойнту, попадает в меньшую категорию.",[516,517,518,537],"table",{},[519,520,521],"thead",{},[522,523,524,528,531,534],"tr",{},[525,526,527],"th",{},"Размер экрана",[525,529,530],{},"Условие",[525,532,533],{},"Диапазон по умолчанию",[525,535,536],{},"Типичное устройство",[538,539,540,559,577],"tbody",{},[522,541,542,548,553,556],{},[543,544,545],"td",{},[186,546,547],{},"ScreenSize.small",[543,549,550],{},[186,551,552],{},"shortestSide \u003C= smallBreakpoint",[543,554,555],{},"0–400 логических пикселей",[543,557,558],{},"Телефоны",[522,560,561,566,571,574],{},[543,562,563],{},[186,564,565],{},"ScreenSize.medium",[543,567,568],{},[186,569,570],{},"shortestSide \u003C= mediumBreakpoint",[543,572,573],{},"401–600 логических пикселей",[543,575,576],{},"Небольшие планшеты",[522,578,579,584,590,593],{},[543,580,581],{},[186,582,583],{},"ScreenSize.large",[543,585,586,587],{},"больше ",[186,588,589],{},"mediumBreakpoint",[543,591,592],{},"601+ логических пикселей",[543,594,595],{},"Большие планшеты, десктоп",[179,597,598,599,601,602,605],{},"Брейкпойнты разрешаются в фиксированном порядке — побеждает первое заданное значение: аргументы ",[186,600,513],{},", затем ближайший ",[186,603,604],{},"BreakpointsSetting"," выше по дереву, затем встроенные 400 и 600.",[174,607,609],{"id":608},"сценарии-использования","Сценарии использования",[611,612,614],"h3",{"id":613},"от-телефона-к-планшету","От телефона к планшету",[179,616,617,618,620,621,623],{},"Приложение, начинавшееся с телефонов, может сохранить все существующие экраны и добавить ",[186,619,457],{}," там, где широкая вёрстка действительно нужна. Экраны без билдера для нужного размера рисуют ",[186,622,231],{}," без изменений, поэтому переход идёт экран за экраном, а не разом.",[611,625,627],{"id":626},"навигация-список-детали","Навигация «список — детали»",[179,629,630],{},"Список, который на телефоне открывает детальный экран отдельным маршрутом, на планшете может показывать список и детали рядом. Оба варианта объявлены в одном виджете, так что разницу между ними видно прямо в диффе.",[611,632,634],{"id":633},"дизайн-системы-со-своими-брейкпойнтами","Дизайн-системы со своими брейкпойнтами",[179,636,637,638,641,642,644,645,647],{},"Оберните ",[186,639,640],{},"MaterialApp"," в ",[186,643,604],{}," один раз — и каждый ",[186,646,227],{}," в дереве возьмёт числа дизайн-системы. Позже поменять их — это правка одной строки, а не поиск по всему проекту.",[611,649,651],{"id":650},"окна-на-web-и-десктопе","Окна на web и десктопе",[179,653,654],{},"На web и десктопе измеряется размер окна, поэтому вёрстка реагирует на изменение его размера: тот же код, что обслуживает планшет, обслуживает и уменьшенное окно браузера.",[174,656,658],{"id":657},"с-чего-начать","С чего начать",[179,660,661],{},"Установите пакет:",[245,663,667],{"className":664,"code":665,"language":666,"meta":249,"style":249},"language-shell shiki shiki-themes material-theme-lighter github-light github-dark","flutter pub add flutter_adaptive_layout\n","shell",[186,668,669],{"__ignoreMap":249},[253,670,671,674,678,681],{"class":255,"line":256},[253,672,41],{"class":673},"sbgvK",[253,675,677],{"class":676},"s_sjI"," pub",[253,679,680],{"class":676}," add",[253,682,683],{"class":676}," flutter_adaptive_layout\n",[179,685,686,687,690],{},"Требуется Dart ",[186,688,689],{},">=2.18.5 \u003C4.0.0"," и Flutter 1.17+.",[179,692,693],{},"Импортируйте его и оберните виджет, вёрстка которого должна адаптироваться:",[245,695,697],{"className":247,"code":696,"language":18,"meta":249,"style":249},"import 'package:flutter_adaptive_layout\u002Fflutter_adaptive_layout.dart';\n\nAdaptiveLayout(\n  largeBuilder: (context, child) => Center(\n    child: SizedBox(width: 600, child: child),\n  ),\n  child: const ArticleView(), \u002F\u002F на маленьких и средних экранах — как есть\n)\n",[186,698,699,711,717,723,741,770,776,795],{"__ignoreMap":249},[253,700,701,705,708],{"class":255,"line":256},[253,702,704],{"class":703},"sw1J6","import",[253,706,707],{"class":676}," 'package:flutter_adaptive_layout\u002Fflutter_adaptive_layout.dart'",[253,709,710],{"class":279},";\n",[253,712,713],{"class":255,"line":266},[253,714,716],{"emptyLinePlaceholder":715},true,"\n",[253,718,719,721],{"class":255,"line":298},[253,720,227],{"class":259},[253,722,263],{"class":262},[253,724,725,727,729,731,733,735,737,739],{"class":255,"line":327},[253,726,330],{"class":262},[253,728,273],{"class":272},[253,730,276],{"class":262},[253,732,280],{"class":279},[253,734,283],{"class":262},[253,736,286],{"class":272},[253,738,314],{"class":259},[253,740,263],{"class":262},[253,742,743,746,748,751,754,756,760,762,764,766,768],{"class":255,"line":348},[253,744,745],{"class":262},"    child",[253,747,273],{"class":272},[253,749,750],{"class":259}," SizedBox",[253,752,753],{"class":262},"(width",[253,755,273],{"class":272},[253,757,759],{"class":758},"srdBf"," 600",[253,761,280],{"class":279},[253,763,289],{"class":262},[253,765,273],{"class":272},[253,767,322],{"class":262},[253,769,295],{"class":279},[253,771,772,774],{"class":255,"line":387},[253,773,390],{"class":262},[253,775,295],{"class":279},[253,777,778,780,782,784,787,789,791],{"class":255,"line":395},[253,779,398],{"class":262},[253,781,273],{"class":272},[253,783,403],{"class":359},[253,785,786],{"class":259}," ArticleView",[253,788,366],{"class":262},[253,790,280],{"class":279},[253,792,794],{"class":793},"sutJx"," \u002F\u002F на маленьких и средних экранах — как есть\n",[253,796,797],{"class":255,"line":413},[253,798,416],{"class":262},[179,800,801,802,804,805,273],{},"Чтобы классифицировать экраны иначе, реализуйте ",[186,803,494],{}," и передайте его в ",[186,806,807],{},"qualifier",[245,809,811],{"className":247,"code":810,"language":18,"meta":249,"style":249},"class WidthQualifier extends ScreenSizeQualifier {\n  @override\n  ScreenSize qualify(BuildContext context) {\n    final width = MediaQuery.of(context).size.width;\n    if (width \u003C 600) return ScreenSize.small;\n    if (width \u003C 1024) return ScreenSize.medium;\n    return ScreenSize.large;\n  }\n}\n",[186,812,813,830,835,853,888,918,942,956,961],{"__ignoreMap":249},[253,814,815,818,821,824,827],{"class":255,"line":256},[253,816,817],{"class":272},"class",[253,819,820],{"class":259}," WidthQualifier",[253,822,823],{"class":272}," extends",[253,825,826],{"class":259}," ScreenSizeQualifier",[253,828,829],{"class":262}," {\n",[253,831,832],{"class":255,"line":266},[253,833,834],{"class":359},"  @override\n",[253,836,837,840,844,847,850],{"class":255,"line":298},[253,838,839],{"class":259},"  ScreenSize",[253,841,843],{"class":842},"sGLFI"," qualify",[253,845,846],{"class":262},"(",[253,848,849],{"class":259},"BuildContext",[253,851,852],{"class":262}," context) {\n",[253,854,855,858,861,864,867,870,873,876,878,881,883,886],{"class":255,"line":327},[253,856,857],{"class":359},"    final",[253,859,860],{"class":262}," width ",[253,862,863],{"class":272},"=",[253,865,866],{"class":259}," MediaQuery",[253,868,869],{"class":279},".",[253,871,872],{"class":842},"of",[253,874,875],{"class":262},"(context)",[253,877,869],{"class":279},[253,879,880],{"class":262},"size",[253,882,869],{"class":279},[253,884,885],{"class":262},"width",[253,887,710],{"class":279},[253,889,890,894,897,900,902,905,908,911,913,916],{"class":255,"line":348},[253,891,893],{"class":892},"sVHd0","    if",[253,895,896],{"class":262}," (width ",[253,898,899],{"class":272},"\u003C",[253,901,759],{"class":758},[253,903,904],{"class":262},") ",[253,906,907],{"class":892},"return",[253,909,910],{"class":259}," ScreenSize",[253,912,869],{"class":279},[253,914,915],{"class":262},"small",[253,917,710],{"class":279},[253,919,920,922,924,926,929,931,933,935,937,940],{"class":255,"line":387},[253,921,893],{"class":892},[253,923,896],{"class":262},[253,925,899],{"class":272},[253,927,928],{"class":758}," 1024",[253,930,904],{"class":262},[253,932,907],{"class":892},[253,934,910],{"class":259},[253,936,869],{"class":279},[253,938,939],{"class":262},"medium",[253,941,710],{"class":279},[253,943,944,947,949,951,954],{"class":255,"line":395},[253,945,946],{"class":892},"    return",[253,948,910],{"class":259},[253,950,869],{"class":279},[253,952,953],{"class":262},"large",[253,955,710],{"class":279},[253,957,958],{"class":255,"line":413},[253,959,960],{"class":262},"  }\n",[253,962,964],{"class":255,"line":963},9,[253,965,966],{"class":262},"}\n",[179,968,969,972],{},[186,970,971],{},"qualify"," вызывается при каждой перестройке, поэтому он должен быть дешёвым и без побочных эффектов.",[179,974,975,976,869],{},"Полноценное рабочее приложение лежит в ",[977,978,982],"a",{"href":979,"rel":980},"https:\u002F\u002Fgithub.com\u002Fnerdy-pro\u002Fflutter-adaptive-layout\u002Ftree\u002Fmain\u002Fexample",[981],"nofollow","каталоге example",[984,985],"questions",{":items":986},"[{\"title\":\"Чем это отличается от LayoutBuilder?\",\"text\":\"LayoutBuilder сообщает ограничения, переданные родительским виджетом, — они могут быть намного меньше экрана. AdaptiveLayout классифицирует устройство или окно через MediaQuery, поэтому виджет глубоко внутри дерева всё равно знает, что работает на планшете. LayoutBuilder нужен, чтобы вписаться в доступную область; AdaptiveLayout — чтобы выбрать вёрстку под устройство.\"},{\"title\":\"Меняется ли вёрстка при повороте устройства?\",\"text\":\"Нет. Классификация использует MediaQuery.of(context).size.shortestSide — эта величина одинакова в книжной и альбомной ориентации, поэтому телефон остаётся ScreenSize.small при повороте. Если вёрстка должна зависеть от ориентации, реализуйте свой ScreenSizeQualifier, который читает size.width.\"},{\"title\":\"Обязательно ли задавать все три билдера?\",\"text\":\"Нет. Любой билдер можно не указывать — для этого размера экрана будет использован child. Если нет ни подходящего билдера, ни child, будет выброшен UnimplementedError.\"},{\"title\":\"Как изменить брейкпойнты по умолчанию?\",\"text\":\"Либо передайте BreakpointsQualifier(smallBreakpoint: ..., mediumBreakpoint: ...) в конкретный AdaptiveLayout, либо оберните приложение в BreakpointsSetting, чтобы поменять их везде. Значения из конструктора важнее BreakpointsSetting, а он важнее значений по умолчанию — 400 и 600.\"},{\"title\":\"Работает ли пакет на web и десктопе?\",\"text\":\"Да. Он зависит только от MediaQuery, поэтому работает на всех платформах Flutter. На web и десктопе измеряется размер окна, так что вёрстка реагирует на его изменение.\"}]",[988,989,990],"style",{},"html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sw1J6, html code.shiki .sw1J6{--shiki-light:#F76D47;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}",{"title":249,"searchDepth":266,"depth":266,"links":992},[993,994,995,996,997,1003],{"id":176,"depth":266,"text":177},{"id":221,"depth":266,"text":222},{"id":436,"depth":266,"text":437},{"id":507,"depth":266,"text":508},{"id":608,"depth":266,"text":609,"children":998},[999,1000,1001,1002],{"id":613,"depth":298,"text":614},{"id":626,"depth":298,"text":627},{"id":633,"depth":298,"text":634},{"id":650,"depth":298,"text":651},{"id":657,"depth":266,"text":658},"Flutter-пакет, который строит разную вёрстку для маленьких, средних и больших экранов — с настраиваемыми брейкпойнтами и без зависимостей, кроме MediaQuery.","md",[1007],{"type":1008,"url":1009},"pub_dev","https:\u002F\u002Fpub.dev\u002Fpackages\u002Fflutter_adaptive_layout",{},"Flutter-пакет, который рисует разную вёрстку на телефонах, планшетах и десктопе. Один билдер на размер экрана, свои брейкпойнты, без зависимостей.","Адаптивная вёрстка во Flutter по размеру экрана","Один билдер на размер экрана вместо проверок ширины, разбросанных по дереву виджетов. Небольшой Flutter-пакет для адаптивного интерфейса.","\u002Fopen-source\u002Fflutter-adaptive-layout",{"type":1016,"company":1017,"name":1018},"github","nerdy-pro","flutter-adaptive-layout",{"title":169,"description":1004},"open-source\u002Fflutter-adaptive-layout",[41,18],"2026-08-15","tR9_0QhJrsv6VXf1amgd3W9GeBLQIJh2jIfvbl7Ilfc",{"forks":1025,"stars":256,"contributors":1026,"last_release":1032,"issues":1025,"has_contribution_guide":1033,"license":1034,"last_updated":1035,"watchers":256},0,[1027],{"login":1028,"profile_url":1029,"avatar_url":1030,"contributions":1031},"thenixan","https:\u002F\u002Fgithub.com\u002Fthenixan","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F3602098?v=4",26,"v1.1.0",false,"MIT","2026-08-13T19:18:10Z"]