如何在Flutter中创建响应式文本小部件?

24

我在响应式文本方面遇到了问题。在我的应用程序中,有不同的文本字体大小,我需要让它们适应不同的屏幕尺寸(仅限于手机和设备方向为纵向)。我还像这样将textScaleFactor: 1.0添加到我的MaterialApp中:

    builder: (context, widget) {
      return MediaQuery(
        child: widget,
        data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
      );
    },

但它并没有帮助太多。我尝试使用MediaQuery.of(context).size.width计算字体大小,但我认为这很危险也是错误的。我希望它能够尽可能接近给我的设计,但在这些步骤中我开始失去控制。

我该如何解决这个问题?


3
你可以使用 MediaQuery.of(context).textScaleFactor * 30 这个表达式。 - hio
11个回答

15

在为Web和移动端开发响应式应用时,我遇到了同样的问题,但是 textScaleFactor 帮助解决了我的问题。默认的 textScaleFactor 是 1,所以我编写了一个方法根据屏幕宽度来改变 textScaleFactor

class ScaleSize {
  static double textScaleFactor(BuildContext context, {double maxTextScaleFactor = 2}) {
    final width = MediaQuery.of(context).size.width;
    double val = (width / 1400) * maxTextScaleFactor;
    return max(1, min(val, maxTextScaleFactor));
  }
}

我们可以根据比例控制大小版本, 在Text小部件中使用。
Text(
     intro.subtitle,
     style: Theme.of(context).textTheme.subtitle1,
     textAlign: TextAlign.center,
     textScaleFactor: ScaleSize.textScaleFactor(context),
   ),

enter image description here


11

您可以使用这个插件flutter_screenutil,它是一个用于适应屏幕和字体大小的flutter插件。让您的UI在不同的屏幕尺寸上显示合理的布局!

初始化并设置适合系统“字体大小”可访问选项的比例系数和字体大小# 在使用之前,请设置设计稿的宽度和高度,设计稿的宽度和高度(单位px)。请务必在MaterialApp的home中设置页面(即入口文件,只需设置一次),以确保在每次使用前设置适配比例:

//fill in the screen size of the device in the design

//default value : width : 1080px , height:1920px , 
allowFontScaling:false
ScreenUtil.instance = ScreenUtil.getInstance()..init(context);

//If the design is based on the size of the iPhone6 ​​(iPhone6 ​​750*1334)
ScreenUtil.instance = ScreenUtil(width: 750, height: 
1334)..init(context);

//If you wang to set the font size is scaled according to the system's 
"font size" assist option
ScreenUtil.instance = ScreenUtil(width: 750, height: 1334, 
allowFontScaling: true)..init(context);

使用方法:# 适应屏幕大小:# 传递设计草稿的像素大小:

适应屏幕宽度: ScreenUtil.getInstance().setWidth(540),

适应屏幕高度: ScreenUtil.getInstance().setHeight(200),

你也可以使用ScreenUtil()代替ScreenUtil.getInstance(),例如:ScreenUtil().setHeight(200)

注意:

高度也会根据setWidth方法进行适应,以确保没有变形(当您需要一个正方形时)

setHeight方法主要是在高度上进行适应,当您想要控制屏幕上UI显示的高度和实际高度相同时使用。

//for example:
//rectangle
Container(
       width: ScreenUtil.getInstance().setWidth(375),
       height: ScreenUtil.getInstance().setHeight(200),
       ...
        ),

////If you want to display a square:
Container(
       width: ScreenUtil.getInstance().setWidth(300),
       height: ScreenUtil.getInstance().setWidth(300),
        ),

适配器字体:

//Incoming font size,the unit is pixel, fonts will not scale to 
respect Text Size accessibility settings
//(AllowallowFontScaling when initializing ScreenUtil)
ScreenUtil.getInstance().setSp(28)    

//Incoming font size,the unit is pixel,fonts will scale to respect Text 
Size accessibility settings
//(If somewhere does not follow the global allowFontScaling setting)
ScreenUtil(allowFontScaling: true).setSp(28)  

//for example:

Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Text(
                'My font size is 24px on the design draft and will not change with the system.',
                style: TextStyle(
                  color: Colors.black,
                  fontSize: ScreenUtil.getInstance().setSp(24),
                )),
            Text(
                'My font size is 24px on the design draft and will change with the system.',
                style: TextStyle(
                  color: Colors.black,
                  fontSize: ScreenUtil(allowFontScaling: true).setSp(24),
                )),
          ],
        )

其他相关的API:

ScreenUtil.pixelRatio       //Device pixel density
ScreenUtil.screenWidth      //Device width
ScreenUtil.screenHeight     //Device height
ScreenUtil.bottomBarHeight  //Bottom safe zone distance, suitable for buttons with full screen
ScreenUtil.statusBarHeight  //Status bar height , Notch will be higher Unit px
ScreenUtil.textScaleFactory //System font scaling factor

ScreenUtil.getInstance().scaleWidth //Ratio of actual width dp to design draft px
ScreenUtil.getInstance().scaleHeight //Ratio of actual height dp to design draft px

