在 Flutter 中按主题调整文本大小
Resizing Text by Theme in 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 '),
您可以复制粘贴 运行 下面的完整代码
因为 textScaleFactor
of Text
引用 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)
),
工作演示
完整代码
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: MediaQuery(data: _mediaQueryData, 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),
),
);
}
}
我正在尝试使用 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 '),
您可以复制粘贴 运行 下面的完整代码
因为 textScaleFactor
of Text
引用 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)
),
工作演示
完整代码
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: MediaQuery(data: _mediaQueryData, 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),
),
);
}
}