다른 화면 크기에 따라 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?
Farhana

28

이 클래스는 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


이 SizeConfig 클래스로 EdgeInsets 하단을 추가하는 방법은 무엇입니까?
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
fontSizes를 계산하는 방법은 무엇입니까? 너비 나 높이를 기준으로 계산하는 것이 좋습니까?
Harsh Bhavsar

너비를 기준으로 계산하고 있습니다. 하지만 솔직히 가로 모드와 세로 모드를 모두 지원하는 앱으로는 시도하지 않았습니다. 그러나 여전히 두 방향에서 다르게 계산할 수 있습니다.
datayeah

이것이 화면 밀도 차이 문제를 정확히 어떻게 해결합니까? 화면을 100 * 100 그리드 블록으로 나누면 결과 블록이 모두 같은 크기 (즉, 정사각형) 인 것처럼 들리지만 그렇지 않습니다. 가로 (화면 너비)보다 세로 (화면 높이)의 픽셀 수가 두 배인 장치가있는 경우 결과 블록은 정사각형 대신 직사각형이됩니다. 즉, 코드에서 해결하려고 시작한 것과 동일한 문제가 여전히 발생 함을 의미합니다. 이를 증명하기 위해 여러 화면 밀도에서 코드를 테스트하십시오. 그래서 이것은 나에게 실제로 해결책이 아닙니다.
SilSur

@SilSur, 블록이 모든 장치 및 밀도에서 동일한 크기가 아닌지 확인하십시오. 그러나 그것이 작동하게 만드는 이유입니다 (대부분의 경우). 블록의 너비 나 높이 또는 둘 다에 대해 위치와 크기를 계산하려면 화면에 추가하는 각 위젯에 대해 결정해야합니다. 기기 별 수정없이 모든 아이폰, 아이 패드 또는 안드로이드 폰 / 태블릿에서 실행되는 앱에서이 방법을 사용했습니다. 풍경과 초상화. 그러나이 방법이 여전히 복잡한 UI 문제를 완벽하게 해결하지 못한다는 것은 맞습니다. 나는 여전히 이것을 처리하기 위해 더 나은 것을 찾고있다.
datayeah

9

MediaQuery수업 확인

예를 들어, 현재 미디어의 크기를 배울 수 (예를 들어 앱이 포함 된 창), 당신은 읽을 수 MediaQueryData.size로부터 재산 MediaQueryData에 의해 반환을 MediaQuery.of: MediaQuery.of(context).size.

따라서 다음을 수행 할 수 있습니다.

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

위치 대신 mediaQuery 사용을 의미합니까 ??
praveen Dp 2018

나는 당신이 뭘 하려는지 이해가 안 돼요
아지자

스택 내부에 postitioned, padding을 사용합니다. 나는 그것을 화면 크기로 조정했습니다. 응답하도록 지금 무엇 대신 mediaquery를 사용해야합니까 ??
praveen Dp

5

스케일 크기에 대한 입력으로 너비 또는 높이의 백분율을 사용할 수 있습니다.

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

끝에있는 승수에 활성 에뮬레이터에서 텍스트를보기 좋게 만드는 값이 있습니다.

아래는 조정 된 모든 차원이 한곳에 집중되도록 설정하는 방법입니다. 이렇게 Media.of()하면 코드 전체에서 호출 을 찾을 필요없이 Hot Reload를 사용하여 쉽고 빠르게 다시 실행할 수 있습니다 .

  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()내 앱에서 기본적으로이 스케일링 작업에 열광 한다는 사실을 알게 될 것입니다. 어떤 스케일이나 화면 밀도에서도 완벽한 픽셀 !! int, doubles, padding, text (장치 간 UI 일관성이 필요한 모든 것)의 비율을 조정합니다. 너비만을 기준으로 텍스트 크기를 조정하지만 다른 모든 크기 조정에 사용할 축을 지정합니다 ( 위 코드 예제에서 호출에 ScaleType.width사용 된 열거 형 으로 수행됨 McGyver.rsDouble()).

나는 이것이 미친 짓이라는 것을 알고 있으며 메인 스레드에서 할 일이 많지만 누군가가 여기에서 내 시도를보고 내 화면 밀도 1 : 1 스케일링에 대한 더 나은 (더 가벼운) 솔루션을 찾도록 도와주기를 바랍니다. 악몽.


