Flutter开发入门——Widget和常用组件

1.什么是Widget?

在Flutter中几乎所有的对象都是一个 widget 。与原生开发中"控件"不同的是,Flutter 中的 widget 的概念更广泛,它不仅可以表示UI元素,也可以表示一些功能性的组件如:用于手势检测的 GestureDetector 、用于APP主题数据传递的 Theme 等等,而原生开发中的控件通常只是指UI元素。可以说,万物皆可Widget。

2.StatelessWidget

StatelessWidget用于不需要维护状态的场景,它通常在build方法中通过嵌套其他 widget 来构建UI,在构建过程中会递归的构建其嵌套的 widget 。

例如启屏页面,有时候可能只是一个展示,就进入下一个流程了。

3.StatefulWidget

和StatelessWidget一样,StatefulWidget也是继承自widget类,并重写了createElement()方法,不同的是返回的Element 对象并不相同;另外StatefulWidget类中添加了一个新的接口createState()。

createState() 用于创建和 StatefulWidget 相关的状态,它在StatefulWidget 的生命周期中可能会被多次调用。例如,当一个 StatefulWidget 同时插入到 widget 树的多个位置时,Flutter 框架就会调用该方法为每一个位置生成一个独立的State实例,其实,本质上就是一个StatefulElement对应一个State实例。

4.State

一个 StatefulWidget 类会对应一个 State 类,State表示与其对应的 StatefulWidget 要维护的状态,State 中的保存的状态信息可以:

  1. 在 widget 构建时可以被同步读取。
  2. 在 widget 生命周期中可以被改变,当State被改变时,可以手动调用其setState()方法通知Flutter 框架状态发生改变,Flutter 框架在收到消息后,会重新调用其build方法重新构建 widget 树,从而达到更新UI的目的。

State 中有两个常用属性:

  1. widget,它表示与该 State 实例关联的 widget 实例,由Flutter 框架动态设置。注意,这种关联并非永久的,因为在应用生命周期中,UI树上的某一个节点的 widget 实例在重新构建时可能会变化,但State实例只会在第一次插入到树中时被创建,当在重新构建时,如果 widget 被修改了,Flutter 框架会动态设置State. widget 为新的 widget 实例。

  2. context。StatefulWidget对应的 BuildContext,作用同StatelessWidget 的BuildContext。

4.1 State生命周期

一个例子来说明

Dart 复制代码
class _CounterWidgetState extends State<CounterWidget> {
  int _counter = 0;

  @override
  void initState() {
    super.initState();
    //初始化状态
    _counter = widget.initValue;
    print("initState");
  }

  @override
  Widget build(BuildContext context) {
    print("build");
    return Scaffold(
      body: Center(
        child: TextButton(
          child: Text('$_counter'),
          //点击后计数器自增
          onPressed: () => setState(
            () => ++_counter,
          ),
        ),
      ),
    );
  }

  @override
  void didUpdateWidget(CounterWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("didUpdateWidget ");
  }

  @override
  void deactivate() {
    super.deactivate();
    print("deactivate");
  }

  @override
  void dispose() {
    super.dispose();
    print("dispose");
  }

  @override
  void reassemble() {
    super.reassemble();
    print("reassemble");
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print("didChangeDependencies");
  }
}

由A页面路由到B界面

会依次调用

复制代码
I/flutter ( 5436): initState
I/flutter ( 5436): didChangeDependencies
I/flutter ( 5436): build

各个方法的作用

  • initState :当 widget 第一次插入到 widget 树时会被调用,对于每一个State对象,Flutter 框架只会调用一次该回调,所以,通常在该回调中做一些一次性的操作,如状态初始化、订阅子树的事件通知等。

  • didChangeDependencies():当State对象的依赖发生变化时会被调用;

  • build():它主要是用于构建 widget 子树的,会在如下场景被调用:

    1. 在调用initState()之后。
    2. 在调用didUpdateWidget()之后。
    3. 在调用setState()之后。
    4. 在调用didChangeDependencies()之后。
    5. 在State对象从树中一个位置移除后(会调用deactivate)又重新插入到树的其他位置之后。
  • reassemble():此回调是专门为了开发调试而提供的,在热重载(hot reload)时会被调用,此回调在Release模式下永远不会被调用。

  • didUpdateWidget () :在 widget 重新构建时,Flutter 框架会调用widget.canUpdate来检测 widget 树中同一位置的新旧节点,然后决定是否需要更新,如果widget.canUpdate返回true则会调用此回调。正如之前所述,widget.canUpdate会在新旧 widget的 key 和 runtimeType 同时相等时会返回true,也就是说在在新旧 widget 的key和runtimeType同时相等时didUpdateWidget()就会被调用。

  • deactivate() :当 State 对象从树中被移除时,会调用此回调。在一些场景下,Flutter 框架会将 State 对象重新插到树中,如包含此 State 对象的子树在树的一个位置移动到另一个位置时(可以通过GlobalKey 来实现)。如果移除后没有重新插入到树中则紧接着会调用dispose()方法。

  • **dispose():**当 State 对象从树中被永久移除时调用;通常在此回调中释放资源。