谢谢,这部分是有效的,但在某些文本上不起作用。 - Babken
1
我再次尝试使用这个软件包,但没有效果,即使使用这个软件包也没有任何改变。 - Babken

6

试试这个:根据不同的屏幕尺寸获取自适应文本大小

    class AdaptiveTextSize {
      const AdaptiveTextSize();

      getadaptiveTextSize(BuildContext context, dynamic value) {
    // 720 is medium screen height
        return (value / 720) * MediaQuery.of(context).size.height;
      }
    }

使用案例:

                 Text("Paras Arora",style: TextStyle(fontSize: 
                 AdaptiveTextSize().getadaptiveTextSize(context, 20)),

1
谢谢您的回答。这也是一个解决方案,但我想在主题中添加字体大小,所以我不能在那里使用上下文。 - Babken
@Babken,你可以为此创建一个自定义助手和主题数据,并在主题数据助手中使用此助手,您将能够拥有所有功能。如果您不想创建自己的软件包,您可以使用Sizer软件包。但对我来说,Sizer也不好,所以我正在创建自己的软件包,并将很快发布它。 - Shoaib Khan

5
class SizeConfig {
  static MediaQueryData _mediaQueryData;
  static double screenWidth;
  static double screenHeight;
  static double blockSizeHorizontal;
  static double blockSizeVertical;

  void init(BuildContext context) {
    _mediaQueryData = MediaQuery.of(context);
    screenWidth = _mediaQueryData.size.width;
    screenHeight = _mediaQueryData.size.height;
    blockSizeHorizontal = screenWidth / 100;
    blockSizeVertical = screenHeight / 100;
  }
}

SizeConfig().init(context); 在 widget build 后添加并使用 style: TextStyle(fontSize: 2 * SizeConfig.blockSizeVertical,),

将其乘以您想要的数字,至少尝试一次。我已经附上了屏幕截图。

我的解决方案:
My solution

其他解决方案:
Other Solutions


2
谢谢您的回答。这也是一个解决方案,但我想在主题中添加字体大小,所以我不能在那里使用上下文。 - Babken
你可以创建一个样式文件夹来描述所有的样式,如果你想的话。 - mufakir

3

您可以从LayoutBuilder中获取constraints,并将其作为以下代码所示传递给ScreenUtil.init()

return LayoutBuilder(
      builder: (BuildContext context, BoxConstraints constraints) {
        return OrientationBuilder(
          builder: (BuildContext context, Orientation orientation) {
            ScreenUtil.init(
              constraints,
              designSize: orientation == Orientation.portrait
                  ? (Platform.isAndroid || Platform.isIOS) ? Size(450.0, 870.0) : 
                    Size(705.0, 1366.0)
                  : (Platform.isAndroid || Platform.isIOS) ? Size(870.0, 450.0) : 
                    Size(1366.0, 705.0),
              allowFontScaling: false,
            );
            return MaterialApp(
              theme: ThemeData(
                textTheme: Theme.of(context).textTheme.copyWith(
                      headline6: TextStyle(
                        fontSize: 24.sp,
                      ),
                    ),
              ),
              home: HomeScreen(),
            );
          },
        );
      },
    );

为了设置设计屏幕的宽度和高度,我们可以检查orientation == Orientation.portrait。如果要支持两个方向则需要相应地反转宽度和高度值。

您还可以检查Platform.isAndroid || Platform.isIOS并给出移动设备的宽度和高度。


3
你可以尝试这样做:
final size = MediaQuery.of(context).size;

你可以在这种情况下应用相同的概念,针对容器进行操作:
Container(
            width: size.width * 0.85,
           ...
)

我只是针对(Container、Padding、SizedBox等)这些进行操作,但我正在寻找一种方法来调整文本的字体大小。 - Babken
我在文本方面使用了这个很好。唯一的缺点是样式将位于小部件内部。我想要在 textTheme 级别定义某些内容,但还没有做到。 - Audacitus

1

对于类似于应用程序手机大小的字体大小:

MaterialApp( home: MediaQuery(data:MediaQuery.of(context).copyWith(textScaleFactor: 1.2), child: HomePage()) );

对于ListTile_Subtitle:

Text('您的文本', textScaleFactor:1.0)

注意:这里,普通文本中的1.2 = 1.2 x(设备的_FontSize), 对于ListTile_Subtitle,我们需要比正常字体大小更小,因此我们使用Text Widget的textScaleFactor参数来控制它,在这里表示: 1x(设备的_FontSize),

如果您需要大于设备正常字体大小的大字体,则为, Text('aaaa' , textScaleFactor:2)


1
我想回答我的问题。在过去的几个月中,我一直在使用flutter_screenutil。但正如我在评论中提到的那样,我需要在主题中添加响应式字体大小,因此我定制了flutter_screenutil包以在其中使用它。我认为它工作得非常完美。我已经在几个项目中使用了这个解决方案,没有遇到任何问题。
import 'package:flutter/material.dart';

class CustomScreenUtil {
  static CustomScreenUtil _instance;
  static const int defaultWidth = 1080;
  static const int defaultHeight = 1920;

  /// Size of the phone in UI Design , px
  num uiWidthPx;
  num uiHeightPx;

  /// allowFontScaling Specifies whether fonts should scale to respect Text Size accessibility settings. The default is false.
  bool allowFontScaling;

  static double _screenWidth;
  static double _screenHeight;
  static double _pixelRatio;
  static double _statusBarHeight;
  static double _bottomBarHeight;
  static double _textScaleFactor;

  CustomScreenUtil._();

  factory CustomScreenUtil() {
    return _instance;
  }

  static void init({num width = defaultWidth,
    num height = defaultHeight,
    bool allowFontScaling = false}) {
    if (_instance == null) {
      _instance = CustomScreenUtil._();
    }
    _instance.uiWidthPx = width;
    _instance.uiHeightPx = height;
    _instance.allowFontScaling = allowFontScaling;

    _pixelRatio = WidgetsBinding.instance.window.devicePixelRatio;
    _screenWidth = WidgetsBinding.instance.window.physicalSize.width;
    _screenHeight = WidgetsBinding.instance.window.physicalSize.height;
    _statusBarHeight = WidgetsBinding.instance.window.padding.top;
    _bottomBarHeight = WidgetsBinding.instance.window.padding.bottom;
    _textScaleFactor = WidgetsBinding.instance.window.textScaleFactor;
  }

  /// The number of font pixels for each logical pixel.
  static double get textScaleFactor => _textScaleFactor;

  /// The size of the media in logical pixels (e.g, the size of the screen).
  static double get pixelRatio => _pixelRatio;

  /// The horizontal extent of this size.
  static double get screenWidthDp => _screenWidth;

  ///The vertical extent of this size. dp
  static double get screenHeightDp => _screenHeight;

  /// The vertical extent of this size. px
  static double get screenWidth => _screenWidth * _pixelRatio;

  /// The vertical extent of this size. px
  static double get screenHeight => _screenHeight * _pixelRatio;

  /// The offset from the top
  static double get statusBarHeight => _statusBarHeight;

  /// The offset from the bottom.
  static double get bottomBarHeight => _bottomBarHeight;

  /// The ratio of the actual dp to the design draft px
  double get scaleWidth => _screenWidth / uiWidthPx;

  double get scaleHeight => _screenHeight / uiHeightPx;

  double get scaleText => scaleWidth;

  /// Adapted to the device width of the UI Design.
  /// Height can also be adapted according to this to ensure no deformation ,
  /// if you want a square
  num setWidth(num width) => width * scaleWidth;

  /// Highly adaptable to the device according to UI Design
  /// It is recommended to use this method to achieve a high degree of adaptation
  /// when it is found that one screen in the UI design
  /// does not match the current style effect, or if there is a difference in shape.
  num setHeight(num height) => height * scaleHeight;

  ///Font size adaptation method
  ///@param [fontSize] The size of the font on the UI design, in px.
  ///@param [allowFontScaling]
  num setSp(num fontSize, {bool allowFontScalingSelf}) =>
      allowFontScalingSelf == null
          ? (allowFontScaling
          ? (fontSize * scaleText)
          : ((fontSize * scaleText) / _textScaleFactor))
          : (allowFontScalingSelf
          ? (fontSize * scaleText)
          : ((fontSize * scaleText) / _textScaleFactor));
}


现在屏幕工具使用WidgetsBinding.instance.window中的大小,而不是MediaQuery中的大小,现在我们可以像这样无需上下文使用它:
_screenUtil = CustomScreenUtil();
ThemeData(
        primaryTextTheme: TextTheme(
          bodyText1: TextStyle(
            fontSize: _screenUtil.setSp(12),
          )
        ))

我不知道这是否是最好的解决方案,但我是这样工作的。


0
LayoutBuilder(
  builder:(context,constraints){
    return Text("this is responsive text",
       style:TextStyle
       (fontSize:constraints.maxWidth*the percentage of your text));
       //("this is how to calculate //    percentage of fontsize"
       //    e.g "fontSize/total Width*100" then for example i recived the percentage on // 
       //  calculator"3.45" then multiply the maxWidth with 0.0345)
    }
  );
)

0

1. 第一种解决方案

您可以使用auto_size_text package

2. 第二种解决方案 输入图像描述

如果您不想使用该软件包并且更喜欢使用自定义主题,则可以使用以下方法:

获取宽度

    @override
  Widget build(BuildContext context) {
    double width = MediaQuery.of(context).size.width;
     return Container(...

在您的theme_provider类中创建自定义的ThemeData,例如用于浅色和深色

static ThemeData dark() {
return ThemeData(
  textTheme: const TextTheme(
   bodyText2:
        TextStyle(color: Colors.white70, fontWeight: FontWeight.w300)...

最后,使用不同的字体大小为不同的屏幕编辑您的文本小部件。您也可以使用屏幕高度...
Text( 
    widget.featuredModel.eventTitle,
    style: Theme.of(context).textTheme.bodyText2!
   .copyWith(  fontSize: width >= 1201
                          ? 22
                          : width >= 601
                              ? 20
                              : width >= 400
                                  ? 16
                                  : 14),
                ),

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接