¿Cómo hacer que la aplicación Flutter responda de acuerdo con diferentes tamaños de pantalla?


85

Estoy enfrentando dificultades para que responda de acuerdo con varios tamaños de pantalla. ¿Cómo hacerlo receptivo?

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

Respuestas:


72

Usando MediaQueryclase:

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

MediaQuery : establece un subárbol en el que las consultas de medios se resuelven en los datos proporcionados.

MediaQueryData : información sobre un medio (por ejemplo, una ventana).

Para obtener la relación de píxeles del dispositivo:

queryData.devicePixelRatio

Para obtener el ancho y el alto de la pantalla del dispositivo:

queryData.size.width
queryData.size.height

Para obtener el factor de escala del texto:

queryData.textScaleFactor

Usando AspectRatioclase:

Del doc:

Un widget que intenta ajustar el tamaño del niño a una relación de aspecto específica.

El widget primero prueba el ancho más grande permitido por las restricciones de diseño. La altura del widget se determina aplicando la relación de aspecto dada al ancho, expresada como una relación de ancho a alto.

Por ejemplo, una relación de aspecto de ancho: alto de 16: 9 tendría un valor de 16,0 / 9,0. Si el ancho máximo es infinito, el ancho inicial se determina aplicando la relación de aspecto a la altura máxima.

Ahora considere un segundo ejemplo, esta vez con una relación de aspecto de 2.0 y restricciones de diseño que requieren que el ancho esté entre 0.0 y 100.0 y la altura entre 0.0 y 100.0. Seleccionaremos un ancho de 100.0 (el más grande permitido) y un alto de 50.0 (para que coincida con la relación de aspecto).

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

También puedes usar :


3
Puedo obtener el ancho y la altura del dispositivo, ¿cómo puedo configurar el tamaño de prueba, el relleno, el margen queryData?
Farhana

28

Esta clase ayudará y luego inicializará la clase con el método 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;
  }
}

luego en la dimensión de tus widgets haz esto

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

Todos los créditos al autor de esta publicación: https://medium.com/flutter-community/flutter-effectively-scale-ui-according-to-different-screen-sizes-2cb7c115ea0a


¿Cómo agregar la parte inferior de EdgeInsets con esta clase SizeConfig?
Farwa

Creo que un acolchado del contenedor funcionará. ¡¡Intenta decirme que te ayude !!

16

Lo que hago es tomar el ancho y la altura de la pantalla y calcular una cuadrícula de 100 * 100 para colocar y escalar cosas y guardarlas como variables estáticas que se pueden reutilizar. Funciona bastante bien en la mayoría de los casos. Me gusta esto:

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

Luego escalo todo de acuerdo con estos valores, así:

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

o

double fontSize = AppConfig.blockSize * 1.2;

A veces, el área segura (muesca, etc.) mata un diseño, por lo que también puede considerar esto:

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

double screenWidthWithoutSafeArea = AppConfig.width - AppConfig.safeAreaHorizontal;

Esto funcionó muy bien en algunos proyectos recientes.


1
¿Cómo calcular los tamaños de fuente? ¿Es bueno calcular según el ancho o el alto?
Harsh Bhavsar

Los estoy calculando en función del ancho. Pero para ser honesto, no lo probé con aplicaciones que admiten tanto el modo horizontal como el vertical. Pero es posible que aún pueda calcularlo de manera diferente en ambas orientaciones.
datayeah

¿Cómo resuelve esto exactamente el problema de las diferencias de densidad de pantalla? Al decir que divide la pantalla en 100 * 100 bloques de cuadrícula, hace que parezca que los bloques resultantes tienen el mismo tamaño (es decir, cuadrados), pero no lo son. Si tiene un dispositivo que tiene el doble de píxeles verticalmente (altura de la pantalla) que horizontalmente (ancho de la pantalla), sus bloques resultantes serán rectángulos en lugar de cuadrados, lo que significa que su código aún produce el mismo problema que comenzó tratando de resolver. Pruebe su código en múltiples densidades de pantalla para probar esto. Así que esto no es realmente una solución para mí.
SilSur

@SilSur, seguro que los bloques no son del mismo tamaño en ningún dispositivo y densidad, pero eso es lo que lo hace funcionar (en la mayoría de los casos). Solo tengo que decidir para cada widget que agregue a la pantalla si quiero que se calcule su posición y tamaño con respecto al ancho o alto de un bloque, o ambos. He usado este método en aplicaciones que se ejecutan en cualquier teléfono / tableta iphone, ipad o android sin correcciones específicas del dispositivo. paisaje y retrato. pero tiene razón en que este método aún no resuelve a la perfección problemas complejos de IU. todavía estoy buscando algo mejor para manejar esto.
datayeah

9

Consultar MediaQueryclase

