flutter架 构项目基础

This commit is contained in:
崔宝新
2026-09-04 15:22:20 +08:00
parent 70967229d0
commit 0c5aee59c0
265 changed files with 35484 additions and 0 deletions
+148
View File
@@ -0,0 +1,148 @@
import 'package:flutter/material.dart';
import 'package:new_learn/core/components/rich_text_content.dart';
/// ============================================================
/// 【RichTextDemoPage】富文本渲染 Demo
///
/// 一、本页演示什么:
/// 1. 题目/解析 HTML 完整渲染:标题、粗体、图片、上标下标、
/// <ul> 选项、<table> 表格;
/// 2. <audio> 标签自动替换成本项目 AudioPlayerCard;
/// 3. 学生端能力:字号 Aa± 实时调节、深色模式切换(textStyle 注入);
/// 4. 自定义样式:.answer 类 → 红色加粗(customStylesBuilder)。
///
/// 二、图片说明:
/// 用 asset:// 协议加载本地图片(最稳),实际后端返回的是
/// 完整 URL 或相对路径(相对路径需业务层拼 baseUrl)。
///
/// 三、高频坑:
/// 1. textStyle 只影响文本,图片/表格/AudioPlayerCard 是各自样式,
/// 深色模式下卡片白底不随主题变(业务侧需按需适配);
/// 2. 内联 CSS 只支持常用属性,复杂布局会降级;
/// 3. 以后接公式(LaTeX)在 RichTextContent 的 customWidgetBuilder
/// 里扩展,本 demo 未含公式。
/// ============================================================
class RichTextDemoPage extends StatefulWidget {
const RichTextDemoPage({super.key});
@override
State<RichTextDemoPage> createState() => _RichTextDemoPageState();
}
class _RichTextDemoPageState extends State<RichTextDemoPage> {
/// 字号(12 ~ 24,学生端可调)
double _fontSize = 15;
/// 深色模式演示开关
bool _dark = false;
/// 模拟后端返回的题目 HTML(含图片/上标/下标/表格/音频/解析)
static const String _html = '''
<h3>第 1 题 · 阅读理解</h3>
<p>阅读下面的材料,完成题目。</p>
<p>光合作用是植物利用光能将<strong>二氧化碳</strong>和<strong>水</strong>合成有机物并释放氧气的过程。化学方程式:6CO<sub>2</sub> + 6H<sub>2</sub>O → C<sub>6</sub>H<sub>12</sub>O<sub>6</sub> + 6O<sub>2</sub>,面积用 x<sup>2</sup> 表示。</p>
<img src="asset://assets/img/luola_logo.png" width="160" height="160"/>
<p>根据材料,下列说法正确的是:</p>
<ul>
<li>A. 光合作用只发生在夜间</li>
<li>B. 光合作用会释放氧气</li>
<li>C. 光合作用会消耗氧气</li>
</ul>
<table border="1" cellpadding="6">
<tr><th>条件</th><th>有光</th><th>黑暗</th></tr>
<tr><td>释放 O₂</td><td>√</td><td>×</td></tr>
</table>
<audio src="asset://assets/audio/sample-30s.mp3" title="听力材料(30 秒)" controls></audio>
<video src="https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" title="实验视频" controls></video>
<p><b>【解析】</b>正确答案:<span class="answer">B</span>。光合作用光反应阶段必须在有光的条件下进行,并释放氧气。</p>
''';
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: _dark ? const Color(0xFF1E1E1E) : Colors.white,
appBar: AppBar(
title: const Text('富文本渲染'),
centerTitle: true,
backgroundColor: _dark ? const Color(0xFF262626) : null,
foregroundColor: _dark ? Colors.white : null,
),
body: Column(
children: [
_buildControlBar(),
const Divider(height: 1),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: RichTextContent(
html: _html,
// 字号 + 深色模式都通过 textStyle 注入(学生端统一入口)
textStyle: TextStyle(
fontSize: _fontSize,
height: 1.6,
color: _dark
? Colors.white70
: const Color(0xFF333333),
),
// 自定义样式:.answer 类 → 红色加粗
customStylesBuilder: (element) {
if (element.className.contains('answer')) {
return const {'color': '#EF4444', 'font-weight': 'bold'};
}
return null;
},
),
),
),
],
),
);
}
/// 顶部控制栏:字号 Aa- / Aa+ + 深色开关
Widget _buildControlBar() {
return Container(
color: _dark ? const Color(0xFF262626) : const Color(0xFFF7F8FA),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Row(
children: [
IconButton(
icon: const Icon(Icons.text_decrease, size: 20),
color: _dark ? Colors.white : null,
onPressed: () => setState(() {
if (_fontSize > 12) _fontSize -= 1;
}),
),
Expanded(
child: Slider(
value: _fontSize,
min: 12,
max: 24,
divisions: 12,
activeColor: const Color(0xFF3B82F6),
onChanged: (v) => setState(() => _fontSize = v),
),
),
IconButton(
icon: const Icon(Icons.text_increase, size: 20),
color: _dark ? Colors.white : null,
onPressed: () => setState(() {
if (_fontSize < 24) _fontSize += 1;
}),
),
const SizedBox(width: 4),
Text(
'深色',
style: TextStyle(fontSize: 13, color: _dark ? Colors.white70 : null),
),
Switch(
value: _dark,
activeThumbColor: const Color(0xFF3B82F6),
onChanged: (v) => setState(() => _dark = v),
),
const SizedBox(width: 4),
],
),
);
}
}