Files
flutter_learning/questions.json
T
2026-08-09 22:10:19 +08:00

2070 lines
92 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"sets": [
{
"id": 1,
"name": "Dart & Flutter 学习测验",
"questions": [
{
"id": 1,
"lv": 1,
"q": "Dart 中声明\"类型自动推断\"的变量,用哪个关键字?",
"opts": [
"var",
"let",
"const",
"dynamic"
],
"ans": 0,
"exp": "Dart 用 <b>var</b> 由编译器自动推断类型;<b>let</b> 是 JS 的;<b>const</b> 表示编译期常量。"
},
{
"id": 2,
"lv": 1,
"q": "Dart 中表示 64 位整数的数据类型是?",
"opts": [
"integer",
"int",
"number",
"long"
],
"ans": 1,
"exp": "Dart 用 <b>int</b> 表示整数(64位),<b>double</b> 表示浮点数,两者父类是 <b>num</b>。"
},
{
"id": 3,
"lv": 1,
"q": "以下哪个是 Dart 中正确的单行注释写法?",
"opts": [
"# 注释",
"/* 注释 */",
"// 注释",
"&lt;!-- 注释 --&gt;"
],
"ans": 2,
"exp": "Dart 单行注释用 <b>//</b>,多行用 <b>/* */</b>。<b>#</b> 是 Python#! 在 Dart 中只是 shebang 声明。"
},
{
"id": 4,
"lv": 1,
"q": "Dart 程序的入口函数是?",
"opts": [
"runApp()",
"main()",
"init()",
"start()"
],
"ans": 1,
"exp": "入口是 <b>main()</b>。Flutter 中通常写成 <code>void main() =&gt; runApp(MyApp());</code>"
},
{
"id": 5,
"lv": 1,
"q": "要在控制台输出文字,应调用哪个函数?",
"opts": [
"console.log()",
"System.out.println()",
"echo",
"print()"
],
"ans": 3,
"exp": "Dart 用 <b>print()</b> 输出到控制台,比如 <code>print(\"Hello Dart\");</code>"
},
{
"id": 6,
"lv": 1,
"q": "<code>int x = 10;</code> 要打印出 <code>x=10</code>,以下哪种写法正确?",
"opts": [
"print(\"x=\" + x);",
"print(\"x=$x\");",
"print('x=${x}');",
"以上都可以"
],
"ans": 3,
"exp": "Dart 中 <b>+</b> 拼接字符串、<b>$x</b> 插值、<b>${x}</b> 表达式插值都合法,三种写法均能输出 <code>x=10</code>。"
},
{
"id": 7,
"lv": 2,
"q": "以下哪个 <b>不是</b> Dart 的循环写法?",
"opts": [
"for (var i=0; i&lt;5; i++)",
"while (ok)",
"foreach (x in list)",
"do { } while (ok)"
],
"ans": 2,
"exp": "Dart <b>没有</b> <code>foreach(x in list)</code> 语法,应写 <code>for (var x in list)</code> 或 <code>list.forEach(...)</code>。"
},
{
"id": 8,
"lv": 2,
"q": "执行 <code>for (var i=0; i&lt;5; i++) print(i);</code> 会输出?",
"opts": [
"1 2 3 4 5",
"0 1 2 3 4",
"0 1 2 3 4 5",
"5 4 3 2 1"
],
"ans": 1,
"exp": "i 从 0 开始,条件 <code>i&lt;5</code>,所以输出 <b>0 1 2 3 4</b>。"
},
{
"id": 9,
"lv": 2,
"q": "Dart 的 switch 语句中,每个 case 结束必须以什么结尾(否则编译报错)?",
"opts": [
"end",
"return / break / throw",
"continue",
"逗号"
],
"ans": 1,
"exp": "Dart 每个非空 case 必须以 <b>break、return 或 throw</b> 等结束,不能像 C 语言那样自动穿透。"
},
{
"id": 10,
"lv": 2,
"q": "定义一个返回两数之和的函数,哪个写法正确?",
"opts": [
"int add(int a,int b){ return a+b; }",
"add(int a,int b) =&gt; a+b;",
"void add(int a,int b){ return a+b; }",
"A 和 B 都可以"
],
"ans": 3,
"exp": "A 是传统写法,B 是<b>箭头函数</b>简写(返回表达式),两者都合法;C 返回 void 却 return 值,编译错误。"
},
{
"id": 11,
"lv": 2,
"q": "以下哪个是<b>可选命名参数</b>的正确声明?",
"opts": [
"void f({String name}) {}",
"void f([String name]) {}",
"void f(String? name) {}",
"void f(String name=\"\") {}"
],
"ans": 0,
"exp": "<b>{}</b> 包裹的是命名参数(调用用 <code>f(name: x)</code>);<b>[]</b> 是可选位置参数;<b>?</b> 是空安全;最后一个是默认值写法但不属于命名参数。"
},
{
"id": 12,
"lv": 2,
"q": "函数 <code>void f({int a = 1}) {}</code>,以下调用哪个正确?",
"opts": [
"f()",
"f(1)",
"f(a: 1)",
"A 和 C 都可以"
],
"ans": 3,
"exp": "命名参数有默认值时可省略(A 合法);必须<b>按名调用</b> <code>f(a: 1)</code>C 合法);<code>f(1)</code> 用位置传参给命名参数会报错。"
},
{
"id": 13,
"lv": 3,
"q": "Dart 中表示\"数组/列表\"的类型是?",
"opts": [
"Array",
"ArrayList",
"List",
"Vector"
],
"ans": 2,
"exp": "Dart 用 <b>List</b>(可增长数组),字面量写法 <code>[1,2,3]</code>。"
},
{
"id": 14,
"lv": 3,
"q": "创建包含 1、2、3 的列表,正确的是?",
"opts": [
"{1, 2, 3}",
"[1, 2, 3]",
"(1, 2, 3)",
"List(1,2,3)"
],
"ans": 1,
"exp": "<b>[]</b> 是 List<b>{}</b> 是 Set/Map<b>()</b> 不是集合字面量。"
},
{
"id": 15,
"lv": 3,
"q": "<code>var list = [10, 20, 30]; print(list[1]);</code> 输出?",
"opts": [
"10",
"20",
"30",
"报错"
],
"ans": 1,
"exp": "Dart 索引从 <b>0</b> 开始,list[1] 是第二个元素 <b>20</b>。"
},
{
"id": 16,
"lv": 3,
"q": "对 List 每个元素做处理并<b>返回一个新集合</b>的方法是?",
"opts": [
"forEach()",
"map()",
"where()",
"add()"
],
"ans": 1,
"exp": "<b>map()</b> 对每个元素变换并返回 Iterable<b>forEach()</b> 只遍历返回 void<b>where()</b> 是过滤。"
},
{
"id": 17,
"lv": 3,
"q": "<code>var a=[1,2,3]; var b=[0, ...a];</code> 那么 <code>b.length</code> 是?",
"opts": [
"3",
"4",
"7",
"报错"
],
"ans": 1,
"exp": "<b>...a</b> 是扩展运算符,把 a 展开成 1,2,3,加上开头的 0b = [0,1,2,3],长度 <b>4</b>。"
},
{
"id": 18,
"lv": 3,
"q": "以下哪种集合类型<b>保证元素不重复</b>",
"opts": [
"List",
"Map",
"Set",
"Queue"
],
"ans": 2,
"exp": "<b>Set</b> 存储唯一元素,重复添加会被忽略;Map 存储键值对,键也不重复。"
},
{
"id": 19,
"lv": 4,
"q": "定义类 <code>class Dog { String name; }</code> 后,创建一个实例哪个正确?",
"opts": [
"Dog d = Dog();",
"var d = new Dog;",
"Dog d = new Dog();",
"以上都可以"
],
"ans": 3,
"exp": "Dart 2 之后 <b>new 关键字可选</b>,且省略括号的 <code>Dog()</code> 也合法,三种写法都能创建实例。"
},
{
"id": 20,
"lv": 4,
"q": "子类继承父类使用哪个关键字?",
"opts": [
"with",
"implements",
"extends",
"inherits"
],
"ans": 2,
"exp": "继承用 <b>extends</b><b>implements</b> 是实现接口;<b>with</b> 用于混入 Mixin。"
},
{
"id": 21,
"lv": 4,
"q": "包含未实现方法、<b>不能被直接实例化</b>的类,用什么关键字声明?",
"opts": [
"abstract",
"interface",
"base",
"pure"
],
"ans": 0,
"exp": "<b>abstract</b> 抽象类可含未实现方法,不能直接 new,需子类实现后实例化。"
},
{
"id": 22,
"lv": 4,
"q": "在构造函数/实例方法中,<code>this</code> 指向?",
"opts": [
"当前实例",
"父类对象",
"类本身",
"静态成员"
],
"ans": 0,
"exp": "<b>this</b> 指向当前实例对象,用于区分参数与成员变量,如 <code>this.name = name</code>。"
},
{
"id": 23,
"lv": 4,
"q": "创建一个只能装 String 的 List,哪个写法正确?",
"opts": [
"List&lt;String&gt; names = [];",
"List names = &lt;String&gt;[];",
"var names = &lt;String&gt;[];",
"以上都是"
],
"ans": 3,
"exp": "Dart 泛型语法多样:类型标注式、泛型字面量 <code>&lt;String&gt;[]</code> 都合法,三种写法结果相同。"
},
{
"id": 24,
"lv": 4,
"q": "抽象类 Animal 有抽象方法 <code>void speak()</code>,子类 Dog 必须?",
"opts": [
"重写 speak() 提供实现",
"调用父类构造",
"删除 speak",
"什么也不用做"
],
"ans": 0,
"exp": "子类必须用 <code>@override</code> 实现所有抽象方法,否则 Dog 也是抽象类无法实例化。"
},
{
"id": 25,
"lv": 5,
"q": "Dart 空安全中,声明一个<b>可能为 null</b> 的字符串类型是?",
"opts": [
"String",
"String?",
"String!",
"nullable String"
],
"ans": 1,
"exp": "在类型后加 <b>?</b> 表示可空:<code>String? s = null;</code>;不加 ? 的 String 保证非空。"
},
{
"id": 26,
"lv": 5,
"q": "空值合并运算符 <code>??</code> 的作用是?",
"opts": [
"若左边为 null 则返回右边",
"强制把 null 变为非空",
"判断对象是否相等",
"抛出空异常"
],
"ans": 0,
"exp": "<code>a ?? b</code>a 不为 null 用 a,否则用 b。常配合 <code>??=</code> 设置默认值。"
},
{
"id": 27,
"lv": 5,
"q": "使用 <code>async/await</code> 时,异步函数一般返回什么类型?",
"opts": [
"Stream&lt;T&gt;",
"Future&lt;T&gt;",
"void",
"T"
],
"ans": 1,
"exp": "async 函数返回 <b>Future&lt;T&gt;</b>await 会挂起等待该 Future 完成后再继续执行。"
},
{
"id": 28,
"lv": 5,
"q": "执行以下代码,输出顺序是?<br><code>Future.delayed(Duration(seconds:1), () =&gt; print(\"A\"));<br>print(\"B\");</code>",
"opts": [
"A 后 B",
"B 后 A",
"同时输出",
"只输出 A"
],
"ans": 1,
"exp": "Future.delayed 是异步任务,先执行同步的 <code>print(\"B\")</code>1 秒后再输出 A。"
},
{
"id": 29,
"lv": 6,
"q": "Flutter 中用于<b>监听持续不断的数据流</b>(如多次事件)的是?",
"opts": [
"Future",
"Stream",
"List",
"Map"
],
"ans": 1,
"exp": "<b>Stream</b> 可监听多次异步事件;Future 只代表一次性的结果。"
},
{
"id": 30,
"lv": 6,
"q": "Flutter 中构建界面的<b>基本单位</b>是?",
"opts": [
"Activity",
"View",
"Widget",
"Component"
],
"ans": 2,
"exp": "Flutter 一切皆 <b>Widget</b>UI 由 Widget 树组合而成(如 Text、Container、Column)。"
},
{
"id": 31,
"lv": 6,
"q": "界面状态需要<b>随数据变化而更新</b>时,应使用哪类组件?",
"opts": [
"StatelessWidget",
"StatefulWidget",
"Container",
"Scaffold"
],
"ans": 1,
"exp": "有动态状态用 <b>StatefulWidget</b>(配合 State 里的 setState 刷新);无状态用 StatelessWidget。"
}
]
},
{
"id": 9,
"name": "Dart 开发实战",
"questions": [
{
"id": 98,
"lv": 1,
"q": "开发注册页时,用户输入的年龄是字符串,需要转成 <code>int</code> 保存。已知 <code>String ageText = \"18\";</code>,正确的转换写法是?",
"opts": [
"<code>int(ageText)</code>",
"<code>toInt(ageText)</code>",
"<code>int.parse(ageText)</code>",
"<code>ageText.parseInt()</code>"
],
"ans": 2,
"exp": "<code>int.parse()</code> 把字符串解析成整数,解析失败会抛 FormatException,更稳妥的是 <code>int.tryParse()</code>(失败返回 null)。注意 Dart 没有 <code>\"18\".parseInt()</code> 这种写法,<code>int(...)</code> 也不是转换函数。"
},
{
"id": 99,
"lv": 1,
"q": "项目里要定义一个\"永远不允许修改\"的 API 地址常量,以下哪种声明最合适?",
"opts": [
"<code>final String api = \"https://api.example.com\";</code>",
"<code>const String api = \"https://api.example.com\";</code>",
"<code>String api = \"https://api.example.com\";</code>",
"<code>var api = \"https://api.example.com\";</code>"
],
"ans": 1,
"exp": "<b>const</b> 是编译期常量:值在编译时就确定,且不可变,编译器可直接内联替换,还能配合 const 构造函数做实例复用。<b>final</b> 是运行期赋值一次后不可变。给字面量常量用 const 是更优选择。"
},
{
"id": 100,
"lv": 1,
"q": "你在重构代码时写下:<code>var x = 10; x = \"hello\";</code> 会发生什么?",
"opts": [
"正常运行,x 变成字符串",
"编译报错:x 已被推断为 int 类型",
"运行时报错",
"x 自动变为 null"
],
"ans": 1,
"exp": "<b>var</b> 声明时由初始值自动推断类型,推断后类型就<b>固定</b>了:x 是 int,再赋 String 在编译期直接报错。想存任意类型,应显式声明 <code>dynamic</code> 或 <code>Object?</code>。"
},
{
"id": 101,
"lv": 1,
"q": "给变量起名时,以下哪个<b>不是</b>合法的 Dart 标识符?",
"opts": [
"<code>_score</code>",
"<code>2x</code>",
"<code>中文名</code>",
"<code>name_2</code>"
],
"ans": 1,
"exp": "Dart 标识符<b>不能以数字开头</b>。<code>_score</code> 合法(下划线开头表示库内私有,是 Dart 的约定俗成);Dart 支持 Unicode 标识符,中文变量名同样合法。"
},
{
"id": 102,
"lv": 1,
"q": "结算功能计算人均消费:<code>int total = 10; int people = 4; double avg = total / people;</code> 打印 <code>avg</code> 得到?",
"opts": [
"2",
"2.5",
"2.0",
"报错"
],
"ans": 1,
"exp": "Dart 中 <b>int 除以 int 的结果一定是 double</b>10 / 4 = 2.5。想得到整除结果用 <code>~/</code><code>10 ~/ 4 == 2</code>,取余用 <code>%</code>。"
},
{
"id": 103,
"lv": 2,
"q": "写优惠逻辑:订单数 &gt; 10 打 9 折,&gt; 50 打 8 折。对 <code>orders = 60</code>,以下代码会得到什么折扣?<br><code>double d; if (orders &gt; 10) { d = 0.9; } else if (orders &gt; 50) { d = 0.8; }</code>",
"opts": [
"0.8",
"0.9",
"1.0",
"0.7"
],
"ans": 1,
"exp": "if-else 链<b>从上到下匹配,命中即止</b>60 &gt; 10 先成立就直接取 0.9,后面的 else if 不再执行。正确写法应把范围大的条件放前面(先判断 &gt; 50)。判断顺序是 if-else 链最经典的坑。"
},
{
"id": 104,
"lv": 2,
"q": "接口返回 <code>Map&lt;String, dynamic&gt; data</code>,想遍历所有 key 打印出来,以下哪种写法正确?",
"opts": [
"<code>for (var k in data) print(k);</code>",
"<code>for (var k in data.keys) print(k);</code>",
"<code>data.keys.forEach(print);</code>",
"B 和 C 都可以"
],
"ans": 3,
"exp": "Map 本身<b>不能</b>直接 for-in 遍历,要先取 <code>keys</code> / <code>values</code> / <code>entries</code>。B 是标准写法;C 中 <code>forEach(print)</code> 利用了函数引用,同样正确。两种都行,按团队规范选择。"
},
{
"id": 105,
"lv": 2,
"q": "写一个返回用户名的函数:<code>String getUserName() { }</code>,忘了写 <code>return</code>,会怎样?",
"opts": [
"返回 null",
"编译报错",
"运行时崩溃",
"自动返回空字符串"
],
"ans": 1,
"exp": "空安全下,声明了非空返回类型 <code>String</code> 的函数<b>必须有 return</b>,漏写直接编译报错,把隐患消灭在编译期。如果确实允许无返回值,应把返回类型声明为 <code>String?</code>。"
},
{
"id": 106,
"lv": 2,
"q": "问候函数:<code>void greet(String name, {String greeting = \"你好\"})</code>,调用 <code>greet(\"小明\", \"早上好\")</code> 会发生什么?",
"opts": [
"正常执行,greeting 为 \"早上好\"",
"编译报错:命名参数不能按位置传参",
"greeting 变为 null",
"编译警告但能运行"
],
"ans": 1,
"exp": "<code>{}</code> 包裹的是<b>命名参数</b>,必须写成 <code>greet(\"小明\", greeting: \"早上好\")</code><code>[]</code> 才是可选位置参数。命名参数让调用意图清晰,是团队协作中常用的做法。"
},
{
"id": 107,
"lv": 2,
"q": "写\"重试发送验证码\"逻辑:要求<b>至少发送一次</b>,失败则继续重试。哪种循环语义最贴切?",
"opts": [
"<code>while</code>",
"<code>do-while</code>",
"<code>for</code>",
"<code>repeat</code>"
],
"ans": 1,
"exp": "<b>do-while</b> 先执行循环体再判断条件,保证至少执行一次,正是\"先发一次、不行再重试\"的语义。while 先判断后执行,可能一次都不执行。"
},
{
"id": 108,
"lv": 3,
"q": "实现\"删除待办\"功能,<code>list.remove(3)</code> 和 <code>list.removeAt(3)</code> 的区别是?",
"opts": [
"remove 按值删除,removeAt 按下标删除",
"两者完全相同",
"remove 删下标,removeAt 按值删除",
"remove 会删除所有等于 3 的元素"
],
"ans": 0,
"exp": "<code>remove(value)</code> 删除<b>第一个</b>值相等的元素并返回是否成功;<code>removeAt(index)</code> 按下标删除并返回被删的元素,越界抛 RangeError。写代码前想清楚你要删的是\"值\"还是\"位置\"。"
},
{
"id": 109,
"lv": 3,
"q": "解析接口数据:用 <code>data[\"name\"]</code> 取一个<b>不存在的 key</b> 时,返回什么?",
"opts": [
"抛异常",
"null",
"空字符串",
"抛出 NoSuchMethodError"
],
"ans": 1,
"exp": "Map 用 <code>[]</code> 取值时,key 不存在<b>返回 null</b> 而不是抛异常。所以 <code>data[\"name\"]</code> 可能为 null,取值后判空再使用是常规操作(配合 <code>??</code> 兜底)。"
},
{
"id": 110,
"lv": 3,
"q": "配置列表按条件插入元素:<code>var l = [1, 2, if (isVip) 3];</code> 当 <code>isVip</code> 为 false 时 l 是?",
"opts": [
"<code>[1, 2]</code>",
"<code>[1, 2, 3]</code>",
"<code>[1, 2, false]</code>",
"编译错误"
],
"ans": 0,
"exp": "这是 <b>collection-if</b> 语法:条件不成立时该元素直接不生成。Flutter 里常用于条件渲染组件列表,配合 <code>...</code> 展开和 collection-for 一起使用。"
},
{
"id": 111,
"lv": 3,
"q": "两个用户各自关注了一批标签,要找出<b>共同关注</b>的标签,用哪种写法?",
"opts": [
"<code>a &amp; b</code>",
"<code>a.intersect(b)</code>",
"<code>a.toSet().intersection(b.toSet())</code>",
"<code>a.union(b)</code>"
],
"ans": 2,
"exp": "List 没有交集方法,要先转 Set。<code>intersection()</code> 返回两个集合的共同元素;<code>union()</code> 是并集。注意方法名是 <code>intersection()</code>,不是其它语言的 <code>intersect()</code>。"
},
{
"id": 112,
"lv": 3,
"q": "计算购物车总价:<code>List&lt;double&gt; prices = [];</code> 空列表调用 <code>prices.reduce((a, b) =&gt; a + b)</code> 会?",
"opts": [
"返回 0",
"抛出 StateError 异常",
"返回 null",
"返回空列表"
],
"ans": 1,
"exp": "<code>reduce</code> 要求列表<b>至少有一个元素</b>,空列表直接抛 \"Bad state: No element\"。更稳的写法是 <code>fold(0, (a, b) =&gt; a + b)</code>:带初始值,空列表返回 0。"
},
{
"id": 113,
"lv": 4,
"q": "写日期工具类,希望 <code>DateUtil.parse(\"2024-01-01\")</code> 在字符串不合法时返回 null 而不是抛异常,用哪种构造函数?",
"opts": [
"factory 构造函数",
"普通构造函数",
"命名构造函数",
"const 构造函数"
],
"ans": 0,
"exp": "<b>factory</b> 构造函数可以不返回新实例——可以返回缓存实例、子类实例,甚至返回 null。它把\"按需创建/校验\"的逻辑封装在构造入口,调用方写法统一且安全。"
},
{
"id": 114,
"lv": 4,
"q": "日志打印、权限检查这类能力要被多个<b>互不相关</b>的类复用,又不想强制继承关系,Dart 的推荐做法是?",
"opts": [
"<code>extends</code> 继承",
"<code>implements</code> 实现",
"<code>with</code> 混入 Mixin",
"<code>abstract</code> 抽象类"
],
"ans": 2,
"exp": "<b>Mixin</b><code>with</code>)把可复用逻辑抽出来按需混入,避免\"为了复用而强行继承\"的长继承链。Flutter 源码大量使用,如 <code>class _X with RouteAware</code>。"
},
{
"id": 115,
"lv": 4,
"q": "类内部要声明一个\"仅供本文件使用\"的私有字段,正确写法是?",
"opts": [
"<code>private String _token;</code>",
"<code>String _token;</code>",
"<code>-String token;</code>",
"<code>#token</code>"
],
"ans": 1,
"exp": "Dart 没有 <code>private</code> 关键字,以<b>下划线开头</b>表示库内私有:<code>_token</code>。注意这是\"库\"级别私有(同文件可见),不是类级别。"
},
{
"id": 116,
"lv": 4,
"q": "用户类的 <code>name</code> 字段希望<b>赋值时自动去掉首尾空格</b>,最佳做法是?",
"opts": [
"声明 getter/setter,在 setter 里 trim",
"直接写公共字段,调用方自己 trim",
"用 final 禁止修改",
"用 static 修饰"
],
"ans": 0,
"exp": "<b>setter</b> 在赋值入口统一处理,所有调用方(包括构造函数)都自动享受规范化,不用每个地方都记得 trim。写法:<code>String get name</code> 与 <code>set name(String v)</code>。"
},
{
"id": 117,
"lv": 4,
"q": "<code>class Dog implements Animal</code> 与 <code>extends</code> 继承的关键区别是?",
"opts": [
"implements 要求实现 Animal 的<b>全部</b>成员(含字段),extends 可复用已有实现",
"两者完全一样",
"implements 只能用于抽象类",
"extends 要求实现全部成员"
],
"ans": 0,
"exp": "<code>implements</code> 把 Animal 当<b>接口</b>:所有成员都要自己重写,即使父类已有实现;<code>extends</code> 继承实现,只覆写需要改的部分。implements 用于\"规定能力\"extends 用于\"复用实现\"。"
},
{
"id": 118,
"lv": 5,
"q": "用户对象可能为 null<code>User? u = await fetchUser();</code>,以下哪种使用方式更安全?",
"opts": [
"<code>if (u != null) { print(u.name); }</code> 判空后直接用 u",
"<code>if (u != null) { print(u!.name); }</code> 必须加 !",
"<code>print(u?.name ?? \"\");</code>",
"A 和 C 都可以"
],
"ans": 3,
"exp": "<b>A</b>:局部变量判空后编译器会<b>自动提升</b>为非空类型,直接使用即可;<b>C</b><code>?.</code> 安全调用 + <code>??</code> 兜底,一行搞定。只有被闭包捕获的变量才需要 <code>!</code>。"
},
{
"id": 119,
"lv": 5,
"q": "详情页要同时请求\"用户信息\"和\"订单列表\"两个接口,都拿到再渲染,用哪个方法?",
"opts": [
"<code>Future.wait([fetchUser(), fetchOrders()])</code>",
"串行写两个 await",
"<code>Future.any</code>",
"<code>Future.delayed</code> 等待"
],
"ans": 0,
"exp": "<b>Future.wait</b> 并发启动多个 Future,全部完成后统一返回结果列表,总耗时约等于最慢的那个;串行 await 则是两个请求耗时相加。<code>Future.any</code> 只等第一个完成的。"
},
{
"id": 120,
"lv": 5,
"q": "某个字段要等登录成功后才能赋值,但又想声明成非空类型,正确声明方式是?",
"opts": [
"<code>late String token;</code>",
"<code>String? token = \"\";</code>",
"<code>dynamic token;</code>",
"<code>final token = null;</code>"
],
"ans": 0,
"exp": "<b>late</b> 表示\"推迟初始化,首次访问时才赋值\",适合依赖异步结果的字段或计算代价高的属性。注意:late 变量在赋值前被访问会抛 LateInitializationError。"
},
{
"id": 121,
"lv": 5,
"q": "把解析结果转成目标类型:<code>var age = data[\"age\"] as int;</code>,当 age 实际是 String 时会发生?",
"opts": [
"返回 null",
"抛出 TypeError(类型转换失败)",
"静默忽略",
"自动转成 int"
],
"ans": 1,
"exp": "<code>as</code> 是<b>强制类型转换</b>,类型不符立即抛异常。安全做法:先用 <code>is</code> 判断类型,或先统一规范数据(如 num 一律 <code>.toInt()</code>)。"
},
{
"id": 122,
"lv": 5,
"q": "对可能为 null 的 <code>String? s</code> 使用 <code>s!</code>,若 s 确实为 null 会?",
"opts": [
"返回 null",
"抛出 TypeError(空检查失败)",
"静默继续",
"编译错误"
],
"ans": 1,
"exp": "<code>!</code> 是<b>强制解包</b>运算符,向编译器承诺\"这里不可能为 null\",承诺落空时运行时抛 TypeError。能用判空提升或 <code>??</code> 的地方尽量别用 <code>!</code>,减少运行时崩溃点。"
},
{
"id": 123,
"lv": 6,
"q": "Flutter 中常见 <code>ButtonStyle()..backgroundColor = ... ..shape = ...</code> 写法,<code>..</code> 级联操作符的核心行为是?",
"opts": [
"对同一对象连续操作且返回对象本身",
"链式调用并返回最后一次结果",
"只能用于 List",
"与 <code>...</code> 展开等价"
],
"ans": 0,
"exp": "<b>级联操作符</b> <code>..</code> 对同一对象连续执行多个操作,<b>不改变返回值</b>(始终是原对象),适合集中配置对象。<code>.</code> 返回的是方法调用的返回值,两者行为不同。"
},
{
"id": 124,
"lv": 6,
"q": "Dart 3 中,函数想直接返回\"用户名 + 年龄\"这组小数据,又不想专门建一个类,推荐用?",
"opts": [
"<code>Map&lt;String, dynamic&gt;</code>",
"Record 记录类型:<code>(String name, int age)</code>",
"<code>List&lt;dynamic&gt;</code>",
"用全局变量"
],
"ans": 1,
"exp": "<b>Record</b> 是 Dart 3 的轻量聚合类型:<code>var r = (\"张三\", 18);</code> 或命名字段 <code>(name: \"张三\", age: 18)</code>,用 <code>r.$1</code> / <code>r.name</code> 取值,还能配合模式匹配解构,比 Map 更类型安全。"
},
{
"id": 125,
"lv": 6,
"q": "Dart 3 的 switch 可以直接当表达式赋值:<code>var s = switch (status) { \"ok\" =&gt; \"成功\", _ =&gt; \"未知\" };</code> 这种写法?",
"opts": [
"合法:switch 表达式返回一个值",
"非法:switch 不能作表达式",
"必须每个分支加 break",
"只能匹配字符串"
],
"ans": 0,
"exp": "<b>switch 表达式</b>用 <code>=&gt;</code> 代替冒号,整体作为值赋给变量,配合模式匹配可以解构、判断类型,比旧式 switch 简洁,也天然避免了漏 break 的坑。"
},
{
"id": 126,
"lv": 6,
"q": "想给系统类 <code>String</code> 加一个 <code>isEmail()</code> 方法,但 String 不能改源码,用?",
"opts": [
"<b>extension</b> 扩展:<code>extension EmailCheck on String { bool isEmail() {...} }</code>",
"继承 String",
"用 mixin 混入",
"写静态工具类"
],
"ans": 0,
"exp": "<b>extension</b> 可以不修改原类给已有类型添加方法,之后 <code>\"a@b.com\".isEmail()</code> 直接调用。注意扩展方法是静态分发(编译期决定),不参与运行时多态。"
},
{
"id": 127,
"lv": 6,
"q": "泛型容器 <code>class Box&lt;T extends num&gt; {}</code> 中 <code>extends num</code> 的含义是?",
"opts": [
"T 只能是 int、double 等数值类型",
"T 可以是任意类型",
"T 必须可空",
"T 必须是集合类型"
],
"ans": 0,
"exp": "<b>泛型约束</b>限定 T 必须是 num 及其子类(int/double),这样 Box 内部就能安全调用数值运算。不加约束时 T 等价于 <code>Object?</code>。"
}
],
"lvNames": [
"基础语法",
"控制流与函数",
"集合",
"面向对象",
"空安全与异步",
"Dart 高级特性"
]
},
{
"id": 10,
"name": "Flutter 开发实战",
"questions": [
{
"id": 128,
"lv": 1,
"q": "新建 Flutter 项目后,把应用真正跑起来的入口代码是?",
"opts": [
"<code>void main() =&gt; runApp(MyApp());</code>",
"<code>void main() { MyApp(); }</code>",
"<code>runApp() { MyApp }</code>",
"<code>main() { MaterialApp(); }</code>"
],
"ans": 0,
"exp": "Dart 程序入口是 <code>main()</code>Flutter 通过 <code>runApp(Widget)</code> 把根组件挂载到引擎上。注意 <code>MyApp()</code> 是创建 Widget 实例,不能少了括号。"
},
{
"id": 129,
"lv": 1,
"q": "实现一个\"3 秒倒计时\"按钮,每秒刷新显示剩余秒数,应使用?",
"opts": [
"StatelessWidget",
"StatefulWidget",
"Container",
"Scaffold"
],
"ans": 1,
"exp": "有<b>动态变化的数据</b>(倒计时数字)就需要 StatefulWidget:数据保存在 State 里,变化时调 <code>setState()</code> 刷新。没有可变状态时应优先用 StatelessWidget。"
},
{
"id": 130,
"lv": 1,
"q": "界面上要显示一行固定文案\"登录成功\",最合适的组件是?",
"opts": [
"Text",
"Label",
"Paragraph",
"RichText"
],
"ans": 0,
"exp": "<code>Text</code> 是最常用的文本组件,可通过 style 配置字号、颜色、字重等。RichText 用于一段文字中混排多种样式的富文本场景。"
},
{
"id": 131,
"lv": 1,
"q": "点击\"确认\"按钮后分数要 +1 并立即显示到界面,正确流程是?",
"opts": [
"修改 State 字段后调用 <code>setState()</code>",
"直接修改字段就行",
"重启整个 App",
"修改 Widget 的构造参数"
],
"ans": 0,
"exp": "State 里的字段只是普通数据,必须调用 <code>setState()</code> 通知框架\"数据变了\",框架才会重新执行 build 刷新 UI。注意 setState 只重建<b>当前 State 的子树</b>,不是整棵树。"
},
{
"id": 132,
"lv": 1,
"q": "要展示 10000 条商品记录,性能最优的做法是?",
"opts": [
"<code>ListView.builder</code>(按需构建可见项)",
"Column 里循环放 10000 个组件",
"SingleChildScrollView 包 Column",
"GridView.count 全部构建"
],
"ans": 0,
"exp": "<code>ListView.builder</code> 只构建<b>当前可见</b>的列表项(懒加载),滚动时回收复用;Column + for 会一次性构建全部组件,万条数据直接卡死。数据量大优先 builder 系列。"
},
{
"id": 133,
"lv": 2,
"q": "登录页要垂直排列\"输入框、输入框、登录按钮\",用哪个布局组件?",
"opts": [
"Column",
"Row",
"Stack",
"Wrap"
],
"ans": 0,
"exp": "<code>Column</code> 垂直方向排列子组件,<code>Row</code> 水平方向,<code>Stack</code> 叠加并支持定位,<code>Wrap</code> 在空间不足时自动换行。"
},
{
"id": 134,
"lv": 2,
"q": "Row 里想让一个按钮<b>占满剩余宽度</b>(剩余空间全给它),按钮外层套?",
"opts": [
"<code>Expanded</code>",
"<code>Padding</code>",
"<code>Center</code>",
"<code>Spacer</code>"
],
"ans": 0,
"exp": "<code>Expanded</code> 让子组件占据主轴的剩余空间(Row 的主轴是水平方向)。多个 Expanded 可通过 flex 参数按比例分配空间。"
},
{
"id": 135,
"lv": 2,
"q": "Column 里有几个按钮,想让它们在<b>水平方向居中</b>,应该设置?",
"opts": [
"<code>crossAxisAlignment: CrossAxisAlignment.center</code>",
"<code>mainAxisAlignment: MainAxisAlignment.center</code>",
"<code>alignment: Alignment.center</code>",
"<code>textAlign: TextAlign.center</code>"
],
"ans": 0,
"exp": "Column 的<b>主轴是垂直</b>方向、<b>交叉轴是水平</b>方向。水平居中=沿交叉轴居中=crossAxisAlignment.centermainAxisAlignment 控制的是垂直方向。主/交叉轴搞反是新手最常踩的坑。"
},
{
"id": 136,
"lv": 2,
"q": "封面图要<b>铺满整个区域且不拉伸变形</b>(多出的部分裁掉),fit 参数应该用?",
"opts": [
"<code>BoxFit.cover</code>",
"<code>BoxFit.fill</code>",
"<code>BoxFit.none</code>",
"<code>BoxFit.scaleDown</code>"
],
"ans": 0,
"exp": "<b>cover</b>:等比缩放填满、超出裁剪,常用于头像、封面;<b>fill</b> 会拉伸变形;<b>contain</b> 完整显示但可能留白;<b>none</b> 不缩放。"
},
{
"id": 137,
"lv": 2,
"q": "商品名过长时希望<b>单行显示,超出部分显示省略号</b>Text 的配置是?",
"opts": [
"<code>maxLines: 1, overflow: TextOverflow.ellipsis</code>",
"只设 overflow: TextOverflow.clip",
"只设 maxLines: 3",
"<code>softWrap: false</code> 就够了"
],
"ans": 0,
"exp": "<code>maxLines</code> 限制最大行数 + <code>overflow: TextOverflow.ellipsis</code> 指定超出的表现(省略号/裁剪/fade),两者配合才能出现\"…\"效果。"
},
{
"id": 138,
"lv": 3,
"q": "输入框需要\"预填默认值 + 按钮点击后读取内容 + 随时清空\",推荐用?",
"opts": [
"<code>TextEditingController</code>",
"每次读 onChanged 参数",
"GlobalKey 临时查找",
"无法实现"
],
"ans": 0,
"exp": "<code>TextEditingController</code> 是输入框的\"数据源\"<code>TextEditingController(text: \"预填\")</code> 初始化,<code>controller.text</code> 读取,<code>controller.clear()</code> 清空。用完后在 dispose 里释放。"
},
{
"id": 139,
"lv": 3,
"q": "页面销毁时,取消监听、释放 Controller 的代码应该写在哪里?",
"opts": [
"<code>dispose()</code>",
"<code>initState()</code>",
"<code>build()</code>",
"<code>didChangeDependencies()</code>"
],
"ans": 0,
"exp": "<code>dispose()</code> 是 State 被移除时的清理钩子:取消 Timer、Stream 订阅、释放 Controller。不清理会造成内存泄漏和 \"setState called after dispose\" 报错。"
},
{
"id": 140,
"lv": 3,
"q": "登录状态、用户信息要被<b>多个页面共享</b>,又不想层层传构造参数,项目常用方案是?",
"opts": [
"Provider / InheritedWidget",
"定义全局变量直接读写",
"每个页面重新请求接口",
"把状态塞进路由参数"
],
"ans": 0,
"exp": "Flutter 官方的底层方案是 <b>InheritedWidget</b><b>Provider</b> 是对它的优雅封装:共享数据放顶层,子组件 <code>context.watch()</code> 取用并自动刷新。全局变量没有通知机制,页面不会自动更新。"
},
{
"id": 141,
"lv": 3,
"q": "商品列表页要支持<b>下拉刷新</b>,应该把 ListView 包进哪个组件?",
"opts": [
"<code>RefreshIndicator</code>",
"<code>GestureDetector</code>",
"<code>AnimatedList</code>",
"<code>Scrollbar</code>"
],
"ans": 0,
"exp": "<code>RefreshIndicator</code> 包住可滚动组件,配合 <code>onRefresh</code> 返回的 Future 实现下拉刷新动画。这是 Material 规范自带的下拉刷新方案。"
},
{
"id": 142,
"lv": 3,
"q": "App 切到后台、再回到前台时,需要暂停/恢复计时器,通过什么监听?",
"opts": [
"<code>WidgetsBindingObserver</code>",
"<code>RouteObserver</code>",
"<code>NavigatorObserver</code>",
"<code>MediaQuery</code>"
],
"ans": 0,
"exp": "给 State 混入 <code>with WidgetsBindingObserver</code>,在 <code>didChangeAppLifecycleState()</code> 中监听 AppLifecycleStatepaused / resumed / inactive),即可感知前后台切换。"
},
{
"id": 143,
"lv": 4,
"q": "列表页点击商品跳转详情页,标准写法是?",
"opts": [
"<code>Navigator.push(context, MaterialPageRoute(builder: (_) =&gt; DetailPage(id: id)))</code>",
"<code>Navigator.go(DetailPage(id: id))</code>",
"<code>Route.open(DetailPage())</code>",
"<code>DetailPage(id: id).show()</code>"
],
"ans": 0,
"exp": "<code>Navigator.push</code> 把新路由压入导航栈,MaterialPageRoute 提供转场动画,返回上一页用 <code>Navigator.pop(context)</code>。"
},
{
"id": 144,
"lv": 4,
"q": "详情页编辑完要把结果<b>回传给列表页</b>,详情页返回时应该写?",
"opts": [
"<code>Navigator.pop(context, result)</code>",
"<code>Navigator.back(result)</code>",
"<code>Navigator.pushResult(result)</code>",
"<code>context.popAndPush(result)</code>"
],
"ans": 0,
"exp": "<code>pop(context, 返回值)</code> 的第二个参数就是回传的数据,列表页 <code>await push(...)</code> 的返回值即可收到。注意 pop 的参数是\"返回给上一页的数据\",不是新路由。"
},
{
"id": 145,
"lv": 4,
"q": "跳转时需要传递商品 id、名称等参数,团队协作中<b>最推荐</b>的传参方式是?",
"opts": [
"<code>DetailPage(id: 1, name: \"商品\")</code> 构造参数传递",
"全局变量临时存",
"static 静态字段存",
"URL 字符串拼接"
],
"ans": 0,
"exp": "构造参数显式、类型安全、可读性好——跳转处一眼看到传了什么。全局变量/静态字段是隐藏依赖,页面多起来后非常难维护。"
},
{
"id": 146,
"lv": 4,
"q": "在 App 里注册命名路由(如 <code>/login</code>、<code>/home</code>),配置位置是?",
"opts": [
"<code>MaterialApp(routes: {...})</code>",
"<code>main()</code> 函数里",
"<code>runApp()</code> 参数",
"每个页面里各自声明"
],
"ans": 0,
"exp": "命名路由集中配置在 <code>MaterialApp</code> 的 <code>routes</code> 表,之后用 <code>Navigator.pushNamed(context, \"/login\")</code> 跳转;需要传参时配合 <code>arguments</code> 参数。"
},
{
"id": 147,
"lv": 4,
"q": "跳到选择页,用户选完返回后要根据<b>选择结果</b>继续处理,列表页怎么写?",
"opts": [
"<code>final r = await Navigator.push(...);</code> 拿到返回值再处理",
"无法等待结果",
"用 setState 轮询",
"跳转时把回调函数传过去"
],
"ans": 0,
"exp": "<code>push</code> 返回 <code>Future&lt;T&gt;</code>await 后拿到 pop 回传的值,再执行后续逻辑(如刷新选中项)。这是\"页面间取结果\"的标准姿势。"
},
{
"id": 148,
"lv": 5,
"q": "接口返回 JSON 字符串,执行 <code>var data = jsonDecode(text);</code> 后 data 的类型是?",
"opts": [
"<code>dynamic</code>(实际可能是 Map 或 List",
"<code>String</code>",
"<code>Map&lt;String, dynamic&gt;</code>",
"<code>Object</code>"
],
"ans": 0,
"exp": "<code>jsonDecode</code> 的返回类型是 <code>dynamic</code>JSON 对象变 Map&lt;String, dynamic&gt;、数组变 List&lt;dynamic&gt;、数字/布尔原样返回。通常需要手动转换:<code>jsonDecode(text) as Map&lt;String, dynamic&gt;</code>。"
},
{
"id": 149,
"lv": 5,
"q": "要把用户对象 <code>User(id: 1, name: \"张三\")</code> 转成 JSON 字符串发给服务器,正确流程是?",
"opts": [
"给 User 写 <code>toJson()</code>,再 <code>jsonEncode(user.toJson())</code>",
"<code>jsonEncode(user)</code> 直接序列化",
"<code>user.toString()</code>",
"<code>JSON.stringify(user)</code>"
],
"ans": 0,
"exp": "Dart 默认不会自动序列化任意对象,惯例是模型类自己实现 <code>Map&lt;String, dynamic&gt; toJson()</code>,再用 <code>jsonEncode</code> 转字符串;反序列化则对应 <code>User.fromJson(Map)</code> 工厂构造。"
},
{
"id": 150,
"lv": 5,
"q": "网络请求可能超时/断网,用户点\"登录\"时抛出了异常,正确的处理是?",
"opts": [
"用 try-catch 包裹 await 请求,捕获异常并提示用户",
"不做处理",
"打印日志后忽略",
"延迟 3 秒自动重试"
],
"ans": 0,
"exp": "<code>try { await api.login(...) } catch (e) { 提示\"网络异常,请重试\" }</code> 是最基础的错误处理;生产环境还会区分 SocketException(断网)、TimeoutException(超时)等做分类提示。"
},
{
"id": 151,
"lv": 5,
"q": "请求进行中需要展示\"加载中\"状态,最合适用?",
"opts": [
"<code>CircularProgressIndicator</code>",
"Text(\"加载中...\")",
"Opacity 半透明遮罩",
"AnimatedContainer 闪烁"
],
"ans": 0,
"exp": "Material 标准的加载指示器是 <code>CircularProgressIndicator</code>(转圈)和 <code>LinearProgressIndicator</code>(条),配合 loading 状态变量做条件渲染。"
},
{
"id": 152,
"lv": 5,
"q": "http 请求要设置 10 秒超时,正确写法是?",
"opts": [
"<code>await http.get(url).timeout(Duration(seconds: 10))</code>",
"<code>http.get(url, timeout: 10)</code>",
"<code>Timer(Duration(seconds: 10), ...)</code>",
"<code>Future.any</code> 自动超时"
],
"ans": 0,
"exp": "Future 自带 <code>.timeout()</code>:超时抛 TimeoutException,可再接 <code>onTimeout</code> 参数兜底。http 包本身没有 timeout 参数,这是常见的误解。"
},
{
"id": 153,
"lv": 6,
"q": "首页同时请求\"轮播图、公告、商品列表\"三个接口,全部完成再渲染页面,用?",
"opts": [
"<code>Future.wait([...])</code> 并发等待",
"三个 await 串行",
"依次 catch 分别显示",
"用 <code>Future.any</code>"
],
"ans": 0,
"exp": "并发场景用 <code>Future.wait</code>,总耗时约等于最慢的请求而不是三个相加。生产上可配合记录单个请求失败做局部降级(某块失败不影响整页)。"
},
{
"id": 154,
"lv": 6,
"q": "商品列表滑到底部要自动加载下一页,监听滚动的方案是?",
"opts": [
"<code>ScrollController</code> 监听 pixels 接近 maxScrollExtent 时触发",
"用 Timer 每 3 秒加载",
"下拉刷新代替",
"GestureDetector 监听手势"
],
"ans": 0,
"exp": "给 ListView 挂 <code>ScrollController</code>,在 <code>addListener</code> 里判断 <code>position.pixels &gt;= position.maxScrollExtent - 200</code>(预留阈值)就加载下一页,配合 loading 状态防止重复触发。"
},
{
"id": 155,
"lv": 6,
"q": "商品标题中要<b>红色高亮\"热卖\"二字</b>、其余部分灰色,用?",
"opts": [
"<code>RichText</code> + <code>TextSpan</code> 组合不同样式",
"拆成多个 Text 排一排",
"用换行符拼接字符串",
"TextStyle 做不到,只能整段同色"
],
"ans": 0,
"exp": "<code>RichText</code> 的 <code>TextSpan</code> 支持树状嵌套:每个 span 可以有自己的 style,是\"一段文字多种样式\"的标准方案。"
},
{
"id": 156,
"lv": 6,
"q": "界面出现<b>黄黑条纹</b>溢出警告(RenderFlex overflowed),说明?",
"opts": [
"子组件超出了父组件给定的约束空间",
"主题颜色配置错误",
"内存泄漏",
"字体加载失败"
],
"ans": 0,
"exp": "Row/Column 空间不够时子组件溢出,debug 模式显示黄黑条纹并打印 \"RenderFlex overflowed by XX pixels\"。解决思路:Expanded/Flexible 让出空间、SingleChildScrollView 滚动、FittedBox 缩放。"
},
{
"id": 157,
"lv": 6,
"q": "瀑布流商品图列表很卡、图片解码占内存高,优先优化手段是?",
"opts": [
"<code>cacheWidth</code> / <code>ResizeImage</code> 限制解码尺寸",
"给图片加圆角",
"提高图片的清晰度",
"减少列表项数量"
],
"ans": 0,
"exp": "Image.network 默认按原图尺寸解码,一张 4000px 大图在手机上就是内存杀手。用 <code>cacheWidth: 300</code> 或 <code>ResizeImage</code> 让引擎按需解码成小尺寸位图,内存占用和滚动性能立竿见影。"
}
],
"lvNames": [
"Widget 基础",
"布局与组件",
"状态管理",
"路由与导航",
"数据与网络",
"实战综合"
]
},
{
"id": 11,
"name": "Dart 基础巩固",
"lvNames": [
"基础语法",
"控制流与函数",
"集合",
"面向对象",
"空安全与异步",
"Dart 高级特性"
],
"questions": [
{
"id": 158,
"lv": 1,
"q": "Dart 中布尔类型 <code>bool</code> 的字面量写法是?",
"opts": [
"<code>TRUE</code>",
"<code>true</code>",
"<code>1</code>",
"<code>yes</code>"
],
"ans": 1,
"exp": "bool 只有两个值:<b>true</b> 和 <b>false</b>(全小写)。TRUE、1、yes 都不是 Dart 的合法写法。"
},
{
"id": 159,
"lv": 1,
"q": "要存储 3.14 这样的浮点数,应声明为什么类型?",
"opts": [
"<code>float</code>",
"<code>decimal</code>",
"<code>big</code>",
"<code>double</code>"
],
"ans": 3,
"exp": "浮点数用 <b>double</b>,整数用 <b>int</b>,两者的共同父类是 <b>num</b>。Dart 没有 float、decimal 类型。"
},
{
"id": 160,
"lv": 1,
"q": "定义一个内容跨多行的字符串,应该用哪组引号包裹?",
"opts": [
"<code>'''</code> 三个单引号(或 <code>\"\"\"</code>",
"<code>\"</code> 单个双引号",
"<code>'</code> 单个单引号",
"<code>`</code> 反引号"
],
"ans": 0,
"exp": "三个单引号 <code>'''</code> 或三个双引号 <code>\"\"\"</code> 包裹的字符串<b>可以直接跨行书写</b>,换行会被保留;单引号/双引号字符串内不能直接换行。Dart 没有反引号模板字符串。"
},
{
"id": 161,
"lv": 1,
"q": "关于 Dart 字符串的特性,以下说法正确的是?",
"opts": [
"字符串创建后可随意修改内部字符",
"字符串只能用双引号包裹",
"字符串方法返回新字符串,原字符串不变",
"字符串本质是数字类型"
],
"ans": 2,
"exp": "Dart 字符串<b>不可变</b><code>toUpperCase()</code> 等方法返回<b>新字符串</b>,原值不变。频繁拼接字符串时建议用 <b>StringBuffer</b> 提升性能。"
},
{
"id": 162,
"lv": 1,
"q": "<code>String s = \"Hello\";</code> 要得到全大写的 \"HELLO\",应调用?",
"opts": [
"<code>s.upper()</code>",
"<code>s.toUpperCase()</code>",
"<code>s.up()</code>",
"<code>String.upper(s)</code>"
],
"ans": 1,
"exp": "转大写用 <b>s.toUpperCase()</b>,转小写用 <code>s.toLowerCase()</code>。注意返回的是<b>新字符串</b>,原字符串不变。"
},
{
"id": 163,
"lv": 1,
"q": "把数字 42 转成字符串 \"42\",正确写法是?",
"opts": [
"<code>(String) 42</code>",
"<code>int.stringify(42)</code>",
"<code>42.toString()</code>",
"<code>String.valueOf(42)</code>"
],
"ans": 2,
"exp": "调用 <b>toString()</b><code>42.toString()</code>。注意 double 的 toString 会带小数:<code>3.0.toString()</code> 得到 \"3.0\"。(String) 强转、String.valueOf 是其它语言的习惯,Dart 没有。"
},
{
"id": 164,
"lv": 2,
"q": "取 a、b 中较大的值,用一行表达式应写?",
"opts": [
"<code>a ?? b</code>",
"<code>a &gt; b ? a : b</code>",
"<code>a || b</code>",
"<code>a &amp;&amp; b</code>"
],
"ans": 1,
"exp": "<b>三元运算符</b> <code>条件 ? 表达式1 : 表达式2</code>:条件成立取第一个,否则取第二个。<code>a ?? b</code> 是空值合并(a 为 null 才取 b),|| 和 &amp;&amp; 是逻辑运算,都不能比较大小。"
},
{
"id": 165,
"lv": 2,
"q": "<code>bool ok = false; print(ok &amp;&amp; check());</code> 中 check() 会被执行吗?",
"opts": [
"会执行",
"报错",
"不会执行,&amp;&amp; 左侧为 false 时短路",
"执行两次"
],
"ans": 2,
"exp": "<b>短路求值</b><code>&amp;&amp;</code> 左侧为 false 时结果已确定为 false<b>右侧不会执行</b><code>||</code> 左侧为 true 时同理。常用于安全判断:<code>user != null &amp;&amp; user.isVip</code>。"
},
{
"id": 166,
"lv": 2,
"q": "循环里要<b>跳过当前这一次迭代</b>、继续下一次,用哪个关键字?",
"opts": [
"<code>break</code>",
"<code>return</code>",
"<code>continue</code>",
"<code>skip</code>"
],
"ans": 2,
"exp": "<b>continue</b> 结束本次迭代、立即进入下一轮;<b>break</b> 直接退出整个循环;return 退出的是整个函数。"
},
{
"id": 167,
"lv": 2,
"q": "<code>var f = (x) =&gt; x * 2;</code> 这种没有名字的函数写法是?",
"opts": [
"命名函数",
"构造函数",
"静态函数",
"匿名函数(Lambda 表达式)"
],
"ans": 3,
"exp": "箭头 <b>=&gt;</b> 配合无名参数列表就是<b>匿名函数 / Lambda</b>,常作为回调、比较器传递:<code>list.sort((a, b) =&gt; a - b)</code>。"
},
{
"id": 168,
"lv": 2,
"q": "<code>void greet(String name, [String word = \"你好\"])</code>,调用 <code>greet(\"小明\")</code> 时 word 的值是?",
"opts": [
"<code>null</code>",
"报错",
"<code>\"你好\"</code>",
"空字符串"
],
"ans": 2,
"exp": "<b>[ ]</b> 包裹的是<b>可选位置参数</b>,不传时使用默认值 <b>\"你好\"</b>。它与命名参数({} 包裹、必须按名传)不同,是按顺序传参的:<code>greet(\"小明\", \"早上好\")</code>。"
},
{
"id": 169,
"lv": 2,
"q": "循环里要<b>立即终止整个循环</b>,用哪个关键字?",
"opts": [
"<code>break</code>",
"<code>continue</code>",
"<code>exit</code>",
"<code>stop</code>"
],
"ans": 0,
"exp": "<b>break</b> 立即结束当前循环;continue 只跳过本次。break 也常用于 switch 的 case 结尾——Dart 要求非空 case 必须以 break、return、throw 等结束。"
},
{
"id": 170,
"lv": 3,
"q": "<code>var data = {'name': '张三', 'age': 18};</code> data 的类型是?",
"opts": [
"<code>Set</code>",
"<code>List</code>",
"<code>Map</code>",
"<code>Tuple</code>"
],
"ans": 2,
"exp": "<b>{}</b> 且含冒号键值对时创建 <b>Map</b>;无冒号的 <code>{1, 2, 3}</code> 是 Set<code>[]</code> 是 List。Dart 没有 Tuple 类型。"
},
{
"id": 171,
"lv": 3,
"q": "<code>var map = {'a': 1}; map['b'] = 2;</code> 之后 map 的内容是?",
"opts": [
"<code>{'a': 1}</code>",
"<code>{'b': 2}</code>",
"报错",
"<code>{'a': 1, 'b': 2}</code>"
],
"ans": 3,
"exp": "<code>map[key] = value</code> 写入时,key 已存在则<b>更新值</b>,不存在则<b>新增条目</b>,所以得到 <code>{'a': 1, 'b': 2}</code>。"
},
{
"id": 172,
"lv": 3,
"q": "<code>['2024', '2025', '2026'].join('-')</code> 的结果是?",
"opts": [
"<code>'2024,2025,2026'</code>",
"报错",
"<code>'2024-2025-2026'</code>",
"<code>'202420252026'</code>"
],
"ans": 2,
"exp": "<b>join(分隔符)</b> 把列表元素按分隔符拼成一个字符串,得到 <code>2024-2025-2026</code>。注意不传参数时是<b>无分隔符</b>直接拼接。"
},
{
"id": 173,
"lv": 3,
"q": "从 <code>[1, 2, 3, 4]</code> 中挑出偶数(2、4),应调用哪个方法?",
"opts": [
"<code>map</code>",
"<code>reduce</code>",
"<code>addAll</code>",
"<code>where</code>"
],
"ans": 3,
"exp": "<b>where()</b> 按条件<b>过滤</b><code>[1,2,3,4].where((n) =&gt; n % 2 == 0)</code> 得到 2、4。map() 是变换每个元素,reduce() 是聚合,addAll() 是批量添加。"
},
{
"id": 174,
"lv": 3,
"q": "<code>var nums = [3, 1, 2]; nums.sort();</code> 排序后 nums 是?",
"opts": [
"<code>[3, 1, 2]</code>",
"<code>[1, 2, 3]</code>",
"报错",
"<code>[2, 3, 1]</code>"
],
"ans": 1,
"exp": "<b>sort()</b> 原地<b>升序</b>排序,结果 <b>[1, 2, 3]</b>。注意它直接修改原列表并返回 void;要降序需传比较器:<code>sort((a, b) =&gt; b - a)</code>。"
},
{
"id": 175,
"lv": 3,
"q": "<code>[1, 2, 3].contains(5)</code> 返回?",
"opts": [
"<code>true</code>",
"<code>5</code>",
"<code>null</code>",
"<code>false</code>"
],
"ans": 3,
"exp": "<b>contains()</b> 判断是否包含指定元素,包含返回 true、否则 <b>false</b>。判断 Map 的键用 <code>containsKey</code>,判断值用 <code>containsValue</code>。"
},
{
"id": 176,
"lv": 4,
"q": "<code>class Point { Point(this.x, this.y); int x; int y; }</code> 中 this.x 的作用是?",
"opts": [
"创建私有变量",
"引用父类构造函数",
"把构造参数自动赋给同名成员变量",
"声明静态字段"
],
"ans": 2,
"exp": "构造参数写成 <b>this.x</b> 时,Dart 会把参数值<b>自动赋给同名成员变量</b>,省去手写 <code>this.x = x;</code> 的样板代码。"
},
{
"id": 177,
"lv": 4,
"q": "想给 Point 提供便捷构造 <code>Point.origin()</code>x、y 初始化为 0),应该用?",
"opts": [
"静态方法",
"构造重载",
"默认构造函数",
"命名构造函数"
],
"ans": 3,
"exp": "<b>命名构造函数</b><code>Point.origin() : x = 0, y = 0;</code>,调用 <code>Point.origin()</code>。Dart 不支持构造重载,用命名构造区分多种初始化方式。"
},
{
"id": 178,
"lv": 4,
"q": "统计 Point 类一共创建了多少个实例,计数变量应声明为?",
"opts": [
"<code>final int count</code>",
"<code>static int count</code>",
"<code>int count</code>",
"<code>late int count</code>"
],
"ans": 1,
"exp": "<b>static</b> 成员属于<b>类本身</b>、所有实例共享一份,在构造函数里 <code>count++</code> 即可统计,读取用 <code>Point.count</code>,不需要实例。"
},
{
"id": 179,
"lv": 4,
"q": "私有字段 _name 想对外<b>只读</b>(能读不能写),应该声明?",
"opts": [
"<code>void name() {}</code>",
"<code>static String name;</code>",
"<code>String get name =&gt; _name;</code>",
"<code>set String name;</code>"
],
"ans": 2,
"exp": "<b>getter</b> 提供只读访问:<code>String get name =&gt; _name;</code>,外部用 <code>p.name</code> 读取;需要可写时再补 <code>set name(String v)</code>。"
},
{
"id": 180,
"lv": 4,
"q": "<code>@override</code> 注解放在重写方法上的作用是?",
"opts": [
"调用父类方法",
"声明重写父类方法,签名不符会编译报错",
"创建新方法",
"删除父类方法"
],
"ans": 1,
"exp": "<b>@override</b> 声明\"此方法重写了父类/接口方法\",签名不一致时<b>编译报错</b>,防止手滑写错。调用父类实现用 <code>super.xxx()</code>。"
},
{
"id": 181,
"lv": 4,
"q": "子类构造函数里显式调用父类构造函数,用哪个关键字?",
"opts": [
"<code>this</code>",
"<code>base</code>",
"<code>super</code>",
"<code>parent</code>"
],
"ans": 2,
"exp": "用 <b>super</b><code>Dog(String n) : super(n);</code>。父类有默认构造时可省略(自动调用);需要传参或指定父类命名构造时才显式写 super。"
},
{
"id": 182,
"lv": 5,
"q": "<code>String? s;</code>(可能为 null),直接写 <code>print(s.length)</code> 会?",
"opts": [
"输出 0",
"输出 null",
"编译报错:可空类型不能直接调用方法",
"正常运行"
],
"ans": 2,
"exp": "空安全下 <code>String?</code> 可能为 null<b>不能直接调用方法</b>,编译期直接报错——这是空安全的核心价值:把空指针问题拦在编译期。需先判空,或用 <code>?</code> / <code>!</code>。"
},
{
"id": 183,
"lv": 5,
"q": "希望\"s 为 null 时整句返回 null,否则调用 length\",应该用?",
"opts": [
"<code>s.length</code>",
"<code>s?.length</code>",
"<code>s!.length</code>",
"<code>s ?? length</code>"
],
"ans": 1,
"exp": "<b>?.</b> 安全调用:s 为 null 时结果为 null、不执行调用。常与 <code>??</code> 配合兜底:<code>s?.length ?? 0</code>。<code>s!.length</code> 是强制解包,s 为 null 时运行时报错。"
},
{
"id": 184,
"lv": 5,
"q": "<code>int? x = null; x ??= 10;</code> 的作用是?",
"opts": [
"把 x 强制转为非空",
"x 为 null 时才赋值为 10,否则保持原值",
"报错",
"x 永远等于 10"
],
"ans": 1,
"exp": "<b>??=</b> 是\"若空则赋值\"x 为 null 时赋 10,若已有值则保持不变。等价于 <code>x = x ?? 10</code>,常用于给可空变量设置默认值。"
},
{
"id": 185,
"lv": 5,
"q": "执行 <code>throw FormatException('格式错误');</code> 会?",
"opts": [
"打印日志后继续执行",
"抛出异常,未被捕获则程序中断",
"返回 null",
"编译错误"
],
"ans": 1,
"exp": "<b>throw</b> 主动抛出异常并中断当前执行,外层 <b>try-catch</b> 能接住就继续运行,否则异常冒泡导致程序崩溃。适合校验不通过时立即终止。"
},
{
"id": 186,
"lv": 5,
"q": "处理可能出错的代码,正确的结构是?",
"opts": [
"<code>catch { } try { }</code>",
"<code>if (error) { }</code>",
"<code>try { } catch (e) { } finally { }</code>",
"<code>except: { }</code>"
],
"ans": 2,
"exp": "<b>try-catch-finally</b>try 放可能异常的代码,catch 捕获处理(可写 <code>on FormatException</code> 精确分类),finally 无论成败都会执行,适合释放资源。"
},
{
"id": 187,
"lv": 6,
"q": "定义星期、状态码这类<b>固定的一组取值</b>,应使用?",
"opts": [
"<code>typedef</code>",
"<code>enum</code>",
"<code>const</code> 列表",
"<code>abstract</code> 类"
],
"ans": 1,
"exp": "<b>enum</b> 枚举:<code>enum Status { pending, done, failed }</code>,用 <code>Status.done</code> 取值,配合 switch 模式匹配处理,比散落的字符串常量更类型安全。"
},
{
"id": 188,
"lv": 6,
"q": "写一个\"返回列表首个元素\"的通用函数,任意类型都能用,正确的签名是?",
"opts": [
"<code>String first(List&lt;String&gt; list)</code>",
"<code>void first(List list)</code>",
"<code>&lt;T&gt; T first(List&lt;T&gt; list)</code>",
"函数不能带类型参数"
],
"ans": 2,
"exp": "<b>泛型函数</b><code>&lt;T&gt; T first(List&lt;T&gt; list)</code>T 是类型占位,调用时自动推断:<code>first([1, 2])</code> 返回 int<code>first(['a'])</code> 返回 String,既通用又保留类型信息。"
},
{
"id": 189,
"lv": 6,
"q": "<code>typedef Compare = int Function(int a, int b);</code> 的作用是?",
"opts": [
"创建了一个新函数",
"声明一个类",
"定义枚举",
"给函数类型起别名"
],
"ans": 3,
"exp": "<b>typedef</b> 给函数类型起别名:之后 <code>Compare cmp;</code> 或作为参数类型 <code>void sort(Compare cmp)</code>,签名一目了然。Dart 2.13+ 还能给普通类型起别名。"
},
{
"id": 190,
"lv": 6,
"q": "<code>var (name, age) = ('张三', 18);</code> 之后 name 和 age 的值是?",
"opts": [
"name=18age='张三'",
"name='张三'age=18",
"报错:不能解构",
"两者都是 null"
],
"ans": 1,
"exp": "这是 Dart 3 的<b>解构</b>:把 Record <code>('张三', 18)</code> 按位置拆开,name='张三'、age=18。函数返回 Record 时配合解构取值非常简洁。"
}
]
},
{
"id": 12,
"name": "Flutter 基础巩固",
"questions": [
{
"id": 191,
"lv": 1,
"q": "新建 Flutter 项目后,日常编写页面代码的入口文件是哪一个?",
"opts": [
"lib/main.dart",
"bin/main.dart",
"web/index.html",
"pubspec.yaml"
],
"ans": 0,
"exp": "Flutter 约定业务代码都放在 <code>lib/</code> 目录下,入口文件是 <code>lib/main.dart</code><code>bin/</code> 是纯 Dart 命令行项目的约定目录,<code>pubspec.yaml</code> 是项目配置而不是代码。"
},
{
"id": 192,
"lv": 1,
"q": "要做一个“顶部有标题栏、中间是内容、右下角有悬浮按钮”的标准页面,最合适的骨架组件是?",
"opts": [
"Scaffold",
"Container",
"Column",
"MaterialApp"
],
"ans": 0,
"exp": "<code>Scaffold</code> 是 Material 页面骨架,直接提供 <code>appBar</code>、<code>body</code>、<code>floatingActionButton</code>、<code>bottomNavigationBar</code> 等插槽;<code>MaterialApp</code> 是整个 App 的根配置,一个 App 通常只需要一个。"
},
{
"id": 193,
"lv": 1,
"q": "<code>MaterialApp(home: LoginPage())</code> 中 <b>home</b> 参数的作用是?",
"opts": [
"指定 App 启动后显示的第一个页面",
"指定 App 在手机桌面上的图标",
"注册全部命名路由的映射表",
"设置按下返回键后回到的页面"
],
"ans": 0,
"exp": "<code>home</code> 就是应用的首页 Widget;注册命名路由用的是 <code>routes</code> 参数;App 图标要在各平台的原生工程里配置。"
},
{
"id": 194,
"lv": 1,
"q": "编辑器提示你把 <code>Text('登录')</code> 改写成 <code>const Text('登录')</code>,这样做的主要好处是?",
"opts": [
"该 Widget 在父组件重建时可复用同一个实例,减少重建开销",
"让这段文字变成只读,用户无法选中复制",
"只是编译更快,运行时没有任何区别",
"保证这个组件在页面中只会出现一次"
],
"ans": 0,
"exp": "<code>const</code> Widget 在编译期就确定下来,父组件的 <code>build</code> 重新执行时会直接复用同一个常量实例,不必重新创建,是 Flutter 里最廉价的性能优化,所以 lint 会主动提示你加上。"
},
{
"id": 195,
"lv": 1,
"q": "改完页面样式想立刻在模拟器上看到效果,并且保留当前页面已经填好的表单内容,应该用?",
"opts": [
"热重载 Hot Reload(按 r",
"热重启 Hot Restart(按 R",
"停止调试后重新执行 flutter run",
"先 flutter clean 再重新编译"
],
"ans": 0,
"exp": "Hot Reload 只把改动注入正在运行的应用并重建 Widget 树,<b>会保留 State</b>Hot Restart 会重新执行 <code>main()</code>,所有状态清零。改 UI 用前者,改 <code>main()</code>、全局变量或依赖时才需要后者。"
},
{
"id": 196,
"lv": 2,
"q": "想让一段文字四周都留出 16 像素的<b>内</b>边距,正确写法是?",
"opts": [
"Padding(padding: EdgeInsets.all(16), child: Text('内容'))",
"Container(margin: EdgeInsets.all(16), child: Text('内容'))",
"SizedBox(width: 16, height: 16, child: Text('内容'))",
"Text('内容', textAlign: TextAlign.center)"
],
"ans": 0,
"exp": "内边距用 <code>Padding</code> 搭配 <code>EdgeInsets</code>。<code>EdgeInsets.all(16)</code> 四边相同,<code>EdgeInsets.symmetric(horizontal: 16, vertical: 8)</code> 分别设置左右和上下,<code>EdgeInsets.only(left: 16)</code> 只设某一边。"
},
{
"id": 197,
"lv": 2,
"q": "<code>Container</code> 的 <b>padding</b> 和 <b>margin</b> 有什么区别?",
"opts": [
"padding 是内容到自身边界的内边距,margin 是自身到外部的外边距",
"两者完全等价,只是写法不同",
"padding 只对文字生效,margin 只对图片生效",
"padding 会撑大父组件,margin 会撑大子组件"
],
"ans": 0,
"exp": "记忆方式:<b>padding 撑开里面,margin 推开外面</b>。给卡片内容留白用 padding,让卡片之间拉开距离用 margin。"
},
{
"id": 198,
"lv": 2,
"q": "只是想在两个按钮之间加 12 像素的垂直间距,最轻量的写法是?",
"opts": [
"SizedBox(height: 12)",
"Container(height: 12, color: Colors.transparent)",
"Padding(padding: EdgeInsets.all(12))",
"Divider(height: 12)"
],
"ans": 0,
"exp": "<code>SizedBox</code> 只占位、不绘制,是做间距的首选;<code>Container</code> 功能多也更重;<code>Divider</code> 会额外画出一条分割线,语义不同。"
},
{
"id": 199,
"lv": 2,
"q": "页面上要展示一排数量不固定的标签,希望一行放不下时自动折到下一行,应该用?",
"opts": [
"Wrap",
"Row",
"Column",
"Expanded"
],
"ans": 0,
"exp": "<code>Row</code> 是单行布局,放不下就会溢出报警告;<code>Wrap</code> 在主轴空间不足时自动换行,再配 <code>spacing</code>(同行间距)和 <code>runSpacing</code>(行与行间距)即可。"
},
{
"id": 200,
"lv": 2,
"q": "页面顶部内容被状态栏/刘海挡住、底部被手势条挡住,最稳妥的处理是?",
"opts": [
"在最外层套一个 SafeArea",
"给最外层写死 EdgeInsets.only(top: 44)",
"把页面改成 StatefulWidget",
"用 Center 把内容整体居中"
],
"ans": 0,
"exp": "<code>SafeArea</code> 会读取系统的安全区信息,自动补上合适的内边距,各机型自适应;写死 44 像素换一台设备就会错位。"
},
{
"id": 201,
"lv": 3,
"q": "页面上放一个“提交”按钮并响应点击,标准写法是?",
"opts": [
"ElevatedButton(onPressed: () { ... }, child: Text('提交'))",
"ElevatedButton(child: Text('提交'), onClick: () { ... })",
"Button(text: '提交', onTap: () { ... })",
"ElevatedButton(onPressed: '提交')"
],
"ans": 0,
"exp": "Flutter 按钮的点击回调统一叫 <code>onPressed</code>,接收一个无参函数。常用的三种:<code>ElevatedButton</code>(填充)、<code>TextButton</code>(纯文字)、<code>OutlinedButton</code>(描边)。"
},
{
"id": 202,
"lv": 3,
"q": "表单没填完时,希望“提交”按钮自动变灰且点不动,最简单的做法是?",
"opts": [
"把 onPressed 设为 null",
"把 child 设为 null",
"用 Visibility 把按钮整个隐藏",
"外面包一层 IgnorePointer 并手动改颜色"
],
"ans": 0,
"exp": "Flutter 按钮的约定是:<code>onPressed == null</code> 即为禁用状态,会自动切换成灰色的 disabled 样式。所以常写成 <code>onPressed: canSubmit ? _submit : null</code>。"
},
{
"id": 203,
"lv": 3,
"q": "要做“左边头像、中间标题加副标题、右边一个箭头”的列表行,最省事的组件是?",
"opts": [
"ListTile",
"Row",
"Card",
"Column"
],
"ans": 0,
"exp": "<code>ListTile</code> 直接提供 <code>leading</code>、<code>title</code>、<code>subtitle</code>、<code>trailing</code> 四个插槽,还内置了 <code>onTap</code> 点击和标准的高度与间距,省去手写 Row/Column 对齐。"
},
{
"id": 204,
"lv": 3,
"q": "想在搜索框前面放一个放大镜图标,写法是?",
"opts": [
"Icon(Icons.search)",
"Image(Icons.search)",
"Text(Icons.search)",
"IconData('search')"
],
"ans": 0,
"exp": "<code>Icon</code> 负责渲染,<code>Icons</code> 是 Material 内置的图标常量集合(<code>Icons.search</code>、<code>Icons.add</code>…)。如果图标本身要能点击,用 <code>IconButton</code>。"
},
{
"id": 205,
"lv": 3,
"q": "商品封面图要显示成圆角矩形,标准做法是?",
"opts": [
"用 ClipRRect(borderRadius: BorderRadius.circular(8)) 把图片包起来",
"给 Image 设置 borderRadius 参数",
"把 Image 的 fit 设为 BoxFit.cover",
"用 Opacity 把四个角变透明"
],
"ans": 0,
"exp": "<code>Image</code> 本身没有圆角参数,需要靠外层裁剪:圆角矩形用 <code>ClipRRect</code>,正圆用 <code>ClipOval</code> 或 <code>CircleAvatar</code>;也可以用 <code>Container</code> 的 <code>decoration: BoxDecoration(borderRadius: ...)</code> 实现。"
},
{
"id": 206,
"lv": 4,
"q": "把设计给的 logo.png 放进项目,想用 <code>Image.asset('assets/logo.png')</code> 显示,还必须做的一步是?",
"opts": [
"在 pubspec.yaml 的 flutter -&gt; assets 下声明这个路径",
"在 main.dart 里 import 这张图片",
"把图片改名成 logo.dart",
"在 AndroidManifest.xml 里注册这张图片"
],
"ans": 0,
"exp": "本地资源必须先在 <code>pubspec.yaml</code> 里声明才会被打包,在 <code>flutter:</code> 节点下写 <code>assets:</code> 再列出 <code>- assets/logo.png</code>;只写目录 <code>- assets/</code> 可以一次声明整个文件夹。注意改完 pubspec 要重新运行,Hot Reload 不会重新打包资源。"
},
{
"id": 207,
"lv": 4,
"q": "<code>Image.asset</code> 和 <code>Image.network</code> 的区别是?",
"opts": [
"asset 加载随 App 打包的本地资源,network 通过 URL 加载网络图片",
"asset 加载网络图片,network 加载本地图片",
"两者完全一样,只是命名习惯不同",
"asset 只能加载 pngnetwork 只能加载 jpg"
],
"ans": 0,
"exp": "<code>Image.asset('assets/logo.png')</code> 读取打包进 App 的本地资源;<code>Image.network('https://...')</code> 运行时才下载,因此通常还要配 <code>loadingBuilder</code> 做占位、<code>errorBuilder</code> 做兜底。"
},
{
"id": 208,
"lv": 4,
"q": "要把一段文字设置成 18 号、加粗、灰色,正确写法是?",
"opts": [
"Text('标题', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.grey))",
"Text('标题', fontSize: 18, bold: true, color: Colors.grey)",
"Text('标题', css: 'font-size:18px;font-weight:bold')",
"TextStyle('标题', size: 18, bold: true)"
],
"ans": 0,
"exp": "文字样式统一放在 <code>Text</code> 的 <code>style</code> 参数里,类型是 <code>TextStyle</code>。想在主题字号的基础上微调,用 <code>Theme.of(context).textTheme.bodyLarge?.copyWith(color: Colors.grey)</code>。"
},
{
"id": 209,
"lv": 4,
"q": "项目里多个页面都要用同一个主色调,最推荐的取色方式是?",
"opts": [
"Theme.of(context).colorScheme.primary",
"每个页面各自写死 Color(0xFF3D6B5E)",
"定义一个全局可变变量,谁都能改",
"每次从设计稿图片里取色"
],
"ans": 0,
"exp": "颜色、字体等统一配置在 <code>MaterialApp(theme: ThemeData(...))</code>,页面里通过 <code>Theme.of(context)</code> 取用。这样换肤、适配深色模式只需要改一处。顺带一提,Flutter 的颜色是 8 位 ARGB<code>0xFF</code> 开头才是不透明。"
},
{
"id": 210,
"lv": 4,
"q": "要让卡片宽度占屏幕宽度的 80%,获取屏幕宽度的写法是?",
"opts": [
"MediaQuery.of(context).size.width",
"context.width",
"Scaffold.of(context).width",
"Window.screenWidth"
],
"ans": 0,
"exp": "<code>MediaQuery</code> 提供屏幕尺寸、状态栏高度、字体缩放比等信息,依赖 <code>context</code>,只能在 <code>build</code> 里或拿到 context 之后使用。如果想按<b>父容器</b>而不是屏幕的尺寸来布局,用 <code>LayoutBuilder</code>。"
},
{
"id": 211,
"lv": 5,
"q": "想让整张商品卡片都能点击,并且点下去有 Material 水波纹反馈,应该用?",
"opts": [
"InkWell",
"GestureDetector",
"Container",
"AbsorbPointer"
],
"ans": 0,
"exp": "<code>GestureDetector</code> 也能响应点击,但没有任何视觉反馈;<code>InkWell</code> 会在点击时绘制水波纹,更符合 Material 交互习惯。注意水波纹画在 <code>Material</code> 上,所以 InkWell 上层要有 Material 背景才看得见。"
},
{
"id": 212,
"lv": 5,
"q": "保存成功后,想在页面底部弹出一条“保存成功”的短提示,写法是?",
"opts": [
"ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('保存成功')))",
"showDialog(context: context, builder: ...)",
"print('保存成功')",
"Navigator.push 跳到一个提示页"
],
"ans": 0,
"exp": "底部短提示用 <code>SnackBar</code>,通过 <code>ScaffoldMessenger.of(context)</code> 弹出(旧写法 <code>Scaffold.of(context).showSnackBar</code> 已废弃)。它会自动消失,不打断用户当前操作。"
},
{
"id": 213,
"lv": 5,
"q": "点“删除”后要弹确认框,并根据用户点的是“确定”还是“取消”决定是否真的删除,正确写法是?",
"opts": [
"final ok = await showDialog&lt;bool&gt;(...); if (ok == true) { 执行删除 }",
"showDialog(...); 紧接着直接执行删除",
"用 SnackBar 代替确认框",
"在 build 方法里判断用户点了什么"
],
"ans": 0,
"exp": "<code>showDialog</code> 返回一个 <code>Future</code>,对话框里用 <code>Navigator.pop(context, true)</code> 把结果带回来,外面 <code>await</code> 拿到后再决定后续动作。注意用户点遮罩关闭时结果是 <code>null</code>,所以要判断 <code>== true</code> 而不是直接当布尔用。"
},
{
"id": 214,
"lv": 5,
"q": "点“选择城市”后,希望从屏幕底部滑出一个选项面板,应该用?",
"opts": [
"showModalBottomSheet",
"showDialog",
"Navigator.pop",
"BottomNavigationBar"
],
"ans": 0,
"exp": "<code>showModalBottomSheet</code> 从底部滑出模态面板,同样返回 <code>Future</code>,面板里用 <code>Navigator.pop(context, value)</code> 回传用户的选择;<code>BottomNavigationBar</code> 是常驻的底部标签栏,用途完全不同。"
},
{
"id": 215,
"lv": 5,
"q": "App 底部要有“首页 / 分类 / 我的”三个标签来回切换,正确做法是?",
"opts": [
"Scaffold 的 bottomNavigationBar 放 BottomNavigationBar,用 currentIndex 记录选中项,onTap 里 setState",
"每点一个标签就 Navigator.push 一个新页面",
"用三个 Column 上下排列,点谁显示谁",
"在 MaterialApp 里配置三个 home"
],
"ans": 0,
"exp": "标签栏切换的是同一个页面里的内容,用 <code>currentIndex</code> + <code>onTap</code> 改状态即可;如果每次都 <code>push</code> 新页面,返回栈会越堆越深。希望切回来时保留各标签的滚动位置,可以配合 <code>IndexedStack</code>。"
},
{
"id": 216,
"lv": 6,
"q": "登录表单要一次性校验“手机号”和“密码”两个输入框,官方推荐做法是?",
"opts": [
"用 Form 包住输入框,配一个 GlobalKey&lt;FormState&gt;,提交时调用 _formKey.currentState!.validate()",
"在每个 TextField 的 onChanged 里手写 if 判断",
"提交时挨个读 controller.text 用 if 判断",
"前端不校验,全部交给服务端返回错误"
],
"ans": 0,
"exp": "<code>Form</code> + <code>GlobalKey&lt;FormState&gt;</code> 是官方表单方案:<code>validate()</code> 会依次触发每个 <code>TextFormField</code> 的 <code>validator</code><b>全部通过才返回 true</b>,并自动把错误文案显示在对应输入框下方。"
},
{
"id": 217,
"lv": 6,
"q": "<code>TextFormField</code> 的 <code>validator</code> 回调中,返回 <code>null</code> 表示什么?",
"opts": [
"校验通过,不显示任何错误提示",
"校验失败,但不提示用户",
"清空输入框里的内容",
"该字段不参与校验"
],
"ans": 0,
"exp": "<code>validator</code> 的约定是:返回 <code>null</code> 代表通过;返回一个字符串代表失败,且这个字符串就是显示在输入框下方的错误文案。例如 <code>validator: (v) =&gt; (v == null || v.isEmpty) ? '请输入手机号' : null</code>。"
},
{
"id": 218,
"lv": 6,
"q": "进入页面时要立刻发一次网络请求拿数据,这段代码应该写在 State 的哪个方法里?",
"opts": [
"initState()",
"build()",
"dispose()",
"setState()"
],
"ans": 0,
"exp": "<code>initState()</code> 在 State 创建后只执行一次,适合做初始化:发首次请求、创建 Controller、注册监听。<code>build()</code> 可能被调用很多次,把请求写在里面会反复触发;<code>dispose()</code> 是销毁时做清理的地方。"
},
{
"id": 219,
"lv": 6,
"q": "页面数据来自一次异步请求,要根据“加载中 / 成功 / 失败”分别渲染不同界面,最省事的组件是?",
"opts": [
"FutureBuilder",
"ListView.builder",
"StatelessWidget",
"AnimatedBuilder"
],
"ans": 0,
"exp": "<code>FutureBuilder</code> 接收一个 <code>Future</code>,在 <code>builder</code> 里根据 <code>snapshot.connectionState</code> 和 <code>snapshot.hasError</code> 分支渲染,不用自己维护一堆 isLoading 字段。<b>坑</b>:不要把 <code>future: fetchData()</code> 直接写在 build 里,那样每次重建都会重新请求,应在 <code>initState</code> 里先创建好再传进去。持续变化的数据流则用 <code>StreamBuilder</code>。"
},
{
"id": 220,
"lv": 6,
"q": "异步请求 <code>await</code> 回来后要调用 <code>setState</code>,为什么通常先判断一下 <code>mounted</code>",
"opts": [
"请求返回时页面可能已被关闭,对已销毁的 State 调用 setState 会报错",
"mounted 是用来判断网络是否连通的",
"不判断的话请求会被系统取消",
"判断 mounted 能让请求返回得更快"
],
"ans": 0,
"exp": "<code>await</code> 期间用户可能已经返回上一页,State 被销毁。此时再 <code>setState</code> 会抛出 setState() called after dispose()。标准写法是拿到结果后先 <code>if (!mounted) return;</code>,再更新状态。"
}
],
"lvNames": [
"项目与骨架",
"布局与间距",
"常用组件",
"资源与样式",
"交互与反馈",
"表单与异步"
]
}
],
"nextSetId": 13,
"nextQId": 221
}