1
@ Abbas.M-예, 비율 스케일링 코드 라인을 약간 변경했습니다 [업데이트 된 코드 참조]. 이것이 내가 실제 1 : 1 비율 스케일링 솔루션에 가장 근접한 것이라고 생각합니다. 꽤 많은 시도를했습니다. 이 옵션을 얻을 수 있습니다. 이 업데이트 된 코드에는 여전히 몇 가지 이상한 [가장자리 케이스] 크기 조정 문제가 있지만 다중 밀도 화면의 UI 유사성은 정말 설득력이 있습니다. 업데이트 된 코드를 사용하는 화면 간에는 매우 미묘한 차이를 볼 수 있습니다. 의견을 보내 주시면 감사하겠습니다.
SilSur

메인 스레드에 대한 분명한 점은 앱 초기화 후 화면 크기가 변경되지 않으므로 초기화 및 앱 초기화 메인 블록에 대한 호출을 이동하여 각 위젯 렌더링이 아닌 앱 초기화시 한 번만 메인 스레드를 맞출 수 있습니다.
Fred Grott

@SilSur, 귀하의 솔루션은 매우 훌륭해 보입니다. McGyver 수업 전체를 공유 하시겠습니까?
David

@David-McGyver 클래스는 매우 무거운 클래스 (및 프로젝트 별)입니다. 이 예제에서 사용한 기능에는 UI 확장 문제와 관련이없는 많은 기능이 있습니다. 그래서 전체 수업을 업로드하는 것은 과도하거나 비효율적입니다. 그러나 나는 수업을 조금 개선하고 다른 SO 질문에 다른 버전의 코드를 게시했습니다 . 제공된 URL에서 개선 된 코드 라인을 따라 스케일링 코드를 업데이트 할 수 있습니다.
SilSur

4

많은 연구와 테스트 끝에 현재 Android / iOS에서 Flutter로 변환중인 앱에 대한 솔루션을 개발했습니다.

Android와 iOS에서는 기본 글꼴 크기에 적용된 'Scaling Factor'를 사용하여 화면 크기에 상대적인 텍스트 크기를 렌더링했습니다.

이 문서는 매우 도움이되었다 : https://medium.com/flutter-community/flutter-effectively-scale-ui-according-to-different-screen-sizes-2cb7c115ea0a

Material Design 인쇄 스타일의 글꼴 크기를 얻기 위해 StatelessWidget을 만들었습니다. MediaQuery를 사용하여 장치 치수 가져 오기, 배율 계수 계산, 머티리얼 디자인 텍스트 크기 재설정. 위젯은 커스텀 머티리얼 디자인 테마를 정의하는 데 사용할 수 있습니다.

사용 된 에뮬레이터 :

  • Pixel C-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));

3
다음 번에 "솔루션"을 게시 할 때 내부에서 일어나는 일에 대한 설명이 좋을까요? 어쨌든이 종속성에 대해 GitHub를 확인했습니다. 기본적으로 입력 너비 및 높이 값을 사용하고 화면 너비 및 높이를 백분율로 기준으로 크기를 조정하는 단일 클래스 (코드 16 줄 포함)입니다. 본질적으로 @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;
            }
          }

0

flutter wiki에서이 페이지를 확인하십시오.

반응 형 앱 만들기

LayoutBuilder 클래스 사용 : 빌더 속성에서 BoxConstraints를 얻습니다. 제약 조건의 속성을 조사하여 표시 할 내용을 결정하십시오. 예를 들어 maxWidth가 너비 중단 점보다 큰 경우 왼쪽에 목록이있는 행이있는 Scaffold 개체를 반환합니다. 더 좁은 경우 해당 목록이 포함 된 서랍이있는 Scaffold 개체를 반환합니다. 장치의 높이, 가로 세로 비율 또는 기타 속성에 따라 디스플레이를 조정할 수도 있습니다. 제약 조건이 변경되면 (예 : 사용자가 휴대폰을 회전하거나 Nougat의 타일 UI에 앱을 넣는 경우) 빌드 기능이 다시 실행됩니다.


0

lib 폴더의 폴더 이름 (responsive_screen)에 파일 이름 (app_config.dart)을 만듭니다.

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


0

다양한 화면 크기에 대해 반응 형 UI를 만드는 가장 쉬운 방법은 Sizer입니다. 플러그인입니다.

모든 화면 크기 장치에서 반응 형 UI를 만들 수 있습니다. 이 플러그인 확인 ⬇️
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)),
);

이 플러그인으로 많은 반응 형 앱을 빌드했습니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.