Por ejemplo, para obtener el tamaño de los medios de comunicación actuales (por ejemplo, la ventana que contiene la aplicación), se puede leer la MediaQueryData.sizepropiedad de la MediaQueryDatadevuelta por MediaQuery.of: MediaQuery.of(context).size.

Entonces puedes hacer lo siguiente:

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

¿Quieres decir que en lugar de utilizar mediaQuery posicionado?
praveen Dp

No entiendo qué estás tratando de hacer
aziza

utilizando relleno posicionado dentro de la pila. Lo he ajustado al tamaño de la pantalla. Ahora, para que responda, debo usar mediaquery en lugar de qué ??
praveen Dp

5

Puede tomar un porcentaje del ancho o alto como entrada para el tamaño de escala.

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

Donde el multiplicador al final tiene un valor que hace que el texto se vea bien para el emulador activo.

A continuación se muestra cómo lo configuré para que todas las dimensiones escaladas estén centralizadas en un solo lugar. De esta manera, puede ajustarlos fácilmente y volver a ejecutarlos rápidamente con Hot Reload sin tener que buscar las Media.of()llamadas en todo el código.

  1. Cree el archivo para almacenar todas las asignaciones 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. Luego haga referencia a eso donde quiera que necesite el valor escalado

    AppScale _scale = AppScale(context);

    // ... 

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

Gracias a las respuestas en esta publicación.


4

He estado criticando un poco las soluciones de otras personas (@datayeah y Vithani Ravi) aquí, así que pensé en compartir mis propios intentos para resolver este problema de escala de densidad de pantalla variable o callarme. Así que abordo este problema desde una base sólida / fija: baso toda mi escala en una proporción fija (inmutable) de 2: 1 (alto: ancho). Tengo una clase de ayuda "McGyver" que hace todo el trabajo pesado (y el código útil) en mi aplicación. Esta clase "McGyver" contiene solo métodos estáticos y miembros de clase constantes estáticos.

MÉTODO DE ESCALA DE RELACIÓN: Escalo tanto el ancho como la altura de forma independiente en función de la relación de aspecto 2: 1. Tomo valores de entrada de ancho y alto y divido cada uno por las constantes de ancho y alto y finalmente calculo un factor de ajuste por el cual escalar los respectivos valores de entrada de ancho y alto. El código real tiene el siguiente aspecto:

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

}

... ... ...

Luego, escalaría individualmente sus widgets (que para mi enfermedad perfeccionista es TODA mi IU) con una simple llamada estática al método "rsWidget ()" de la siguiente manera:

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

¡Lo bueno es que el método "rsWidget ()" devuelve un widget! Por lo tanto, puede asignar el widget escalado a otra variable _rsBtnLoginpara usar en todo el lugar, o simplemente puede usar la McGyver.rsWidget()llamada al método completo en el lugar dentro de subuild() método (exactamente cómo necesita colocarlo en el árbol de widgets) y funcionará perfectamente como debería.

Para aquellos programadores más astutos: habrán notado que utilicé dos métodos adicionales de escala de proporción McGyver.rsText()y McGyver.rsDouble()(no definido en el código anterior) en mi RaisedButton()- así que básicamente me vuelvo loco con estas cosas de escala ... porque exijo que mis aplicaciones sean ¡absolutamente perfecto en píxeles a cualquier escala o densidad de pantalla! Escalo en proporción mis entradas, dobles, relleno, texto (todo lo que requiere coherencia de la interfaz de usuario en todos los dispositivos). Escalo mis textos solo en función del ancho, pero especifico qué eje usar para todas las demás escalas (como se hizo con la ScaleType.widthenumeración utilizada para la McGyver.rsDouble()llamada en el ejemplo de código anterior).

Sé que esto es una locura, y hay mucho trabajo por hacer en el hilo principal, pero espero que alguien vea mi intento aquí y me ayude a encontrar una solución mejor (más liviana) para mi escala de densidad de pantalla 1: 1 pesadillas.


1
@ Abbas.M - Sí, hice un pequeño cambio en la línea de código de escala de proporción [ver código actualizado] y creo que esto es lo más cerca que puedo llegar a una solución de escala de proporción real 1: 1 - He probado bastantes números de opciones para conseguir este. Todavía hay algunos problemas de escalado extraños [casos de borde] con este código actualizado, pero la similitud de la interfaz de usuario en pantallas de densidad múltiple es realmente convincente: una diferencia muy sutil observable entre las pantallas con el código actualizado. Hágame saber lo que piensa. Agradecemos mucho sus comentarios.
SilSur

lo obvio sobre el hilo principal, mueva el init y las llamadas al bloque principal de inicio de la aplicación, ya que el tamaño de la pantalla no cambiará después del inicio de la aplicación, por lo que solo tendrá el hilo principal una vez en el inicio de la aplicación en lugar de, por ejemplo, cada representación de widget
Fred Grott

@SilSur, su solución se ve muy bien. ¿Te importaría compartir toda la clase de McGyver?
David

