Як зробити додаток Flutter чуйним відповідно до різного розміру екрану?


85

Я стикаюся з труднощами, щоб зробити його адаптивним відповідно до різних розмірів екрану. Як зробити його чуйним?

@override
       Widget build(BuildContext context) {
       return new Container(
       decoration: new BoxDecoration(color: Colors.white),
       child: new Stack(
        children: [
          new Padding(
            padding: const EdgeInsets.only(bottom: 350.0),
            child: new GradientAppBar(" "),
          ),
          new Positioned(
            bottom: 150.0,
            height: 260.0,
            left: 10.0,
            right: 10.0,
            child: new Padding(
              padding: new EdgeInsets.all(10.0),
              child: new Card(
                child: new Column(
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    const ListTile(
                      title: const Text(
                        'LOGIN',
                        textAlign: TextAlign.center,
                        style: const TextStyle(
                          fontSize: 16.50,
                          fontFamily: "Helvetica",
                          fontWeight: FontWeight.bold,
                          color: Colors.black87,
                          letterSpacing: 1.00,
                        ),
                      ),
                    ),
                    new ListTile(
                      leading: const Icon(Icons.person),
                      title: new TextField(
                        controller: _user1,
                        decoration: new InputDecoration(
                            labelText: '     Enter a username'),
                      ),
                    ),
                    new ListTile(
                      leading: const Icon(Icons.person_pin),
                      title: new TextField(
                        controller: _pass1,
                        decoration: new InputDecoration(
                            labelText: '     Enter a password'),
                        obscureText: true,
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ),
          new Positioned(
            bottom: 70.0,
            left: 15.0,
            right: 05.0,
            child: new ButtonTheme.bar(
            // make buttons use the appropriate styles for cards
              child: new ButtonBar(
                children: <Widget>[
                  new FlatButton(
                    padding: new EdgeInsets.only(right: 13.0),
                    child: new Text(
                      'REGISTER HERE',
                      style: new TextStyle(
                          color: Colors.black87,
                          fontFamily: "Helvetica",
                          fontSize: 15.00,
                          fontWeight: FontWeight.bold),
                    ),
                    onPressed: () {
                      Navigator.of(context).pushNamed('/facebook');
                    },
                  ),
                  new FlatButton(
                    padding: new EdgeInsets.only(right: 22.0),
                    child: new Text(
                      'FORGOT PASSWORD?',
                      style: new TextStyle(
                          color: Colors.black87,
                          fontFamily: "Helvetica",
                          fontSize: 15.00,
                          fontWeight: FontWeight.bold),
                    ),
                    onPressed: () {
                      Navigator.of(context).pushNamed('/Forgot');
                    },
                  ),
                ],
              ),
            ),
          ),
          new Positioned(
            bottom: 73.0,
            height: 180.0,
            left: 20.0,
            right: 52.0,
            child: new Padding(
              padding: new EdgeInsets.all(0.00),
              child: new ButtonTheme(
                minWidth: 10.0,
                height: 20.0,
                padding: new EdgeInsets.only(right: 37.0),
                child: new ButtonBar(children: <Widget>[
                  new CupertinoButton(
                      borderRadius:
                          const BorderRadius.all(const Radius.circular(36.0)),
                      padding: new EdgeInsets.only(left: 70.0),
                      color: const Color(0xFF426DB7),
                      child: new Text(
                        "     LOGIN                            ",
                        style: new TextStyle(
                            color: Colors.white,
                            fontSize: 12.50,
                            fontFamily: "Handwriting",
                            fontWeight: FontWeight.w500,
                            letterSpacing: 0.00),
                      ),
                      onPressed: () {})
                ]),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

Відповіді:


72

Використання MediaQueryкласу:

MediaQueryData queryData;
queryData = MediaQuery.of(context);

MediaQuery : встановлює піддерево, в якому медіа-запити вирішують задані дані.

MediaQueryData : Інформація про фрагмент носія (наприклад, вікно).

Щоб отримати співвідношення пікселів пристрою:

queryData.devicePixelRatio

Щоб отримати ширину та висоту екрана пристрою:

queryData.size.width
queryData.size.height

Щоб отримати коефіцієнт масштабування тексту:

queryData.textScaleFactor

Використання AspectRatioкласу:

З документа:

Віджет, який намагається встановити розмір дитини до певного співвідношення сторін.

Віджет спочатку пробує найбільшу ширину, дозволену обмеженнями макета. Висота віджета визначається шляхом застосування заданого співвідношення сторін до ширини, вираженого як відношення ширини до висоти.

Наприклад, співвідношення сторін 16: 9 ширина: висота матиме значення 16,0 / 9,0. Якщо максимальна ширина нескінченна, початкова ширина визначається шляхом застосування пропорції до максимальної висоти.

Тепер розглянемо другий приклад, цього разу із співвідношенням сторін 2,0 та обмеженнями макета, які вимагають, щоб ширина була від 0,0 до 100,0, а висота - від 0,0 до 100,0. Ми виберемо ширину 100,0 (найбільша дозволена) та висоту 50,0 (щоб відповідати співвідношенню сторін).

//example
new Center(
 child: new AspectRatio(
  aspectRatio: 100 / 100,
  child: new Container(
    decoration: new BoxDecoration(
      shape: BoxShape.rectangle,
      color: Colors.orange,
      )
    ),
  ),
),

Також ви можете використовувати :


3
Я можу визначити ширину та висоту пристрою, як я можу встановити розмір тесту, відступ, поле queryData.
Фархана

29

Цей клас допоможе, а потім ініціалізує клас методом init.

import 'package:flutter/widgets.dart';

class SizeConfig {
  static MediaQueryData _mediaQueryData;
  static double screenWidth;
  static double screenHeight;
  static double blockSizeHorizontal;
  static double blockSizeVertical;
  static double _safeAreaHorizontal;
  static double _safeAreaVertical;
  static double safeBlockHorizontal;
  static double safeBlockVertical;

  void init(BuildContext context){
    _mediaQueryData = MediaQuery.of(context);
    screenWidth = _mediaQueryData.size.width;
    screenHeight = _mediaQueryData.size.height;
    blockSizeHorizontal = screenWidth/100;
    blockSizeVertical = screenHeight/100;
    _safeAreaHorizontal = _mediaQueryData.padding.left +
        _mediaQueryData.padding.right;
    _safeAreaVertical = _mediaQueryData.padding.top +
        _mediaQueryData.padding.bottom;
    safeBlockHorizontal = (screenWidth - _safeAreaHorizontal)/100;
    safeBlockVertical = (screenHeight - _safeAreaVertical)/100;
  }
}

тоді у своєму вимірі віджетів зробіть це

Widget build(BuildContext context) {
    SizeConfig().init(context);
    return Container(
    height: SizeConfig.safeBlockVertical * 10, //10 for example
    width: SizeConfig.safeBlockHorizontal * 10, //10 for example
    );}

Усі автори цього допису: https://medium.com/flutter-community/flutter-effectively-scale-ui-according-to-different-screen-sizes-2cb7c115ea0a


Як додати EdgeInsets знизу за допомогою цього класу SizeConfig?
Farwa

Я думаю, як заповнення контейнера буде працювати. Спробуйте і скажіть мені допомогти вам !!

16

Що я роблю, так це взяти ширину та висоту екрана та розрахувати з нього сітку 100 * 100, щоб розташувати та масштабувати речі та зберегти її як статичні змінні, які можна використовувати повторно. Працює досить добре в більшості випадків. Подобається це:

AppConfig.width = MediaQuery.of(context).size.width;
AppConfig.height = MediaQuery.of(context).size.height;
AppConfig.blockSize = AppConfig.width / 100;
AppConfig.blockSizeVertical = AppConfig.height / 100;

Тоді я масштабую все відповідно до цих значень, наприклад:

double elementWidth = AppConfig.blockSize * 10.0;   // 10% of the screen width

або

double fontSize = AppConfig.blockSize * 1.2;

Іноді безпечна зона (виїмка тощо) вбиває макет, тому ви можете врахувати і це:

AppConfig.safeAreaHorizontal = MediaQuery.of(context).padding.left +
    MediaQuery.of(context).padding.right;

double screenWidthWithoutSafeArea = AppConfig.width - AppConfig.safeAreaHorizontal;

Це чудово працювало на деяких останніх проектах.


1
Як розрахувати розміри шрифтів? Чи добре обчислювати на основі ширини або висоти?
Харш

Я обчислюю їх на основі ширини. Але, чесно кажучи, я не пробував із програмами, що підтримують як альбомний, так і портретний режим. Але ви все одно зможете розрахувати це по-різному за обома орієнтаціями.
datayeah

Як це точно вирішує проблему різниці щільності екрану? Вимовляючи, що ви ділите екран на блоки розміром 100 * 100 сітки, ви робите звук, ніби отримані блоки мають однаковий розмір (тобто квадрати), але вони не такі. Якщо у вас є пристрій, який має подвійну кількість пікселів по вертикалі (висота екрана), ніж по горизонталі (ширина екрану), ваші результуючі блоки будуть прямокутниками, а не квадратами - це означає, що ваш код все одно створює ту саму проблему, яку ви почали намагатися вирішити. Перевірте свій код на декількох щільностях екрану, щоб довести це. Тож це насправді не є рішенням для мене.
SilSur

@SilSur, впевнений, що блоки не мають однакового розміру на будь-якому пристрої та щільності, але саме це змушує його працювати (у більшості випадків). я просто повинен визначитися з кожним віджетом, який я додаю на екран, чи хочу я, щоб його положення та розмір обчислювались щодо ширини або висоти блоку - або обох. Я використовував цей метод у додатках, які працюють на будь-якому iphone, ipad чи телефоні / планшеті Android без спеціальних корекцій для пристрою. пейзаж і портрет. але ви праві, що цей метод все ще не ідеально вирішує складні проблеми інтерфейсу. я все ще шукаю щось краще, щоб впоратися з цим.
datayeah

9

Перевірити MediaQueryклас

Наприклад, щоб дізнатися розмір поточних середовищ (наприклад, вікно , що містить додаток), ви можете прочитати MediaQueryData.sizeвластивість з MediaQueryDataповертається MediaQuery.of: MediaQuery.of(context).size.

Отже, ви можете зробити наступне:

 new Container(
                      height: MediaQuery.of(context).size.height/2,
..            )

ви маєте на увазі замість позиціонування використовувати mediaQuery ??
praveen Dp

Я не розумію, що ти намагаєшся робити
aziza

з використанням розміщеного, заповнення всередині стека. я пристосував його до розміру екрану. тепер, щоб він реагував, я повинен використовувати медіазапити замість чого?
praveen Dp

5

Ви можете взяти відсоток ширини або висоти як вхідні дані для розміру шкали.

fontSize: MediaQuery.of(_ctxt).size.height * 0.065

Де множник в кінці має значення, завдяки якому Текст добре виглядає для активного емулятора.

Нижче наведено, як я це налаштував, щоб усі масштабовані розміри були централізовані в одному місці. Таким чином, ви можете легко і швидко їх налаштувати за допомогою Hot Reload без необхідності шукати Media.of()дзвінки по коду.

  1. Створіть файл, щоб зберегти всі відображення appScale.dart

    class AppScale {
      BuildContext _ctxt;
    
      AppScale(this._ctxt);
    
      double get labelDim => scaledWidth(.04);
      double get popupMenuButton => scaledHeight(.065); 

      double scaledWidth(double widthScale) {
        return MediaQuery.of(_ctxt).size.width * widthScale;
      }
    
      double scaledHeight(double heightScale) {
        return MediaQuery.of(_ctxt).size.height * heightScale;
      }
    }

  1. Тоді вкажіть, де де-небудь вам потрібно масштабоване значення

    AppScale _scale = AppScale(context);

    // ... 

    Widget label1 = Text(
      "Some Label",
      style: TextStyle(fontSize: _scale.labelDim),
    );

Завдяки відповідям у цьому дописі


4

Я трохи важко стукав тут рішення інших людей (@datayeah & Vithani Ravi), тому подумав поділитися власною спробою вирішити цю проблему масштабування змінної щільності екрану або замовкнути. Тож я підходжу до цієї проблеми з твердої / фіксованої основи: я базую все своє масштабування на фіксованому (незмінному) співвідношенні 2: 1 (висота: ширина). У мене є допоміжний клас "McGyver", який виконує всі важкі дії (і корисний фінінг коду) у моєму додатку. Цей клас "McGyver" містить лише статичні методи та статичні постійні члени класу.

СПОСІБ МАТЕРІАЛЬНОГО МАШУВАННЯ: Я масштабую як ширину, так і висоту незалежно від співвідношення сторін 2: 1. Я беру вхідні значення ширини та висоти і ділю їх кожне на константи ширини та висоти і, нарешті, обчислюю коефіцієнт коригування, за допомогою якого можна масштабувати відповідні вхідні значення ширини та висоти. Фактичний код виглядає так:

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

class McGyver {

  static const double _fixedWidth = 410;    // Set to an Aspect Ratio of 2:1 (height:width)
  static const double _fixedHeight = 820;   // Set to an Aspect Ratio of 2:1 (height:width) 

  // Useful rounding method (@andyw solution -> /programming/28419255/how-do-you-round-a-double-in-dart-to-a-given-degree-of-precision-after-the-decim/53500405#53500405)
  static double roundToDecimals(double val, int decimalPlaces){
    double mod = pow(10.0, decimalPlaces);
    return ((val * mod).round().toDouble() / mod);
  }

  // The 'Ratio-Scaled' Widget method (takes any generic widget and returns a "Ratio-Scaled Widget" - "rsWidget")
  static Widget rsWidget(BuildContext ctx, Widget inWidget, double percWidth, double percHeight) {

    // ---------------------------------------------------------------------------------------------- //
    // INFO: Ratio-Scaled "SizedBox" Widget - Scaling based on device's height & width at 2:1 ratio.  //
    // ---------------------------------------------------------------------------------------------- //

    final int _decPlaces = 5;
    final double _fixedWidth = McGyver._fixedWidth;
    final double _fixedHeight = McGyver._fixedHeight;

    Size _scrnSize = MediaQuery.of(ctx).size;                // Extracts Device Screen Parameters.
    double _scrnWidth = _scrnSize.width.floorToDouble();     // Extracts Device Screen maximum width.
    double _scrnHeight = _scrnSize.height.floorToDouble();   // Extracts Device Screen maximum height.

    double _rsWidth = 0;
    if (_scrnWidth == _fixedWidth) {   // If input width matches fixedWidth then do normal scaling.
      _rsWidth = McGyver.roundToDecimals((_scrnWidth * (percWidth / 100)), _decPlaces);
    } else {   // If input width !match fixedWidth then do adjustment factor scaling.
      double _scaleRatioWidth = McGyver.roundToDecimals((_scrnWidth / _fixedWidth), _decPlaces);
      double _scalerWidth = ((percWidth + log(percWidth + 1)) * pow(1, _scaleRatioWidth)) / 100;
      _rsWidth = McGyver.roundToDecimals((_scrnWidth * _scalerWidth), _decPlaces);
    }

    double _rsHeight = 0;
    if (_scrnHeight == _fixedHeight) {   // If input height matches fixedHeight then do normal scaling.
      _rsHeight = McGyver.roundToDecimals((_scrnHeight * (percHeight / 100)), _decPlaces);
    } else {   // If input height !match fixedHeight then do adjustment factor scaling.
      double _scaleRatioHeight = McGyver.roundToDecimals((_scrnHeight / _fixedHeight), _decPlaces);
      double _scalerHeight = ((percHeight + log(percHeight + 1)) * pow(1, _scaleRatioHeight)) / 100;
      _rsHeight = McGyver.roundToDecimals((_scrnHeight * _scalerHeight), _decPlaces);
    }

    // Finally, hand over Ratio-Scaled "SizedBox" widget to method call.
    return SizedBox(
      width: _rsWidth,
      height: _rsHeight,
      child: inWidget,
    );
  }

}

... ... ...

Тоді ви окремо масштабуєте свої віджети (що для моєї хвороби перфекціонізму - ВСІЙ мій інтерфейс) простим статичним викликом методу "rsWidget ()" таким чином:

  // Step 1: Define your widget however you like (this widget will be supplied as the "inWidget" arg to the "rsWidget" method in Step 2)...
  Widget _btnLogin = RaisedButton(color: Colors.blue, elevation: 9.0, 
                                  shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(McGyver.rsDouble(context, ScaleType.width, 2.5))),
                                  child: McGyver.rsText(context, "LOGIN", percFontSize: EzdFonts.button2_5, textColor: Colors.white, fWeight: FontWeight.bold),
                                  onPressed: () { _onTapBtnLogin(_tecUsrId.text, _tecUsrPass.text); }, );

  // Step 2: Scale your widget by calling the static "rsWidget" method...
  McGyver.rsWidget(context, _btnLogin, 34.5, 10.0)   // ...and Bob's your uncle!!

Класна річ полягає в тому, що метод "rsWidget ()" повертає віджет !! Таким чином, ви можете або призначити масштабований віджет іншій змінній, наприклад, _rsBtnLoginдля використання всюди - або ви можете просто використовувати повний McGyver.rsWidget()виклик методу на місці всередині вашогоbuild() методу (саме так, як вам потрібно, щоб він був розміщений у дереві віджетів), і він буде працювати ідеально, як слід.

Для тих більш проникливих кодерів: ви зауважили, що я використав два додаткові методи, масштабовані за співвідношенням, McGyver.rsText()і McGyver.rsDouble()(не визначений у коді вище) у своєму RaisedButton()- тому я в основному божеволію від цього масштабування ... тому що я вимагаю, щоб мої програми були абсолютно піксельний ідеально підходить для будь-якого масштабу або щільності екрану !! Я пропорційно масштабую свої ints, double, padding, text (все, що вимагає узгодженості інтерфейсу користувача на різних пристроях). Я масштабую свої тексти лише на основі ширини, але вказую, яку вісь використовувати для всіх інших масштабів (як це було зроблено з ScaleType.widthпереліком, що використовується для McGyver.rsDouble()виклику у прикладі коду вище).

Я знаю, що це божевілля - і я маю багато роботи над основною темою - але я сподіваюся, що хтось побачить мою спробу тут і допоможе мені знайти краще (більш легке) рішення для масштабування моєї щільності екрану 1: 1 кошмари.


1
@ Abbas.M - Так, я зробив невелику зміну в рядку коду масштабування співвідношення [див. Оновлений код], і я вважаю, що це найближче до реального рішення масштабування співвідношення 1: 1 - я спробував досить багато варіантів отримати цей. З цим оновленим кодом все ще є кілька дивних проблем [масштабування] масштабування, але подібність інтерфейсу користувача на екранах з кількома щільностями справді переконлива - дуже тонка різниця, що спостерігається між екранами з оновленим кодом. Будь ласка, дайте мені знати, що ви думаєте - відгук дуже вдячний.
SilSur

очевидна річ щодо основного потоку, перемістіть init та виклики до основного блоку програми init, оскільки розмір екрану не зміниться після запуску програми, тому у вас лише один раз головний потік потрапляє на init програми, а не тобто кожен віджет відтворюється
Fred Гротт

@SilSur, твоє рішення виглядає дуже чудово. Чи не заперечуєте ви, щоб поділитися цілим класом Макгівера?
Девід

@David - Клас McGyver - це дуже важкий клас (і конкретний проект). Той, який я використав у цьому прикладі, має багато функцій, які не стосуються проблеми масштабування інтерфейсу користувача. Тож для мене це надмірно / неефективно завантажувати весь клас. Однак я трохи вдосконалив клас і опублікував іншу версію коду до іншого запитання SO . Можливо, ви можете оновити код масштабування за рядками вдосконаленого коду за вказаною URL-адресою.
SilSur

4

Після довгих досліджень та тестувань я розробив рішення для програми, яку зараз перетворюю з Android / iOS на Flutter.

В Android та iOS я використовував коефіцієнт масштабування, застосований до базових розмірів шрифтів, відображаючи розміри тексту, які відповідали розміру екрана.

Ця стаття була дуже корисною: https://medium.com/flutter-community/flutter-effectively-scale-ui-according-to-different-screen-sizes-2cb7c115ea0a

Я створив StategetWidget, щоб отримати розміри шрифтів друкарських стилів Material Design. Отримання розмірів пристрою за допомогою MediaQuery, обчислення коефіцієнта масштабування, а потім скидання розмірів тексту Material Design. Віджет можна використовувати для визначення власної теми дизайну матеріалів.

Емулятори, що використовуються:

  • Піксель С - планшет розміром 9,94 дюйма
  • Pixel 3 - 5,46 "телефон
  • iPhone 11 Pro Max - 5,8-дюймовий телефон

Зі стандартними розмірами шрифту

З масштабованими розмірами шрифту

set_app_theme.dart (віджет SetAppTheme)

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

class SetAppTheme extends StatelessWidget {

  final Widget child;

  SetAppTheme({this.child});

  @override
  Widget build(BuildContext context) {

    final _divisor = 400.0;

    final MediaQueryData _mediaQueryData = MediaQuery.of(context);

    final _screenWidth = _mediaQueryData.size.width;
    final _factorHorizontal = _screenWidth / _divisor;

    final _screenHeight = _mediaQueryData.size.height;
    final _factorVertical = _screenHeight / _divisor;

    final _textScalingFactor = min(_factorVertical, _factorHorizontal);

    final _safeAreaHorizontal = _mediaQueryData.padding.left + _mediaQueryData.padding.right;
    final _safeFactorHorizontal = (_screenWidth - _safeAreaHorizontal) / _divisor;

    final _safeAreaVertical = _mediaQueryData.padding.top + _mediaQueryData.padding.bottom;
    final _safeFactorVertical = (_screenHeight - _safeAreaVertical) / _divisor;

    final _safeAreaTextScalingFactor = min(_safeFactorHorizontal, _safeFactorHorizontal);

    print('Screen Scaling Values:' + '_screenWidth: $_screenWidth');
    print('Screen Scaling Values:' + '_factorHorizontal: $_factorHorizontal ');

    print('Screen Scaling Values:' + '_screenHeight: $_screenHeight');
    print('Screen Scaling Values:' + '_factorVertical: $_factorVertical ');

    print('_textScalingFactor: $_textScalingFactor ');

    print('Screen Scaling Values:' + '_safeAreaHorizontal: $_safeAreaHorizontal ');
    print('Screen Scaling Values:' + '_safeFactorHorizontal: $_safeFactorHorizontal ');

    print('Screen Scaling Values:' + '_safeAreaVertical: $_safeAreaVertical ');
    print('Screen Scaling Values:' + '_safeFactorVertical: $_safeFactorVertical ');

    print('_safeAreaTextScalingFactor: $_safeAreaTextScalingFactor ');

    print('Default Material Design Text Themes');
    print('display4: ${Theme.of(context).textTheme.display4}');
    print('display3: ${Theme.of(context).textTheme.display3}');
    print('display2: ${Theme.of(context).textTheme.display2}');
    print('display1: ${Theme.of(context).textTheme.display1}');
    print('headline: ${Theme.of(context).textTheme.headline}');
    print('title: ${Theme.of(context).textTheme.title}');
    print('subtitle: ${Theme.of(context).textTheme.subtitle}');
    print('body2: ${Theme.of(context).textTheme.body2}');
    print('body1: ${Theme.of(context).textTheme.body1}');
    print('caption: ${Theme.of(context).textTheme.caption}');
    print('button: ${Theme.of(context).textTheme.button}');

    TextScalingFactors _textScalingFactors = TextScalingFactors(
        display4ScaledSize: (Theme.of(context).textTheme.display4.fontSize * _safeAreaTextScalingFactor),
        display3ScaledSize: (Theme.of(context).textTheme.display3.fontSize * _safeAreaTextScalingFactor),
        display2ScaledSize: (Theme.of(context).textTheme.display2.fontSize * _safeAreaTextScalingFactor),
        display1ScaledSize: (Theme.of(context).textTheme.display1.fontSize * _safeAreaTextScalingFactor),
        headlineScaledSize: (Theme.of(context).textTheme.headline.fontSize * _safeAreaTextScalingFactor),
        titleScaledSize: (Theme.of(context).textTheme.title.fontSize * _safeAreaTextScalingFactor),
        subtitleScaledSize: (Theme.of(context).textTheme.subtitle.fontSize * _safeAreaTextScalingFactor),
        body2ScaledSize: (Theme.of(context).textTheme.body2.fontSize * _safeAreaTextScalingFactor),
        body1ScaledSize: (Theme.of(context).textTheme.body1.fontSize * _safeAreaTextScalingFactor),
        captionScaledSize: (Theme.of(context).textTheme.caption.fontSize * _safeAreaTextScalingFactor),
        buttonScaledSize: (Theme.of(context).textTheme.button.fontSize * _safeAreaTextScalingFactor));

    return Theme(
      child: child,
      data: _buildAppTheme(_textScalingFactors),
    );
  }
}

final ThemeData customTheme = ThemeData(
  primarySwatch: appColorSwatch,
  // fontFamily: x,
);

final MaterialColor appColorSwatch = MaterialColor(0xFF3787AD, appSwatchColors);

Map<int, Color> appSwatchColors =
{
  50  : Color(0xFFE3F5F8),
  100 : Color(0xFFB8E4ED),
  200 : Color(0xFF8DD3E3),
  300 : Color(0xFF6BC1D8),
  400 : Color(0xFF56B4D2),
  500 : Color(0xFF48A8CD),
  600 : Color(0xFF419ABF),
  700 : Color(0xFF3787AD),
  800 : Color(0xFF337799),
  900 : Color(0xFF285877),
};

_buildAppTheme (TextScalingFactors textScalingFactors) {

  return customTheme.copyWith(

    accentColor: appColorSwatch[300],
    buttonTheme: customTheme.buttonTheme.copyWith(buttonColor: Colors.grey[500],),
    cardColor: Colors.white,
    errorColor: Colors.red,
    inputDecorationTheme: InputDecorationTheme(border: OutlineInputBorder(),),
    primaryColor: appColorSwatch[700],
    primaryIconTheme: customTheme.iconTheme.copyWith(color: appColorSwatch),
    scaffoldBackgroundColor: Colors.grey[100],
    textSelectionColor: appColorSwatch[300],
    textTheme: _buildAppTextTheme(customTheme.textTheme, textScalingFactors),
    appBarTheme: customTheme.appBarTheme.copyWith(
        textTheme: _buildAppTextTheme(customTheme.textTheme, textScalingFactors)),

//    accentColorBrightness: ,
//    accentIconTheme: ,
//    accentTextTheme: ,
//    appBarTheme: ,
//    applyElevationOverlayColor: ,
//    backgroundColor: ,
//    bannerTheme: ,
//    bottomAppBarColor: ,
//    bottomAppBarTheme: ,
//    bottomSheetTheme: ,
//    brightness: ,
//    buttonBarTheme: ,
//    buttonColor: ,
//    canvasColor: ,
//    cardTheme: ,
//    chipTheme: ,
//    colorScheme: ,
//    cupertinoOverrideTheme: ,
//    cursorColor: ,
//    dialogBackgroundColor: ,
//    dialogTheme: ,
//    disabledColor: ,
//    dividerColor: ,
//    dividerTheme: ,
//    floatingActionButtonTheme: ,
//    focusColor: ,
//    highlightColor: ,
//    hintColor: ,
//    hoverColor: ,
//    iconTheme: ,
//    indicatorColor: ,
//    materialTapTargetSize: ,
//    pageTransitionsTheme: ,
//    platform: ,
//    popupMenuTheme: ,
//    primaryColorBrightness: ,
//    primaryColorDark: ,
//    primaryColorLight: ,
//    primaryTextTheme: ,
//    secondaryHeaderColor: ,
//    selectedRowColor: ,
//    sliderTheme: ,
//    snackBarTheme: ,
//    splashColor: ,
//    splashFactory: ,
//    tabBarTheme: ,
//    textSelectionHandleColor: ,
//    toggleableActiveColor: ,
//    toggleButtonsTheme: ,
//    tooltipTheme: ,
//    typography: ,
//    unselectedWidgetColor: ,
  );
}

class TextScalingFactors {

  final double display4ScaledSize;
  final double display3ScaledSize;
  final double display2ScaledSize;
  final double display1ScaledSize;
  final double headlineScaledSize;
  final double titleScaledSize;
  final double subtitleScaledSize;
  final double body2ScaledSize;
  final double body1ScaledSize;
  final double captionScaledSize;
  final double buttonScaledSize;

  TextScalingFactors({

    @required this.display4ScaledSize,
    @required this.display3ScaledSize,
    @required this.display2ScaledSize,
    @required this.display1ScaledSize,
    @required this.headlineScaledSize,
    @required this.titleScaledSize,
    @required this.subtitleScaledSize,
    @required this.body2ScaledSize,
    @required this.body1ScaledSize,
    @required this.captionScaledSize,
    @required this.buttonScaledSize
  });
}

TextTheme _buildAppTextTheme(

    TextTheme _customTextTheme,
    TextScalingFactors _scaledText) {

  return _customTextTheme.copyWith(

    display4: _customTextTheme.display4.copyWith(fontSize: _scaledText.display4ScaledSize),
    display3: _customTextTheme.display3.copyWith(fontSize: _scaledText.display3ScaledSize),
    display2: _customTextTheme.display2.copyWith(fontSize: _scaledText.display2ScaledSize),
    display1: _customTextTheme.display1.copyWith(fontSize: _scaledText.display1ScaledSize),
    headline: _customTextTheme.headline.copyWith(fontSize: _scaledText.headlineScaledSize),
    title: _customTextTheme.title.copyWith(fontSize: _scaledText.titleScaledSize),
    subtitle: _customTextTheme.subtitle.copyWith(fontSize: _scaledText.subtitleScaledSize),
    body2: _customTextTheme.body2.copyWith(fontSize: _scaledText.body2ScaledSize),
    body1: _customTextTheme.body1.copyWith(fontSize: _scaledText.body1ScaledSize),
    caption: _customTextTheme.caption.copyWith(fontSize: _scaledText.captionScaledSize),
    button: _customTextTheme.button.copyWith(fontSize: _scaledText.buttonScaledSize),

  ).apply(bodyColor: Colors.black);
}

main.dart (демонстраційний додаток)

import 'package:flutter/material.dart';
import 'package:scaling/set_app_theme.dart';


void main() => runApp(MyApp());


class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      home: SetAppTheme(child: HomePage()),
    );
  }
}


class HomePage extends StatelessWidget {

  final demoText = '0123456789';

  @override
  Widget build(BuildContext context) {

    return SafeArea(
      child: Scaffold(
        appBar: AppBar(
          title: Text('Text Scaling with SetAppTheme',
            style: TextStyle(color: Colors.white),),
        ),
        body: SingleChildScrollView(
          child: Center(
            child: Padding(
              padding: const EdgeInsets.all(8.0),
              child: Column(
                children: <Widget>[
                  Text(
                    demoText,
                    style: TextStyle(
                      fontSize: Theme.of(context).textTheme.display4.fontSize,
                    ),
                  ),
                  Text(
                    demoText,
                    style: TextStyle(
                      fontSize: Theme.of(context).textTheme.display3.fontSize,
                    ),
                  ),
                  Text(
                    demoText,
                    style: TextStyle(
                      fontSize: Theme.of(context).textTheme.display2.fontSize,
                    ),
                  ),
                  Text(
                    demoText,
                    style: TextStyle(
                      fontSize: Theme.of(context).textTheme.display1.fontSize,
                    ),
                  ),
                  Text(
                    demoText,
                    style: TextStyle(
                      fontSize: Theme.of(context).textTheme.headline.fontSize,
                    ),
                  ),
                  Text(
                    demoText,
                    style: TextStyle(
                      fontSize: Theme.of(context).textTheme.title.fontSize,
                    ),
                  ),
                  Text(
                    demoText,
                    style: TextStyle(
                      fontSize: Theme.of(context).textTheme.subtitle.fontSize,
                    ),
                  ),
                  Text(
                    demoText,
                    style: TextStyle(
                      fontSize: Theme.of(context).textTheme.body2.fontSize,
                    ),
                  ),
                  Text(
                    demoText,
                    style: TextStyle(
                      fontSize: Theme.of(context).textTheme.body1.fontSize,
                    ),
                  ),
                  Text(
                    demoText,
                    style: TextStyle(
                      fontSize: Theme.of(context).textTheme.caption.fontSize,
                    ),
                  ),
                  Text(
                    demoText,
                    style: TextStyle(
                      fontSize: Theme.of(context).textTheme.button.fontSize,
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

3
Place dependency in pubspec.yaml

flutter_responsive_screen: ^1.0.0

Function hp = Screen(MediaQuery.of(context).size).hp;
Function wp = Screen(MediaQuery.of(context).size).wp;

Example :
return Container(height: hp(27),weight: wp(27));

4
Можливо, пояснення того, що відбувається під капотом, було б чудовим наступного разу, коли ви опублікуєте "рішення"? У будь-якому випадку, я перевірив GitHub для цієї залежності. В основному це єдиний клас (з 16 рядками коду), який приймає введені значення ширини та висоти та масштабує їх на основі ширини та висоти екрану у відсотках. Це, по суті, те саме, що рішення @datayeah - різниця лише в тому, що це акуратно упаковано. Тут застосовуються ті самі проблеми, що й datayeah - зовсім не гарне рішення для масштабування 1: 1 на різних пристроях щільності екрану. Проблема щільності екрану НЕ РІШЕНА цим "рішенням".
SilSur

1

Ви можете використовувати MediaQuery для батьківського виміру або FractionallySizedBox як контейнери.


1

Мій підхід до проблеми подібний до того, як це зробив datayeah. У мене було багато кодованих значень ширини та висоти, і програма виглядала чудово на певному пристрої. Отже, я отримав висоту екрану пристрою і просто створив коефіцієнт для масштабування жорстко закодованих значень.

double heightFactor = MediaQuery.of(context).size.height/708

де 708 - висота конкретного пристрою.


1

Я намагаюся зробити це якомога простіше. спробуй. Я роблю адаптивну утиліту, яка має функцію getresponsivevalue, відповідальну за надання значення відповідно до розміру екрана, якщо ви не призначаєте значення для середнього екрана, великого екрану, альбомного режиму. За замовчуванням воно надає коротке значення екрана. теплий прийом для будь-яких запитів. Я хотів би вдосконалитися

class SampleView extends StatelessWidget {
@override
Widget build(BuildContext context) {
 return Center(
  child: Container(
    width: 200,
    height: 200,
    color: Responsive().getResponsiveValue(
        forLargeScreen: Colors.red,
        forMediumScreen: Colors.green,
        forShortScreen: Colors.yellow,
        forMobLandScapeMode: Colors.blue,
        context: context),
  ),
);

}}

 // utility class
          class Responsive {
            // function reponsible for providing value according to screensize
            getResponsiveValue(
                {dynamic forShortScreen,
                dynamic forMediumScreen,
                dynamic forLargeScreen,
                dynamic forMobLandScapeMode,
                BuildContext context}) {

              if (isLargeScreen(context)) {

                return forLargeScreen ?? forShortScreen;
              } else if (isMediumScreen(context)) {

                return forMediumScreen ?? forShortScreen;
              } 
           else if (isSmallScreen(context) && isLandScapeMode(context)) {

                return forMobLandScapeMode ?? forShortScreen;
              } else {
                return forShortScreen;
              }
            }
          
            isLandScapeMode(BuildContext context) {
              if (MediaQuery.of(context).orientation == Orientation.landscape) {
                return true;
              } else {
                return false;
              }
            }
          
            static bool isLargeScreen(BuildContext context) {
              return getWidth(context) > 1200;
            }
          
            static bool isSmallScreen(BuildContext context) {
              return getWidth(context) < 800;
            }
          
            static bool isMediumScreen(BuildContext context) {
              return getWidth(context) > 800 && getWidth(context) < 1200;
            }
          
            static double getWidth(BuildContext context) {
              return MediaQuery.of(context).size.width;
            }
          }

1

Найпростіший спосіб зробити адаптивний інтерфейс для різних розмірів екрану - Sizer плагін .

Зробіть адаптивний інтерфейс на будь-якому пристрої розміром екрану, а також планшеті. Перевірте цей плагін ⬇️
https://pub.dev/packages/sizer

.h  - for widget height
.w  - for widget width
.sp - for font size

Використовуйте .h, .w,.sp після значення , як це ⬇️

Приклад:

Container(
  height: 10.0.h,  //10% of screen height
  width: 80.0.w,   //80% of screen width
  child: Text('Sizer', style: TextStyle(fontSize: 12.0.sp)),
);

Я створив багато адаптивних програм за допомогою цього плагіна.


0

ознайомтесь із цією сторінкою з віртуальної вікі:

Створення адаптивних програм

Використовуйте клас LayoutBuilder: З його властивості конструктора ви отримуєте BoxConstraints. Вивчіть властивості обмеження, щоб вирішити, що відображати. Наприклад, якщо ваша maxWidth більша за вашу точку розриву ширини, поверніть об’єкт Scaffold із рядком, який має список ліворуч. Якщо воно вужче, поверніть об’єкт Ліси з шухлядою, що містить цей список. Ви також можете налаштувати дисплей на основі висоти пристрою, співвідношення сторін або деяких інших властивостей. Коли обмеження змінюються (наприклад, користувач обертає телефон або розміщує вашу програму в інтерфейсі плитки в Нуга), функція збірки повториться.


0

створити ім'я файлу (app_config.dart) в назві папки (responsive_screen) у папці lib:

import 'package:flutter/material.dart';

class AppConfig {
  BuildContext _context;
  double _height;
  double _width;
  double _heightPadding;
  double _widthPadding;

  AppConfig(this._context) {
    MediaQueryData _queryData = MediaQuery.of(_context);
    _height = _queryData.size.height / 100.0;
    _width = _queryData.size.width / 100.0;
    _heightPadding =
    _height - ((_queryData.padding.top + _queryData.padding.bottom) / 100.0);
    _widthPadding =
      _width - (_queryData.padding.left + _queryData.padding.right) / 100.0;
  }

  double rH(double v) {
   return _height * v;
  }

  double rW(double v) {
    return _width * v;
  }

  double rHP(double v) {
    return _heightPadding * v;
  }

 double rWP(double v) {
   return _widthPadding * v;
 }
}

тоді:

import 'responsive_screen/app_config.dart';
 ...
class RandomWordsState extends State<RandomWords> {
  AppConfig _ac;
  ...
  @override
  Widget build(BuildContext context) {
    _ac = AppConfig(context);
    ...
    return Scaffold(
      body: Container(
        height: _ac.rHP(50),
        width: _ac.rWP(50),
        color: Colors.red,
        child: Text('Test'),
      ),
    );
    ...
  }

0

Цю проблему можна вирішити за допомогою MediaQuery.of (context)

Щоб отримати ширину екрана: MediaQuery.of(context).size.width

Щоб отримати висоту екрану: MediaQuery.of(context).size.height

Для отримання додаткової інформації про годинник віджета MediaQuery, https://www.youtube.com/watch?v=A3WrA4zAaPw


0
  padding: EdgeInsets.only(
      left: 4.0,
      right: ResponsiveWidget.isSmallScreen(context) ? 4: 74, //Check for screen type
      top: 10,
      bottom: 40),

За рекомендацією Google це нормально, але, можливо, не ідеально.


0

Використовується ResponsiveBuilder або ScreenTypeLayout

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:responsive_builder/responsive_builder.dart';

class Sample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        elevation: 0,
        backgroundColor: Colors.black,
      ),
      body: ResponsiveBuilder(
        builder: (context, info) {
          var screenType = info.deviceScreenType;
          String _text;
          switch (screenType){
            case DeviceScreenType.desktop: {
              _text = 'Desktop';
              break;
            }
            case DeviceScreenType.tablet: {
              _text = 'Tablet';
              break;
            }
            case DeviceScreenType.mobile: {
              _text = 'Mobile';
              break;
            }
            case DeviceScreenType.watch: {
              _text = 'Watch';
              break;
            }
            default:
              return null;
          }
          return Center(child: Text(_text, style: TextStyle(fontSize: 32, color: Colors.black),));
        },
      ),
    );
  }
}

// screen type layout
ScreenTypeLayout.builder(
  mobile: MobilePage(),
  tablet: TabletPage(),
  desktop: DesktopPage(),
  watch: Watchpage(),
);


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.