Jak sprawić, by aplikacja Flutter odpowiadała w zależności od rozmiaru ekranu?


85

Mam trudności z dostosowaniem go do różnych rozmiarów ekranu. Jak sprawić, by był responsywny?

@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: () {})
                ]),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

Odpowiedzi:


73

Korzystanie z MediaQueryklasy:

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

MediaQuery : ustanawia poddrzewo, w którym zapytania o media są rozstrzygane na podane dane.

MediaQueryData : informacje o elemencie multimedialnym (np. Okno).

Aby uzyskać współczynnik pikseli urządzenia:

queryData.devicePixelRatio

Aby uzyskać szerokość i wysokość ekranu urządzenia:

queryData.size.width
queryData.size.height

Aby uzyskać współczynnik skali tekstu:

queryData.textScaleFactor

Korzystanie z AspectRatioklasy:

Z dokumentu:

Widżet, który próbuje dopasować rozmiar dziecka do określonego współczynnika proporcji.

Widżet najpierw próbuje największej szerokości dozwolonej przez ograniczenia układu. Wysokość widżetu jest określana przez zastosowanie danego współczynnika proporcji do szerokości, wyrażonego jako stosunek szerokości do wysokości.

Na przykład współczynnik proporcji szerokość: wysokość 16: 9 miałby wartość 16,0 / 9,0. Jeśli maksymalna szerokość jest nieskończona, szerokość początkowa jest określana przez zastosowanie współczynnika proporcji do maksymalnej wysokości.

Rozważmy teraz drugi przykład, tym razem ze współczynnikiem proporcji wynoszącym 2,0 i ograniczeniami układu, które wymagają, aby szerokość wynosiła od 0,0 do 100,0, a wysokość od 0,0 do 100,0. Wybierzemy szerokość 100,0 (największa dozwolona) i wysokość 50,0 (aby dopasować proporcje).

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

Możesz również użyć :


3
Jestem w stanie uzyskać szerokość i wysokość urządzenia, jak ustawić rozmiar testu, wypełnienie, margines queryData.
Farhana

29

Ta klasa pomoże, a następnie zainicjuje klasę metodą 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;
  }
}

zrób to w swoim wymiarze widgetów

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

Wszystkie podziękowania dla autora tego postu: https://medium.com/flutter-community/flutter-effectively-scale-ui-according-to-different-screen-sizes-2cb7c115ea0a


Jak dodać spód EdgeInsets z tą klasą SizeConfig?
Farwa

Myślę, że sprawdzi się wyściółka pojemnika. Spróbuj i powiedz mi, żebym ci pomógł !!

16

To, co robię, to wziąć szerokość i wysokość ekranu i obliczyć z niego siatkę 100 * 100, aby ustawić i skalować rzeczy, a następnie zapisać ją jako zmienne statyczne, które można ponownie wykorzystać. W większości przypadków działa całkiem dobrze. Lubię to:

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

Następnie skaluję wszystko zgodnie z tymi wartościami, na przykład:

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

lub

double fontSize = AppConfig.blockSize * 1.2;

Czasami bezpieczny obszar (wycięcie itp.) Zabija układ, więc możesz również rozważyć to:

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

double screenWidthWithoutSafeArea = AppConfig.width - AppConfig.safeAreaHorizontal;

To działało świetnie w niektórych ostatnich projektach.


1
Jak obliczyć rozmiary czcionek? Czy warto obliczać na podstawie szerokości czy wysokości?
Harsh Bhavsar

Obliczam je na podstawie szerokości. Ale szczerze mówiąc, nie próbowałem tego z aplikacjami obsługującymi zarówno tryb poziomy, jak i portret. Ale nadal możesz być w stanie obliczyć to inaczej w obu orientacjach.
datayeah

Jak to dokładnie rozwiązuje problem różnic w gęstości ekranu? Mówiąc, że dzielisz ekran na 100 * 100 bloków siatki, sprawiasz, że brzmi to tak, jakby wszystkie wynikowe bloki były jednakowej wielkości (tj. Kwadraty), ale tak nie jest. Jeśli masz urządzenie, które ma dwa razy więcej pikseli w pionie (wysokość ekranu) niż w poziomie (szerokość ekranu), powstałe bloki będą prostokątami zamiast kwadratów - co oznacza, że ​​Twój kod nadal powoduje ten sam problem, który zacząłeś próbować rozwiązać. Przetestuj swój kod na wielu gęstościach ekranu, aby to udowodnić. Więc to nie jest dla mnie rozwiązaniem.
SilSur

