generated from dellevin/template
原生epub阅读
This commit is contained in:
301
lib/pages/epub_reader/reader_style_sheet.dart
Normal file
301
lib/pages/epub_reader/reader_style_sheet.dart
Normal file
@@ -0,0 +1,301 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'widgets/integer_stepper.dart';
|
||||
import 'widgets/reader_scale_slider.dart';
|
||||
|
||||
/// Simplified reader style configuration bottom sheet.
|
||||
///
|
||||
/// Provides zoom slider, margin controls, and font size adjustments.
|
||||
class ReaderStyleSheet extends StatefulWidget {
|
||||
final double zoom;
|
||||
final double marginTop;
|
||||
final double marginBottom;
|
||||
final double marginLeft;
|
||||
final double marginRight;
|
||||
final double fontSize;
|
||||
final ValueChanged<double> onZoomChanged;
|
||||
final ValueChanged<double> onMarginTopChanged;
|
||||
final ValueChanged<double> onMarginBottomChanged;
|
||||
final ValueChanged<double> onMarginLeftChanged;
|
||||
final ValueChanged<double> onMarginRightChanged;
|
||||
final ValueChanged<double> onFontSizeChanged;
|
||||
|
||||
const ReaderStyleSheet({
|
||||
super.key,
|
||||
required this.zoom,
|
||||
required this.marginTop,
|
||||
required this.marginBottom,
|
||||
required this.marginLeft,
|
||||
required this.marginRight,
|
||||
required this.fontSize,
|
||||
required this.onZoomChanged,
|
||||
required this.onMarginTopChanged,
|
||||
required this.onMarginBottomChanged,
|
||||
required this.onMarginLeftChanged,
|
||||
required this.onMarginRightChanged,
|
||||
required this.onFontSizeChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ReaderStyleSheet> createState() => _ReaderStyleSheetState();
|
||||
}
|
||||
|
||||
class _ReaderStyleSheetState extends State<ReaderStyleSheet> {
|
||||
late double _scale;
|
||||
late int _topMargin;
|
||||
late int _bottomMargin;
|
||||
late int _leftMargin;
|
||||
late int _rightMargin;
|
||||
late double _fontSize;
|
||||
|
||||
static const int _marginMin = 0;
|
||||
static const int _marginMax = 64;
|
||||
static const int _marginStep = 2;
|
||||
static const double _fontSizeMin = 12.0;
|
||||
static const double _fontSizeMax = 32.0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_scale = widget.zoom;
|
||||
_topMargin = widget.marginTop.toInt();
|
||||
_bottomMargin = widget.marginBottom.toInt();
|
||||
_leftMargin = widget.marginLeft.toInt();
|
||||
_rightMargin = widget.marginRight.toInt();
|
||||
_fontSize = widget.fontSize;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final bottomPadding = MediaQuery.of(context).padding.bottom;
|
||||
|
||||
return DraggableScrollableSheet(
|
||||
initialChildSize: 1.0,
|
||||
minChildSize: 0.5,
|
||||
expand: false,
|
||||
builder: (BuildContext context, ScrollController scrollController) {
|
||||
return SingleChildScrollView(
|
||||
controller: scrollController,
|
||||
physics: const ClampingScrollPhysics(),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(24, 12, 24, 24 + bottomPadding),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// -- 缩放比例 --
|
||||
const _SectionTitle(label: '缩放比例'),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
children: [
|
||||
const _SubLabel(label: '缩放'),
|
||||
const Spacer(),
|
||||
Text(
|
||||
'${_scale.toStringAsFixed(1)}x',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: colorScheme.onSurfaceVariant.withValues(
|
||||
alpha: 0.5,
|
||||
),
|
||||
fontFeatures: const [FontFeature.tabularFigures()],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
ReaderScaleSlider(
|
||||
value: _scale,
|
||||
onChanged: (v) {
|
||||
setState(() => _scale = v);
|
||||
widget.onZoomChanged(v);
|
||||
},
|
||||
),
|
||||
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// -- 字号 --
|
||||
const _SectionTitle(label: '字号'),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
children: [
|
||||
const _SubLabel(label: '字号'),
|
||||
const Spacer(),
|
||||
Text(
|
||||
'${_fontSize.toInt()}px',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: colorScheme.onSurfaceVariant.withValues(
|
||||
alpha: 0.5,
|
||||
),
|
||||
fontFeatures: const [FontFeature.tabularFigures()],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: _fontSize > _fontSizeMin
|
||||
? () {
|
||||
final v =
|
||||
(_fontSize - 1).clamp(_fontSizeMin, _fontSizeMax);
|
||||
setState(() => _fontSize = v);
|
||||
widget.onFontSizeChanged(v);
|
||||
}
|
||||
: null,
|
||||
child: Text(
|
||||
'A',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: _fontSize > _fontSizeMin
|
||||
? colorScheme.onSurfaceVariant
|
||||
: colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Slider(
|
||||
value: _fontSize,
|
||||
min: _fontSizeMin,
|
||||
max: _fontSizeMax,
|
||||
divisions: 20,
|
||||
label: _fontSize.toInt().toString(),
|
||||
onChanged: (v) {
|
||||
setState(() => _fontSize = v);
|
||||
widget.onFontSizeChanged(v);
|
||||
},
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: _fontSize < _fontSizeMax
|
||||
? () {
|
||||
final v =
|
||||
(_fontSize + 1).clamp(_fontSizeMin, _fontSizeMax);
|
||||
setState(() => _fontSize = v);
|
||||
widget.onFontSizeChanged(v);
|
||||
}
|
||||
: null,
|
||||
child: Text(
|
||||
'A',
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
color: _fontSize < _fontSizeMax
|
||||
? colorScheme.onSurfaceVariant
|
||||
: colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// -- 边距 --
|
||||
const _SectionTitle(label: '边距'),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: IntegerStepper(
|
||||
label: '上',
|
||||
value: _topMargin,
|
||||
min: _marginMin,
|
||||
max: _marginMax,
|
||||
step: _marginStep,
|
||||
onChanged: (v) {
|
||||
setState(() => _topMargin = v);
|
||||
widget.onMarginTopChanged(v.toDouble());
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: IntegerStepper(
|
||||
label: '下',
|
||||
value: _bottomMargin,
|
||||
min: _marginMin,
|
||||
max: _marginMax,
|
||||
step: _marginStep,
|
||||
onChanged: (v) {
|
||||
setState(() => _bottomMargin = v);
|
||||
widget.onMarginBottomChanged(v.toDouble());
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: IntegerStepper(
|
||||
label: '左',
|
||||
value: _leftMargin,
|
||||
min: _marginMin,
|
||||
max: _marginMax,
|
||||
step: _marginStep,
|
||||
onChanged: (v) {
|
||||
setState(() => _leftMargin = v);
|
||||
widget.onMarginLeftChanged(v.toDouble());
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: IntegerStepper(
|
||||
label: '右',
|
||||
value: _rightMargin,
|
||||
min: _marginMin,
|
||||
max: _marginMax,
|
||||
step: _marginStep,
|
||||
onChanged: (v) {
|
||||
setState(() => _rightMargin = v);
|
||||
widget.onMarginRightChanged(v.toDouble());
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Section title (equivalent to lumina's SettingsSectionTitle)
|
||||
class _SectionTitle extends StatelessWidget {
|
||||
const _SectionTitle({required this.label});
|
||||
final String label;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Sub-label (equivalent to lumina's SettingsSubLabel)
|
||||
class _SubLabel extends StatelessWidget {
|
||||
const _SubLabel({required this.label});
|
||||
final String label;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user