Come rendere reattiva l'app Flutter in base alle diverse dimensioni dello schermo?


93

Sto incontrando difficoltà per renderlo reattivo in base alle varie dimensioni dello schermo. Come renderlo reattivo?

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

Ho scritto una semplice soluzione qui jaycoding.tech/tutorials/guides/… perché non credo MediaQuerysia sufficiente. Potresti volerlo controllare.
NduJay

Risposte:


77

Utilizzando la MediaQueryclasse:

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

MediaQuery : stabilisce una sottostruttura in cui le query multimediali vengono risolte sui dati forniti.

MediaQueryData : informazioni su un supporto (ad esempio, una finestra).

Per ottenere il rapporto pixel del dispositivo:

queryData.devicePixelRatio

Per ottenere larghezza e altezza dello schermo del dispositivo:

queryData.size.width
queryData.size.height

Per ottenere il fattore di scala del testo:

queryData.textScaleFactor

Utilizzando la AspectRatioclasse:

Da doc:

Un widget che tenta di ridimensionare il bambino in base a proporzioni specifiche.

Il widget cerca prima la larghezza massima consentita dai vincoli di layout. L'altezza del widget viene determinata applicando le proporzioni date alla larghezza, espressa come rapporto tra larghezza e altezza.

Ad esempio, un rapporto larghezza: altezza di 16: 9 avrebbe un valore di 16,0 / 9,0. Se la larghezza massima è infinita, la larghezza iniziale viene determinata applicando le proporzioni all'altezza massima.

Consideriamo ora un secondo esempio, questa volta con un rapporto di aspetto di 2,0 e vincoli di layout che richiedono che la larghezza sia compresa tra 0,0 e 100,0 e l'altezza tra 0,0 e 100,0. Selezioneremo una larghezza di 100,0 (la più grande consentita) e un'altezza di 50,0 (in modo che corrisponda alle proporzioni).

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

Inoltre puoi usare :


3
Sono in grado di ottenere la larghezza e l'altezza del dispositivo, come posso impostare la dimensione del test, il riempimento, il margine queryData.
Farhana

33

Questa classe aiuterà e quindi inizializzerà la classe con il metodo 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;
  }
}

quindi nella dimensione dei tuoi widget fallo

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

Tutti i riconoscimenti all'autore di questo post: https://medium.com/flutter-community/flutter-effectively-scale-ui-according-to-different-screen-sizes-2cb7c115ea0a


Come aggiungere il fondo EdgeInsets con questa classe SizeConfig?
Farwa

Penso che un'imbottitura del contenitore funzionerà. Prova a dirmi di aiutarti !!

16

Quello che faccio è prendere la larghezza e l'altezza dello schermo e calcolare una griglia di 100 * 100 da essa per posizionare e scalare le cose e salvarla come variabili statiche che possono essere riutilizzate. Funziona abbastanza bene nella maggior parte dei casi. Come questo:

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

Quindi ridimensiono tutto in base a questi valori, in questo modo:

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

o

double fontSize = AppConfig.blockSize * 1.2;

A volte l'area sicura (tacca, ecc.) Uccide un layout, quindi puoi considerare anche questo:

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

double screenWidthWithoutSafeArea = AppConfig.width - AppConfig.safeAreaHorizontal;

Ha funzionato alla grande su alcuni progetti recenti.


1
Come calcolare le dimensioni dei caratteri? È utile calcolare in base alla larghezza o all'altezza?
Harsh Bhavsar

Li sto calcolando in base alla larghezza. Ma ad essere onesti, non l'ho provato con app che supportano sia la modalità orizzontale che quella verticale. Ma potresti comunque essere in grado di calcolarlo in modo diverso su entrambi gli orientamenti.
datayeah