@SilSur, upewnij się, że bloki nie są tego samego rozmiaru na żadnym urządzeniu i gęstości, ale to sprawia, że ​​działa (w większości przypadków). Muszę tylko zdecydować dla każdego widżetu, który dodam do ekranu, czy chcę, aby jego pozycja i rozmiar były obliczane na podstawie szerokości lub wysokości bloku - lub obu. Użyłem tej metody w aplikacjach, które działają na dowolnym telefonie iPhone, iPadzie lub telefonie / tablecie z Androidem bez korekt specyficznych dla urządzenia. krajobraz i portret. ale masz rację, że ta metoda nadal nie rozwiązuje doskonale złożonych problemów z interfejsem użytkownika. wciąż szukam czegoś lepszego, aby sobie z tym poradzić.
datayeah

10

Sprawdź MediaQueryklasę

Na przykład, aby dowiedzieć się o wielkości bieżącego nośnika (na przykład okno zawierające aplikację), można odczytać MediaQueryData.sizewłaściwość z MediaQueryDatazwracany przez MediaQuery.of: MediaQuery.of(context).size.

Możesz więc wykonać następujące czynności:

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

masz na myśli zamiast pozycjonowanego użyj mediaQuery ??
praveen Dp

Nie rozumiem, co próbujesz zrobić
aziza

używając postitioned, padding wewnątrz stosu. dostosowałem go do rozmiaru ekranu. teraz, aby był responsywny, zamiast czego powinienem użyć mediaquery?
praveen Dp

5

Możesz wziąć procent szerokości lub wysokości jako dane wejściowe dla rozmiaru skali.

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

Gdzie mnożnik na końcu ma wartość, która sprawia, że ​​tekst wygląda dobrze dla aktywnego emulatora.

Poniżej przedstawiam, jak to skonfigurowałem, aby wszystkie skalowane wymiary były scentralizowane w jednym miejscu. W ten sposób można je łatwo i szybko dostosować ponownie za pomocą funkcji Hot Reload bez konieczności wyszukiwania Media.of()wywołań w całym kodzie.

  1. Utwórz plik do przechowywania wszystkich mapowań 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. Następnie odwołaj się do tego, gdziekolwiek potrzebujesz skalowanej wartości

    AppScale _scale = AppScale(context);

    // ... 

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

Dzięki odpowiedziom w tym poście


4

Trochę ciężko pukałem tutaj rozwiązania innych ludzi (@datayeah i Vithani Ravi), więc pomyślałem, że podzielę się własnymi próbami rozwiązania tego problemu ze zmienną gęstością ekranu lub zamknę się. Więc podchodzę do tego problemu od solidnego / stałego fundamentu: opieram całe moje skalowanie na stałym (niezmiennym) stosunku 2: 1 (wysokość: szerokość). Mam klasę pomocniczą „McGyver”, która wykonuje wszystkie czynności związane z podnoszeniem ciężarów (i użyteczną finezją kodu) w mojej aplikacji. Ta klasa „McGyver” zawiera tylko statyczne metody i statyczne stałe elementy składowe.

METODA SKALOWANIA RATIO: Skaluję zarówno szerokość, jak i wysokość niezależnie w oparciu o współczynnik proporcji 2: 1. Biorę wartości wejściowe szerokości i wysokości i dzielę je przez stałe szerokości i wysokości, a na koniec obliczam współczynnik korekty, za pomocą którego skaluję odpowiednie wartości wejściowe szerokości i wysokości. Rzeczywisty kod wygląda następująco:

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,
    );
  }

}

... ... ...

Następnie indywidualnie skalowałbyś swoje widżety (które dla mojej perfekcjonistycznej choroby to CAŁY mój interfejs użytkownika) za pomocą prostego statycznego wywołania metody „rsWidget ()” w następujący sposób:

  // 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!!