4.2 在 widget 树中获取State对象

Flutter有一种通用的获取State对象的方法------通过GlobalKey来获取! 步骤分两步:

  1. 给目标StatefulWidget添加GlobalKey。

    Dart 复制代码
    //定义一个globalKey, 由于GlobalKey要保持全局唯一性,我们使用静态变量存储
    static GlobalKey<ScaffoldState> _globalKey= GlobalKey();
    ...
    Scaffold(
        key: _globalKey , //设置key
        ...  
    )

2.通过GlobalKey来获取State对象

Dart 复制代码
_globalKey.currentState.openDrawer()

GlobalKey 是 Flutter 提供的一种在整个 App 中引用 element 的机制。如果一个 widget 设置了GlobalKey,那么我们便可以通过globalKey.currentWidget获得该 widget 对象、globalKey.currentElement来获得 widget 对应的element对象,如果当前 widget 是StatefulWidget,则可以通过globalKey.currentState来获得该 widget 对应的state对象。

5.Flutter里的基础组件

基础组件
  • Text:该组件可让您创建一个带格式的文本。
  • Row,Column: 这些具有弹性空间的布局类 widget 可让您在水平(Row)和垂直(Column)方向上创建灵活的布局。其设计是基于 Web 开发中的 Flexbox 布局模型。
  • Stack: 取代线性布局 (译者语:和 Android 中的FrameLayout相似),`Stack`允许子 widget 堆叠, 你可以使用 Positioned来定位他们相对于Stack的上下左右四条边的位置。Stacks是基于Web开发中的绝对定位(absolute positioning )布局模型设计的。
  • Container: 可让您创建矩形视觉元素。Container 可以装饰一个BoxDecoration, 如 background、一个边框、或者一个阴影。 Container也可以具有边距(margins)、填充(padding)和应用于其大小的约束(constraints)。另外, Container可以使用矩阵在三维空间中对其进行变换。
Material组件

Flutter 提供了一套丰富 的Material 组件,它可以帮助我们构建遵循 Material Design 设计规范的应用程序。Material 应用程序以MaterialApp组件开始, 该组件在应用程序的根部创建了一些必要的组件,比如Theme组件,它用于配置应用的主题。 是否使用MaterialApp完全是可选的,但是使用它是一个很好的做法。

常用的组件

PreferredSize

它的作用是指定一个widget的首选尺寸。

在Flutter中,PreferredSize通常用于定制AppBar的尺寸。AppBar是一个常用的顶部导航栏组件,它通常包含一个标题、一些操作按钮和其他自定义的内容。PreferredSize可以用来指定AppBar的高度和宽度,以确保它在UI中占据所需的空间。

例如:

Dart 复制代码
PreferredSize(
  preferredSize: Size.fromHeight(100),
  child: AppBar(
    title: Text('My App'),
    // 其他AppBar的属性和内容
  ),
)

AutoSizeText

会根据可用空间自动调整文本的大小,以确保文本在给定空间内完整显示,并且尽可能大而清晰可读。

Dart 复制代码
AutoSizeText(
  'This is a long text that will automatically adjust its size to fit the available space.',
  style: TextStyle(fontSize: 20),
  minFontSize: 16,
  maxFontSize: 24,
  textAlign: TextAlign.center,
)

SizedBox

SizedBox是Flutter中的一个widget,它可以用来指定一个固定尺寸的盒子。

在Flutter中,SizedBox可以用来创建一个具有指定宽度和高度的盒子,无论其子项的大小如何。它可以用于调整布局中的间距、创建固定大小的容器等。

Center

Center是Flutter中的一个widget,它可以将其子项居中显示在父容器中。

CircularPercentIndicator

依赖:percent_indicator: ^4.2.3

CircularPercentIndicator是一个Flutter库中的widget,它可以用来创建一个圆形的进度指示器,显示一个百分比的完成进度。

TextSpan

TextSpan是Flutter中的一个widget,它可以用来创建富文本(Rich Text)。

ListView