In che modo questo risolve esattamente il problema delle differenze di densità dello schermo? Dicendo che dividi lo schermo in 100 * 100 blocchi di griglia fai sembrare che i blocchi risultanti siano tutti della stessa dimensione (cioè quadrati), ma non lo sono. Se hai un dispositivo che ha il doppio del numero di pixel verticalmente (altezza dello schermo) rispetto all'orizzontale (larghezza dello schermo), i blocchi risultanti saranno rettangoli anziché quadrati, il che significa che il tuo codice produce ancora lo stesso problema che hai iniziato cercando di risolvere. Prova il tuo codice su più densità di schermo per dimostrarlo. Quindi questa non è davvero una soluzione per me.
SilSur

@ SilSur, certo che i blocchi non hanno le stesse dimensioni su nessun dispositivo e densità, ma questo è ciò che lo fa funzionare (nella maggior parte dei casi). Devo solo decidere per ogni widget che aggiungo allo schermo se voglio che la sua posizione e dimensione siano calcolate in base alla larghezza o all'altezza di un blocco - o entrambi. Ho utilizzato questo metodo in app che funzionano su qualsiasi iPhone, iPad o telefono / tablet Android senza correzioni specifiche del dispositivo. paesaggio e ritratto. ma hai ragione sul fatto che questo metodo non risolve ancora perfettamente problemi complessi dell'interfaccia utente. Sto ancora cercando qualcosa di meglio per gestire questo.
datayeah

11

Controlla la MediaQueryclasse

Ad esempio, per imparare le dimensioni dei supporti attuali (ad esempio, la finestra contenente la vostra applicazione), si può leggere la MediaQueryData.sizeproprietà dal MediaQueryDatarestituito da MediaQuery.of: MediaQuery.of(context).size.

Quindi puoi fare quanto segue:

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

intendi invece di utilizzare mediaQuery ??
praveen Dp

Non capisco cosa stai cercando di fare
aziza

using postitioned, padding inside stack. l'ho adattato alle dimensioni dello schermo. ora per renderlo reattivo dovrei usare mediaquery al posto di cosa ??
praveen Dp

7

Il modo più semplice per rendere reattiva l'interfaccia utente per diverse dimensioni dello schermo è il plug-in Sizer . Screenshot di Sizer

Rendi reattiva l'interfaccia utente in qualsiasi dispositivo di dimensioni dello schermo, anche tablet. Controlla questo plugin ⬇️
https://pub.dev/packages/sizer

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

Usa .h, .w,.sp dopo il valore come questo ⬇️

Esempio:

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

Ho creato molte app reattive con questo plugin.


2
l'approccio è ottimo cercherò di osservare il comportamento e poi andrò in produzione con esso se mi soddisfa.
Wambert Orion

2
Grazie mille @urmish patel è il modo più semplice per rendere le app reattive.
Tarun Sharma

5

Puoi prendere una percentuale della larghezza o dell'altezza come input per la dimensione della scala.

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

Dove il moltiplicatore alla fine ha un valore che fa sembrare il testo buono per l'emulatore attivo.

Di seguito è riportato come l'ho impostato in modo che tutte le dimensioni ridimensionate siano centralizzate in un unico posto. In questo modo puoi regolarli facilmente e rieseguirli rapidamente con Hot Reload senza dover cercare le Media.of()chiamate in tutto il codice.

  1. Crea il file per memorizzare tutte le mappature 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. Quindi fai riferimento a quello in cui hai bisogno del valore scalato

    AppScale _scale = AppScale(context);

    // ... 

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

Grazie alle risposte in questo post


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
Forse una spiegazione di quello che succede sotto il cofano sarebbe ottima la prossima volta che pubblichi una "soluzione"? Ad ogni modo, ho controllato GitHub per questa dipendenza. È fondamentalmente una singola classe (con 16 righe di codice) che accetta valori di larghezza e altezza di input e la scala in base alla larghezza e all'altezza dello schermo come percentuale. È essenzialmente la stessa della soluzione di @datayeah - l'unica differenza è che questa è ben confezionata. Gli stessi problemi di datayeah si applicano qui - non è affatto una buona soluzione per il ridimensionamento 1: 1 su dispositivi con densità dello schermo variabile. Il problema della densità dello schermo NON È RISOLTO con questa "soluzione".
SilSur