Fajne jest to, że metoda "rsWidget ()" zwraca widżet !! Możesz więc przypisać skalowany widżet do innej zmiennej, na przykład _rsBtnLogindo użytku w dowolnym miejscu - lub możesz po prostu użyć pełnego McGyver.rsWidget()wywołania metody wewnątrz swojej build()metody (dokładnie tak, jak chcesz, aby był umieszczony w drzewie widżetów) i będzie działał idealnie tak, jak powinien.

Dla tych bardziej wnikliwych programistów: zauważyliście, że użyłem dwóch dodatkowych metod skalowanych współczynnikiem McGyver.rsText()i McGyver.rsDouble()(nie zdefiniowanych w powyższym kodzie) w moim RaisedButton()- więc po prostu szaleję z tym skalowaniem ... ponieważ wymagam, aby moje aplikacje były absolutnie doskonały piksel w dowolnej skali i gęstości ekranu !! Skaluję proporcjonalnie moje ints, double, padding, text (wszystko, co wymaga spójności interfejsu użytkownika na różnych urządzeniach). Skaluję moje teksty tylko na podstawie szerokości, ale określam, której osi użyć do wszystkich innych skalowań (tak jak zostało to zrobione z ScaleType.widthwyliczeniem używanym do McGyver.rsDouble()wywołania w powyższym przykładzie kodu).

Wiem, że to szalone - i mam dużo pracy do wykonania w głównym wątku - ale mam nadzieję, że ktoś zobaczy tutaj moją próbę i pomoże mi znaleźć lepsze (lżejsze) rozwiązanie dla mojego skalowania 1: 1 z gęstością ekranu koszmary.


1
@ Abbas.M - Tak, dokonałem niewielkiej zmiany w linii kodu ze skalowaniem współczynnika [zobacz zaktualizowany kod] i uważam, że jest to najbliższe rozwiązanie do skalowania współczynnika 1: 1 - wypróbowałem całkiem sporo opcji, aby uzyskać ten. W tym zaktualizowanym kodzie nadal występuje kilka dziwnych [skrajnych] problemów ze skalowaniem, ale podobieństwo interfejsu użytkownika na ekranach o wielu gęstościach jest naprawdę przekonujące - bardzo subtelna różnica między ekranami ze zaktualizowanym kodem. Daj mi znać, co myślisz - opinie są bardzo cenne.
SilSur

oczywistą rzeczą dotyczącą głównego wątku, przenieś init i wywołania do głównego bloku inicjalizacji aplikacji, ponieważ rozmiar ekranu nie zmieni się po inicjalizacji aplikacji, więc główny wątek zostanie trafiony tylko raz podczas inicjalizacji aplikacji, a nie np. renderowanie każdego widżetu
Fred Grott

@SilSur, Twoje rozwiązanie wygląda bardzo dobrze. Czy możesz podzielić się całą klasą McGyvera?
David

@David - Klasa McGyver to bardzo ciężka klasa (i specyficzna dla projektu). Ten, którego użyłem w tym przykładzie, ma wiele funkcji, które nie są istotne dla problemu skalowania interfejsu użytkownika. Przesyłanie całej klasy jest więc przesadzone / nieefektywne. Jednak poprawiłem nieco klasę i wysłałem inną wersję kodu na inne pytanie SO . Być może możesz zaktualizować swój kod skalujący zgodnie z poprawionym kodem pod podanym adresem URL.
SilSur

4

Po wielu badaniach i testach opracowałem rozwiązanie dla aplikacji, którą obecnie konwertuję z systemu Android / iOS na Flutter.

W systemie Android i iOS użyłem „współczynnika skalowania” zastosowanego do podstawowych rozmiarów czcionek, renderując rozmiary tekstu, które były zależne od rozmiaru ekranu.

Ten artykuł był bardzo pomocny: https://medium.com/flutter-community/flutter-effectively-scale-ui-according-to-different-screen-sizes-2cb7c115ea0a

Utworzyłem StatelessWidget, aby uzyskać rozmiary czcionek stylów typograficznych Material Design. Pobieranie wymiarów urządzenia za pomocą MediaQuery, obliczanie współczynnika skalowania, a następnie resetowanie rozmiarów tekstu Material Design. Widżetu można użyć do zdefiniowania niestandardowego motywu Material Design.