@David: la clase McGyver es una clase muy pesada (y específica del proyecto). El que utilicé en este ejemplo tiene muchas funciones que no son relevantes para el problema de escala de la interfaz de usuario. Así que es excesivo / ineficaz para mí cargar la clase completa. Sin embargo, mejoré un poco la clase y publiqué una versión diferente del código en otra pregunta SO . Quizás pueda actualizar su código de escala según las líneas del código mejorado en la URL proporcionada.
SilSur

4

Después de mucha investigación y pruebas, he desarrollado una solución para una aplicación que actualmente estoy convirtiendo de Android / iOS a Flutter.

Con Android e iOS utilicé un 'Factor de escala' aplicado a los tamaños de fuente base, representando tamaños de texto que eran relativos al tamaño de pantalla.

Este artículo fue muy útil: https://medium.com/flutter-community/flutter-effectively-scale-ui-according-to-different-screen-sizes-2cb7c115ea0a

Creé un StatelessWidget para obtener los tamaños de fuente de los estilos tipográficos de Material Design. Obtener las dimensiones del dispositivo mediante MediaQuery, calcular un factor de escala y luego restablecer los tamaños de texto de Material Design. El widget se puede utilizar para definir un tema de diseño de materiales personalizado.

Emuladores utilizados:

  • Pixel C: tableta de 9,94 "
  • Pixel 3: teléfono de 5,46 "
  • iPhone 11 Pro Max - Teléfono de 5.8 "

Con tamaños de fuente estándar

Con tamaños de fuente escalados

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 (aplicación de demostración)

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
¿Quizás una explicación de lo que sucede bajo el capó sería genial la próxima vez que publique una "solución"? De todos modos, revisé GitHub para esta dependencia. Es básicamente una clase única (con 16 líneas de código) que toma los valores de ancho y alto de entrada y los escala en función del ancho y alto de la pantalla como un porcentaje. Es esencialmente lo mismo que la solución de @datayeah; la única diferencia es que esta está perfectamente empaquetada. Aquí se aplican los mismos problemas que datayeah; no es una buena solución para el escalado 1: 1 en dispositivos de densidad de pantalla variada. El problema de la densidad de la pantalla NO SE RESUELVE con esta "solución".
SilSur

1

Puede usar MediaQuery para la dimensión principal o FractionallySizedBox como contenedores.


1

Mi enfoque del problema es similar a la forma en que lo hizo datayeah. Tenía muchos valores de ancho y alto codificados y la aplicación se veía bien en un dispositivo específico. Así que obtuve la altura de la pantalla del dispositivo y creé un factor para escalar los valores codificados.

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

donde 708 es la altura del dispositivo específico.


1

Intento hacerlo lo más simple posible. pruébalo. Hago una utilidad receptiva que tiene la función getresponsivevalue responsable de proporcionar valor de acuerdo con el tamaño de la pantalla si no asigna un valor para una pantalla mediana, pantalla grande, modo horizontal De forma predeterminada, proporciona un valor asignado de pantalla corta. una cálida bienvenida para cualquier consulta. Me encantaría mejorar

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

echa un vistazo a esta página de flutter wiki:

Creación de aplicaciones receptivas

Use la clase LayoutBuilder: de su propiedad de constructor, obtiene un BoxConstraints. Examine las propiedades de la restricción para decidir qué mostrar. Por ejemplo, si su maxWidth es mayor que su punto de interrupción de ancho, devuelva un objeto Scaffold con una fila que tenga una lista a la izquierda. Si es más estrecho, devuelva un objeto Scaffold con un cajón que contenga esa lista. También puede ajustar su pantalla según la altura del dispositivo, la relación de aspecto o alguna otra propiedad. Cuando las restricciones cambian (por ejemplo, el usuario gira el teléfono o coloca su aplicación en una interfaz de usuario de mosaico en Nougat), la función de compilación se volverá a ejecutar.


0

cree el nombre del archivo (app_config.dart) en el nombre de la carpeta (responsive_screen) en la carpeta 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;
 }
}

entonces:

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

Este problema se puede resolver usando MediaQuery.of (contexto)

Para obtener el ancho de la pantalla: MediaQuery.of(context).size.width

Para obtener la altura de la pantalla: MediaQuery.of(context).size.height

Para obtener más información sobre el reloj 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),

Esto está bien por recomendación de Google, pero puede que no sea perfecto.


0

ResponsiveBuilder o ScreenTypeLayout usados

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

La forma más fácil de hacer una interfaz de usuario receptiva para diferentes tamaños de pantalla es el complemento Sizer .

Haga una interfaz de usuario receptiva en cualquier dispositivo de tamaño de pantalla, también en tableta. Compruébalo en este complemento ⬇️
https://pub.dev/packages/sizer

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

Uso .h, .w,.sp después de valor como esto ⬇️

Ejemplo:

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

He creado muchas aplicaciones receptivas con este complemento.


Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.