4

Dopo molte ricerche e test, ho sviluppato una soluzione per un'app che sto attualmente convertendo da Android / iOS a Flutter.

Con Android e iOS ho utilizzato un "fattore di scala" applicato alle dimensioni dei caratteri di base, rendendo le dimensioni del testo relative alle dimensioni dello schermo.

Questo articolo è stato molto utile: https://medium.com/flutter-community/flutter-effectively-scale-ui-according-to-different-screen-sizes-2cb7c115ea0a

Ho creato uno StatelessWidget per ottenere le dimensioni dei caratteri degli stili tipografici di Material Design. Ottenere le dimensioni del dispositivo utilizzando MediaQuery, calcolare un fattore di scala, quindi reimpostare le dimensioni del testo di Material Design. Il widget può essere utilizzato per definire un tema di Material Design personalizzato.

Emulatori utilizzati:

  • Pixel C - Tablet da 9,94 pollici
  • Pixel 3 - Telefono da 5,46 pollici
  • iPhone 11 Pro Max - Telefono da 5,8 pollici

Con dimensioni dei caratteri standard

Con dimensioni dei caratteri ridimensionate

set_app_theme.dart (widget 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 (App demo)

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

Ho criticato un po 'le soluzioni di altre persone (@datayeah e Vithani Ravi) qui, quindi ho pensato di condividere i miei tentativi di risolvere questo problema di ridimensionamento della densità dello schermo variabile o di stare zitto. Quindi mi avvicino a questo problema da una base solida / fissa: baso tutto il mio ridimensionamento su un rapporto fisso (immutabile) di 2: 1 (altezza: larghezza). Ho una classe di supporto "McGyver" che fa tutto il lavoro pesante (e l'utile elaborazione del codice) nella mia app. Questa classe "McGyver" contiene solo metodi statici e membri di classe costanti statici.

METODO DI SCALA DEL RAPPORTO: scala sia larghezza che altezza indipendentemente in base al rapporto di aspetto 2: 1. Prendo i valori di input di larghezza e altezza e li divido ciascuno per le costanti di larghezza e altezza e infine calcolo un fattore di regolazione in base al quale scalare i rispettivi valori di input di larghezza e altezza. Il codice effettivo appare come segue:

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

}

... ... ...

Quindi ridimensionerai individualmente i tuoi widget (che per la mia malattia perfezionista è TUTTA la mia interfaccia utente) con una semplice chiamata statica al metodo "rsWidget ()" come segue:

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

La cosa interessante è che il metodo "rsWidget ()" restituisce un widget !! Quindi puoi assegnare il widget ridimensionato a un'altra variabile come _rsBtnLoginper l'uso ovunque, oppure puoi semplicemente usare la McGyver.rsWidget()chiamata al metodo completo sul posto all'interno del tuobuild() metodo (esattamente come devi posizionarlo nell'albero del widget) e funzionerà perfettamente come dovrebbe.

