Flutter uygulamasını farklı ekran boyutlarına göre nasıl duyarlı hale getirebilirim?


93

Çeşitli ekran boyutlarına göre duyarlı hale getirme konusunda zorluklarla karşılaşıyorum. Nasıl duyarlı hale getirilir?

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

Yeterli olduğunu düşünmediğim için burada jaycoding.tech/tutorials/guides/… basit bir çözüm yazdım MediaQuery. Kontrol etmek isteyebilirsin.
NduJay

Yanıtlar:


77

MediaQuerySınıfı kullanma :

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

MediaQuery : Medya sorgularının verilen verilere çözümlendiği bir alt ağaç oluşturur.

MediaQueryData : Bir medya parçası hakkında bilgi (örneğin bir pencere).

Cihaz Piksel Oranını almak için:

queryData.devicePixelRatio

Cihaz ekranının genişliğini ve yüksekliğini almak için:

queryData.size.width
queryData.size.height

Metin ölçek faktörünü elde etmek için:

queryData.textScaleFactor

AspectRatioSınıfı kullanma :

Dokümandan:

Çocuğu belirli bir en boy oranına göre boyutlandırmaya çalışan bir widget.

Parçacık, ilk olarak düzen kısıtlamalarının izin verdiği en büyük genişliği dener. Widget'ın yüksekliği, genişlik / yükseklik oranı olarak ifade edilen, genişliğe belirli en boy oranı uygulanarak belirlenir.

Örneğin, 16: 9 genişlik: yükseklik en boy oranı 16.0 / 9.0 değerine sahip olacaktır. Maksimum genişlik sonsuz ise, başlangıç ​​genişliği, en boy oranı maksimum yüksekliğe uygulanarak belirlenir.

Şimdi, bu sefer en boy oranı 2.0 olan ve genişliğin 0.0 ile 100.0 arasında ve yüksekliğin 0.0 ile 100.0 arasında olmasını gerektiren düzen kısıtlamalarına sahip ikinci bir örneği düşünün. 100,0'lık bir genişlik (izin verilen en büyük) ve 50,0'lık bir yükseklik (en boy oranına uyacak şekilde) seçeceğiz.

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

Ayrıca şunları kullanabilirsiniz :


3
Cihazın genişliğini ve yüksekliğini alabiliyorum, test boyutunu, dolguyu, marjı nasıl ayarlayabilirim queryData.
Farhana

33

Bu sınıf yardımcı olacak ve ardından init yöntemi ile sınıfı başlatacaktır.

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

o zaman widget boyutunuzda bunu yapın

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

Bu yazının yazarının tüm kredileri: https://medium.com/flutter-community/flutter-effectively-scale-ui-according-to-different-screen-sizes-2cb7c115ea0a


Bu SizeConfig sınıfıyla EdgeInsets altına nasıl eklenir?
Farwa

Ben kabın dolgusu olarak işe yarayacağını düşünüyorum. Deneyin ve bana yardım etmemi söyleyin !!

16

Yaptığım şey, ekran genişliğini ve yüksekliğini almak ve bir şeyleri konumlandırmak ve ölçeklendirmek ve yeniden kullanılabilecek statik değişkenler olarak kaydetmek için bundan 100 * 100'lük bir ızgara hesaplamaktır. Çoğu durumda oldukça iyi çalışıyor. Bunun gibi:

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

Sonra her şeyi şu değerlere göre ölçeklendiriyorum:

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

veya

double fontSize = AppConfig.blockSize * 1.2;

Bazen güvenli alan (çentik vb.) Bir düzeni öldürür, bu yüzden bunu da düşünebilirsiniz:

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

double screenWidthWithoutSafeArea = AppConfig.width - AppConfig.safeAreaHorizontal;

Bu, bazı yeni projelerde harika çalıştı.


1
FontSizes nasıl hesaplanır? Genişliğe veya yüksekliğe göre hesaplamak iyi mi?
Harsh Bhavsar

Genişliğe göre hesaplıyorum. Ancak dürüst olmak gerekirse, hem manzara hem de portre modunu destekleyen uygulamalarla denemedim. Ancak yine de her iki yönde de farklı hesaplayabilirsiniz.
datayeah

