Irbisa · Шпаргалка
Адаптивный интерфейс
Брейкпоинты, LayoutBuilder, NavigationRail, складные экраны, ориентация, SafeArea, масштаб текста
Чем отличается responsive UI от adaptive UI во Flutter?
Чем отличается
MediaQueryотLayoutBuilder, и что из них должно определять раскладку виджета?class ProductCard extends StatelessWidget { const ProductCard({super.key, required this.product}); final Product product; @override…Какие брейкпоинты вы используете, чтобы переключать экран между раскладками телефона, планшета и десктопа, и где должна стоять эта проверка?
enum WindowSize { compact, medium, expanded, large, extraLarge } WindowSize windowSizeOf(double width) => switch (width) { < 600 => WindowSize.compact, < 840 => WindowSize.medium, < 1200 => WindowSize.expanded,…Как обрабатывать ориентацию во Flutter:
MediaQuery.orientationOf,OrientationBuilderи блокировка черезSystemChrome?Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); // a hint: large Android screens and iPad windows may ignore it await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); runApp(const App());}…Коллега масштабирует все размеры по ширине экрана через
flutter_screenutil. Почему многие команды так не делают и что используют вместо этого?// wrong: every size proportional to a 375 x 812 design frame// SizedBox(// width: 343.w, height: 48.h, // 763 x 71 dp on an 834 dp wide iPad// child: FilledButton(onPressed: pay, child: Text('Pay', style: TextStyle(fontSize: 16.sp))),// )…Открытие клавиатуры перестраивает виджеты, рядом с которыми нет ни одного текстового поля. При чём тут
MediaQuery.of(context)?На iPhone отступ 34 px под этим
SendBarпропадает, как только открывается клавиатура. Какой отступ читаетSafeArea, и баг ли это?Scaffold( body: Column( children: [ const Expanded(child: MessageList()), SafeArea( top: false, child: SendBar(controller: _controller), ), ], ),);Найдите баг: этот экран входа выглядит нормально, пока не откроется клавиатура, а затем внизу появляется жёлто-чёрная полоса переполнения.
class SignInScreen extends StatelessWidget { const SignInScreen({super.key}); @override Widget build(BuildContext context) { final height = MediaQuery.sizeOf(context).height; return Scaffold( appBar: AppBar(title: const Text('Sign in')), body: Column(children: [ SizedBox(height: height * 0.4, child: const Center(child: FlutterLogo(size: 96))), const SignInForm(), // two fields and a button, about 200 px ]), ); }}Пользователь выставил системный размер шрифта 200%, и ваша самодельная нижняя панель превратила подписи в три точки. Что даёт
MediaQuery.textScalerOf, и где ограничение масштаба оправдано?final scaler = MediaQuery.textScalerOf(context);final large = scaler.scale(14) > 20; // the real painted size, not 14 * factor // right: reflow instead of truncating at 200%final userRow = large ? Column(…Эта сетка шириной 1000 px. Сколько колонок она построит, какой ширины будет каждая карточка, и что изменится в боковой панели шириной 400 px?
GridView.builder( gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 240, crossAxisSpacing: 16, mainAxisSpacing: 16, childAspectRatio: 0.75, ), itemCount: products.length, itemBuilder: (context, i) => ProductCard(product: products[i]),);Ряд карточек обёрнут в
IntrinsicHeight, чтобы они были одной высоты с самой высокой, и список начал подтормаживать. Во что обходится этот виджет?Найдите баг:
StatCardправильно раскладывается в сетке, но горизонтальная лента из тех же карточек на дашборде падает с ошибкой layout.class StatCard extends StatelessWidget { const StatCard({super.key, required this.stat}); final Stat stat; @override Widget build(BuildContext context) => LayoutBuilder( builder: (context, c) => c.maxWidth >= 240 ? Row(children: [Expanded(child: Text(stat.label)), Text(stat.value)]) : Column(children: [Text(stat.label), Text(stat.value)]), );}// dashboard: a strip of cards that scrolls sidewaysWidget statStrip(List<Stat> stats) => ListView( scrollDirection: Axis.horizontal, children: [for (final s in stats) StatCard(stat: s)], );При растягивании окна десктопа с ширины телефона на весь экран сбрасывается открытая вкладка. Как подключить нижнюю панель и navigation rail к одному роутеру?
const destinations = [ (icon: Icons.inbox, label: 'Inbox'), (icon: Icons.send, label: 'Sent'),]; // builder of StatefulShellRoute.indexedStack…Сборка под iOS должна ощущаться нативной. Сколько дают конструкторы
.adaptiveво Flutter, и что придётся ветвить руками?// Controls: the adaptive constructor is the whole changeSwitch.adaptive(value: enabled, onChanged: onToggle); // CupertinoSwitch on Apple showAdaptiveDialog<void>( context: context, builder: (ctx) => AlertDialog.adaptive(…Тот же экран на Flutter теперь едет на десктоп и в web. Что нужно поменять ради мыши и клавиатуры, кроме более широкой раскладки?
class ContactRow extends StatelessWidget { const ContactRow({super.key, required this.contact}); final Contact contact; @override Widget build(BuildContext context) {…Входящие представляют собой список на телефоне и список плюс письмо на планшете. После поворота в две панели жест «назад» снимает невидимый роут. Как это моделировать?
class _InboxScreenState extends State<InboxScreen> { String? _selectedId; // lives ABOVE the breakpoint final _navKey = GlobalKey<NavigatorState>(); void _select(String? id) => setState(() => _selectedId = id);…Пользователь на десктопе сужает это окно: список прыгает наверх, выбор пропадает. Что происходит с состоянием, когда ширина пересекает 840?
class OrdersScreen extends StatelessWidget { const OrdersScreen({super.key}); @override Widget build(BuildContext context) { return LayoutBuilder( builder: (context, c) => c.maxWidth >= 840 ? const WideOrders() // StatefulWidget: list + detail, owns _selectedId : const NarrowOrders(), // StatefulWidget: list only, owns _selectedId ); }}Приложение перетащили в split screen на Android, а на складном устройстве половина формы оказалась под шарниром. Что Flutter сообщает об этом железе?
class HingeAwarePanes extends StatelessWidget { const HingeAwarePanes({super.key, required this.start, required this.end}); final Widget start; final Widget end; @override…Дизайн должен держаться на четырёх ширинах и при масштабе текста 200%. Как это протестировать, не поддерживая сорок golden-файлов?
const sizes = [Size(390, 844), Size(673, 841), Size(1024, 768), Size(1440, 900)]; void main() { for (final size in sizes) { for (final scale in const [1.0, 2.0]) { testWidgets('orders at ${size.width.toInt()} dp, ${scale}x text', (tester) async {…Телефон, планшет и десктоп из одной кодовой базы Flutter: писать три дерева виджетов или одно дерево, полное условий по ширине?