Per i programmatori più astuti: avrete notato che ho usato due metodi aggiuntivi con rapporto proporzionale McGyver.rsText()e McGyver.rsDouble()(non definiti nel codice sopra) nel mio RaisedButton()- quindi praticamente impazzisco con questa roba di ridimensionamento ... perché chiedo che le mie app siano pixel assolutamente perfetto a qualsiasi scala o densità dello schermo !! Scala le proporzioni dei miei int, doppi, padding, testo (tutto ciò che richiede la coerenza dell'interfaccia utente tra i dispositivi). Ridimensiono i miei testi solo in base alla larghezza, ma specifica quale asse utilizzare per tutti gli altri ridimensionamenti (come è stato fatto con l' ScaleType.widthenumerazione utilizzata per la McGyver.rsDouble()chiamata nell'esempio di codice sopra).

So che è pazzesco - ed è molto lavoro da fare sul thread principale - ma spero che qualcuno veda il mio tentativo qui e mi aiuti a trovare una soluzione migliore (più leggera) per il ridimensionamento 1: 1 della densità dello schermo incubi.


1
@ Abbas.M - Sì, ho apportato una piccola modifica alla riga di codice con ridimensionamento del rapporto [vedi codice aggiornato] e credo che questo sia il più vicino possibile a una soluzione di ridimensionamento del rapporto 1: 1 reale - Ho provato un bel numero di opzioni per ottenere questo. Ci sono ancora alcuni strani problemi di ridimensionamento [edge-case] con questo codice aggiornato, ma la somiglianza dell'interfaccia utente su schermi a densità multipla è davvero convincente: una differenza molto sottile osservabile tra le schermate con il codice aggiornato. Per favore fatemi sapere cosa ne pensate: il feedback è molto apprezzato.
SilSur

la cosa ovvia del thread principale, sposta l'init e le chiamate al blocco principale di inizializzazione dell'app poiché la dimensione dello schermo non cambierà dopo l'inizializzazione dell'app, quindi avrai solo il thread principale colpito una volta all'avvio dell'app anziché ad esempio il rendering di ogni widget
Fred Grott

@SilSur, la tua soluzione sembra ottima. Ti dispiace condividere l'intera lezione di McGyver?
David

@David - La classe McGyver è una classe molto pesante (e specifica del progetto). Quello che ho usato in questo esempio ha molte funzioni che non sono rilevanti per il problema di ridimensionamento dell'interfaccia utente. Quindi è eccessivo / inefficiente per me caricare l'intera classe. Tuttavia, ho migliorato un po 'la classe e ho pubblicato una versione diversa del codice in un'altra domanda SO . Forse puoi aggiornare il tuo codice di ridimensionamento sulla falsariga del codice migliorato all'URL fornito.
SilSur

1

È possibile utilizzare MediaQuery per la dimensione del genitore o FractionallySizedBox come contenitori.


1

Il mio approccio al problema è simile al modo in cui lo ha fatto datayeah. Avevo molti valori di larghezza e altezza hardcoded e l'app sembrava a posto su un dispositivo specifico. Quindi ho ottenuto l'altezza dello schermo del dispositivo e ho appena creato un fattore per ridimensionare i valori codificati.

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

dove 708 è l'altezza del dispositivo specifico.


1

Cerco di renderlo il più semplice possibile. Provalo. Creo un'utilità reattiva con la funzione getresponsivevalue responsabile di fornire valore in base alle dimensioni dello schermo se non si assegna un valore per uno schermo medio, uno schermo grande, in modalità orizzontale Per impostazione predefinita, fornisce un valore assegnato allo schermo breve. un caloroso benvenuto per qualsiasi domanda. Mi piacerebbe migliorare

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

dai un'occhiata a questa pagina da flutter wiki:

Creazione di app reattive

Usa la classe LayoutBuilder: dalla sua proprietà builder, ottieni un BoxConstraints. Esaminare le proprietà del vincolo per decidere cosa visualizzare. Ad esempio, se il tuo maxWidth è maggiore del punto di interruzione della larghezza, restituisci un oggetto Scaffold con una riga che ha un elenco a sinistra. Se è più stretto, restituisce un oggetto Scaffold con un cassetto contenente tale elenco. Puoi anche regolare il display in base all'altezza del dispositivo, alle proporzioni o ad altre proprietà. Quando i vincoli cambiano (ad esempio, l'utente ruota il telefono o inserisce la tua app in un'interfaccia utente affiancata in Nougat), la funzione build verrà rieseguita.


0

crea il nome del file (app_config.dart) nel nome della cartella (responsive_screen) nella cartella 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;
 }
}

poi:

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

Questo problema può essere risolto utilizzando MediaQuery.of (context)

Per ottenere la larghezza dello schermo: MediaQuery.of(context).size.width

Per ottenere l'altezza dello schermo: MediaQuery.of(context).size.height

Per ulteriori informazioni sull'orologio 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),

Questo va bene per la raccomandazione di Google ma potrebbe non essere perfetto.


0

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


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.