Bu, ekran yoğunluğu farklılıkları sorununu tam olarak nasıl çözer? Ekranı 100 * 100 ızgara bloklarına böldüğünüzü söyleyerek, elde edilen blokların hepsinin eşit büyüklükte (yani kareler) olduğu gibi ses çıkarırsınız, ancak bunlar değildir. Dikey olarak (ekran yüksekliği) yataydan (ekran genişliği) iki kat fazla piksele sahip bir cihazınız varsa, ortaya çıkan bloklarınız kareler yerine dikdörtgenler olacaktır - bu, kodunuzun hala çözmeye çalıştığınız aynı sorunu oluşturduğu anlamına gelir. Bunu kanıtlamak için kodunuzu birden çok ekran yoğunluğunda test edin. Yani bu benim için gerçekten bir çözüm değil.
SilSur

@SilSur, blokların herhangi bir cihazda ve yoğunlukta aynı boyutta olmadığından emin olun, ancak çalışmasını sağlayan şey budur (çoğu durumda). Sadece, ekrana eklediğim her widget için konumunun ve boyutunun bir bloğun genişliği veya yüksekliğine veya her ikisine göre hesaplanmasını istiyorsam karar vermem gerekiyor. Bu yöntemi cihaza özel düzeltmeler olmadan herhangi bir iphone, ipad veya android telefon / tablet üzerinde çalışan uygulamalarda kullandım. manzara ve portre. ancak bu yöntemin hala karmaşık UI sorunlarını mükemmel şekilde çözmediği konusunda haklısınız. Hala bunun üstesinden gelmek için daha iyi bir şey arıyorum.
datayeah

11

MediaQuerySınıfı kontrol et

Örneğin, mevcut ortamın boyutunu öğrenmek için (örneğin, uygulamanızı içeren pencere), okuyabilir MediaQueryData.sizemalını MediaQueryDatatarafından döndürülen MediaQuery.of: MediaQuery.of(context).size.

Böylece şunları yapabilirsiniz:

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

konumlandırılmış mediaQuery yerine kullanmak mı istiyorsunuz?
Praveen Dp

Ne yapmaya çalıştığını anlamıyorum
aziza

postitioned kullanarak, yığın içinde dolgu. Bunu ekran boyutuna ayarladım. şimdi duyarlı hale getirmek için medya sorgusunu ne yerine kullanmalıyım?
praveen Dp

7

Farklı ekran boyutları için duyarlı kullanıcı arayüzü oluşturmanın en kolay yolu Sizer eklentisidir. Sizer Ekran Görüntüsü

Herhangi bir ekran boyutundaki cihazda da tablette duyarlı kullanıcı arayüzü oluşturun. Bu eklentiyi kontrol edin ⬇️
https://pub.dev/packages/sizer

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

Kullanım .h , .w, .spböyle değerden sonra ⬇️

Misal:

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

Bu eklenti ile birçok duyarlı Uygulama oluşturdum.


2
yaklaşım harika. Davranışı gözlemleyip deneyeceğim ve beni tatmin ederse onunla üretime geçeceğim.
Wambert Orion

2
Çok teşekkür ederim @urmish patel, uygulamaları duyarlı hale getirmenin en kolay yolu.
Tarun Sharma

5

Ölçek boyutu için giriş olarak genişliğin veya yüksekliğin yüzdesini alabilirsiniz.

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

Sondaki çarpanın, Metnin etkin öykünücü için iyi görünmesini sağlayan bir değere sahip olduğu yer.

Aşağıda, tüm ölçeklendirilmiş boyutların tek bir yerde merkezileştirilmesi için onu nasıl kurduğum açıklanmaktadır. Bu şekilde Media.of(), kod boyunca aramaları aramak zorunda kalmadan Hızlı Yeniden Yükleme ile bunları kolayca ve hızlı bir şekilde yeniden çalıştırabilirsiniz .

  1. Tüm eşlemeleri depolamak için dosyayı oluşturun 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. Ardından, ölçeklenmiş değere ihtiyacınız olan her yerde referans

    AppScale _scale = AppScale(context);

    // ... 

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

Bu gönderideki cevaplara teşekkürler


4
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
Belki de bir dahaki sefere bir "çözüm" yayınladığınızda kaputun altında neler olup bittiğine dair bir açıklama harika olur? Her neyse, bu bağımlılık için GitHub'a baktım. Temelde, giriş genişliği ve yükseklik değerlerini alan ve yüzde olarak ekran genişliği ve yüksekliğine göre ölçeklendiren tek bir sınıftır (16 satır kodlu). Aslında @datayeah'ın çözümüyle aynı - tek fark, bunun düzgün bir şekilde paketlenmiş olmasıdır. Verilerle aynı sorunlar burada da geçerlidir - çeşitli ekran yoğunluklu cihazlarda 1: 1 ölçeklendirme için hiç de iyi bir çözüm değildir. Ekran yoğunluğu sorunu bu "çözüm" ile ÇÖZÜLMEZ.
SilSur

