flutter_for_openharmony逆向思维训练app实战+反证法实现
本文介绍了一个基于Flutter实现的反证法训练页面,通过"证明√2是无理数"的经典例题展示反证法的应用逻辑。文章分析了页面架构的三个关键部分: 入口设计:通过LogicPuzzlesPage中的ListTile跳转到ProofByContradictionPage,采用标准Material路由过渡动画。 页面结构:使用StatelessWidget构建静态展示页面,包含: S

这篇文章基于你当前仓库 qwer 的真实代码来写,聚焦“反证法”训练页。
反证法是逆向思维里最经典、也最“可复用”的方法之一。
它训练的是一种非常实用的推理路径:
- 想证明某个命题成立
- 不直接正面推
- 而是假设它不成立
- 推导出矛盾
- 从而证明原命题成立
你在项目里用一个很直观的例题“证明 √2 是无理数”来讲这个方法。
本文涉及文件
lib/feature_pages.dartlib/app.dartlib/main.dart
1. 入口在哪里:从逻辑谜题列表进入
反证法属于 LogicPuzzlesPage(逻辑谜题)里的一个训练项。
入口页通过卡片 push 到 ProofByContradictionPage,核心跳转逻辑如下:
// 逻辑谜题列表页的跳转代码片段
ListTile(
title: Text('反证法训练'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProofByContradictionPage(),
),
);
},
)
- 采用
ListTile作为列表项,符合 Flutter 原生交互习惯 onTap回调触发页面跳转,是最基础的路由跳转方式MaterialPageRoute保证跳转时有原生的页面过渡动画- 跳转目标直接指向
ProofByContradictionPage,路由关系清晰
2. ProofByContradictionPage 的核心结构拆解
2.1 页面类定义与基础结构
首先是页面的核心类定义,作为纯展示型页面,选择 StatelessWidget 是最优解:
class ProofByContradictionPage extends StatelessWidget {
const ProofByContradictionPage({super.key});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('反证法')),
body: Padding(
padding: EdgeInsets.all(16.w),
// 后续内容嵌套在此处
),
);
}
}
- 类修饰为
const构造方法,提升性能(不可变Widget可被缓存) Scaffold是 Flutter 页面的标准容器,包含导航栏和内容区AppBar设置明确的页面标题,保证导航层级清晰- 全局
Padding设为16.w,采用自适应单位适配不同屏幕尺寸 16.w是基于屏幕宽度的自适应单位,比固定像素更适配鸿蒙多终端
2.2 可滚动内容容器:ListView
页面主体内容采用 ListView 承载,解决小屏溢出问题:
child: ListView(
children: [
Text('反证法训练',
style: TextStyle(
fontSize: 24.sp,
fontWeight: FontWeight.bold
)
),
SizedBox(height: 24.h),
// 证明卡片嵌套在此处
],
)
ListView天然支持垂直滚动,无需额外封装SingleChildScrollView- 标题文字使用
24.sp加粗,突出页面核心主题 SizedBox(height: 24.h)作为垂直间距,24.h适配屏幕高度- 间距值选择遵循“8的倍数”设计原则,视觉节奏更统一
- ListView 默认无固定高度,内容自适应,性能更优
2.3 证明步骤卡片封装
核心证明步骤放在 Card 组件中,提升视觉层次感:
Card(
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('例题:证明√2是无理数',
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.bold
)
),
SizedBox(height: 16.h),
// 证明步骤文本在此处依次排列
],
),
),
)
Card组件提供天然的阴影和圆角,区分内容区域- 卡片内部
16.w内边距,避免文字贴边,提升阅读舒适度 Column垂直排列证明步骤,符合线性阅读逻辑crossAxisAlignment: CrossAxisAlignment.start让文本左对齐,符合中文阅读习惯- 例题标题
18.sp加粗,层级低于页面标题但高于步骤文本
2.4 反证法第一步:核心假设
反证法的关键第一步是“反向假设”,代码实现如下:
Text('1. 假设√2是有理数',
style: TextStyle(
fontSize: 16.sp,
height: 1.5 // 行高提升阅读体验
)
),
SizedBox(height: 8.h),
- 第一步明确写出“假设”,是反证法的核心动作
- 字体大小
16.sp适配正文阅读,行高1.5避免文字拥挤 8.h垂直间距,让步骤间有呼吸感,避免视觉疲劳- 假设命题必须与最终要证明的命题完全相反,这是反证法的逻辑基础
- 此处的假设是后续所有推导的前提,必须清晰明确
2.5 第二步:有理数的分数表达
基于假设,将√2表示为最简分数形式:
Text('2. 则√2 = p/q (p,q互质)',
style: TextStyle(fontSize: 16.sp)
),
SizedBox(height: 8.h),
- 有理数的定义就是“可以表示为两个整数的比值”
- 特别标注
p,q互质,是为后续制造矛盾埋下伏笔 - 最简分数形式是有理数的“最强条件”,让后续矛盾更有说服力
- 互质意味着 p 和 q 没有除1以外的公因数,这是关键约束条件
- 这一步是从“假设”到“推导”的过渡,逻辑衔接必须严谨
2.6 第三步到第五步:代数推导
通过等式变换推导 p 和 q 的偶性,核心代码片段:
Text('3. 2 = p²/q² → p² = 2q²', style: TextStyle(fontSize: 16.sp)),
SizedBox(height: 8.h),
Text('4. p²是偶数,所以p是偶数', style: TextStyle(fontSize: 16.sp)),
SizedBox(height: 8.h),
Text('5. 设p=2k,则4k²=2q² → q²=2k²', style: TextStyle(fontSize: 16.sp)),
- 第三步是等式两边同乘 q² 的基本代数变换,逻辑无漏洞
- 第四步利用“平方数为偶数则原数为偶数”的数论定理
- 第五步代入替换,继续推导 q² 的偶性,层层递进
- 每一步推导都基于前一步的结论,形成完整的逻辑链
- 步骤间的
8.h间距保持视觉一致性,阅读节奏更稳定
2.7 第六步:矛盾点暴露
推导出与前提矛盾的结论,这是反证法的核心转折点:
Text('6. q也是偶数,与p,q互质矛盾',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w500 // 半粗体强调矛盾点
)
),
SizedBox(height: 8.h),
- 明确指出“q是偶数”与“p,q互质”的矛盾,这是反证法的关键
- 使用
FontWeight.w500半粗体,突出矛盾点,吸引用户注意 - 矛盾点必须是“无法同时成立”的两个条件,否则反证法不成立
- 此处的矛盾是“最强矛盾”:互质的两个数不可能都是偶数
- 这一步是从“推导”到“结论”的关键桥梁
2.8 第七步:结论落地
最终得出原命题成立的结论,代码实现:
Text('7. 因此假设错误,√2是无理数',
style: TextStyle(
fontSize: 16.sp,
color: Colors.green,
fontWeight: FontWeight.bold
)
),
- 绿色文字直观传达“结论正确”的视觉信号
- 明确否定初始假设,回归原命题的证明
- 加粗字体强化结论,让用户快速捕捉核心信息
- 颜色选择符合用户认知习惯:绿色=正确/结论,黑色=推导过程
- 这一步是反证法的收尾,必须清晰、明确、无歧义
3. 为什么用 StatelessWidget:这是“阅读型证明流程”
你把页面写成 StatelessWidget,因为它的定位是:
- 展示一段固定的证明流程,无动态数据变化
// 无状态组件的核心特征:无state,无setState class ProofByContradictionPage extends StatelessWidget { const ProofByContradictionPage({super.key}); Widget build(BuildContext context) { // 仅返回固定UI,无业务逻辑 return Scaffold(...); } } - 用户主要是阅读和理解,无需交互状态维护
- 无输入框、无按钮、无滑动选择等交互元素
- 页面生命周期内,UI结构和内容完全固定
- 这类页面与“推理故事”一样,属于概念与方法论补给
- 重点在于信息传递的准确性,而非交互体验
- 无状态组件的性能更优,无需重建状态
4. 为什么外层用 ListView:长文本必须可滚动
证明流程是一串步骤文本,在小屏设备上很容易超过一屏高度。
你采用:
child: ListView(
// 物理滚动特性,适配鸿蒙系统
physics: ClampingScrollPhysics(),
children: [
// 标题、卡片等内容
]
)
ClampingScrollPhysics()适配鸿蒙系统的滚动物理特性- ListView 相比
Column + SingleChildScrollView有以下优势:- 更简洁的代码结构,无需额外嵌套
- 天然支持按需加载,性能更优
- 滚动行为更符合用户对“长文本阅读”的预期
- 自动处理边界回弹,体验更原生
- 能保证内容始终可读,不会出现溢出报错
- 适配手机、平板、智慧屏等鸿蒙多终端尺寸
- 即使未来增加更多证明步骤,也无需修改布局结构
5. 证明结构:标题 + 一张卡片承载步骤
页面结构很清楚:
-
标题:反证法训练
Text('反证法训练', style: TextStyle( fontSize: 24.sp, fontWeight: FontWeight.bold, letterSpacing: 0.5 // 字间距优化 ) ),- 字间距
0.5提升标题可读性,避免文字拥挤 - 24.sp 字号在各种屏幕上都能保证标题的视觉优先级
- 加粗处理让标题在页面中一眼可识别
- 字间距
-
Card:例题与 7 步推导
Card( elevation: 2.0, // 阴影深度,提升层次感 shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.w), // 圆角适配 ), child: Padding(...), )elevation: 2.0轻微阴影,区分卡片与背景- 圆角
8.w适配屏幕宽度,视觉更柔和 - 卡片的视觉边界让证明步骤形成独立的阅读单元
Card 的价值在于:
- 把证明步骤限定在一个可视边界里,避免内容分散
- 阅读体验更像“看一张证明卡片”,符合记忆习惯
- 卡片的阴影和圆角提升页面的视觉质感
- 与页面其他元素形成清晰的视觉层次,信息分区明确
- 适配鸿蒙系统的设计规范,视觉风格统一
6. 反证法的关键动作:第 1 步“假设”
你在第 1 步写:
Text('1. 假设√2是有理数',
style: TextStyle(
fontSize: 16.sp,
color: Colors.blueAccent // 假设用蓝色标注
)
),
- 新增蓝色标注,直观区分“假设”与“推导”步骤
- 这就是反证法最核心的动作:
- 不是直接证明 √2 无理,而是先假设它有理
- 假设必须是原命题的“否命题”,逻辑上完全对立
- 假设是后续所有推导的基础,必须清晰标注
- 假设的合理性直接决定反证法的有效性
- 后续所有推导都建立在这个假设之上:
- 每一步推导都不能脱离这个假设前提
- 推导过程中不能引入新的未证明的假设
- 所有代数变换都必须符合数学规则
7. “互质”是为了制造矛盾
第 2 步:
Text('2. 则√2 = p/q (p,q互质)',
style: TextStyle(
fontSize: 16.sp,
fontStyle: FontStyle.italic // 关键条件斜体强调
)
),
- 新增斜体样式,强调“互质”这个关键约束条件
- 这一句的意义是:
- 如果 √2 是有理数,就能写成最简分数 p/q
- 有理数的定义就是可以表示为两个整数的比值
- 最简分数是有理数的标准表达形式
- 最简就意味着 p 与 q 互质
- 互质是分数最简的充要条件
- 这是数论中的基本定理,无需额外证明
- 如果 √2 是有理数,就能写成最简分数 p/q
反证法的技巧之一就是:
- 你先把假设推到“最强条件”
- 最简分数是有理数的最强表达形式
- 条件越强,后续推出的矛盾越有说服力
- 然后在最强条件上推出矛盾
- 从“最简分数”推出“非最简”,矛盾更彻底
- 避免“弱条件推导”导致的逻辑漏洞
8. 第 3~6 步:从偶性推出 p、q 同为偶数
你后面步骤的核心链条是:
// 偶性推导核心步骤封装
Column(
children: [
Text('3. 2 = p²/q² → p² = 2q²', style: TextStyle(fontSize: 16.sp)),
SizedBox(height: 8.h),
Text('4. p²是偶数,所以p是偶数', style: TextStyle(fontSize: 16.sp)),
SizedBox(height: 8.h),
Text('5. 设p=2k,则4k²=2q² → q²=2k²', style: TextStyle(fontSize: 16.sp)),
SizedBox(height: 8.h),
Text('6. q也是偶数,与p,q互质矛盾', style: TextStyle(fontSize: 16.sp)),
],
)
- 用 Column 封装推导步骤,保持结构统一
- 核心逻辑链条拆解:
- p² = 2q² ⇒ p² 是偶数 ⇒ p 是偶数
- 偶数的平方是偶数,奇数的平方是奇数
- 这是数论中的基本性质,无需额外证明
- p 是偶数 ⇒ p=2k ⇒ q² 也是偶数 ⇒ q 也是偶数
- 代入替换后,q² 同样满足偶数条件
- 同理可证 q 也是偶数
- p² = 2q² ⇒ p² 是偶数 ⇒ p 是偶数
一旦 p、q 都是偶数,就意味着:
- p 与 q 有公因子 2
- 偶数的定义就是能被 2 整除
- 公因子 2 的存在直接否定了“互质”条件
- 这与“互质”矛盾
- 互质的定义是“没有除1以外的公因子”
- 矛盾点清晰、明确、无歧义
所以你的第 6 步写:
Text('6. q也是偶数,与p,q互质矛盾', style: TextStyle(fontSize: 16.sp)),
把矛盾点明确写出来,这是反证法的核心转折点。
9. 第 7 步用绿色强调:把结论落地
你最后一步写成绿色:
Text('7. 因此假设错误,√2是无理数',
style: TextStyle(
fontSize: 16.sp,
color: Colors.green,
fontSize: 16.sp,
fontWeight: FontWeight.bold,
letterSpacing: 0.3 // 字间距优化
)
),
- 新增字间距优化,提升结论可读性
- 这让用户在阅读时快速捕捉到:
- 矛盾来自哪里
- 明确指向“p,q同为偶数”与“互质”的矛盾
- 矛盾点是反证法的核心依据
- 结论是什么
- 明确否定初始假设,证明原命题成立
- 结论必须与初始假设完全相反
- 矛盾来自哪里
这种视觉强调对“方法论训练”很重要:
- 因为用户往往需要先抓住整体框架,再回头看细节
- 绿色结论能快速帮用户建立“假设→推导→矛盾→结论”的框架
- 颜色编码符合认知习惯
- 蓝色(假设)→ 黑色(推导)→ 红色(矛盾)→ 绿色(结论)
- 强化记忆点,帮助用户理解反证法的完整流程
10. 如何把它升级成可训练交互
你当前是阅读型页面。如果要升级成“训练型”,最自然的方式是:
10.1 步骤分步展示
// 分步展示的核心逻辑
class InteractiveProofPage extends StatefulWidget {
_InteractiveProofPageState createState() => _InteractiveProofPageState();
}
class _InteractiveProofPageState extends State<InteractiveProofPage> {
int currentStep = 0; // 当前显示的步骤
// 步骤列表
final List<String> steps = [
'1. 假设√2是有理数',
'2. 则√2 = p/q (p,q互质)',
// 其他步骤...
];
Widget build(BuildContext context) {
return Column(
children: [
Text(steps[currentStep]),
ElevatedButton(
onPressed: () => setState(() => currentStep++),
child: Text('下一步'),
),
],
);
}
}
- 改为
StatefulWidget维护当前步骤状态 - 点击“下一步”按钮展示下一个步骤
- 核心优势:用户需要主动触发步骤切换,加深理解
10.2 步骤选择题
// 步骤选择题示例
Column(
children: [
Text('第3步:从√2 = p/q 可以推导出什么?'),
// 选项按钮
ElevatedButton(
onPressed: () => checkAnswer('p² = 2q²'),
child: Text('p² = 2q²'),
),
ElevatedButton(
onPressed: () => checkAnswer('p = 2q'),
child: Text('p = 2q'),
),
],
)
// 答案验证函数
void checkAnswer(String answer) {
if (answer == 'p² = 2q²') {
// 正确逻辑
} else {
// 错误提示
}
}
- 把证明步骤拆成选择题,用户需要选择正确的推导结果
- 答案验证函数可以给出即时反馈
- 核心优势:用户主动思考推导过程,而非被动阅读
10.3 关键步骤填空
// 填空交互示例
TextField(
hintText: '请输入第1步的假设:√2是____',
onSubmitted: (value) {
if (value == '有理数') {
// 正确反馈
}
},
)
- 关键步骤设置填空,用户需要输入正确内容
- 提交后验证答案,给出反馈
- 核心优势:强化关键步骤的记忆,理解更深刻
例如:
- 第 1 步假设:用户填写“有理数”
- 第 2 步写出 p/q:用户选择“互质”选项
- 第 3 步推到 p² = 2q²:用户选择正确的代数变换
这样用户会真的走一遍反证法流程,而不只是阅读。
11. 小结:反证法实现的关键点
- 阅读型页面:StatelessWidget + ListView
// 阅读型页面核心结构总结 class ProofByContradictionPage extends StatelessWidget { const ProofByContradictionPage({super.key}); Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('反证法')), body: Padding( padding: EdgeInsets.all(16.w), child: ListView(children: [...]), // 可滚动内容 ), ); } } - 信息组织清晰:标题 + 卡片承载步骤
- 标题层级分明:24.sp(页面)→ 18.sp(例题)→ 16.sp(步骤)
- 卡片封装核心内容,视觉边界清晰
- 关键动作明确:从“假设相反命题”开始
- 第一步明确标注假设,逻辑起点清晰
- 假设必须是原命题的否命题
- 矛盾点落地:互质 vs 同为偶数
- 矛盾点明确、无歧义
- 矛盾是“最强矛盾”,说服力强
- 结论强调:绿色高亮最后一步
- 颜色编码强化结论
- 结论直接回应初始假设
12. 这页为什么是 ListView 而不是 Column
反证法的内容天然是“多段文字步骤”。在小屏设备上,哪怕只有 7 步,也可能超过一屏高度。
12.1 ListView vs Column 对比
// ListView 实现
ListView(
children: [/* 所有内容 */],
)
// Column + SingleChildScrollView 实现(不推荐)
SingleChildScrollView(
child: Column(
children: [/* 所有内容 */],
),
)
12.2 核心差异分析
- 代码简洁性:
- ListView 只需一层嵌套,代码更简洁
- Column 方案需要两层嵌套,代码冗余
- 性能表现:
- ListView 按需构建子组件,性能更优
- Column 方案一次性构建所有子组件,内存占用更高
- 滚动体验:
- ListView 天然支持滚动物理特性
- Column 方案需要额外配置滚动行为
- 适配性:
- ListView 适配鸿蒙多终端更友好
- Column 方案在某些设备上可能出现滚动异常
你用 ListView 的好处是:
- 不会出现溢出,适配所有屏幕尺寸
- 用户可以按自己的节奏滚动阅读
- 性能更优,内存占用更低
- 代码更简洁,维护成本更低
对于方法论训练页,这种“可滚动阅读”比固定布局更稳。
13. 为什么把 1~7 步写成多条 Text,而不是一条长 Text
你把每一步都写成独立的 Text(...):
// 推荐:分步 Text 组件
Column(
children: [
Text('1. 假设√2是有理数'),
SizedBox(height: 8.h),
Text('2. 则√2 = p/q (p,q互质)'),
// 其他步骤...
],
)
// 不推荐:单条长 Text
Text('1. 假设√2是有理数 2. 则√2 = p/q (p,q互质) ...'),
13.3 分步 Text 的核心价值
- 每一步是一个清晰的“推理单元”
- 每个 Text 对应一个逻辑步骤,边界清晰
- 符合“一步一推导”的逻辑思维习惯
- 用户阅读时更容易停顿
- 每个步骤独立成行,便于逐句理解
- 避免长文本“一扫而过”的阅读习惯
- 你也更容易在某一步插入额外解释
- 可以在任意步骤后添加注释或说明
- 可以为不同步骤设置不同的样式
- 样式定制更灵活
- 不同步骤可以设置不同的颜色、字体、间距
- 关键步骤可以特殊标注,提升可读性
- 响应式适配更友好
- 分步 Text 更容易适配不同屏幕宽度
- 避免长文本在小屏上的换行混乱
如果你把 7 步写成一大段文本,用户会更容易“扫过去”,训练效果会弱。
14. 反证法训练的关键:矛盾必须落在“无法同时成立”的条件上
你的矛盾点是:
// 矛盾点高亮展示
Text('6. q也是偶数,与p,q互质矛盾',
style: TextStyle(
fontSize: 16.sp,
color: Colors.redAccent,
fontWeight: FontWeight.bold
)
),
- 新增红色标注,突出矛盾点
- 核心矛盾拆解:
- 假设 √2 = p/q 且 p,q 互质
- 互质:p 和 q 没有除1以外的公因子
- 推导出 p 与 q 都是偶数
- 偶数:能被 2 整除,即有公因子 2
- 两者无法同时成立
- 公因子 2 与“无公因子”直接冲突
- 矛盾是“逻辑上不可能同时成立”
- 假设 √2 = p/q 且 p,q 互质
反证法最容易写坏的地方就在这里:
- 如果矛盾点不够硬
- 比如“推导复杂”而非“逻辑矛盾”
- 用户会觉得你只是“换个说法”
- 你的矛盾点非常硬,因此作为训练例题很合适
- 矛盾是数论中的基本逻辑矛盾
- 无需额外解释,用户容易理解
15. 颜色高亮最后一步:在训练页里是必要的“收束”
你把最后一步写成绿色:
Text('7. 因此假设错误,√2是无理数',
style: TextStyle(
fontSize: 16.sp,
color: Colors.green,
fontWeight: FontWeight.bold
)
),
15.1 视觉收束的核心价值
- 这相当于给用户一个明确的结束信号:
- 前面都是推导(黑色/蓝色)
- 这里是结论(绿色)
- 视觉上形成“推导→结论”的闭环
- 方法论训练页如果缺少这种“收束”,用户会读完但记不住框架
- 颜色编码帮助用户快速定位结论
- 强化“假设→矛盾→结论”的反证法框架
- 符合认知心理学的“收尾效应”
- 用户对最后出现的信息记忆更深刻
- 绿色结论能强化反证法的核心结论
15.2 扩展颜色编码方案
// 完整颜色编码示例
Column(
children: [
Text('1. 假设√2是有理数', style: TextStyle(color: Colors.blueAccent)),
Text('2. 则√2 = p/q (p,q互质)', style: TextStyle(color: Colors.black87)),
// ... 推导步骤都是黑色 ...
Text('6. q也是偶数,与p,q互质矛盾', style: TextStyle(color: Colors.redAccent)),
Text('7. 因此假设错误,√2是无理数', style: TextStyle(color: Colors.green)),
],
)
- 假设(蓝色)→ 推导(黑色)→ 矛盾(红色)→ 结论(绿色)
- 颜色编码符合用户的直觉认知
- 强化反证法的完整逻辑链
16. 如何把例题拆成“互动训练”:让用户补全某一步
你现在是阅读型。如果要做互动训练,最小的做法是:
16.1 基础互动:步骤填空
// 互动填空组件示例
class StepFillBlank extends StatefulWidget {
final String hint; // 填空提示
final String correctAnswer; // 正确答案
const StepFillBlank({
super.key,
required this.hint,
required this.correctAnswer,
});
State<StepFillBlank> createState() => _StepFillBlankState();
}
class _StepFillBlankState extends State<StepFillBlank> {
String userAnswer = '';
Widget build(BuildContext context) {
return Column(
children: [
TextField(
hintText: widget.hint,
onChanged: (value) => setState(() => userAnswer = value),
),
ElevatedButton(
onPressed: () {
if (userAnswer == widget.correctAnswer) {
// 正确反馈
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('回答正确!')),
);
}
},
child: Text('验证答案'),
),
],
);
}
}
- 封装可复用的填空组件
- 用户输入答案后验证,给出即时反馈
- 状态管理清晰,交互逻辑简单
16.2 使用示例
// 填空组件使用示例
StepFillBlank(
hint: '第1步:假设√2是____',
correctAnswer: '有理数',
),
StepFillBlank(
hint: '第2步:√2 = p/q,其中p和q____',
correctAnswer: '互质',
),
- 保留 1~7 的结构,把其中 1~2 步挖空
- 让用户选填,验证后再显示下一步
- 核心优势:用户主动参与推理,而非被动阅读
这样用户会真正参与一次反证法推理,而不是只读结论。
17. 如何扩展题库:从数学例题迁移到生活例题
反证法不只在数学里有用。你可以把题库扩展成:
17.1 生活例题代码示例
// 生活例题卡片
Card(
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
children: [
Text('生活例题:证明"这个系统没有任何bug"不成立',
style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold)),
SizedBox(height: 16.h),
Text('1. 假设这个系统没有任何bug', style: TextStyle(fontSize: 16.sp)),
Text('2. 则系统运行时不会产生错误日志', style: TextStyle(fontSize: 16.sp)),
Text('3. 但监控显示有错误日志产生', style: TextStyle(fontSize: 16.sp)),
Text('4. 与假设矛盾,因此系统存在bug',
style: TextStyle(fontSize: 16.sp, color: Colors.green)),
],
),
),
)
17.2 题库扩展方向
- 证明某个说法不可能
- 数学:证明“不存在最大的质数”
- 生活:证明“这个方案永远不会出问题”
- 从“不可能”推出矛盾
- 工程:证明“这个接口响应时间不可能小于100ms”
- 逻辑:证明“这个推理过程不可能出错”
例如:
- “这个系统没有任何 bug”
- 假设没有 bug,那么某个错误日志从何而来?
- 错误日志的存在与“无bug”矛盾
- 因此系统必然存在bug
把反证法迁移到生活/工程例子,会让用户更愿意使用这种方法:
- 贴近实际场景,更容易理解
- 能直接应用到工作和生活中
- 强化“反证法是通用思维方法”的认知
18. 小结补充:反证法在你的 App 里的定位
你的 App 很多页面都在训练“逆向思维”。反证法是其中最标准的套路之一。
// 逆向思维训练页面入口列表
ListView(
children: [
ListTile(
title: Text('反证法训练'),
onTap: () => Navigator.push(context, MaterialPageRoute(
builder: (context) => ProofByContradictionPage(),
)),
),
ListTile(
title: Text('逆向推理训练'),
// 其他逆向思维训练页面...
),
],
)
当用户掌握:
- 假设相反
// 反证法核心:假设相反命题 Text('假设原命题不成立', style: TextStyle(color: Colors.blueAccent)), - 推到矛盾
// 反证法核心:推出矛盾 Text('推导出与前提矛盾的结论', style: TextStyle(color: Colors.redAccent)),
他们在面对很多推理题时,会更有策略,而不是凭直觉猜。
19. 常见踩坑点:忘记 ListView 导致溢出
如果没有 ListView,长文本在小屏上会溢出报错:
// 错误示例:无 ListView 导致溢出
Scaffold(
body: Padding(
padding: EdgeInsets.all(16.w),
child: Column( // 小屏会溢出
children: [/* 大量文本 */],
),
),
)
// 正确示例:使用 ListView 避免溢出
Scaffold(
body: Padding(
padding: EdgeInsets.all(16.w),
child: ListView( // 不会溢出
children: [/* 大量文本 */],
),
),
)
你用了 ListView,保证页面在各种屏幕尺寸下都能正常显示。这是"响应式布局"的基本保障。
20. 常见踩坑点:Card 的 padding 为 0
如果 Card 没有 padding,内容会贴边,视觉拥挤:
// 错误示例:Card 无 padding
Card(
child: Column( // 内容贴边,阅读体验差
children: [/* 文本内容 */],
),
)
// 正确示例:Card 有 padding
Card(
child: Padding(
padding: EdgeInsets.all(16.w), // 内边距舒适
child: Column(
children: [/* 文本内容 */],
),
),
)
你用了 EdgeInsets.all(16.w),保证内边距舒适。这是"视觉层次"的体现。
21. 常见踩坑点:SizedBox 高度为 0
// 错误示例:高度为0,无间距
SizedBox(height: 0.h),
// 正确示例:明确高度,间距生效
SizedBox(height: 8.h),
你给了 .h 值,保证间距生效。这是"间距明确"的体现。
22. 常见踩坑点:EdgeInsets.all 的参数为 0
// 错误示例:无内边距
EdgeInsets.all(0.w),
// 正确示例:合理内边距
EdgeInsets.all(16.w),
你用了 16.w,保证内容不贴边。这是"间距合理"的体现。
23. 常见踩坑点:AppBar 的 title 为空
// 错误示例:title 为空
AppBar(title: const Text('')),
// 正确示例:明确标题
AppBar(title: const Text('反证法')),
你给了明确标题,保证导航栏正常。这是"导航完整性"的体现。
24. 常见踩坑点:Scaffold 的 body 为 null
// 错误示例:body 为空
Scaffold(appBar: AppBar(title: Text('反证法')), body: null),
// 正确示例:完整 body
Scaffold(
appBar: AppBar(title: Text('反证法')),
body: Padding(...),
),
你给了完整 body,保证页面有内容。这是"页面完整性"的体现。
25. 常见踩坑点:StatelessWidget 的 key 为 null
// 不推荐:key 为 null
class ProofByContradictionPage extends StatelessWidget {
const ProofByContradictionPage(); // 无 key
// ...
}
// 推荐:使用 const key
class ProofByContradictionPage extends StatelessWidget {
const ProofByContradictionPage({super.key}); // 有 key
// ...
}
这说明你对 Flutter 的基础组件和布局系统有扎实理解。即使未来扩展功能,这些稳健的写法也会减少维护成本。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐
所有评论(0)