#flutter #gesturedetector #flutter-theme
Вопрос:
Я пытаюсь использовать a 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));
});
},
);
}
Я могу получить следующий код для настройки масштаба a 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()));
});
// }
}
Это странно. В приведенном ниже коде я могу использовать сохраненный масштаб шрифта для инициализации размера шрифта для всего приложения при следующей загрузке, но почему приведенный выше код, который, похоже, обращается к одному и тому же свойству темы, не даст тех же результатов?
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 😀'),
Ответ №1:
Вы можете скопировать вставить полный код ниже
Из-за textScaleFactor
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),
),
);
}
}
Комментарии:
1. Классно! Спасибо за работу по созданию демо-версии!
2. Рад помочь. пожалуйста, отметьте это как ответ, если это вам поможет. Спасибо.