4

Çok fazla araştırma ve testten sonra, şu anda Android / iOS'tan Flutter'a dönüştürdüğüm bir uygulama için bir çözüm geliştirdim.

Android ve iOS ile, temel yazı tipi boyutlarına uygulanan bir 'Ölçeklendirme Faktörü' kullandım ve ekran boyutuna göre metin boyutlarını işledim.

Bu makale çok yardımcı oldu: https://medium.com/flutter-community/flutter-effectively-scale-ui-according-to-different-screen-sizes-2cb7c115ea0a

Materyal Tasarım tipografik stillerinin yazı tipi boyutlarını almak için bir StatelessWidget oluşturdum. MediaQuery kullanarak cihaz boyutlarını alma, ölçeklendirme faktörü hesaplama ve ardından Materyal Tasarım metin boyutlarını sıfırlama. Widget, özel bir Materyal Tasarım Teması tanımlamak için kullanılabilir.

Kullanılan emülatörler:

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

Standart yazı tipi boyutları ile

Ölçekli yazı tipi boyutları ile

set_app_theme.dart (SetAppTheme Widget)

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 (Demo Uygulaması)

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

Başkalarının (@datayeah & Vithani Ravi) çözümlerini burada biraz zorladım, bu yüzden bu değişken ekran yoğunluğu ölçeklendirme problemini çözmek için kendi girişimlerimi paylaşacağımı veya susacağımı düşündüm. Bu yüzden bu soruna sağlam / sabit bir temelden yaklaşıyorum: Tüm ölçeklendirmemi sabit (değişmez) 2: 1 (yükseklik: genişlik) oranına dayandırıyorum. Uygulamamda tüm ağır işleri (ve kullanışlı kod inceltmeyi) yapan yardımcı bir "McGyver" sınıfım var. Bu "McGyver" sınıfı yalnızca statik yöntemler ve statik sabit sınıf üyelerini içerir.

ORAN ÖLÇEKLENDİRME YÖNTEMİ: Hem genişliği hem de yüksekliği 2: 1 En Boy Oranına göre bağımsız olarak ölçeklendiriyorum. Genişlik ve yükseklik giriş değerlerini alıp her birini genişlik ve yükseklik sabitlerine bölerim ve son olarak ilgili genişlik ve yükseklik giriş değerlerini ölçeklendirmek için bir ayar faktörü hesaplarım. Gerçek kod aşağıdaki gibi görünür:

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

}

... ... ...

Daha sonra, "rsWidget ()" yöntemine basit bir statik çağrı yaparak (benim mükemmeliyetçi hastalığım için TÜM kullanıcı arayüzümdür) widget'larınızı aşağıdaki gibi tek tek ölçeklendirirsiniz:

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

Güzel olan şey, "rsWidget ()" yönteminin bir parçacık döndürmesidir !! Böylece, ölçeklenmiş parçacığı _rsBtnLoginher yerde kullanmak gibi başka bir değişkene atayabilir ya da McGyver.rsWidget()yönteminizin içinde yerinde tam yöntem çağrısını kullanabilirsiniz build()(parçacık ağacında tam olarak nasıl konumlandırmanız gerektiği) ve onu olması gerektiği gibi mükemmel çalışacaktır.

Daha zeki olan kodlayıcılar için: İki ek oran ölçeklendirilmiş yöntem kullandığımı McGyver.rsText()ve McGyver.rsDouble()(yukarıdaki kodda tanımlanmayan) benimde RaisedButton()- bu yüzden temelde bu ölçeklendirme işlerinde çıldırıyorum ... çünkü uygulamalarımın Herhangi bir ölçekte veya ekran yoğunluğunda kesinlikle mükemmel piksel !! İnts, double, padding, text'imi oran-ölçeklendiriyorum (cihazlar arasında kullanıcı arayüzü tutarlılığı gerektiren her şey). Metinlerimi yalnızca genişliğe göre ölçeklendiriyorum, ancak diğer tüm ölçeklendirmeler için hangi eksenin kullanılacağını belirtiyorum ( yukarıdaki kod örneğinde çağrı ScaleType.widthiçin kullanılan numaralandırma ile yapıldığı McGyver.rsDouble()gibi).

Bunun çılgınca olduğunu biliyorum - ve ana iş parçacığında yapılacak çok iş var - ancak birinin girişimimi burada görmesini ve ekran yoğunluğu 1: ​​1 ölçeklememe daha iyi (daha hafif) bir çözüm bulmama yardımcı olmasını umuyorum kabuslar.


