在 Flutter 中按主题调整文本大小

问题描述

我正在尝试使用 GestureDetector 来允许用户通过捏合来更改字体大小:

class _PinchToScaleFontState extends State<PinchToScaleFont> {
  double _baseFontScale = 1;
  double _fontScale = 1;
  ThemeData _themeData;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Theme(
        data: _themeData,child: widget.child       // The desired outcome is that all Text is resized
      ),onScaleStart: (ScaleStartDetails scaleStartDetails) {
        _baseFontScale = _fontScale;
      },onScaleUpdate: (ScaleUpdateDetails scaleUpdateDetails) {
        // don't update the UI if the scale didn't change
        if (scaleUpdateDetails.scale == 1.0) {
          return;
        }
        setState(() {
          double fontScale = (_baseFontScale * scaleUpdateDetails.scale).clamp(0.5,5.0);
          _updateFontScale(fontScale);
          SharedPreferences.getInstance().then((prefs) => prefs.setDouble('fontScale',fontScale));
        });
      },);
  }

我可以获得以下代码来调整 TextField 的比例,但它不会调整任何 Text 小部件的大小。

  _updateFontScale(double fontScale) {
    setState(() {
      _fontScale = fontScale;
      ThemeData theme = Theme.of(context);

      /// This doesn't seem to work at all
      // _themeData = theme.copyWith(textTheme: theme.textTheme.merge(TextTheme(bodyText2: TextStyle(fontSize: 14 * fontScale))));

      /// This works for `TextField` but not `Text`
      _themeData = theme.copyWith(textTheme: theme.textTheme.apply(fontSizefactor: fontScale)); // merge(TextTheme()));
    });
    // }
  }

很奇怪。在下面的代码中,我可以使用保存的 fontScale 在下次加载整个应用程序时初始化整个应用程序的字体大小,但是为什么上面的代码不会,它似乎正在访问相同的主题属性给出相同的结果?

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  double savedFontScale = (await SharedPreferences.getInstance()).getDouble('fontScale') ?? 1.0;

  runApp(MyApp(savedFontScale));
}

class MyApp extends StatelessWidget {
  final double fontScale;

  MyApp(this.fontScale);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: APP_NAME,theme: ThemeData(
        textTheme: TextTheme(
          /// This works for all `Text` widgets - but you've got to restart the app
          bodyText2: TextStyle(fontSize: 14 * fontScale),...
      home: 
         ...
         PinchToScaleFont(
             ...
             TextField('This _will_ resize ?'),Text('This will not resize,but it should ?'),

解决方法

您可以在下面复制粘贴运行完整代码
因为 textScaleFactorText 引用 MediaQueryData.textScaleFactor
Text.dart https://github.com/flutter/flutter/blob/97295dc9a885c995cda99ba9cee421d3ab1a8e2d/packages/flutter/lib/src/widgets/text.dart#L479

的源代码
  /// The value given to the constructor as textScaleFactor. If null,will
  /// use the [MediaQueryData.textScaleFactor] obtained from the ambient
  /// [MediaQuery],or 1.0 if there is no [MediaQuery] in scope.
  final double? textScaleFactor;

你可以用 widget.child 包裹 MediaQuery
并设置 mediaQueryData.copyWith(textScaleFactor: fontScale)
代码片段

  MediaQueryData _mediaQueryData;

  _updateFontScale(double fontScale) {
    setState(() {
      _fontScale = fontScale;
      ThemeData theme = Theme.of(context);
      MediaQueryData mediaQueryData = MediaQuery.of(context);
      ...

      _mediaQueryData = mediaQueryData.copyWith(textScaleFactor: fontScale);
    });
  ...   

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Theme(
          data: _themeData,child: MediaQuery(data: _mediaQueryData,child: widget.child)          
          ),

工作演示

enter image description here

完整代码

import 'package:flutter/material.dart';

class PinchToScaleFont extends StatefulWidget {
  final Widget child;

  const PinchToScaleFont({Key key,this.child}) : super(key: key);
  @override
  _PinchToScaleFontState createState() => _PinchToScaleFontState();
}

class _PinchToScaleFontState extends State<PinchToScaleFont> {
  double _baseFontScale = 1;
  double _fontScale = 1;
  ThemeData _themeData;
  MediaQueryData _mediaQueryData;

  _updateFontScale(double fontScale) {
    setState(() {
      _fontScale = fontScale;
      ThemeData theme = Theme.of(context);
      MediaQueryData mediaQueryData = MediaQuery.of(context);

      /// This doesn't seem to work at all
      // _themeData = theme.copyWith(textTheme: theme.textTheme.merge(TextTheme(bodyText2: TextStyle(fontSize: 14 * fontScale))));

      /// This works for `TextField` but not `Text`
      _themeData = theme.copyWith(
          textTheme: theme.textTheme
              .apply(fontSizeFactor: fontScale)); // merge(TextTheme()));

      _mediaQueryData = mediaQueryData.copyWith(textScaleFactor: fontScale);
    });
    // }
  }

  @override
  void didChangeDependencies() {
    _themeData = Theme.of(context);
    _mediaQueryData = MediaQuery.of(context);
    super.didChangeDependencies();
  }

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Theme(
          data: _themeData,child: widget.child)
          // The desired outcome is that all Text is resized
          ),onScaleStart: (ScaleStartDetails scaleStartDetails) {
        _baseFontScale = _fontScale;
      },onScaleUpdate: (ScaleUpdateDetails scaleUpdateDetails) {
        // don't update the UI if the scale didn't change
        if (scaleUpdateDetails.scale == 1.0) {
          return;
        }
        setState(() {
          double fontScale =
              (_baseFontScale * scaleUpdateDetails.scale).clamp(0.5,5.0);
          _updateFontScale(fontScale);
          //SharedPreferences.getInstance().then((prefs) => prefs.setDouble('fontScale',fontScale));
        });
      },);
  }
}

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',theme: ThemeData(
        primarySwatch: Colors.blue,),home: MyHomePage(title: 'Flutter Demo Home Page'),);
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key,this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[
            PinchToScaleFont(
                child: Column(
              children: [
                TextField(),Text('This will not resize,but it should ?'),],)),Text(
              'You have pushed the button this many times:',Text(
              '$_counter',style: Theme.of(context).textTheme.headline4,floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,tooltip: 'Increment',child: Icon(Icons.add),);
  }
}

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...