SMARTWATCH 学习手册有 C 基础 · 嵌入式从零开始
支线三 / Dart 与 Flutter App / 02

第一个页面:代码怎样变成文字与按钮

只摆出电量读数和一个按钮,沿着入口、页面对象和布局读代码。先有能看见的结果,再解释框架调用关系。

接续上一篇:Dart 语法入门:从 C 迁移到新语言

1 · 从 main 走到页面

独立练习工程 lib/main.dart 的完整内容

import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(
    home: Scaffold(body: Center(child: Text('4000 mV'))),
  ));
}

import 引入框架的界面组件。runApp 把根组件交给 Flutter,MaterialApp 提供应用的基本环境,home 指定首页;Scaffold 提供页面结构;Center 把子组件放在中央;Text 描述要显示的文字。层层括号对应对象包含关系,先从最外层向内读。

Widget 通常译为组件,用来描述界面的一部分。创建 Text 对象没有直接操作屏幕像素,框架会根据这些描述安排布局和绘制。对象嵌套形成组件树,和前面看到的函数调用栈是不同关系。

2 · 把页面命名,才便于继续增加内容

定义放在 main() 外;home 改成 const BatteryPage()

class BatteryPage extends StatelessWidget {
  const BatteryPage({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(child: Text("4000 mV")),
    );
  }
}

StatelessWidget 表示这个组件自身不保存会变化的状态。build 接收框架提供的 BuildContext,返回界面描述;context 用于查找位置相关的主题、导航等环境,本篇不用主动操作。key 用来帮助框架辨认组件,这里沿用父类的可选参数。

框架会在需要时调用 build,不要自己写 while(1) 不断调用。build 可能多次发生,因此不能把开始蓝牙扫描、增加监听、发送设置等操作顺手放在里面。

3 · 怎样把两项内容排在一起

替换 Scaffold 的 body 参数

body: Padding(
  padding: const EdgeInsets.all(24),
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      const Text("4000 mV"),
      const SizedBox(height: 16),
      ElevatedButton(onPressed: () {}, child: const Text("Refresh")),
    ],
  ),
),

Padding 留出内边距,Column 让 children 列表沿竖直方向排列,Row 则沿水平方向排列。SizedBox 留出明确的间隔。crossAxisAlignment.start 让这一列的内容靠横向起点对齐。按钮的空回调暂时什么都不做。

父组件会给子组件尺寸约束,子组件在约束中决定尺寸。出现黄色溢出提示时,要先看内容是否超过可用宽高;增加 Row 或固定巨大宽度并不会自动解决。内容较长时再引入滚动组件。

4 · 布局变化与代码位置怎样对应

每个层级只承担一项可观察的布局职责。
每个层级只承担一项可观察的布局职责。 · 教学示意

先修改边距、文字、间隔,观察每个参数改变哪一块。再把两个读数放进 Row,故意放很长的文字看约束错误。阅读项目主页面时,先找到 Scaffold、导航与主要内容容器,暂时不要展开所有装饰属性。

5 · 什么时候才需要拆组件

一张简单页面先直接写清楚。当同一种读数卡片出现多次,或 build 已难以辨认几个区域时,再把那一块提取为独立组件,传入标题、数值和单位。不要在只有一个 Text 时就建立通用卡片系统。

动手观察

让页面同时显示电压和百分比;用 Column 和 Row 各排一次。指出文字来自哪个参数、按钮的回调在哪里。下一篇让读数真正随点击变化。

资料与代码