Zastosowane emulatory:

  • Tablet Pixel C - 9,94 cala
  • Pixel 3 - telefon 5,46 cala
  • iPhone 11 Pro Max - telefon 5,8 cala

Ze standardowymi rozmiarami czcionek

Ze skalowanymi rozmiarami czcionek

set_app_theme.dart (widżet 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 (aplikacja demonstracyjna)

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
Może wyjaśnienie tego, co dzieje się pod maską, byłoby świetne, gdy następnym razem opublikujesz „rozwiązanie”? W każdym razie sprawdziłem GitHub pod kątem tej zależności. Jest to w zasadzie pojedyncza klasa (z 16 wierszami kodu), która pobiera wejściowe wartości szerokości i wysokości i skaluje je na podstawie szerokości i wysokości ekranu jako procent. Zasadniczo jest to to samo, co rozwiązanie @datayeah - jedyną różnicą jest to, że to jest starannie zapakowane. Te same problemy, co w przypadku datayeah - nie jest to wcale dobre rozwiązanie dla skalowania 1: 1 na urządzeniach o różnej gęstości ekranu. To „rozwiązanie” nie rozwiązuje problemu z gęstością ekranu.
SilSur

1

Możesz użyć MediaQuery dla wymiaru nadrzędnego lub FractionallySizedBox jako kontenerów.


1

Moje podejście do problemu jest podobne do sposobu, w jaki zrobił to datayeah. Miałem wiele wartości szerokości i wysokości zakodowanych na stałe, a aplikacja wyglądała dobrze na określonym urządzeniu. Otrzymałem więc wysokość ekranu urządzenia i właśnie utworzyłem współczynnik skalowania zakodowanych wartości.

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

gdzie 708 to wysokość konkretnego urządzenia.


1

Staram się, aby było to tak proste, jak to tylko możliwe. spróbuj. Tworzę responsywne narzędzie z funkcją getresponsivevalue odpowiedzialną za dostarczanie wartości zgodnie z rozmiarem ekranu, jeśli nie przypisujesz wartości do średniego ekranu, dużego ekranu, trybu poziomego. Domyślnie zapewnia wartość przypisaną do krótkiego ekranu. serdecznie witamy w przypadku jakichkolwiek pytań. Bardzo chciałbym się poprawić

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

Najłatwiejszym sposobem na stworzenie responsywnego interfejsu użytkownika dla różnych rozmiarów ekranu jest wtyczka Sizer .

Zrób responsywny interfejs użytkownika na urządzeniu o dowolnym rozmiarze ekranu, a także tablecie. Sprawdź tę wtyczkę ⬇️
https://pub.dev/packages/sizer

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

Posługiwać się .h , .w,.sp po wartości jak ta ⬇️

Przykład:

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)),
);

Zbudowałem wiele responsywnych aplikacji z tą wtyczką.


0

sprawdź tę stronę na flutter wiki:

Tworzenie responsywnych aplikacji

Użyj klasy LayoutBuilder: z jej właściwości konstruktora otrzymasz BoxConstraints. Sprawdź właściwości wiązania, aby zdecydować, co wyświetlić. Na przykład, jeśli twój maxWidth jest większy niż punkt przerwania szerokości, zwróć obiekt Scaffold z wierszem, który ma listę po lewej stronie. Jeśli jest węższy, zwróć obiekt Scaffold z szufladą zawierającą tę listę. Możesz także dostosować wyświetlacz na podstawie wysokości urządzenia, współczynnika proporcji lub innej właściwości. Gdy ograniczenia się zmienią (np. Użytkownik obróci telefon lub umieści aplikację w interfejsie użytkownika kafelków w Nougat), funkcja kompilacji zostanie ponownie uruchomiona.


0

utwórz nazwę pliku (app_config.dart) w nazwie folderu (responsive_screen) w folderze 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;
 }
}

następnie:

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

Ten problem można rozwiązać za pomocą MediaQuery.of (kontekst)

Aby uzyskać szerokość ekranu: MediaQuery.of(context).size.width

Aby uzyskać wysokość ekranu: MediaQuery.of(context).size.height

Aby uzyskać więcej informacji na temat zegarka MediaQuery Widget, 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),

Jest to w porządku według zaleceń Google, ale może nie być idealne.


0

Użyto ResponsiveBuilder lub 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(),
);


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.