1
@ Abbas.M - Evet, oran ölçekleme kod satırında küçük bir değişiklik yaptım [güncellenmiş koda bakın] ve buna inanıyorum ki gerçek bir 1: 1 oran ölçekleme çözümüne en yakın şekilde gelebilirim - Oldukça fazla sayıda denedim bunu almak için seçenekler. Bu güncellenmiş kodla ilgili hala birkaç tuhaf [uç durum] ölçekleme sorunu var, ancak çoklu yoğunluk ekranlarındaki kullanıcı arayüzü benzerliği gerçekten ikna edicidir - güncellenmiş kodla ekranlar arasında çok ince farklar gözlemlenebilir. Lütfen bana ne düşündüğünüzü bildirin - geri bildirimleriniz bizim için çok değerlidir.
SilSur

ana iş parçacığı hakkında bariz olan şey, init'i hareket ettirin ve uygulama başlatıldıktan sonra ekran boyutu değişmeyeceğinden, ana iş parçacığını uygulama başlangıcında her bir widget oluşturma yerine yalnızca bir kez vurursunuz
Fred Grott

@SilSur, çözümünüz çok güzel görünüyor. Tüm McGyver sınıfını paylaşabilir misin?
David

@David - McGyver sınıfı çok ağır bir sınıftır (ve projeye özel). Bu örnekte kullandığım, UI ölçekleme problemiyle ilgili olmayan birçok işleve sahip. Bu yüzden sınıfın tamamını yüklemek benim için aşırı / verimsiz. Bununla birlikte, sınıfı biraz geliştirdim ve başka bir SO sorusuna kodun farklı bir versiyonunu gönderdim . Belki de ölçeklendirme kodunuzu, sağlanan URL’de geliştirilmiş kodun satırları boyunca güncelleyebilirsiniz.
SilSur

1

Üst öğe boyutu için MediaQuery'yi veya kapsayıcı olarak FractionallySizedBox'ı kullanabilirsiniz.


1

Benim probleme yaklaşımım datayeah'ın yaptığı gibi. Çok fazla kodlanmış genişlik ve yükseklik değerim vardı ve uygulama belirli bir cihazda iyi görünüyordu. Böylece cihazın ekran yüksekliğini aldım ve kodlanmış değerleri ölçeklendirmek için bir faktör oluşturdum.

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

708, belirli aygıtın yüksekliğidir.


1

Mümkün olduğunca basitleştirmeye çalışıyorum. bir dene. Orta ekran, geniş ekran, manzara modu için değer atamazsanız ekran boyutuna göre değer sağlamaktan sorumlu getresponsivevalue işlevine sahip duyarlı bir yardımcı program yaparım. Varsayılan olarak kısa ekrana atanan değer sağlar. herhangi bir sorunuz için sıcak bir karşılama. Gelişmeyi çok isterim

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'den bu sayfaya göz atın:

Duyarlı Uygulamalar Oluşturma

LayoutBuilder sınıfını kullanın: Builder özelliğinden bir BoxConstraints elde edersiniz. Neyin görüntüleneceğine karar vermek için kısıtlamanın özelliklerini inceleyin. Örneğin, maxWidth değeriniz genişlik kesme noktanızdan büyükse, solda listesi olan bir satıra sahip bir Scaffold nesnesi döndürün. Daha darsa, bu listeyi içeren bir çekmeceye sahip bir Scaffold nesnesi iade edin. Ekranınızı ayrıca cihazın yüksekliğine, en boy oranına veya başka bir özelliğe göre de ayarlayabilirsiniz. Kısıtlamalar değiştiğinde (örneğin, kullanıcı telefonu döndürdüğünde veya uygulamanızı Nougat'ta bir kutucuk kullanıcı arayüzüne koyduğunda), oluşturma işlevi yeniden çalışacaktır.


0

lib klasöründeki klasör adında (responsive_screen) dosya adı (app_config.dart) oluşturun:

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

sonra:

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

Bu sorun kullanılarak çözülebilir MediaQuery.of (bağlam)

Ekran genişliğini elde etmek için: MediaQuery.of(context).size.width

Ekran yüksekliğini almak için: MediaQuery.of(context).size.height

MediaQuery Widget izleme hakkında daha fazla bilgi için 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),

Bu, Google'ın önerisine göre sorun değildir, ancak mükemmel olmayabilir.


0

ResponsiveBuilder veya ScreenTypeLayout kullanıldı

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


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.