Files
MookNote/lib/pages/reader/style_panel.dart
2026-06-20 00:54:40 +08:00

163 lines
5.1 KiB
Dart

import 'package:flutter/material.dart';
import '../../utils/user_prefs.dart';
import 'epub_player.dart';
/// 预设主题
const _presetThemes = [
{'bg': 'FFFFFFFF', 'fg': 'FF1A1A1A', 'name': '默认'},
{'bg': 'FF1A1A1A', 'fg': 'FFE5E5E5', 'name': '暗黑'},
{'bg': 'FFF8F0E3', 'fg': 'FF333333', 'name': '护眼'},
{'bg': 'FF2B2B2B', 'fg': 'FFCCCCCC', 'name': '深灰'},
{'bg': 'FF2D3E50', 'fg': 'FFD4D4D4', 'name': '蓝灰'},
];
/// 样式设置面板 — 字号、行距、主题
class StylePanel extends StatefulWidget {
final GlobalKey<EpubPlayerState> epubPlayerKey;
const StylePanel({super.key, required this.epubPlayerKey});
@override
State<StylePanel> createState() => _StylePanelState();
}
class _StylePanelState extends State<StylePanel> {
double _fontSize = 1.0;
double _lineHeight = 1.6;
int _selectedTheme = 0;
@override
void initState() {
super.initState();
_loadPrefs();
}
void _loadPrefs() {
final sp = UserPrefs().prefs;
setState(() {
_fontSize = sp.getDouble('reader_font_size') ?? 1.0;
_lineHeight = sp.getDouble('reader_line_height') ?? 1.6;
_selectedTheme = sp.getInt('reader_theme_index') ?? 0;
});
}
void _savePrefs() {
final sp = UserPrefs().prefs;
sp.setDouble('reader_font_size', _fontSize);
sp.setDouble('reader_line_height', _lineHeight);
sp.setInt('reader_theme_index', _selectedTheme);
}
void _applyStyle() {
widget.epubPlayerKey.currentState?.changeStyle(
fontSize: _fontSize,
lineHeight: _lineHeight,
);
_savePrefs();
}
void _applyTheme(int index) {
final theme = _presetThemes[index];
final bg = theme['bg']!;
final fg = theme['fg']!;
widget.epubPlayerKey.currentState?.changeTheme(bg, fg);
setState(() => _selectedTheme = index);
_savePrefs();
}
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 字号
Row(
children: [
Text('字号', style: TextStyle(fontSize: 13, color: colors.onSurface.withValues(alpha: 0.6))),
Expanded(
child: Slider(
value: _fontSize,
min: 0.5,
max: 3.0,
divisions: 25,
label: '${(_fontSize * 100).round()}%',
onChanged: (value) {
setState(() => _fontSize = value);
_applyStyle();
},
),
),
SizedBox(
width: 44,
child: Text('${(_fontSize * 100).round()}%',
textAlign: TextAlign.end,
style: TextStyle(fontSize: 12, color: colors.onSurface.withValues(alpha: 0.5))),
),
],
),
// 行距
Row(
children: [
Text('行距', style: TextStyle(fontSize: 13, color: colors.onSurface.withValues(alpha: 0.6))),
Expanded(
child: Slider(
value: _lineHeight,
min: 1.0,
max: 3.0,
divisions: 20,
label: _lineHeight.toStringAsFixed(1),
onChanged: (value) {
setState(() => _lineHeight = value);
_applyStyle();
},
),
),
SizedBox(
width: 44,
child: Text(_lineHeight.toStringAsFixed(1),
textAlign: TextAlign.end,
style: TextStyle(fontSize: 12, color: colors.onSurface.withValues(alpha: 0.5))),
),
],
),
const SizedBox(height: 8),
// 主题色块
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(_presetThemes.length, (index) {
final theme = _presetThemes[index];
final bg = Color(int.parse(theme['bg']!, radix: 16));
final fg = Color(int.parse(theme['fg']!, radix: 16));
final isSelected = index == _selectedTheme;
return GestureDetector(
onTap: () => _applyTheme(index),
child: Container(
width: 42,
height: 42,
margin: const EdgeInsets.symmetric(horizontal: 6),
decoration: BoxDecoration(
color: bg,
borderRadius: BorderRadius.circular(21),
border: Border.all(
color: isSelected ? colors.primary : colors.outlineVariant,
width: isSelected ? 2.5 : 1,
),
),
child: Center(
child: Text('A', style: TextStyle(color: fg, fontSize: 16, fontWeight: FontWeight.w500)),
),
),
);
}),
),
],
),
);
}
}