它可以用来创建一个可滚动的列表视图。

在Flutter中,ListView可以用于显示大量的项目,并且可以根据需要进行滚动。它是一种非常常用的布局方式,用于展示列表数据。

EdgeInsets(这个不是组件)

EdgeInsets是Flutter中的一个类,用于定义边距(padding)和边界(margin)。

在Flutter中,EdgeInsets可以用于在布局中设置小部件的边距和边界。它可以应用于各种小部件,例如Container、Padding、ListView等。

EdgeInsets类提供了多个构造函数和属性,用于指定边距和边界的值。一些常用的属性包括:

  • EdgeInsets.all:设置所有边的边距或边界的值。
  • EdgeInsets.symmetric:设置水平和垂直方向上的对称边距或边界的值。
  • EdgeInsets.only:设置特定边的边距或边界的值。
  • EdgeInsets.fromLTRB:设置左、上、右、下边的边距或边界的值。

Scaffold

用于创建具有基本应用程序布局的屏幕。

在Flutter中,Scaffold提供了一个基本的应用程序布局结构,包括应用栏、抽屉菜单、底部导航栏等。它还提供了一些常用的小部件,如FloatingActionButton、SnackBar等,以方便构建应用程序界面。

Dart 复制代码
Scaffold(
  appBar: AppBar(
    title: Text('My App'),
  ),
  body: Center(
    child: Text('Hello, Flutter!'),
  ),
  floatingActionButton: FloatingActionButton(
    onPressed: () {
      // 按钮点击逻辑
    },
    child: Icon(Icons.add),
  ),
)

KeyboardVisibilityBuilder

依赖库:flutter_keyboard_visibility: ^6.0.0

KeyboardVisibilityBuilder是一个Flutter小部件,用于检测键盘的可见性并响应相应的变化。

在Flutter中,当键盘弹出或隐藏时,可以使用KeyboardVisibilityBuilder来监听键盘的可见性,并根据需要执行相应的操作。例如键盘可见时,上滚到页面底部;

Expanded

Expanded是Flutter中的一个小部件,用于在布局中占据可用空间的剩余部分。

在Flutter中,Expanded小部件通常与Row、Column或Flex等父级小部件一起使用,以便将子级小部件扩展以填充可用空间。

例如,水平方向的登录信息输入,前面是账号文本,后面是输入框,我们可以把Expanded把输入框包裹设置属性使得输入框尽可能的宽。

SafeArea

SafeArea是Flutter中的一个小部件,用于确保应用程序内容在屏幕上显示时不会被设备的安全区域(例如刘海屏、圆角屏等)遮挡。

在Flutter中,SafeArea小部件可以包裹其他小部件,以确保它们在屏幕上的可见区域内显示,而不会被设备的安全区域遮挡。

例如我在屏幕底部有一个提交按钮,如果不使用SafeArea包裹,可能会出现当我唤出底部导航栏的时候,导航栏会遮挡按钮,或者我有一个列表数据展示,但是因为手机的原边框屏幕导致我的最上面和最下面的数据展示不完整,也可以使用此方法。

相关推荐
梦想的颜色35 分钟前
【编程实战】AI 时代 APP 开发全栈硬核指南:技术选型 + AI 架构 + 模型落地全维度决策
python·flutter·react native·react.js·ai·桌面应用·milvus
yuanlaile20 小时前
2026年全新Flutter 跨平台开发全栈学习资料整理
flutter·getx·鸿蒙 next·flutter开发鸿蒙·flutter入门实战·flutter全栈
恋猫de小郭1 天前
Genkit Dart 1.0 发布,Flutter 原生的 AI Agent 终于完整了
android·前端·flutter
第10086个小白1 天前
flutter桌面端打包方式(Inno Setup Compiler)版本
前端·flutter
陆断枫2 天前
Flutter 3.47 动画引擎盘点:Impeller 全平台,和那些"新特性"
flutter
程序员老刘2 天前
Flet 1.0 思路很好,可惜来晚了
flutter·ai编程·客户端
m0_738185822 天前
Flutter 鸿蒙化实战:qr_code_scanner_plus 适配 OpenHarmony,二维码扫描
flutter·华为·harmonyos·鸿蒙
恋猫de小郭2 天前
Flutter + EmbeddingGemma 2,谷歌发布完全端侧的 AI Edge Foresight
android·前端·flutter
君赏3 天前
基于 FlutterPatch 快速接入 Flutter 热更新
flutter
律宏阔3 天前
Dart FFI 内存管理:用 using + Arena 替代嵌套 try-finally
前端·flutter